diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/CREDITS linux.19p1/CREDITS
--- linux.vanilla/CREDITS	Mon Dec 11 22:12:31 2000
+++ linux.19p1/CREDITS	Wed Dec 13 21:50:25 2000
@@ -23,6 +23,12 @@
 S: University of Limerick
 S: Ireland
 
+N: Tigran Aivazian
+E: tigran@veritas.com
+W: http://www.moses.uklinux.net/patches
+D: Intel IA32 microcode update driver
+S: United Kingdom
+
 N: Werner Almesberger
 E: werner.almesberger@lrc.di.epfl.ch
 D: dosfs, LILO, some fd features, various other hacks here and there
@@ -1333,6 +1339,13 @@
 D: Linux/MIPS Deskstation support, Provided OI/OB for Linux
 S: 8786 Niwot Road
 S: Niwot, Colorado 80503
+S: USA
+
+N: Robert M. Love
+E: rml@tech9.net
+E: rml@ufl.edu
+D: misc. kernel hacking and debugging
+S: Gainesville, Florida 32608
 S: USA
 
 N: H.J. Lu
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/Changes linux.19p1/Documentation/Changes
--- linux.vanilla/Documentation/Changes	Mon Dec 11 22:12:31 2000
+++ linux.19p1/Documentation/Changes	Wed Dec 13 21:50:25 2000
@@ -517,6 +517,31 @@
 Older isdn4k-utils versions don't support EXTRAVERSION into kernel version
 string. A upgrade to isdn4k-utils.v3.1beta7 or later is recomented.
 
+Intel IA32 microcode
+====================
+
+A driver has been added to allow updating of Intel IA32 microcode,
+accessible as a misc character device.  If you have not done so already
+you might want to:
+
+mkdir /dev/cpu
+mknod /dev/cpu/microcode c 10 184
+chmod 0644 /dev/cpu/microcode
+
+as root before you can use this.  You'll probably also want to
+get the user-space microcode_ctl utility to use with this.  The utility is
+available at:
+
+http://www.urbanmyth.org/microcode
+
+If you have compiled the driver as a module you may need to add
+the following line:
+
+alias char-major-10-184 microcode
+
+to your /etc/modules.conf file.
+
+
 Where to get the files
 **********************
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/Configure.help linux.19p1/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Mon Dec 11 22:12:31 2000
+++ linux.19p1/Documentation/Configure.help	Wed Dec 13 22:54:22 2000
@@ -1765,7 +1765,7 @@
    - "586" for generic Pentium CPUs, possibly lacking the TSC 
      (time stamp counter) register.
    - "Pentium" for the Intel Pentium/Pentium MMX, AMD K5, K6 and 
-     K6-3D.
+     K6-3D, as well as the Cyrix/VIA CyrixIII
    - "PPro" for the Cyrix/IBM/National Semiconductor 6x86MX, MII and
      Intel Pentium II/Pentium Pro.
 
@@ -2233,10 +2233,18 @@
   The module will be called parport.o. If you have more than one
   parallel port and want to specify which port and IRQ to be used by
   this driver at module load time, read
-  Documentation/networking/net-modules.txt.
+  Documentation/parport.txt.
 
   If unsure, say Y.
 
+IEEE1284 transfer modes
+CONFIG_PARPORT_1284
+  If you have a printer that supports status readback or device ID, or
+  want to use a device that uses enhanced parallel port transfer modes
+  such as EPP, you can say Y here to enable advanced IEEE 1284
+  transfer modes. Also say Y if you want device ID information to
+  appear in /proc/parport/*/autoprobe*. It is safe to say N.
+ 
 PC-style hardware 
 CONFIG_PARPORT_PC
   You should say Y here if you have a PC-style parallel port. All IBM
@@ -3771,6 +3779,24 @@
   module, say M here and read Documentation/modules.txt and
   Documentation/scsi.txt .
 
+OnStream SC-x0 SCSI tape support
+CONFIG_CHR_DEV_OSST
+  The OnStream SC-x0 SCSI tape drives can not be driven by the 
+  standard st driver, but instead need this special osst driver and 
+  use the  /dev/osstX char device nodes (major 206).
+  For more information, you may have a look at the SCSI-HOWTO
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO  and
+  drivers/scsi/README.osst  in the kernel source. 
+  Most info may be found on http://linux1.onstream.nl/test/
+  Please also have a look at the standard st docu, as most of it
+  applies to osst as well.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called osst.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt and
+  Documentation/scsi.txt .
+
 SCSI CDROM support
 CONFIG_BLK_DEV_SR
   If you want to use a SCSI CDROM under Linux, say Y and read the
@@ -6790,6 +6816,16 @@
   Documentation/networking/net-modules.txt. The module will be called
   apricot.o.
 
+LP486E on board Ethernet
+CONFIG_LP486E
+  If you have an Intel Panther motherboard with on board 82596 network
+  (Ethernet) controller, say Y here.
+  The initialization code tries to guess the ethernet address by looking
+  at the EISA configuration area. Probably it finds precisely one thing
+  that looks like an address, but if this fails use the command
+    ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6
+  to assign the address (revealed by the BIOS Setup) by hand.
+
 Generic DECchip & DIGITAL EtherWORKS PCI/EISA
 CONFIG_DE4X5
   This is support for the DIGITAL series of PCI/EISA Ethernet cards.
@@ -8730,6 +8766,12 @@
   partition tables of Macintosh hard drives, and thus use partitions
   on those drives.
 
+Minix subpartition support
+CONFIG_MINIX_SUBPARTITION
+  Minix 2.0.0/2.0.2 subpartition table support for Linux.
+  Say Y here if you want to mount and use Minix 2.0.0/2.0.2
+  subpartitions.
+
 SMB filesystem support (to mount Windows shares etc...)
 CONFIG_SMB_FS
   SMB (Server Message Block) is the protocol Windows for Workgroups
@@ -10411,12 +10453,12 @@
  it as a module.  The module will be called sbc60xxwdt.o.
 
 CONFIG_MICROCODE
-  /dev/cpu/microcode - Intel P6 CPU microcode support
+  /dev/cpu/microcode - Intel IA32 CPU microcode support
 
   If you say Y here you will be able to update the microcode on
-  Intel processors in the P6 family, e.g. Pentium Pro, Pentium II,
-  Pentium III, Xeon etc. You will obviously need the actual microcode
-  binary data itself which is not shipped with the Linux kernel.
+  Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II,
+  Pentium III, Xeon, Pentium 4 etc. You will obviously need the actual
+  microcode binary data itself which is not shipped with the Linux kernel.
  
   For latest news and information on obtaining all the required
   ingredients for this driver, check:
@@ -10425,7 +10467,9 @@
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
   The module will be called microcode.o. If you want to compile it as
-  a module, say M here and read Documentation/modules.txt.
+  a module, say M here and read Documentation/modules.txt. If you use
+  modprobe or kmod you may also want to add the line
+  'alias char-major-10-184 microcode' to your /etc/modules.conf file.
 
 /dev/cpu/*/msr - Model-specific register support
 CONFIG_X86_MSR
@@ -10476,8 +10520,8 @@
 
 Intel 440LX/BX/GX support
 CONFIG_AGP_INTEL
-  This option give you AGP support for the GLX component of the
-  "soon to be released" XFree86-4 on Intel 440LX/BX/GX chipsets.
+  This option gives you AGP support for the GLX component of
+  XFree86 4.x on Intel 440LX/BX/GX, 815, and 840 chipsets.
 
   For the moment, most people should say no, unless you want to
   test the GLX component which can be downloaded from
@@ -10485,9 +10529,9 @@
 
 Intel I810/I810 DC100/I810e support
 CONFIG_AGP_I810
-  This option give you AGP support for the Xserver for the intel
-  810 chipset boards. This is required to do any useful video
-  modes.
+  This option gives you AGP support for XFree86 on the Intel 810
+  and 815 chipsets for their on-board integrated graphics. This
+  is required to do any useful video modes with these boards.
 
 VIA VP3/MVP3/Apollo Pro support
 CONFIG_AGP_VIA
@@ -10976,8 +11020,7 @@
 Support for OPTi MAD16 and/or Mozart based cards
 CONFIG_SOUND_MAD16
   Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi
-  82C928 or 82C929 or 82C931) audio interface chip. For the 82C931,
-  please read drivers/sound/README.C931. These chips are currently
+  82C928 or 82C929 or 82C931) audio interface chip. These chips are 
   quite common so it's possible that many no-name cards have one of
   them. In addition the MAD16 chip is used in some cards made by known
   manufacturers such as Turtle Beach (Tropez), Reveal (some models)
@@ -11560,6 +11603,10 @@
 CONFIG_HISAX_1TR6
   Enable this if you have a old german 1TR6 line.
 
+HiSax Support for US NI1
+CONFIG_HISAX_NI1
+  Enable this if you like to use ISDN in US on a NI1 basic rate interface.
+
 Teles 16.0/8.0
 CONFIG_HISAX_16_0
   This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
@@ -11688,6 +11735,13 @@
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
+NETspider U card
+CONFIG_HISAX_NETJET_U
+  This enables HiSax support for the Netspider U interface ISDN card from
+  Traverse Technologies.
+  See Documentation/isdn/README.HiSax on how to configure it using a
+  different D-channel protocol, or non-standard IRQ/port settings.
+
 Niccy PnP/PCI card
 CONFIG_HISAX_NICCY
   This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI. 
@@ -11800,22 +11854,19 @@
   an ISDN-fax-machine. This must be supported by the lowlevel driver
   also. See Documentation/isdn/README.fax for more information.
 
-AVM CAPI2.0 support
-CONFIG_ISDN_DRV_AVMB1
-  This enables support for the AVM B1/T1 ISDN networking cards.In
-  addition, a CAPI (Common ISDN Application Programming Interface, a
-  standard making it easy for programs to access ISDN hardware, see
-  http://www.capi.org/; to browse the WWW, you need to have access to
-  a machine on the Internet that has a program like lynx or netscape)
-  interface for this card is provided. In order to use this card,
-  additional firmware is necessary, which has to be downloaded into
-  the card using a utility which is distributed separately. Please
-  read the file Documentation/isdn/README.avmb1. 
-  
-  This code is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called avmb1.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt.
+CAPI2.0 support
+CONFIG_ISDN_CAPI
+  This option enables a CAPI2.0 (Common ISDN Application Programming
+  Interface), a standard making it easy for programs to access ISDN
+  hardware, see http://www.capi.org/
+
+CAPI2.0 Middleware support
+CONFIG_ISDN_CAPI_MIDDLEWARE
+  This option enables CAPI2.0 Middleware support
+
+CAPI2.0 filesystem support
+CONFIG_ISDN_CAPIFS
+  This option enables CAPI CAPI2.0 filesystem support 
 
 AVM B1 ISA support
 CONFIG_ISDN_DRV_AVMB1_B1ISA
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/minix-part.txt linux.19p1/Documentation/filesystems/minix-part.txt
--- linux.vanilla/Documentation/filesystems/minix-part.txt	Thu Jan  1 01:00:00 1970
+++ linux.19p1/Documentation/filesystems/minix-part.txt	Wed Dec 13 22:43:46 2000
@@ -0,0 +1,43 @@
+Minix 2.0.0/2.0.2 subpartition support.
+=======================================
+
+  Minix subpartition support is provided in `linux/drivers/block/genhd.c'.
+Minix partitions are shown with the device name suffixed with an `@'
+followed by any detected subpartitions inside angle brackets, like this:
+
+Partition check:
+ hda: hda1 hda2@ < hda5 hda6 > hda3 hda4
+
+Usage
+=====
+
+Add entries to /etc/fstab, change for your setup:
+ /dev/hda5	/mnt/minix	minix	rw,noauto	0	0
+ /dev/hda6	/mnt/minix/usr	minix	rw,noauto	0	0
+
+To mount your Minix filesystems:
+ mount /mnt/minix		# mount /
+ mount /mnt/minix/usr		# mount /usr
+
+
+Note
+====
+
+The first sector of a Minix 2.0.0/2.0.2 partition containing 
+subpartitions looks like the master boot record (MBR) on the first 
+sector of the hard disk. 
+
+It has
+ 1) a small loader program which loads the boot sector from
+          the Minix root partition (usually hd?a).
+ 2) a small partition table which gives the starting 
+	  C/H/S values and sizes for the 4 Minix subpartitions.
+
+If there are no subpartitions then the first sector of the Minix
+partition contains the usual boot sector.
+
+Bug Reports
+===========
+
+Anand Krishnamurthy <anandk@wiproge.med.ge.com>
+Rajeev V. Pillai    <rajeevvp@yahoo.com> 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/ioctl-number.txt linux.19p1/Documentation/ioctl-number.txt
--- linux.vanilla/Documentation/ioctl-number.txt	Mon Dec 11 22:12:32 2000
+++ linux.19p1/Documentation/ioctl-number.txt	Wed Dec 13 21:50:26 2000
@@ -113,6 +113,8 @@
 					<mailto:hdstich@connectu.ulm.circular.de>
 'z'	40-7F	CAN bas card		in development:
 					<mailto:oe@port.de>
+'6'	00-10	<asm-i386/processor.h>	Intel IA32 microcode update driver
+					<mailto:tigran@veritas.com>
 0x89	00-0F	asm-i386/sockios.h
 0x89	10-DF	linux/sockios.h
 0x89	E0-EF	linux/sockios.h		SIOCPROTOPRIVATE range
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.HiSax linux.19p1/Documentation/isdn/README.HiSax
--- linux.vanilla/Documentation/isdn/README.HiSax	Mon Dec 11 22:12:32 2000
+++ linux.19p1/Documentation/isdn/README.HiSax	Wed Dec 13 22:05:54 2000
@@ -53,7 +53,8 @@
 Sedlbauer ISDN-Controller PC/104
 USR Sportster internal TA (compatible Stollmann tina-pp V3)
 ith Kommunikationstechnik GmbH MIC 16 ISA card
-Traverse Technologie NETjet PCI S0 card
+Traverse Technologie NETjet PCI S0 card and NETspider U card
+Ovislink ISDN sc100-p card (NETjet driver)
 Dr. Neuhaus Niccy PnP/PCI
 Siemens I-Surf 1.0
 Siemens I-Surf 2.0 (with IPAC, try type 12 asuscom) 
@@ -134,7 +135,12 @@
 
 The parameter for the D-Channel protocol may be omitted if you selected the
 correct one during kernel config. Valid values are "1" for German 1TR6,
-"2" for EDSS1 (Euro ISDN) and "3" for leased lines (no D-Channel).
+"2" for EDSS1 (Euro ISDN), "3" for leased lines (no D-Channel) and "4"
+for US NI1.
+With US NI1 you have to include your SPID into the MSN setting in the form
+<MSN>:<SPID> for example (your phonenumber is 1234 your SPID 5678):
+AT&E1234:5678                       on ttyI interfaces
+isdnctrl eaz ippp0 1234:5678        on network devices
 
 The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying
 the I/O addresses of the ISAC and HSCX chips, respectively.
@@ -186,8 +192,9 @@
    34	Gazel ISDN cards (PCI)   none
    35	HFC 2BDS0 PCI            none
    36	W6692 based PCI cards    none
-   37   HFC 2BDS0 S+, SP/PCMCIA  irq,io (pcmcia must be set with cardmgr)
-
+   37	HFC 2BDS0 S+, SP         irq,io 
+   38	NETspider U PCI card     none
+   39	HFC 2BDS0 SP/PCMCIA      irq,io (set with cardmgr)
 
 At the moment IRQ sharing is only possible with PCI cards. Please make sure
 that your IRQ is free and enabled for ISA use.
@@ -291,7 +298,9 @@
    34	Gazel ISDN cards (PCI)  no parameter
    35	HFC 2BDS0 PCI           no parameter
    36	W6692 based PCI cards   none
-   37   HFC 2BDS0 S+,SP/PCMCIA  pa=irq, pb=io
+   37	HFC 2BDS0 S+,SP/PCMCIA  ONLY WORKS AS A MODULE !
+   38	NETspider U PCI card    none
+
 
 Running the driver
 ------------------
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/ip_masq/rc.masqfw linux.19p1/Documentation/networking/ip_masq/rc.masqfw
--- linux.vanilla/Documentation/networking/ip_masq/rc.masqfw	Thu Jan  1 01:00:00 1970
+++ linux.19p1/Documentation/networking/ip_masq/rc.masqfw	Wed Dec 13 22:22:39 2000
@@ -0,0 +1,106 @@
+#!/bin/sh
+#
+#	rc.masqfw	v0.2
+#
+#	Author: Juanjo Ciarlante <jjo@mendoza.gov.ar>
+#	03-Oct-00	
+#
+# 	Setup reverse masquerading (ala portfw) with using firewall mark-ing
+#	_AND_ userspace "redir" support for complete redirection (even local
+#	connects).
+#
+# Semantics:
+#	Let ip_masq_mfw intercept truly "forward-able" packets based
+#	on ipchains ruling _AND_ leave the rest for redir tool.
+#
+# Requirement:
+#	- ipmasqadm	http://juanjox.kernelnotes.org/
+#			. setups kernel mark forwarding)
+#	- redir 2.2.x	http://freshmeat.net/search/?q=redir
+#			. setups "local" socket forwarding)
+#	- ip_masq_ftp	PATCHED for firewall marking (module parm "in_mark")
+#			. support internal server forwarding for PASV clients)
+#
+# Setup:
+# EXT_IP, EXT_PORT:	external (visible) IP address (can be '0') and port. 
+# INT_IP, INT_PORT:	internal server address and port
+# IN_MARK:		arbitrary value to use when marking pkts (ipchains -m)
+# FTP:			if not-null activate ftp support
+# REDIR:		path to redir-2.2.x (set to "" to avoid using)
+
+export PATH="/sbin:/usr/sbin:$PATH"
+
+EXT_IP=192.168.2.16
+EXT_PORT=2021
+INT_IP=10.1.1.128
+INT_PORT=21
+IN_MARK=4321
+FTP=1
+REDIR="/usr/sbin/redir"
+
+FW_IPCHAINS="-i eth0 -d $EXT_IP $EXT_PORT -p tcp"
+PID_FILE="/var/run/redir-$EXT_IP-$EXT_PORT"
+
+# seems ascii art... but it runs!!  =)
+#
+run() {
+	$* || {
+		echo "->	'$*'"
+		return 1
+	}
+}
+
+get_pid() {
+	test -f $PID_FILE || return 1
+	typeset pid=`cat $PID_FILE`
+	test -n "$pid" || return 1
+	kill -0 $pid || return 1
+	echo $pid
+}
+exit_err() {
+	ERR=$1;shift
+	echo $@ >&2
+	exit $ERR
+}	
+redir_on() {
+	test -n "$REDIR"  || return 1
+	test -n "$FTP" && REDIR="$REDIR --ftp=both"
+	$SHELL -c 'echo $$ > '"$PID_FILE
+		exec $REDIR \
+		--laddr $EXT_IP --lport $EXT_PORT \
+		--caddr $INT_IP --cport $INT_PORT 
+	" &
+
+}
+redir_off() {
+	test -n "$REDIR"  || return 1
+	if pid=`get_pid`;then
+		kill $pid 
+		rm $PID_FILE
+	fi
+	fuser -k -n tcp $EXT_PORT
+}
+
+case "$1" in
+start)
+	#run modprobe ip_masq_mfw
+	run ipmasqadm mfw -I -m $IN_MARK -r $INT_IP $INT_PORT
+	if test -n "$FTP";then
+		run modprobe ip_masq_ftp in_mark=$IN_MARK || \
+			exit_err 1 "Incorrect ftp module version ?"
+	fi
+	run ipchains -m $IN_MARK -I input $FW_IPCHAINS
+	redir_on
+	;;
+stop)
+	run ipchains -m $IN_MARK -D input $FW_IPCHAINS
+	if test -n "$FTP";then
+		run rmmod ip_masq_ftp
+	fi
+	run ipmasqadm mfw -D -m $IN_MARK -r $INT_IP $INT_PORT
+	#run ipmasqadm mfw -F
+	#run rmmod ip_masq_mfw
+	redir_off
+	;;
+
+esac
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/s390/cds.txt linux.19p1/Documentation/s390/cds.txt
--- linux.vanilla/Documentation/s390/cds.txt	Mon Dec 11 22:12:32 2000
+++ linux.19p1/Documentation/s390/cds.txt	Wed Dec 13 22:43:46 2000
@@ -241,7 +241,7 @@
 
 The get_dev_info_by_irq() / get_dev_info_by_devno() functions return:
 
-      0 - sucessful completion
+      0 - successful completion
 -ENODEV - irq or devno don't specify a known subchannel or device
           number.
 -EINVAL - invalid devinfo value.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/MAINTAINERS linux.19p1/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Mon Dec 11 22:12:32 2000
+++ linux.19p1/MAINTAINERS	Wed Dec 13 21:50:26 2000
@@ -493,6 +493,13 @@
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
+INTEL IA32 MICROCODE UPDATE DRIVER
+P:	Tigran Aivazian
+M:	Tigran Aivazian <tigran@veritas.com>
+W:      http://www.urbanmyth.org/microcode
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
 IP FIREWALL
 P:	Paul Russell
 M:	Paul.Russell@rustcorp.com.au
@@ -723,6 +730,13 @@
 L:	linux-net@vger.kernel.org
 L:	linux-tr@emissary.aus-etc.com
 W:	http://www.linuxtr.net
+S:	Maintained
+
+ONSTREAM SCSI TAPE DRIVER
+P:	Willem Riede
+M:	osst@riede.org
+L:	osst@linux1.onstream.nl
+L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
 
 OPL3-SA2, SA3, and SAx DRIVER
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Makefile linux.19p1/Makefile
--- linux.vanilla/Makefile	Mon Dec 11 22:12:32 2000
+++ linux.19p1/Makefile	Mon Dec 11 22:17:07 2000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 18
-EXTRAVERSION =
+SUBLEVEL = 19
+EXTRAVERSION = pre1
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/boot/setup.S linux.19p1/arch/i386/boot/setup.S
--- linux.vanilla/arch/i386/boot/setup.S	Mon Dec 11 22:12:33 2000
+++ linux.19p1/arch/i386/boot/setup.S	Thu Dec 14 10:25:21 2000
@@ -32,7 +32,7 @@
 !
 !
 ! A20 gating fiddled to work on AMD Elan AmSC4xx series by kira@linuxgrrls.org
-! july 1999
+! july 1999 - from a 2.0 patch by Christian Lademann <cal@zls.de>
 !
 
 #define __ASSEMBLY__
@@ -248,16 +248,46 @@
 
 loader_ok:
 ! Get memory size (extended mem, kB)
-
+	xor     eax, eax
+	mov     [0x1e0], eax
 #ifndef STANDARD_MEMORY_BIOS_CALL
-	push	ebx
+	mov     [0x1e8], al
+meme820:
+	mov     edx, #0x534d4150
+	xor     ebx, ebx
+	mov     di,#0x2d0
+
+jmpe820:
+	mov     eax,#0x0000e820
+	mov     ecx,#20
+	push    ds
+	pop     es
+	int     0x15
+	jc      bail820
 
-        xor     ebx,ebx		! preload new memory slot with 0k
-        mov	[0x1e0], ebx
+	cmp     eax,#0x534d4150
+	jne     bail820
 
-        mov     ax,#0xe801
-	int     0x15
-	jc      oldstylemem
+	cmp     16(di),#1
+	jne     again820
+
+good820:
+	mov     al,[0x1e8]
+	cmp     al,#32
+	jnl     bail820
+
+	incb    [0x1e8]
+	mov     ax,di
+	add     ax,#20
+	mov     di,ax
+again820:
+	cmp     ebx,#0
+	jne     jmpe820
+bail820:
+meme801:
+	mov	ax,#0xe801
+	int	0x15
+	jc	mem88
 
 ! Memory size is in 1 k chunksizes, to avoid confusing loadlin.
 ! We store the 0xe801 memory size in a completely different place,
@@ -266,23 +296,19 @@
 ! alternative new memory detection scheme, and it's sensible
 ! to write everything into the same place.)
 
-	and     ebx, #0xffff    ! clear sign extend
-	shl     ebx, 6          ! and go from 64k to 1k chunks
-	mov     [0x1e0],ebx     ! store extended memory size
-
-	and     eax, #0xffff    ! clear sign extend
- 	add     [0x1e0],eax     ! and add lower memory into total size.
-  
-	! and fall into the old memory detection code to populate the
-	! compatibility slot.
+	and 	ebx, #0xffff	! clear sign extend
+	shl	ebx, 6		! and go from 64k to 1k chunks
+	mov	[0x1e0],ebx	! store extented memory size
+	
+	and	eax, #0xffff	! clear sign extend
+	add	[0x1e0],eax	! and add lower memory into total size. 
 
-oldstylemem:
-	pop	ebx
-#else
-	mov     dword ptr [0x1e0], #0
+	! and fall into the old memory detection to populate the
+	! compatibility slot.	
+mem88:
 #endif
-	mov	ah,#0x88
-	int	0x15
+        mov     ah,#0x88
+        int     0x15
 	mov	[2],ax
 
 ! Set the keyboard repeat rate to the max
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/config.in linux.19p1/arch/i386/config.in
--- linux.vanilla/arch/i386/config.in	Mon Dec 11 22:12:33 2000
+++ linux.19p1/arch/i386/config.in	Wed Dec 13 22:53:39 2000
@@ -12,11 +12,11 @@
 mainmenu_option next_comment
 comment 'Processor type and features'
 choice 'Processor family' \
-	"386			CONFIG_M386	\
-	 486/Cx486		CONFIG_M486	\
-	 586/K5/5x86/6x86	CONFIG_M586	\
-	 Pentium/K6/TSC		CONFIG_M586TSC	\
-	 PPro/6x86MX		CONFIG_M686" PPro
+	"386				CONFIG_M386	\
+	 486/Cx486			CONFIG_M486	\
+	 586/K5/5x86/6x86		CONFIG_M586	\
+	 Pentium/K6/TSC/CyrixIII	CONFIG_M586TSC	\
+	 PPro/6x86MX			CONFIG_M686" PPro
 #
 # Define implied options from the CPU selection here
 #
@@ -33,7 +33,7 @@
   define_bool CONFIG_X86_GOOD_APIC y
 fi
 
-tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE
+tristate '/dev/cpu/microcode - Intel IA32 CPU microcode support' CONFIG_MICROCODE
 tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR
 tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/microcode.c linux.19p1/arch/i386/kernel/microcode.c
--- linux.vanilla/arch/i386/kernel/microcode.c	Mon Dec 11 22:12:34 2000
+++ linux.19p1/arch/i386/kernel/microcode.c	Wed Dec 13 21:50:26 2000
@@ -4,12 +4,13 @@
  *	Copyright (C) 2000 Tigran Aivazian
  *
  *	This driver allows to upgrade microcode on Intel processors
- *	belonging to P6 family - PentiumPro, Pentium II, Pentium III etc.
+ *	belonging to IA32 family - PentiumPro, Pentium II, Pentium III,
+ *	Pentium II Xeon, Pentium III Xeon, Pentium 4 etc.
  *
- *	Reference: Section 8.10 of Volume III, Intel Pentium III Manual, 
- *	Order Number 243192 or download from:
+ *	Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual, 
+ *	Order Number 245472 or free download from:
  *		
- *	http://developer.intel.com/design/pentiumii/manuals/243192.htm
+ *	http://developer.intel.com/design/pentium4/manuals/245472.htm
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -30,6 +31,8 @@
  *		Added MICROCODE_IOCFREE ioctl to clear memory.
  *	1.05	09 June 2000, Simon Trimmer <simon@veritas.com>
  *		Messages for error cases (non intel & no suitable microcode).
+ *	1.06	07 Dec 2000, Tigran Aivazian <tigran@veritas.com>
+ *		Pentium 4 support + backported fixes from 2.4
  */
 
 #include <linux/init.h>
@@ -37,7 +40,6 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
-#include <linux/smp_lock.h>
 #include <linux/miscdevice.h>
 #include <linux/smp.h>
 
@@ -45,45 +47,25 @@
 #include <asm/uaccess.h>
 #include <asm/processor.h>
 
-#define MICROCODE_MINOR 184
-#define MICROCODE_IOCFREE _IO('6',0)
-struct microcode {
-	unsigned int hdrver;
-	unsigned int rev;
-	unsigned int date;
-	unsigned int sig;
-	unsigned int cksum;
-	unsigned int ldrver;
-	unsigned int pf;
-	unsigned int reserved[5];
-	unsigned int bits[500];
-};
-
-#define MICROCODE_VERSION 	"1.05"
+#define MICROCODE_VERSION 	"1.06"
 
-MODULE_DESCRIPTION("Intel CPU (P6) microcode update driver");
+MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver");
 MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>");
 EXPORT_NO_SYMBOLS;
 
 /* VFS interface */
 static int microcode_open(struct inode *, struct file *);
-static int microcode_release(struct inode *, struct file *);
 static ssize_t microcode_read(struct file *, char *, size_t, loff_t *);
 static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *);
 static int microcode_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 
+/* read()/write()/ioctl() are serialized on this */
+static struct semaphore microcode_sem = MUTEX;
 
 /* internal helpers to do the work */
 static int do_microcode_update(void);
 static void do_update_one(void *);
 
-/*
- *  Bits in microcode_status. (31 bits of room for future expansion)
- */
-#define MICROCODE_IS_OPEN	0	/* set if device is in use */
-
-static unsigned long microcode_status;
-
 /* the actual array of microcode blocks, each 2048 bytes */
 static struct microcode *microcode;
 static unsigned int microcode_num;
@@ -95,7 +77,6 @@
 	write:		microcode_write,
 	ioctl:		microcode_ioctl,
 	open:		microcode_open,
-	release:	microcode_release,
 };
 
 static struct miscdevice microcode_dev = {
@@ -114,18 +95,18 @@
 			MICROCODE_MINOR);
 		error = 1;
 	}
-	printk(KERN_INFO "P6 Microcode Update Driver v%s registered\n", 
+	printk(KERN_INFO "IA-32 Microcode Update Driver: v%s <tigran@veritas.com>\n", 
 			MICROCODE_VERSION);
 	return 0;
 }
 
 #ifdef MODULE
-static void microcode_exit(void)
+void cleanup_module(void)
 {
 	misc_deregister(&microcode_dev);
 	if (mc_applied)
 		kfree(mc_applied);
-	printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", 
+	printk(KERN_INFO "IA-32 Microcode Update Driver v%s unregistered\n", 
 			MICROCODE_VERSION);
 }
 
@@ -133,34 +114,14 @@
 {
 	return microcode_init();
 }
-void cleanup_module(void)
-{
-	microcode_exit();
-}
 #endif
 
-/*
- * We enforce only one user at a time here with open/close.
- */
 static int microcode_open(struct inode *inode, struct file *file)
 {
-	if (!capable(CAP_SYS_RAWIO))
-		return -EPERM;
-
-	/* one at a time, please */
-	if (test_and_set_bit(MICROCODE_IS_OPEN, &microcode_status))
-		return -EBUSY;
-
-	return 0;
-}
-
-static int microcode_release(struct inode *inode, struct file *file)
-{
-	clear_bit(MICROCODE_IS_OPEN, &microcode_status);
-	return 0;
+	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
 }
 
-/* a pointer to 'struct update_req' is passed to the IPI handler = do_update_one()
+/*
  * update_req[cpu].err is set to 1 if update failed on 'cpu', 0 otherwise
  * if err==0, microcode[update_req[cpu].slot] points to applied block of microcode
  */
@@ -174,10 +135,10 @@
 	int i, error = 0, err;
 	struct microcode *m;
 
-	if (smp_call_function(do_update_one, (void *)update_req, 1, 1) != 0)
+	if (smp_call_function(do_update_one, NULL, 1, 1) != 0)
 		panic("do_microcode_update(): timed out waiting for other CPUs\n");
 
-	do_update_one((void *)update_req);
+	do_update_one(NULL);
 
 	for (i=0; i<smp_num_cpus; i++) {
 		err = update_req[i].err;
@@ -194,22 +155,23 @@
 {
 	int cpu_num = smp_processor_id();
 	struct cpuinfo_x86 *c = cpu_data + cpu_num;
-	struct update_req *req = (struct update_req *)arg + cpu_num;
+	struct update_req *req = update_req + cpu_num;
 	unsigned int pf = 0, val[2], rev, sig;
 	int i,found=0;
 
 	req->err = 1; /* be pessimistic */
 
-	if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6){
-		printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num );
+	if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
+		test_bit(X86_FEATURE_30, &c->x86_capability)) { /* IA64 */
+		printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num);
 		return;
 	}
 
 	sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8);
 
-	if (c->x86_model >= 5) {
-		/* get processor flags from BBL_CR_OVRD MSR (0x17) */
-		rdmsr(0x17, val[0], val[1]);
+	if ((c->x86_model >= 5) || (c->x86 > 6)) {
+		/* get processor flags from MSR 0x17 */
+		rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
 		pf = 1 << ((val[1] >> 18) & 7);
 	}
 
@@ -219,7 +181,12 @@
 
 			found=1;
 
-			rdmsr(0x8B, val[0], rev);
+			/* trick, to work even if there was no prior update by the BIOS */
+			wrmsr(MSR_IA32_UCODE_REV, 0, 0);
+			__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
+
+			/* get current (on-cpu) revision into rev (ignore val[0]) */
+			rdmsr(MSR_IA32_UCODE_REV, val[0], rev);
 			if (microcode[i].rev < rev) {
 				printk(KERN_ERR 
 					"microcode: CPU%d not 'upgrading' to earlier revision"
@@ -241,10 +208,16 @@
 					break;
 				}
 
-				wrmsr(0x79, (unsigned int)(m->bits), 0);
+				/* write microcode via MSR 0x79 */
+				wrmsr(MSR_IA32_UCODE_WRITE, (unsigned int)(m->bits), 0);
+
+				/* serialize */
 				__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
-				rdmsr(0x8B, val[0], val[1]);
 
+				/* get the current revision from MSR 0x8B */
+				rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
+
+				/* notify the caller of success on this cpu */
 				req->err = 0;
 				req->slot = i;
 				printk(KERN_ERR "microcode: CPU%d updated from revision "
@@ -255,18 +228,23 @@
 		}
 
 	if(!found)
-		printk(KERN_ERR "microcode: found no data for CPU%d (sig=%x, pflags=%d)\n", cpu_num, sig, pf);
+		printk(KERN_ERR "microcode: CPU%d no microcode found! (sig=%x, pflags=%d)\n",
+				cpu_num, sig, pf);
 }
 
 static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos)
 {
 	if (*ppos >= mc_fsize)
 		return 0;
+	down(&microcode_sem);
 	if (*ppos + len > mc_fsize)
 		len = mc_fsize - *ppos;
-	if (copy_to_user(buf, mc_applied + *ppos, len))
+	if (copy_to_user(buf, mc_applied + *ppos, len)) {
+		up(&microcode_sem);
 		return -EFAULT;
+	}
 	*ppos += len;
+	up(&microcode_sem);
 	return len;
 }
 
@@ -279,17 +257,18 @@
 			sizeof(struct microcode));
 		return -EINVAL;
 	}
+	down(&microcode_sem);
 	if (!mc_applied) {
 		mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode),
 				GFP_KERNEL);
 		if (!mc_applied) {
+			up(&microcode_sem);
 			printk(KERN_ERR "microcode: out of memory for saved microcode\n");
 			return -ENOMEM;
 		}
 		memset(mc_applied, 0, mc_fsize);
 	}
 	
-	lock_kernel();
 	microcode_num = len/sizeof(struct microcode);
 	microcode = vmalloc(len);
 	if (!microcode) {
@@ -310,7 +289,7 @@
 out_fsize:
 	vfree(microcode);
 out_unlock:
-	unlock_kernel();
+	up(&microcode_sem);
 	return ret;
 }
 
@@ -319,6 +298,7 @@
 {
 	switch(cmd) {
 		case MICROCODE_IOCFREE:
+			down(&microcode_sem);
 			if (mc_applied) {
 				memset(mc_applied, 0, mc_fsize);
 				kfree(mc_applied);
@@ -326,8 +306,10 @@
 				printk(KERN_WARNING 
 					"microcode: freed %d bytes\n", mc_fsize);
 				mc_fsize = 0;
+				up(&microcode_sem);
 				return 0;
 			}
+			up(&microcode_sem);
 			return -ENODATA;
 
 		default:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/setup.c linux.19p1/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Mon Dec 11 22:12:34 2000
+++ linux.19p1/arch/i386/kernel/setup.c	Thu Dec 14 10:25:21 2000
@@ -73,6 +73,7 @@
 #include <asm/cobalt.h>
 #include <asm/msr.h>
 #include <asm/dma.h>
+#include <asm/e820.h>
 
 /*
  * Machine setup..
@@ -103,6 +104,7 @@
 	unsigned short length;
 	unsigned char table[0];
 };
+struct e820map e820;
 
 unsigned char aux_device_present;
 
@@ -123,6 +125,8 @@
 #define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
 #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
+#define E820_MAP_NR (*(char*) (PARAM+E820NR))
+#define E820_MAP  ((struct e820entry *) (PARAM+E820MAP))
 #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
 #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
 #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
@@ -268,6 +272,87 @@
 					visws_board_rev);
 	}
 #endif
+void __init add_memory_region(unsigned long long start,
+                                  unsigned long long size, int type)
+{
+       int x = e820.nr_map;
+
+       if (x == E820MAX) {
+           printk("Ooops! Too many entries in the memory map!\n");
+           return;
+       }
+
+       e820.map[x].addr = start;
+       e820.map[x].size = size;
+       e820.map[x].type = type;
+       e820.nr_map++;
+} /* add_memory_region */
+
+static int __init copy_e820_map(struct e820entry * biosmap, int nr_map)
+{
+       /* Only one memory region (or negative)? Ignore it */
+       if (nr_map < 2)
+               return -1;
+
+       do {
+               unsigned long long start = biosmap->addr;
+               unsigned long long size = biosmap->size;
+               unsigned long long end = start + size;
+               unsigned long type = biosmap->type;
+
+               /* Overflow in 64 bits? Ignore the memory map. */
+               if (start > end)
+                       return -1;
+
+               /*
+                * Some BIOSes claim RAM in the 640k - 1M region.
+                * Not right. Fix it up.
+                */
+               if (type == E820_RAM) {
+                       if (start < 0x100000ULL && end > 0xA0000ULL) {
+                               if (start < 0xA0000ULL)
+                                       add_memory_region(start, 0xA0000ULL-start, type);
+                               if (end < 0x100000ULL)
+                                       continue;
+                               start = 0x100000ULL;
+                               size = end - start;
+                       }
+               }
+               add_memory_region(start, size, type);
+       } while (biosmap++,--nr_map);
+       return 0;
+}
+
+#define LOWMEMSIZE() (0x9f000)
+
+void __init setup_memory_region(void)
+{
+       char *who = "BIOS-e820";
+
+       /*
+        * Try to copy the BIOS-supplied E820-map.
+        *
+        * Otherwise fake a memory map; one section from 0k->640k,
+        * the next section from 1mb->appropriate_mem_k
+        */
+       if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) {
+               unsigned long mem_size;
+
+               /* compare results from other methods and take the greater */
+               if (ALT_MEM_K < EXT_MEM_K) {
+                       mem_size = EXT_MEM_K;
+                       who = "BIOS-88";
+               } else {
+                       mem_size = ALT_MEM_K;
+                       who = "BIOS-e801";
+               }
+
+               e820.nr_map = 0;
+               add_memory_region(0, LOWMEMSIZE(), E820_RAM);
+               add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM);
+       }
+       printk("BIOS-provided physical RAM map:\n");
+} /* setup_memory_region */
 
 
 static char command_line[COMMAND_LINE_SIZE] = { 0, };
@@ -278,9 +363,11 @@
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
 	unsigned long memory_start, memory_end;
+	unsigned long max_pfn;
 	char c = ' ', *to = command_line, *from = COMMAND_LINE;
 	int len = 0;
 	int read_endbase_from_BIOS = 1;
+	int i;
 
 #ifdef CONFIG_VISWS
 	visws_get_board_type_and_rev();
@@ -297,17 +384,7 @@
 		BIOS_revision = SYS_DESC_TABLE.table[2];
 	}
 	aux_device_present = AUX_DEVICE_INFO;
-	memory_end = (1<<20) + (EXT_MEM_K<<10);
-#ifndef STANDARD_MEMORY_BIOS_CALL
-	{
-		unsigned long memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
-		/* printk(KERN_DEBUG "Memory sizing: %08x %08x\n", memory_end, memory_alt_end); */
-		if (memory_alt_end > memory_end)
-			memory_end = memory_alt_end;
-	}
-#endif
 
-	memory_end &= PAGE_MASK;
 #ifdef CONFIG_BLK_DEV_RAM
 	rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
 	rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
@@ -324,6 +401,26 @@
 	/* Save unparsed command line copy for /proc/cmdline */
 	memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
 	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+
+       setup_memory_region();
+
+#define PFN_UP(x)      (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+#define PFN_DOWN(x)    ((x) >> PAGE_SHIFT)
+       max_pfn = 0;
+       for (i = 0; i < e820.nr_map; i++) {
+               unsigned long start, end;
+               /* RAM? */
+               if (e820.map[i].type != E820_RAM)
+                       continue;
+               start = PFN_UP(e820.map[i].addr);
+               end = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
+               if (start >= end)
+                       continue;
+               if (end > max_pfn)
+                       max_pfn = end;
+       }
+       memory_end = (max_pfn << PAGE_SHIFT);
+
 
 	for (;;) {
 		/*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/time.c linux.19p1/arch/i386/kernel/time.c
--- linux.vanilla/arch/i386/kernel/time.c	Mon Dec 11 22:12:34 2000
+++ linux.19p1/arch/i386/kernel/time.c	Wed Dec 13 22:38:07 2000
@@ -176,6 +176,14 @@
 
 	count |= inb_p(0x40) << 8;
 
+	/* VIA686a test code... reset the latch if count > max */
+ 	if (count > LATCH-1) {
+		outb_p(0x34, 0x43);
+		outb_p(LATCH & 0xff, 0x40);
+		outb(LATCH >> 8, 0x40);
+		count = LATCH - 1;
+	}	
+	
 	/*
 	 * avoiding timer inconsistencies (they are rare, but they happen)...
 	 * there are two kinds of problems that must be avoided here:
@@ -468,6 +476,22 @@
 
 		count = inb_p(0x40);    /* read the latched count */
 		count |= inb(0x40) << 8;
+
+		/* VIA686a test code... reset the latch if count > max */
+		if (count > LATCH-1) {
+			static int last_whine;
+			outb_p(0x34, 0x43);
+			outb_p(LATCH & 0xff, 0x40);
+			outb(LATCH >> 8, 0x40);
+			count = LATCH - 1;
+			if(time_after(jiffies, last_whine))
+			{
+				printk(KERN_WARNING "probable hardware bug: clock timer configuration lost - probably a VIA686a.\n");
+				printk(KERN_WARNING "probable hardware bug: restoring chip configuration.\n");
+				last_whine = jiffies + HZ;
+			}			
+		}	
+
 #if 0
 		spin_unlock(&i8253_lock);
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/genhd.c linux.19p1/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c	Mon Dec 11 22:12:40 2000
+++ linux.19p1/drivers/block/genhd.c	Wed Dec 13 22:43:46 2000
@@ -54,6 +54,14 @@
 				le32_to_cpu(__a); \
 			})
 
+#define MSDOS_LABEL_MAGIC1		0x55
+#define MSDOS_LABEL_MAGIC2		0xAA
+
+static inline int
+msdos_magic_present(unsigned char *p) {
+	return (p[0] == MSDOS_LABEL_MAGIC1 && p[1] == MSDOS_LABEL_MAGIC2);
+}
+
 struct gendisk *gendisk_head = NULL;
 
 static int current_minor = 0;
@@ -244,8 +252,6 @@
  * only for the actual data partitions.
  */
 
-#define MSDOS_LABEL_MAGIC		0xAA55
-
 static void extended_partition(struct gendisk *hd, kdev_t dev, int sector_size)
 {
 	struct buffer_head *bh;
@@ -273,10 +279,10 @@
 	   */
 		bh->b_state = 0;
 
-		if ((*(unsigned short *) (bh->b_data+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC))
+		if (!(msdos_magic_present(bh->b_data + 510)))
 			goto done;
 
-		p = (struct partition *) (0x1BE + bh->b_data);
+		p = (struct partition *) (bh->b_data + 0x1be);
 
 		this_size = hd->part[MINOR(dev)].nr_sects;
 
@@ -499,7 +505,51 @@
 	printk(" >");
 }
 #endif
+		
+#ifdef CONFIG_MINIX_SUBPARTITION
+/*
+ * Minix 2.0.0/2.0.2 subpartition support.
+ * Anand Krishnamurthy <anandk@wiproge.med.ge.com>
+ * Rajeev V. Pillai    <rajeevvp@yahoo.com>
+ */
+#define MINIX_PARTITION         0x81  /* Minix Partition ID */
+#define MINIX_NR_SUBPARTITIONS  4
+static void minix_partition(struct gendisk *hd, kdev_t dev)
+{
+	struct buffer_head *bh;
+	struct partition *p;
+	int mask = (1 << hd->minor_shift) - 1;
+	int i;
+
+	if (!(bh = bread(dev, 0, get_ptable_blocksize(dev))))
+		return;
+	bh->b_state = 0;
+
+	p = (struct partition *)(bh->b_data + 0x1be);
 
+	/* The first sector of a Minix partition can have either
+	 * a secondary MBR describing its subpartitions, or
+	 * the normal boot sector. */
+	if (msdos_magic_present(bh->b_data + 510) &&
+	    SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */
+
+		printk(" <");
+		for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) {
+			if ((current_minor & mask) == 0) 
+				break;
+			/* add each partition in use */
+			if (SYS_IND(p) == MINIX_PARTITION) {
+				add_partition(hd, current_minor,
+					      START_SECT(p), NR_SECTS(p), 0);
+				current_minor++;
+			}
+		}
+		printk(" >");
+	}
+	brelse(bh);
+}
+#endif /* CONFIG_MINIX_SUBPARTITION */
+ 
 static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
 {
 	int i, minor = current_minor;
@@ -540,11 +590,11 @@
 #ifdef CONFIG_BLK_DEV_IDE
 check_table:
 #endif
-	if (*(unsigned short *)  (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) {
+	if (!(msdos_magic_present(data + 510))) {
 		brelse(bh);
 		return 0;
 	}
-	p = (struct partition *) (0x1be + data);
+	p = (struct partition *) (data + 0x1be);
 
 #ifdef CONFIG_BLK_DEV_IDE
 	if (!tested_for_xlate++) {	/* Do this only once per disk */
@@ -649,8 +699,8 @@
 	for (i=1 ; i<=4 ; minor++,i++,p++) {
 		if (!NR_SECTS(p))
 			continue;
-               add_partition(hd, minor, first_sector+START_SECT(p)*sector_size, NR_SECTS(p)*sector_size, 
-               		     ptype(SYS_IND(p)));
+               add_partition(hd, minor, first_sector+START_SECT(p)*sector_size,
+			     NR_SECTS(p)*sector_size, ptype(SYS_IND(p)));
 		if (is_extended_partition(p)) {
 			printk(" <");
 			/*
@@ -682,6 +732,12 @@
 			}
 		}
 #endif
+#ifdef CONFIG_MINIX_SUBPARTITION
+		if (SYS_IND(p) == MINIX_PARTITION) {
+			printk("@");	/* Minix partitions are indicated by '@' */
+			minix_partition(hd, MKDEV(hd->major, minor));
+		}
+#endif
 #ifdef CONFIG_UNIXWARE_DISKLABEL
 		if (SYS_IND(p) == UNIXWARE_PARTITION)
 			unixware_partition(hd, MKDEV(hd->major, minor));
@@ -709,7 +765,7 @@
 	/*
 	 *  Check for old-style Disk Manager partition table
 	 */
-	if (*(unsigned short *) (data+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC)) {
+	if (msdos_magic_present(data + 0xfc)) {
 		p = (struct partition *) (0x1be + data);
 		for (i = 4 ; i < 16 ; i++, current_minor++) {
 			p--;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Config.in linux.19p1/drivers/char/Config.in
--- linux.vanilla/drivers/char/Config.in	Mon Dec 11 22:12:40 2000
+++ linux.19p1/drivers/char/Config.in	Wed Dec 13 22:02:31 2000
@@ -99,7 +99,13 @@
   mainmenu_option next_comment
   comment 'Watchdog Cards'
   bool '   Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
-  tristate '   WDT Watchdog timer' CONFIG_WDT
+  tristate '   Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
+  tristate '   Advantech SBC Watchdog Timer' CONFIG_ADVANTECH_WDT
+  tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
+  tristate '   Mixcom Watchdog' CONFIG_MIXCOMWD 
+  tristate '   SBC-60XX Watchdog Timer' CONFIG_60XX_WDT
+  tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
+  tristate '   WDT ISA Watchdog timer' CONFIG_WDT
   if [ "$CONFIG_WDT" != "n" ]; then
      bool '       WDT501 features' CONFIG_WDT_501
      if [ "$CONFIG_WDT_501" = "y" ]; then
@@ -107,11 +113,6 @@
      fi
   fi
   tristate '   WDT PCI Watchdog timer' CONFIG_WDTPCI
-  tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
-  tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
-  tristate '   Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
-  tristate '   SBC-60XX Watchdog Timer' CONFIG_60XX_WDT
-  tristate '   Mixcom Watchdog' CONFIG_MIXCOMWD 
   endmenu
 fi
 
@@ -128,8 +129,8 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    tristate '/dev/agpgart (AGP Support) (EXPERIMENTAL)' CONFIG_AGP n
    if [ "$CONFIG_AGP" != "n" ]; then
-      bool '   Intel 440LX/BX/GX support' CONFIG_AGP_INTEL
-      bool '   Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810
+      bool '   Intel 440LX/BX/GX and I815/820 support' CONFIG_AGP_INTEL
+      bool '   Intel I810/I815 (on-board video) support' CONFIG_AGP_I810
       bool '   VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA
       bool '   AMD Irongate support' CONFIG_AGP_AMD
       bool '   Generic SiS support' CONFIG_AGP_SIS
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Makefile linux.19p1/drivers/char/Makefile
--- linux.vanilla/drivers/char/Makefile	Mon Dec 11 22:12:40 2000
+++ linux.19p1/drivers/char/Makefile	Wed Dec 13 22:01:37 2000
@@ -295,6 +295,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ADVANTECH_WDT),y)
+O_OBJS += advantechwdt.o
+else
+  ifeq ($(CONFIG_ADVANTECH_WDT),m)
+  M_OBJS += advantechwdt.o
+  endif
+endif
+
 ifeq ($(CONFIG_60XX_WDT),y)
 O_OBJS += sbc60xxwdt.o
 else
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/advantechwdt.c linux.19p1/drivers/char/advantechwdt.c
--- linux.vanilla/drivers/char/advantechwdt.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/char/advantechwdt.c	Wed Dec 13 22:01:01 2000
@@ -0,0 +1,243 @@
+/*
+ *	Advantech Single Board Computer WDT driver for Linux 2.2.x
+ *
+ *	(c) Copyright 2000 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ *	Based on acquirewdt.c which is based on wdt.c.
+ *	Original copyright messages:
+ *
+ *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ *				http://www.redhat.com
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *	
+ *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
+ *
+ *	(c) Copyright 1995    Alan Cox <alan@redhat.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+
+static int advwdt_is_open = 0;
+
+/*
+ *	You must set these - there is no sane way to probe for this board.
+ *
+ *	To enable or restart, write the timeout value in seconds (1 to 63)
+ *	to I/O port WDT_START.  To disable, read I/O port WDT_STOP.
+ *	Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
+ *	check your manual (at least the PCA-6159 seems to be different -
+ *	the manual says WDT_STOP is 0x43, not 0x443).
+ *	(0x43 is also a write-only control register for the 8254 timer!)
+ *
+ *	TODO: module parameters to set the I/O port addresses and NOWAYOUT
+ *	option at load time.
+ */
+ 
+#define WDT_STOP 0x443
+#define WDT_START 0x443
+
+#define WD_TIMO 60		/* 1 minute */
+
+
+/*
+ *	Kernel methods.
+ */
+ 
+
+static void
+advwdt_ping(void)
+{
+	/* Write a watchdog value */
+	outb_p(WD_TIMO, WDT_START);
+}
+
+static ssize_t
+advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	/*  Can't seek (pwrite) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (count) {
+		advwdt_ping();
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t
+advwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static int
+advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	  unsigned long arg)
+{
+	static struct watchdog_info ident = {
+		WDIOF_KEEPALIVEPING, 1, "Advantech WDT"
+	};
+	
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+	  if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
+	    return -EFAULT;
+	  break;
+	  
+	case WDIOC_GETSTATUS:
+	  if (copy_to_user((int *)arg, &advwdt_is_open,  sizeof(int)))
+	    return -EFAULT;
+	  break;
+
+	case WDIOC_KEEPALIVE:
+	  advwdt_ping();
+	  break;
+
+	default:
+	  return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static int
+advwdt_open(struct inode *inode, struct file *file)
+{
+	switch (MINOR(inode->i_rdev)) {
+		case WATCHDOG_MINOR:
+			if (advwdt_is_open)
+				return -EBUSY;
+			MOD_INC_USE_COUNT;
+			/*
+			 *	Activate 
+			 */
+	 
+			advwdt_is_open = 1;
+			advwdt_ping();
+			return 0;
+		default:
+			return -ENODEV;
+	}
+}
+
+static int
+advwdt_close(struct inode *inode, struct file *file)
+{
+	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
+#ifndef CONFIG_WATCHDOG_NOWAYOUT	
+		inb_p(WDT_STOP);
+#endif		
+		advwdt_is_open = 0;
+	}
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Notifier for system down
+ */
+
+static int
+advwdt_notify_sys(struct notifier_block *this, unsigned long code,
+	void *unused)
+{
+	if (code == SYS_DOWN || code == SYS_HALT) {
+		/* Turn the card off */
+		inb_p(WDT_STOP);
+	}
+	return NOTIFY_DONE;
+}
+ 
+/*
+ *	Kernel Interfaces
+ */
+ 
+ 
+static struct file_operations advwdt_fops = {
+	NULL,
+	advwdt_read,
+	advwdt_write,
+	NULL,		/* No Readdir */
+	NULL,		/* No Select */
+	advwdt_ioctl,
+	NULL,		/* No mmap */
+	advwdt_open,
+	NULL,		/* flush */
+	advwdt_close
+};
+
+static struct miscdevice advwdt_miscdev = {
+	WATCHDOG_MINOR,
+	"watchdog",
+	&advwdt_fops
+};
+
+
+/*
+ *	The WDT card needs to learn about soft shutdowns in order to
+ *	turn the timebomb registers off. 
+ */
+ 
+static struct notifier_block advwdt_notifier = {
+	advwdt_notify_sys,
+	NULL,
+	0
+};
+
+#ifdef MODULE
+
+#define advwdt_init init_module
+
+void
+cleanup_module(void)
+{
+	misc_deregister(&advwdt_miscdev);
+	unregister_reboot_notifier(&advwdt_notifier);
+#if WDT_START != WDT_STOP
+	release_region(WDT_STOP,1);
+#endif
+	release_region(WDT_START,1);
+}
+
+#endif
+
+int __init
+advwdt_init(void)
+{
+	printk("WDT driver for Advantech single board computer initialising.\n");
+
+	misc_register(&advwdt_miscdev);
+#if WDT_START != WDT_STOP
+	request_region(WDT_STOP, 1, "Advantech WDT");
+#endif
+	request_region(WDT_START, 1, "Advantech WDT");
+	register_reboot_notifier(&advwdt_notifier);
+	return 0;
+}
+
+/* end of advantechwdt.c */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agp_backend.h linux.19p1/drivers/char/agp/agp_backend.h
--- linux.vanilla/drivers/char/agp/agp_backend.h	Mon Dec 11 22:12:40 2000
+++ linux.19p1/drivers/char/agp/agp_backend.h	Wed Dec 13 21:32:45 2000
@@ -45,6 +45,7 @@
 	INTEL_BX,
 	INTEL_GX,
 	INTEL_I810,
+	INTEL_I815,
 	INTEL_I840,
 	VIA_GENERIC,
 	VIA_VP3,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agpgart_be.c linux.19p1/drivers/char/agp/agpgart_be.c
--- linux.vanilla/drivers/char/agp/agpgart_be.c	Mon Dec 11 22:12:40 2000
+++ linux.19p1/drivers/char/agp/agpgart_be.c	Wed Dec 13 21:32:45 2000
@@ -2026,6 +2026,13 @@
 		"Intel",
 		"440GX",
 		intel_generic_setup },
+	/* could we add support for PCI_DEVICE_ID_INTEL_815_1 too ? */
+	{ PCI_DEVICE_ID_INTEL_815_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_I815,
+		"Intel",
+		"i815",
+		intel_generic_setup },
 	{ PCI_DEVICE_ID_INTEL_840_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I840,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/console.c linux.19p1/drivers/char/console.c
--- linux.vanilla/drivers/char/console.c	Mon Dec 11 22:12:41 2000
+++ linux.19p1/drivers/char/console.c	Wed Dec 13 22:26:19 2000
@@ -2571,6 +2571,7 @@
 	console_blanked = 0;
 	if (console_blank_hook)
 		console_blank_hook(0);
+	set_palette(currcons);
 	if (sw->con_blank(vc_cons[currcons].d, 0))
 		/* Low-level driver cannot restore -> do it ourselves */
 		update_screen(fg_console);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/gamma_drv.c linux.19p1/drivers/char/drm/gamma_drv.c
--- linux.vanilla/drivers/char/drm/gamma_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p1/drivers/char/drm/gamma_drv.c	Wed Dec 13 22:45:57 2000
@@ -35,11 +35,6 @@
 
 #include <linux/agp_backend.h>
 
-static void __attribute__((unused)) unused(void)
-{
-	agp_enable(0);
-}
-
 #ifndef PCI_DEVICE_ID_3DLABS_GAMMA
 #define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_drv.c linux.19p1/drivers/char/drm/i810_drv.c
--- linux.vanilla/drivers/char/drm/i810_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p1/drivers/char/drm/i810_drv.c	Wed Dec 13 22:46:11 2000
@@ -35,11 +35,6 @@
 
 #include <linux/agp_backend.h>
 
-static void __attribute__((unused)) unused(void)
-{
-	agp_enable(0);
-}
-
 #define I810_NAME	 "i810"
 #define I810_DESC	 "Intel I810"
 #define I810_DATE	 "20000719"
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_drv.c linux.19p1/drivers/char/drm/mga_drv.c
--- linux.vanilla/drivers/char/drm/mga_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p1/drivers/char/drm/mga_drv.c	Wed Dec 13 22:46:26 2000
@@ -36,11 +36,6 @@
 
 #include <linux/agp_backend.h>
 
-static void __attribute__((unused)) unused(void)
-{
-	agp_enable(0);
-}
-
 #define MGA_NAME	 "mga"
 #define MGA_DESC	 "Matrox G200/G400"
 #define MGA_DATE	 "20000910"
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_drv.c linux.19p1/drivers/char/drm/r128_drv.c
--- linux.vanilla/drivers/char/drm/r128_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p1/drivers/char/drm/r128_drv.c	Wed Dec 13 22:52:21 2000
@@ -35,11 +35,6 @@
 
 #include <linux/agp_backend.h>
 
-static void __attribute__((unused)) unused(void)
-{
-	agp_enable(0);
-}
-
 #define R128_NAME	 "r128"
 #define R128_DESC	 "ATI Rage 128"
 #define R128_DATE	 "20000719"
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/tdfx_drv.c linux.19p1/drivers/char/drm/tdfx_drv.c
--- linux.vanilla/drivers/char/drm/tdfx_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p1/drivers/char/drm/tdfx_drv.c	Wed Dec 13 22:45:41 2000
@@ -38,13 +38,6 @@
 #include <linux/agp_backend.h>
 #endif
 
-static void __attribute__((unused)) unused(void)
-{
-#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
-	agp_enable(0);
-#endif
-}
-
 #define TDFX_NAME	 "tdfx"
 #define TDFX_DESC	 "3dfx Banshee/Voodoo3+"
 #define TDFX_DATE	 "20000719"
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/misc.c linux.19p1/drivers/char/misc.c
--- linux.vanilla/drivers/char/misc.c	Mon Dec 11 22:12:43 2000
+++ linux.19p1/drivers/char/misc.c	Wed Dec 13 22:03:35 2000
@@ -230,6 +230,9 @@
 #ifdef CONFIG_ACQUIRE_WDT
 	acq_init();
 #endif
+#ifdef CONFIG_ADVANTECH_WDT
+	advwdt_init();
+#endif
 #ifdef CONFIG_60XX_WDT
 	sbc60xxwdt_init();
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/Config.in linux.19p1/drivers/isdn/Config.in
--- linux.vanilla/drivers/isdn/Config.in	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/Config.in	Wed Dec 13 23:19:37 2000
@@ -10,7 +10,7 @@
 fi
 bool '  Support audio via ISDN' CONFIG_ISDN_AUDIO
 if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then
-   bool '    Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX
+   bool '    Support AT-Fax Class 1 and 2 commands' CONFIG_ISDN_TTY_FAX
 fi
 if [ "$CONFIG_X25" != "n" ]; then
    bool '  X.25 PLP on top of ISDN' CONFIG_ISDN_X25
@@ -37,6 +37,7 @@
       bool '    Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD
    fi
    bool '  HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+   bool '  HiSax Support for US NI1' CONFIG_HISAX_NI1
    comment '  HiSax supported cards'
    bool '  Teles 16.0/8.0' CONFIG_HISAX_16_0
    bool '  Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
@@ -44,7 +45,6 @@
    bool '  Teles S0Box' CONFIG_HISAX_S0BOX 
    bool '  AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
    bool '  AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
-   bool '  AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
    bool '  Elsa cards' CONFIG_HISAX_ELSA
    bool '  ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
    bool '  Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
@@ -55,6 +55,7 @@
    bool '  USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
    bool '  MIC card' CONFIG_HISAX_MIC
    bool '  NETjet card' CONFIG_HISAX_NETJET
+   bool '  NETspider U card' CONFIG_HISAX_NETJET_U
    bool '  Niccy PnP/PCI card' CONFIG_HISAX_NICCY
    bool '  Siemens I-Surf card' CONFIG_HISAX_ISURF
    bool '  HST Saphir card' CONFIG_HISAX_HSTSAPHIR
@@ -85,8 +86,14 @@
 if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then
    bool '  Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA
 fi
-dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
-if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
+dep_tristate 'CAPI2.0 support' CONFIG_ISDN_CAPI $CONFIG_ISDN
+if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+   bool 'CAPI2.0 Middleware support' CONFIG_ISDN_CAPI_MIDDLEWARE
+   if [ "$CONFIG_ISDN_CAPI_MIDDLEWARE" != "n" ]; then
+   	bool 'CAPI2.0 filesystem support' CONFIG_ISDN_CAPIFS
+   fi
+fi
+if [ "$CONFIG_ISDN_CAPI" != "n" ]; then
    bool '  AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA
    bool '  AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI
    if [ "$CONFIG_ISDN_DRV_AVMB1_B1PCI" != "n" ]; then
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/Makefile linux.19p1/drivers/isdn/Makefile
--- linux.vanilla/drivers/isdn/Makefile	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/Makefile	Wed Dec 13 22:06:01 2000
@@ -97,12 +97,12 @@
   endif
 endif
 
-ifeq ($(CONFIG_ISDN_DRV_AVMB1),y)
+ifeq ($(CONFIG_ISDN_CAPI),y)
   L_OBJS += avmb1/avmb1.o
   SUB_DIRS += avmb1
   MOD_SUB_DIRS += avmb1
 else
-  ifeq ($(CONFIG_ISDN_DRV_AVMB1),m)
+  ifeq ($(CONFIG_ISDN_CAPI),m)
     MOD_SUB_DIRS += avmb1
   endif
 endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/Makefile linux.19p1/drivers/isdn/act2000/Makefile
--- linux.vanilla/drivers/isdn/act2000/Makefile	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/act2000/Makefile	Wed Dec 13 22:06:01 2000
@@ -7,7 +7,7 @@
   O_TARGET += act2000.o
 else
   ifeq ($(CONFIG_ISDN_DRV_ACT2000),m)
-  	O_TARGET += act2000.o
+	O_TARGET += act2000.o
 	M_OBJS = act2000.o
   endif
 endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/act2000.h linux.19p1/drivers/isdn/act2000/act2000.h
--- linux.vanilla/drivers/isdn/act2000/act2000.h	Wed May  3 22:34:32 2000
+++ linux.19p1/drivers/isdn/act2000/act2000.h	Thu Dec 14 16:55:18 2000
@@ -1,4 +1,4 @@
-/* $Id: act2000.h,v 1.7 1999/04/12 13:13:54 fritz Exp $
+/* $Id: act2000.h,v 1.8 2000/11/12 16:32:06 kai Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
@@ -18,31 +18,6 @@
  * 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. 
- *
- * $Log: act2000.h,v $
- * Revision 1.7  1999/04/12 13:13:54  fritz
- * Made cards pointer static to avoid name-clash.
- *
- * Revision 1.6  1998/11/05 22:12:38  fritz
- * Changed mail-address.
- *
- * Revision 1.5  1997/10/09 22:22:59  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *
- * Revision 1.4  1997/09/25 17:25:37  fritz
- * Support for adding cards at runtime.
- * Support for new Firmware.
- *
- * Revision 1.3  1997/09/24 23:11:43  fritz
- * Optimized IRQ load and polling-mode.
- *
- * Revision 1.2  1997/09/24 19:44:12  fritz
- * Added MSN mapping support, some cleanup.
- *
- * Revision 1.1  1997/09/23 18:00:05  fritz
- * New driver for IBM Active 2000.
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/act2000_isa.c linux.19p1/drivers/isdn/act2000/act2000_isa.c
--- linux.vanilla/drivers/isdn/act2000/act2000_isa.c	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/act2000/act2000_isa.c	Wed Dec 13 22:06:07 2000
@@ -1,4 +1,4 @@
-/* $Id: act2000_isa.c,v 1.10 1999/10/24 18:46:05 fritz Exp $
+/* $Id: act2000_isa.c,v 1.11 2000/11/12 16:32:06 kai Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
  *
@@ -19,43 +19,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
- * $Log: act2000_isa.c,v $
- * Revision 1.10  1999/10/24 18:46:05  fritz
- * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest
- * kernels.
- *
- * Revision 1.9  1999/09/04 06:20:04  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.8  1999/01/05 18:29:25  he
- * merged remaining schedule_timeout() changes from 2.1.127
- *
- * Revision 1.7  1998/11/05 22:12:41  fritz
- * Changed mail-address.
- *
- * Revision 1.6  1998/06/17 19:51:09  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- * Revision 1.5  1998/02/12 23:06:47  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.4  1997/10/09 22:23:00  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *
- * Revision 1.3  1997/09/25 17:25:38  fritz
- * Support for adding cards at runtime.
- * Support for new Firmware.
- *
- * Revision 1.2  1997/09/24 23:11:44  fritz
- * Optimized IRQ load and polling-mode.
- *
- * Revision 1.1  1997/09/23 18:00:05  fritz
- * New driver for IBM Active 2000.
- *
  */
 
 #define __NO_VERSION__
@@ -78,7 +41,7 @@
 act2000_isa_delay(long t)
 {
         sti();
-        current->state = TASK_INTERRUPTIBLE;
+        set_current_state(TASK_INTERRUPTIBLE);
         schedule_timeout(t);
         sti();
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/act2000_isa.h linux.19p1/drivers/isdn/act2000/act2000_isa.h
--- linux.vanilla/drivers/isdn/act2000/act2000_isa.h	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/act2000/act2000_isa.h	Wed Dec 13 22:06:13 2000
@@ -1,4 +1,4 @@
-/* $Id: act2000_isa.h,v 1.3 1999/10/24 18:46:05 fritz Exp $
+/* $Id: act2000_isa.h,v 1.4 2000/11/12 16:32:06 kai Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
  *
@@ -18,17 +18,6 @@
  * 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. 
- *
- * $Log: act2000_isa.h,v $
- * Revision 1.3  1999/10/24 18:46:05  fritz
- * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest
- * kernels.
- *
- * Revision 1.2  1998/11/05 22:12:43  fritz
- * Changed mail-address.
- *
- * Revision 1.1  1997/09/23 18:00:07  fritz
- * New driver for IBM Active 2000.
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/capi.c linux.19p1/drivers/isdn/act2000/capi.c
--- linux.vanilla/drivers/isdn/act2000/capi.c	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/act2000/capi.c	Wed Dec 13 22:06:13 2000
@@ -1,4 +1,4 @@
-/* $Id: capi.c,v 1.8 1998/11/05 22:12:46 fritz Exp $
+/* $Id: capi.c,v 1.9 2000/11/12 16:32:06 kai Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *        CAPI encoder/decoder
@@ -19,34 +19,6 @@
  * 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. 
- *
- * $Log: capi.c,v $
- * Revision 1.8  1998/11/05 22:12:46  fritz
- * Changed mail-address.
- *
- * Revision 1.7  1998/02/23 23:35:41  fritz
- * Eliminated some compiler warnings.
- *
- * Revision 1.6  1998/02/12 23:06:50  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.5  1997/10/09 22:23:02  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *
- * Revision 1.4  1997/09/25 17:25:39  fritz
- * Support for adding cards at runtime.
- * Support for new Firmware.
- *
- * Revision 1.3  1997/09/24 19:44:14  fritz
- * Added MSN mapping support, some cleanup.
- *
- * Revision 1.2  1997/09/23 19:41:24  fritz
- * Disabled Logging of DATA_B3_IND/RESP/REQ/CONF Messages.
- *
- * Revision 1.1  1997/09/23 18:00:08  fritz
- * New driver for IBM Active 2000.
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/capi.h linux.19p1/drivers/isdn/act2000/capi.h
--- linux.vanilla/drivers/isdn/act2000/capi.h	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/act2000/capi.h	Wed Dec 13 22:06:13 2000
@@ -1,4 +1,4 @@
-/* $Id: capi.h,v 1.5 1998/11/05 22:12:48 fritz Exp $
+/* $Id: capi.h,v 1.6 2000/11/12 16:32:06 kai Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
@@ -18,25 +18,6 @@
  * 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. 
- *
- * $Log: capi.h,v $
- * Revision 1.5  1998/11/05 22:12:48  fritz
- * Changed mail-address.
- *
- * Revision 1.4  1997/10/01 09:21:04  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.3  1997/09/25 17:25:41  fritz
- * Support for adding cards at runtime.
- * Support for new Firmware.
- *
- * Revision 1.2  1997/09/24 19:44:15  fritz
- * Added MSN mapping support, some cleanup.
- *
- * Revision 1.1  1997/09/23 18:00:10  fritz
- * New driver for IBM Active 2000.
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/module.c linux.19p1/drivers/isdn/act2000/module.c
--- linux.vanilla/drivers/isdn/act2000/module.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/act2000/module.c	Wed Dec 13 22:06:20 2000
@@ -1,4 +1,4 @@
-/* $Id: module.c,v 1.11 1999/10/30 09:48:04 keil Exp $
+/* $Id: module.c,v 1.14 2000/11/12 16:32:06 kai Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
@@ -19,44 +19,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
- * $Log: module.c,v $
- * Revision 1.11  1999/10/30 09:48:04  keil
- * miss one prefix act2000
- *
- * Revision 1.10  1999/10/24 18:46:05  fritz
- * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest
- * kernels.
- *
- * Revision 1.9  1999/04/12 13:13:56  fritz
- * Made cards pointer static to avoid name-clash.
- *
- * Revision 1.8  1998/11/05 22:12:51  fritz
- * Changed mail-address.
- *
- * Revision 1.7  1998/02/12 23:06:52  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.6  1998/01/31 22:10:42  keil
- * changes for 2.1.82
- *
- * Revision 1.5  1997/10/09 22:23:04  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *
- * Revision 1.4  1997/09/25 17:25:43  fritz
- * Support for adding cards at runtime.
- * Support for new Firmware.
- *
- * Revision 1.3  1997/09/24 23:11:45  fritz
- * Optimized IRQ load and polling-mode.
- *
- * Revision 1.2  1997/09/24 19:44:17  fritz
- * Added MSN mapping support, some cleanup.
- *
- * Revision 1.1  1997/09/23 18:00:13  fritz
- * New driver for IBM Active 2000.
- *
  */
 
 #include "act2000.h"
@@ -561,37 +523,6 @@
                         card->status_buf_read = card->status_buf;
         }
         return count;
-}
-
-static void
-act2000_putmsg(act2000_card *card, char c)
-{
-        ulong flags;
-
-        save_flags(flags);
-        cli();
-        *card->status_buf_write++ = c;
-        if (card->status_buf_write == card->status_buf_read) {
-                if (++card->status_buf_read > card->status_buf_end)
-                card->status_buf_read = card->status_buf;
-        }
-        if (card->status_buf_write > card->status_buf_end)
-                card->status_buf_write = card->status_buf;
-        restore_flags(flags);
-}
-
-static void
-act2000_logstat(struct act2000_card *card, char *str)
-{
-        char *p = str;
-        isdn_ctrl c;
-
-	while (*p)
-		act2000_putmsg(card, *p++);
-        c.command = ISDN_STAT_STAVAIL;
-        c.driver = card->myid;
-        c.arg = strlen(str);
-        card->interface.statcallb(&c);
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/Makefile linux.19p1/drivers/isdn/avmb1/Makefile
--- linux.vanilla/drivers/isdn/avmb1/Makefile	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/avmb1/Makefile	Wed Dec 13 23:22:51 2000
@@ -1,5 +1,5 @@
 #
-# $Id: Makefile,v 1.8 2000/01/25 14:33:38 calle Exp $
+# $Id: Makefile,v 1.18 2000/04/03 16:39:25 calle Exp $
 #
 # Makefile for the CAPI and AVM-B1 device drivers.
 #
@@ -11,6 +11,55 @@
 # parent makes..
 #
 # $Log: Makefile,v $
+# Revision 1.18  2000/04/03 16:39:25  calle
+# Makefile checked in with future things :-(
+#
+# Revision 1.17  2000/04/03 16:38:05  calle
+# made suppress_pollack static.
+#
+# Revision 1.16  2000/03/17 12:15:44  calle
+# ALL_SUB_DIRS were wrong.
+#
+# Revision 1.15  2000/03/16 15:21:03  calle
+# Bugfix in c4_remove: loop 5 times instead of 4 :-(
+#
+# Revision 1.14  2000/03/13 17:50:55  calle
+# Added avm_cs.c for 2.3.x PCMCIA support.
+#
+# Revision 1.13  2000/03/08 17:06:33  calle
+# - changes for devfs and 2.3.49
+# - capifs now configurable (no need with devfs)
+# - New Middleware ioctl CAPI_NCCI_GETUNIT
+# - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs)
+#
+# Revision 1.12  2000/03/06 18:00:23  calle
+# - Middleware extention now working with 2.3.49 (capifs).
+# - Fixed typos in debug section of capi.c
+# - Bugfix: Makefile corrected for b1pcmcia.c
+#
+# Revision 1.11  2000/03/06 09:17:07  calle
+# - capifs: fileoperations now in inode (change for 2.3.49)
+# - Config.in: Middleware extention not a tristate, uups.
+#
+# Revision 1.10  2000/03/03 16:48:38  calle
+# - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI)
+#   It is now possible to create a connection with a CAPI2.0 applikation
+#   and than to handle the data connection from /dev/capi/ (capifs) and also
+#   using async or sync PPP on this connection.
+#   The two major device number 190 and 191 are not confirmed yet,
+#   but I want to save the code in cvs, before I go on.
+#
+# Revision 1.9  2000/03/03 15:50:42  calle
+# - kernel CAPI:
+#   - Changed parameter "param" in capi_signal from __u32 to void *.
+#   - rewrote notifier handling in kcapi.c
+#   - new notifier NCCI_UP and NCCI_DOWN
+# - User CAPI:
+#   - /dev/capi20 is now a cloning device.
+#   - middleware extentions prepared.
+# - capidrv.c
+#   - locking of list operations and module count updates.
+#
 # Revision 1.8  2000/01/25 14:33:38  calle
 # - Added Support AVM B1 PCI V4.0 (tested with prototype)
 #   - splitted up t1pci.c into b1dma.c for common function with b1pciv4
@@ -64,6 +113,9 @@
 #
 #
 
+SUB_DIRS     :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS :=
 #
 # Objects that don't export a symtab
 #
@@ -82,53 +134,53 @@
 O_TARGET := # used for .o targets (from O and OX objects)
 L_TARGET := # used for .a targets (from L and LX objects)
 
-ifeq ($(CONFIG_ISDN_DRV_AVMB1),y)
+ifeq ($(CONFIG_ISDN_CAPI),y)
   O_TARGET += avmb1.o
   OX_OBJS  += kcapi.o
   O_OBJS   += capi.o
+  ifdef CONFIG_ISDN_CAPIFS
+  	OX_OBJS   += capifs.o
+  endif
   ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA
-  O_OBJS   += b1isa.o
+  	O_OBJS   += b1isa.o
   endif
   ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI
-  O_OBJS  += b1pci.o
+  	O_OBJS  += b1pci.o
   endif
   ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA
-  O_OBJS   += t1isa.o
-  endif
-  ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
-  OX_OBJS   += b1pcmcia.o
+  	O_OBJS   += t1isa.o
   endif
   ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI
-  O_OBJS   += t1pci.o
+  	O_OBJS   += t1pci.o
   endif
   ifdef CONFIG_ISDN_DRV_AVMB1_C4
-  O_OBJS   += c4.o
+  	O_OBJS   += c4.o
   endif
   OX_OBJS  += capiutil.o capidrv.o b1.o b1dma.o
 else
-  ifeq ($(CONFIG_ISDN_DRV_AVMB1),m)
-  O_TARGET += kernelcapi.o
-  OX_OBJS  += kcapi.o
-  M_OBJS   += capi.o kernelcapi.o
-  ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA
-  M_OBJS   += b1isa.o
-  endif
-  ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI
-  M_OBJS  += b1pci.o
-  endif
-  ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA
-  M_OBJS   += t1isa.o
-  endif
-  ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
-  MX_OBJS  += b1pcmcia.o
-  endif
-  ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI
-  M_OBJS   += t1pci.o
-  endif
-  ifdef CONFIG_ISDN_DRV_AVMB1_C4
-  M_OBJS   += c4.o
-  endif
-  MX_OBJS  += capiutil.o capidrv.o b1.o b1dma.o
+  ifeq ($(CONFIG_ISDN_CAPI),m)
+  	O_TARGET += kernelcapi.o
+  	OX_OBJS  += kcapi.o
+  	M_OBJS   += capi.o kernelcapi.o
+  	ifdef CONFIG_ISDN_CAPIFS
+  		MX_OBJS   += capifs.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA
+  		M_OBJS   += b1isa.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI
+  		M_OBJS  += b1pci.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA
+  		M_OBJS   += t1isa.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI
+  		M_OBJS   += t1pci.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_C4
+  		M_OBJS   += c4.o
+  	endif
+  	MX_OBJS  += capiutil.o capidrv.o b1.o b1dma.o
   endif
 endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/avm_cs.c linux.19p1/drivers/isdn/avmb1/avm_cs.c
--- linux.vanilla/drivers/isdn/avmb1/avm_cs.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/avmb1/avm_cs.c	Wed Dec 13 22:06:20 2000
@@ -0,0 +1,528 @@
+/*======================================================================
+
+    A PCMCIA client driver for AVM B1/M1/M2
+
+    Written by Carsten Paeth, calle@calle.in-berlin.de
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <linux/skbuff.h>
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+#include <linux/b1pcmcia.h>
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* This means pick from 15, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static int default_irq_list[10] = { 15, 12, 11, 10, 9, 7, 5, 4, 3, -1 };
+static int irq_list[10] = { -1 };
+
+MODULE_PARM(irq_list, "1-10i");
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the skeleton event
+   handler.
+*/
+
+static void avmcs_config(dev_link_t *link);
+static void avmcs_release(u_long arg);
+static int avmcs_event(event_t event, int priority,
+			  event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *avmcs_attach(void);
+static void avmcs_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "avm_cs";
+
+/*
+   A linked list of "instances" of the skeleton device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally can't be allocated dynamically.
+*/
+   
+typedef struct local_info_t {
+    dev_node_t	node;
+} local_info_t;
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*======================================================================
+
+    avmcs_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+    
+======================================================================*/
+
+static dev_link_t *avmcs_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret, i;
+    
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    memset(link, 0, sizeof(struct dev_link_t));
+    link->release.function = &avmcs_release;
+    link->release.data = (u_long)link;
+
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 16;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+    link->io.IOAddrLines = 5;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] != -1) {
+	    for (i = 0; i < 10 && irq_list[i] > 0; i++)
+	       link->irq.IRQInfo2 |= 1 << irq_list[i];
+    } else {
+	    for (i = 0; i < 10 && default_irq_list[i] > 0; i++)
+	       link->irq.IRQInfo2 |= 1 << default_irq_list[i];
+    }
+    
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    memset(local, 0, sizeof(local_info_t));
+    link->priv = local;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &avmcs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	avmcs_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* avmcs_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void avmcs_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    /*
+       If the device is currently configured and active, we won't
+       actually delete it yet.  Instead, it is marked so that when
+       the release() function is called, that will trigger a proper
+       detach().
+    */
+    if (link->state & DEV_CONFIG) {
+	link->state |= DEV_STALE_LINK;
+	return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->priv) {
+	kfree_s(link->priv, sizeof(local_info_t));
+    }
+    kfree_s(link, sizeof(struct dev_link_t));
+    
+} /* avmcs_detach */
+
+/*======================================================================
+
+    avmcs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+    
+======================================================================*/
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i;
+    i = CardServices(fn, handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    i = CardServices(GetTupleData, handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+static void avmcs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    local_info_t *dev;
+    int i;
+    u_char buf[64];
+    char devname[128];
+    int cardtype;
+    int (*addcard)(unsigned int port, unsigned irq);
+    
+    handle = link->handle;
+    dev = link->priv;
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    do {
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	i = CardServices(GetFirstTuple, handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	i = CardServices(GetTupleData, handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	i = CardServices(ParseTuple, handle, &tuple, &parse);
+	if (i != CS_SUCCESS) break;
+	link->conf.ConfigBase = parse.config.base;
+    } while (0);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, ParseTuple, i);
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+    }
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    do {
+
+	tuple.Attributes = 0;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 254;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_VERS_1;
+
+	devname[0] = 0;
+	if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
+	    strncpy(devname,parse.version_1.str + parse.version_1.ofs[1], 
+			sizeof(devname));
+	}
+	/*
+         * find IO port
+         */
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	i = first_tuple(handle, &tuple, &parse);
+	while (i == CS_SUCCESS) {
+	    if (cf->io.nwin > 0) {
+		link->conf.ConfigIndex = cf->index;
+		link->io.BasePort1 = cf->io.win[0].base;
+		link->io.NumPorts1 = cf->io.win[0].len;
+                printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
+			link->io.BasePort1,
+		        link->io.BasePort1+link->io.NumPorts1);
+		i = CardServices(RequestIO, link->handle, &link->io);
+		if (i == CS_SUCCESS) goto found_port;
+	    }
+	    i = next_tuple(handle, &tuple, &parse);
+	}
+
+found_port:
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIO, i);
+	    break;
+	}
+	
+	/*
+	 * allocate an interrupt line
+	 */
+	i = CardServices(RequestIRQ, link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIRQ, i);
+	    CardServices(ReleaseIO, link->handle, &link->io);
+	    break;
+	}
+	
+	/*
+         * configure the PCMCIA socket
+	  */
+	i = CardServices(RequestConfiguration, link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestConfiguration, i);
+	    CardServices(ReleaseIO, link->handle, &link->io);
+	    CardServices(ReleaseIRQ, link->handle, &link->irq);
+	    break;
+	}
+
+    } while (0);
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. */
+
+    if (devname[0]) {
+	char *s = strrchr(devname, ' ');
+	if (!s)
+	   s = devname;
+	else s++;
+	strcpy(dev->node.dev_name, s);
+        if (strcmp("M1", s) == 0) {
+           cardtype = AVM_CARDTYPE_M1;
+        } else if (strcmp("M2", s) == 0) {
+           cardtype = AVM_CARDTYPE_M2;
+	} else {
+           cardtype = AVM_CARDTYPE_B1;
+	}
+    } else {
+        strcpy(dev->node.dev_name, "b1");
+        cardtype = AVM_CARDTYPE_B1;
+    }
+
+    dev->node.major = 64;
+    dev->node.minor = 0;
+    link->dev = &dev->node;
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    /* If any step failed, release any partially configured state */
+    if (i != 0) {
+	avmcs_release((u_long)link);
+	return;
+    }
+
+
+    switch (cardtype) {
+        case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
+        case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
+	default:
+        case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
+    }
+    if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) {
+        printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
+		dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
+	avmcs_release((u_long)link);
+	return;
+    }
+    dev->node.minor = i;
+
+} /* avmcs_config */
+
+/*======================================================================
+
+    After a card is removed, avmcs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void avmcs_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+
+    /*
+       If the device is currently in use, we won't release until it
+       is actually closed.
+    */
+    if (link->open) {
+	link->state |= DEV_STALE_CONFIG;
+	return;
+    }
+
+    b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
+
+    /* Unlink the device chain */
+    link->dev = NULL;
+    
+    /* Don't bother checking to see if these succeed or not */
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+    
+    if (link->state & DEV_STALE_LINK)
+	avmcs_detach(link);
+    
+} /* avmcs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+    
+======================================================================*/
+
+static int avmcs_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG) {
+	    link->release.expires = jiffies + (HZ/20);
+	    add_timer(&link->release);
+	}
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	avmcs_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG)
+	    CardServices(ReleaseConfiguration, link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG)
+	    CardServices(RequestConfiguration, link->handle, &link->conf);
+	break;
+    }
+    return 0;
+} /* avmcs_event */
+
+/*====================================================================*/
+
+int init_module(void)
+{
+    servinfo_t serv;
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	printk(KERN_NOTICE "avm_cs: Card Services release "
+	       "does not match!\n");
+	return -1;
+    }
+    register_pccard_driver(&dev_info, &avmcs_attach, &avmcs_detach);
+    return 0;
+}
+
+void cleanup_module(void)
+{
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL) {
+	if (dev_list->state & DEV_CONFIG)
+	    avmcs_release((u_long)dev_list);
+	avmcs_detach(dev_list);
+    }
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1.c linux.19p1/drivers/isdn/avmb1/b1.c
--- linux.vanilla/drivers/isdn/avmb1/b1.c	Sat Sep  9 18:42:38 2000
+++ linux.19p1/drivers/isdn/avmb1/b1.c	Wed Dec 13 22:06:26 2000
@@ -1,11 +1,14 @@
 /*
- * $Id: b1.c,v 1.13 2000/01/25 14:33:38 calle Exp $
+ * $Id: b1.c,v 1.14 2000/06/19 16:51:53 keil Exp $
  * 
  * Common module for AVM B1 cards.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1.c,v $
+ * Revision 1.14  2000/06/19 16:51:53  keil
+ * don't free skb in irq context
+ *
  * Revision 1.13  2000/01/25 14:33:38  calle
  * - Added Support AVM B1 PCI V4.0 (tested with prototype)
  *   - splitted up t1pci.c into b1dma.c for common function with b1pciv4
@@ -91,7 +94,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.13 $";
+static char *revision = "$Revision: 1.14 $";
 
 /* ------------------------------------------------------------- */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1dma.c linux.19p1/drivers/isdn/avmb1/b1dma.c
--- linux.vanilla/drivers/isdn/avmb1/b1dma.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/avmb1/b1dma.c	Wed Dec 13 22:06:26 2000
@@ -1,11 +1,17 @@
 /*
- * $Id: b1dma.c,v 1.3 2000/02/26 01:00:53 keil Exp $
+ * $Id: b1dma.c,v 1.5 2000/06/19 16:51:53 keil Exp $
  * 
  * Common module for AVM B1 cards that support dma with AMCC
  * 
  * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1dma.c,v $
+ * Revision 1.5  2000/06/19 16:51:53  keil
+ * don't free skb in irq context
+ *
+ * Revision 1.4  2000/04/03 16:38:05  calle
+ * made suppress_pollack static.
+ *
  * Revision 1.3  2000/02/26 01:00:53  keil
  * changes from 2.3.47
  *
@@ -34,7 +40,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.3 $";
+static char *revision = "$Revision: 1.5 $";
 
 /* ------------------------------------------------------------- */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1isa.c linux.19p1/drivers/isdn/avmb1/b1isa.c
--- linux.vanilla/drivers/isdn/avmb1/b1isa.c	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/avmb1/b1isa.c	Wed Dec 13 22:06:26 2000
@@ -1,11 +1,15 @@
 /*
- * $Id: b1isa.c,v 1.7 2000/02/02 18:36:03 calle Exp $
+ * $Id: b1isa.c,v 1.8 2000/04/03 13:29:24 calle Exp $
  * 
  * Module for AVM B1 ISA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1isa.c,v $
+ * Revision 1.8  2000/04/03 13:29:24  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
  * Revision 1.7  2000/02/02 18:36:03  calle
  * - Modules are now locked while init_module is running
  * - fixed problem with memory mapping if address is not aligned
@@ -69,7 +73,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.7 $";
+static char *revision = "$Revision: 1.8 $";
 
 /* ------------------------------------------------------------- */
 
@@ -265,6 +269,9 @@
 {
 	struct capi_driver *driver = &b1isa_driver;
 	char *p;
+	int retval = 0;
+
+	MOD_INC_USE_COUNT;
 
 	if ((p = strchr(revision, ':'))) {
 		strncpy(driver->revision, p + 1, sizeof(driver->revision));
@@ -279,9 +286,10 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
-		return -EIO;
+		retval = -EIO;
 	}
-	return 0;
+	MOD_DEC_USE_COUNT;
+	return retval;
 }
 
 #ifdef MODULE
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1pci.c linux.19p1/drivers/isdn/avmb1/b1pci.c
--- linux.vanilla/drivers/isdn/avmb1/b1pci.c	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/avmb1/b1pci.c	Wed Dec 13 22:06:32 2000
@@ -1,11 +1,28 @@
 /*
- * $Id: b1pci.c,v 1.20 2000/02/02 18:36:03 calle Exp $
+ * $Id: b1pci.c,v 1.25 2000/05/29 12:29:18 keil Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.25  2000/05/29 12:29:18  keil
+ * make pci_enable_dev compatible to 2.2 kernel versions
+ *
+ * Revision 1.24  2000/05/19 15:43:22  calle
+ * added calls to pci_device_start().
+ *
+ * Revision 1.23  2000/05/06 00:52:36  kai
+ * merged changes from kernel tree
+ * fixed timer and net_device->name breakage
+ *
+ * Revision 1.22  2000/04/21 13:01:33  calle
+ * Revision in b1pciv4 driver was missing.
+ *
+ * Revision 1.21  2000/04/03 13:29:24  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
  * Revision 1.20  2000/02/02 18:36:03  calle
  * - Modules are now locked while init_module is running
  * - fixed problem with memory mapping if address is not aligned
@@ -70,12 +87,13 @@
 #include <linux/pci.h>
 #include <linux/capi.h>
 #include <asm/io.h>
+#include <linux/isdn.h>
 #include "capicmd.h"
 #include "capiutil.h"
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.20 $";
+static char *revision = "$Revision: 1.25 $";
 
 /* ------------------------------------------------------------- */
 
@@ -469,6 +487,19 @@
 		param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK;
 		param.port = dev->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK;
 		param.irq = dev->irq;
+
+		retval = pci_enable_device (dev);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: failed to enable AVM-B1 V4 at i/o %#x, irq %d, mem %#x err=%d\n",
+			driver->name, param.port, param.irq, param.membase, retval);
+#ifdef MODULE
+			cleanup_module();
+#endif
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+
 		printk(KERN_INFO
 		"%s: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
 		driver->name, param.port, param.irq, param.membase);
@@ -486,6 +517,18 @@
 		param.membase = 0;
 		param.port = dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
 		param.irq = dev->irq;
+
+		retval = pci_enable_device (dev);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: failed to enable AVM-B1 at i/o %#x, irq %d, err=%d\n",
+			driver->name, param.port, param.irq, retval);
+#ifdef MODULE
+			cleanup_module();
+#endif
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
 		printk(KERN_INFO
 		"%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
 		driver->name, param.port, param.irq);
@@ -509,10 +552,18 @@
 	char *p;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	if ((p = strchr(revision, ':'))) {
 		strncpy(driver->revision, p + 1, sizeof(driver->revision));
 		p = strchr(driver->revision, '$');
 		*p = 0;
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+	        p = strchr(revision, ':');
+		strncpy(driverv4->revision, p + 1, sizeof(driverv4->revision));
+		p = strchr(driverv4->revision, '$');
+		*p = 0;
+#endif
 	}
 
 	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
@@ -522,6 +573,7 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -534,6 +586,7 @@
     		detach_capi_driver(driver);
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driverv4->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 #endif
@@ -545,6 +598,7 @@
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
     		detach_capi_driver(driverv4);
 #endif
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -554,6 +608,7 @@
 #ifdef MODULE
 			cleanup_module();
 #endif
+			MOD_DEC_USE_COUNT;
 			return retval;
 		}
 		ncards++;
@@ -561,12 +616,15 @@
 	if (ncards) {
 		printk(KERN_INFO "%s: %d B1-PCI card(s) detected\n",
 				driver->name, ncards);
+		MOD_DEC_USE_COUNT;
 		return 0;
 	}
 	printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name);
+	MOD_DEC_USE_COUNT;
 	return -ESRCH;
 #else
 	printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name);
+	MOD_DEC_USE_COUNT;
 	return -EIO;
 #endif
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1pcmcia.c linux.19p1/drivers/isdn/avmb1/b1pcmcia.c
--- linux.vanilla/drivers/isdn/avmb1/b1pcmcia.c	Fri Apr 21 12:46:07 2000
+++ linux.19p1/drivers/isdn/avmb1/b1pcmcia.c	Wed Dec 13 22:06:32 2000
@@ -1,11 +1,24 @@
 /*
- * $Id: b1pcmcia.c,v 1.7 2000/02/02 18:36:03 calle Exp $
+ * $Id: b1pcmcia.c,v 1.10 2000/05/06 00:52:36 kai Exp $
  * 
  * Module for AVM B1/M1/M2 PCMCIA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pcmcia.c,v $
+ * Revision 1.10  2000/05/06 00:52:36  kai
+ * merged changes from kernel tree
+ * fixed timer and net_device->name breakage
+ *
+ * Revision 1.9  2000/04/03 13:29:24  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
+ * Revision 1.8  2000/03/06 18:00:23  calle
+ * - Middleware extention now working with 2.3.49 (capifs).
+ * - Fixed typos in debug section of capi.c
+ * - Bugfix: Makefile corrected for b1pcmcia.c
+ *
  * Revision 1.7  2000/02/02 18:36:03  calle
  * - Modules are now locked while init_module is running
  * - fixed problem with memory mapping if address is not aligned
@@ -70,7 +83,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.7 $";
+static char *revision = "$Revision: 1.10 $";
 
 /* ------------------------------------------------------------- */
 
@@ -296,6 +309,9 @@
 {
 	struct capi_driver *driver = &b1pcmcia_driver;
 	char *p;
+	int retval = 0;
+
+	MOD_INC_USE_COUNT;
 
 	if ((p = strchr(revision, ':'))) {
 		strncpy(driver->revision, p + 1, sizeof(driver->revision));
@@ -310,9 +326,10 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
-		return -EIO;
+		retval = -EIO;
 	}
-	return 0;
+	MOD_DEC_USE_COUNT;
+	return retval;
 }
 
 #ifdef MODULE
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/c4.c linux.19p1/drivers/isdn/avmb1/c4.c
--- linux.vanilla/drivers/isdn/avmb1/c4.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/avmb1/c4.c	Wed Dec 13 22:06:43 2000
@@ -1,11 +1,34 @@
 /*
- * $Id: c4.c,v 1.5 2000/03/16 15:21:03 calle Exp $
+ * $Id: c4.c,v 1.12 2000/06/19 16:51:53 keil Exp $
  * 
  * Module for AVM C4 card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: c4.c,v $
+ * Revision 1.12  2000/06/19 16:51:53  keil
+ * don't free skb in irq context
+ *
+ * Revision 1.11  2000/06/19 15:11:24  keil
+ * avoid use of freed structs
+ * changes from 2.4.0-ac21
+ *
+ * Revision 1.10  2000/05/29 12:29:18  keil
+ * make pci_enable_dev compatible to 2.2 kernel versions
+ *
+ * Revision 1.9  2000/05/19 15:43:22  calle
+ * added calls to pci_device_start().
+ *
+ * Revision 1.8  2000/04/03 16:38:05  calle
+ * made suppress_pollack static.
+ *
+ * Revision 1.7  2000/04/03 13:29:24  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
+ * Revision 1.6  2000/03/17 12:21:08  calle
+ * send patchvalues now working.
+ *
  * Revision 1.5  2000/03/16 15:21:03  calle
  * Bugfix in c4_remove: loop 5 times instead of 4 :-(
  *
@@ -36,6 +59,7 @@
 #include <linux/ioport.h>
 #include <linux/pci.h>
 #include <linux/capi.h>
+#include <linux/isdn.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include "capicmd.h"
@@ -43,7 +67,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.12 $";
 
 #undef CONFIG_C4_DEBUG
 #undef CONFIG_C4_POLLDEBUG
@@ -774,45 +798,78 @@
 	c4_dispatch_tx(card);
 }
 
-static int c4_send_config(avmcard *card, capiloaddatapart * config)
+static int queue_sendconfigword(avmcard *card, __u32 val)
 {
 	struct sk_buff *skb;
-	__u8 val[sizeof(__u32)];
 	void *p;
-	unsigned char *dp;
-	int left, retval;
-	
-	skb = alloc_skb(12 + ((config->len+3)/4)*5, GFP_ATOMIC);
+
+	skb = alloc_skb(3+4, GFP_ATOMIC);
 	if (!skb) {
-		printk(KERN_CRIT "%s: no memory, can't send config.\n",
+		printk(KERN_CRIT "%s: no memory, send config\n",
 					card->name);
-		return	-ENOMEM;
+		return -ENOMEM;
 	}
 	p = skb->data;
 	_put_byte(&p, 0);
 	_put_byte(&p, 0);
 	_put_byte(&p, SEND_CONFIG);
-	_put_word(&p, 1);
+	_put_word(&p, val);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+	return 0;
+}
+
+static int queue_sendconfig(avmcard *card, char cval[4])
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3+4, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, send config\n",
+					card->name);
+		return -ENOMEM;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
 	_put_byte(&p, SEND_CONFIG);
-	_put_word(&p, config->len);	/* 12 */
+	_put_byte(&p, cval[0]);
+	_put_byte(&p, cval[1]);
+	_put_byte(&p, cval[2]);
+	_put_byte(&p, cval[3]);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+	return 0;
+}
+
+static int c4_send_config(avmcard *card, capiloaddatapart * config)
+{
+	__u8 val[4];
+	unsigned char *dp;
+	int left, retval;
+	
+	if ((retval = queue_sendconfigword(card, 1)) != 0)
+		return retval;
+	if ((retval = queue_sendconfigword(card, config->len)) != 0)
+		return retval;
 
 	dp = config->data;
 	left = config->len;
 	while (left >= sizeof(__u32)) {
 	        if (config->user) {
 			retval = copy_from_user(val, dp, sizeof(val));
-			if (retval) {
-				dev_kfree_skb(skb);
+			if (retval)
 				return -EFAULT;
-			}
 		} else {
 			memcpy(val, dp, sizeof(val));
 		}
-		_put_byte(&p, SEND_CONFIG);
-		_put_byte(&p, val[0]);
-		_put_byte(&p, val[1]);
-		_put_byte(&p, val[2]);
-		_put_byte(&p, val[3]);
+		if ((retval = queue_sendconfig(card, val)) != 0)
+			return retval;
 		left -= sizeof(val);
 		dp += sizeof(val);
 	}
@@ -820,25 +877,15 @@
 		memset(val, 0, sizeof(val));
 		if (config->user) {
 			retval = copy_from_user(&val, dp, left);
-			if (retval) {
-				dev_kfree_skb(skb);
+			if (retval)
 				return -EFAULT;
-			}
 		} else {
 			memcpy(&val, dp, left);
 		}
-		_put_byte(&p, SEND_CONFIG);
-		_put_byte(&p, val[0]);
-		_put_byte(&p, val[1]);
-		_put_byte(&p, val[2]);
-		_put_byte(&p, val[3]);
+		if ((retval = queue_sendconfig(card, val)) != 0)
+			return retval;
 	}
 
-	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
-
-	skb_queue_tail(&card->dma->send_queue, skb);
-	c4_dispatch_tx(card);
-
 	return 0;
 }
 
@@ -875,8 +922,15 @@
 	c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
 	restore_flags(flags);
 
-	if (data->configuration.len > 0 && data->configuration.data)
-		c4_send_config(card, &data->configuration);
+	if (data->configuration.len > 0 && data->configuration.data) {
+		retval = c4_send_config(card, &data->configuration);
+		if (retval) {
+			printk(KERN_ERR "%s: failed to set config!!\n",
+					card->name);
+			c4_reset(card);
+			return retval;
+		}
+	}
 
         c4_send_init(card);
 
@@ -910,8 +964,10 @@
 
         for (i=0; i < 4; i++) {
 		cinfo = &card->ctrlinfo[i];
-		if (cinfo->capi_ctrl)
+		if (cinfo->capi_ctrl) {
 			di->detach_ctr(cinfo->capi_ctrl);
+			cinfo->capi_ctrl = NULL;
+		}
 	}
 
 	free_irq(card->irq, card);
@@ -1124,7 +1180,6 @@
         cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info)*4, GFP_ATOMIC);
 	if (!cinfo) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
-	        kfree(card->ctrlinfo);
 		kfree(card->dma);
 		kfree(card);
 	        MOD_DEC_USE_COUNT;
@@ -1282,6 +1337,8 @@
 	char *p;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	if ((p = strchr(revision, ':'))) {
 		strncpy(driver->revision, p + 1, sizeof(driver->revision));
 		p = strchr(driver->revision, '$');
@@ -1295,6 +1352,7 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -1302,6 +1360,7 @@
 	if (!pci_present()) {
 		printk(KERN_ERR "%s: no PCI bus present\n", driver->name);
     		detach_capi_driver(driver);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -1314,6 +1373,18 @@
 		param.irq = dev->irq;
 		param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK;
 
+		retval = pci_enable_device (dev);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: failed to enable AVM-C4 at i/o %#x, irq %d, mem %#x err=%d\n",
+			driver->name, param.port, param.irq, param.membase, retval);
+#ifdef MODULE
+			cleanup_module();
+#endif
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+
 		printk(KERN_INFO
 			"%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n",
 			driver->name, param.port, param.irq, param.membase);
@@ -1325,6 +1396,7 @@
 #ifdef MODULE
 			cleanup_module();
 #endif
+			MOD_DEC_USE_COUNT;
 			return retval;
 		}
 		ncards++;
@@ -1332,12 +1404,15 @@
 	if (ncards) {
 		printk(KERN_INFO "%s: %d C4 card(s) detected\n",
 				driver->name, ncards);
+		MOD_DEC_USE_COUNT;
 		return 0;
 	}
 	printk(KERN_ERR "%s: NO C4 card detected\n", driver->name);
+	MOD_DEC_USE_COUNT;
 	return -ESRCH;
 #else
 	printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name);
+	MOD_DEC_USE_COUNT;
 	return -EIO;
 #endif
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capi.c linux.19p1/drivers/isdn/avmb1/capi.c
--- linux.vanilla/drivers/isdn/avmb1/capi.c	Sun Jun 11 21:44:13 2000
+++ linux.19p1/drivers/isdn/avmb1/capi.c	Wed Dec 13 22:06:43 2000
@@ -1,11 +1,68 @@
 /*
- * $Id: capi.c,v 1.23 2000/02/26 01:00:53 keil Exp $
+ * $Id: capi.c,v 1.35 2000/06/19 15:11:24 keil Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capi.c,v $
+ * Revision 1.35  2000/06/19 15:11:24  keil
+ * avoid use of freed structs
+ * changes from 2.4.0-ac21
+ *
+ * Revision 1.34  2000/06/18 16:09:54  keil
+ * more changes for 2.4
+ *
+ * Revision 1.33  2000/05/18 16:35:43  calle
+ * Uaaahh. Bad memory leak fixed.
+ *
+ * Revision 1.32  2000/04/21 12:38:42  calle
+ * Bugfix: error in proc_ functions, begin-off => off-begin
+ *
+ * Revision 1.31  2000/04/03 13:29:24  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
+ * Revision 1.30  2000/03/19 12:31:36  calle
+ * PPP over CAPI raw driver disabled for now, ppp_generic has been changed.
+ *
+ * Revision 1.29  2000/03/13 17:48:13  calle
+ * removed unused variable.
+ *
+ * Revision 1.28  2000/03/08 17:06:33  calle
+ * - changes for devfs and 2.3.49
+ * - capifs now configurable (no need with devfs)
+ * - New Middleware ioctl CAPI_NCCI_GETUNIT
+ * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs)
+ *
+ * Revision 1.27  2000/03/06 18:00:23  calle
+ * - Middleware extention now working with 2.3.49 (capifs).
+ * - Fixed typos in debug section of capi.c
+ * - Bugfix: Makefile corrected for b1pcmcia.c
+ *
+ * Revision 1.26  2000/03/03 16:48:38  calle
+ * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI)
+ *   It is now possible to create a connection with a CAPI2.0 applikation
+ *   and than to handle the data connection from /dev/capi/ (capifs) and also
+ *   using async or sync PPP on this connection.
+ *   The two major device number 190 and 191 are not confirmed yet,
+ *   but I want to save the code in cvs, before I go on.
+ *
+ * Revision 1.25  2000/03/03 16:37:11  kai
+ * incorporated some cosmetic changes from the official kernel tree back
+ * into CVS
+ *
+ * Revision 1.24  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.23  2000/02/26 01:00:53  keil
  * changes from 2.3.47
  *
@@ -126,84 +183,705 @@
 #include <linux/mm.h>
 #include <linux/timer.h>
 #include <linux/wait.h>
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#include <linux/tty.h>
+#ifdef CONFIG_PPP
+#include <linux/netdevice.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#undef CAPI_PPP_ON_RAW_DEVICE
+#endif /* CONFIG_PPP */
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
 #include <linux/poll.h>
 #include <linux/capi.h>
 #include <linux/kernelcapi.h>
-
 #include "capiutil.h"
 #include "capicmd.h"
-#include "capidev.h"
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#include "capifs.h"
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+static char *revision = "$Revision: 1.35 $";
 
 MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)");
 
+#undef _DEBUG_REFCOUNT		/* alloc/free and open/close debug */
+#undef _DEBUG_TTYFUNCS		/* call to tty_driver */
+#undef _DEBUG_DATAFLOW		/* data flow */
+
 /* -------- driver information -------------------------------------- */
 
 int capi_major = 68;		/* allocated */
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+int capi_rawmajor = 190;
+int capi_ttymajor = 191;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
 MODULE_PARM(capi_major, "i");
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+MODULE_PARM(capi_rawmajor, "i");
+MODULE_PARM(capi_ttymajor, "i");
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- defines ------------------------------------------------- */
+
+#define CAPINC_MAX_RECVQUEUE	10
+#define CAPINC_MAX_SENDQUEUE	10
+#define CAPI_MAX_BLKSIZE	2048
+
+/* -------- data structures ----------------------------------------- */
+
+struct capidev;
+struct capincci;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+struct capiminor;
+
+struct capiminor {
+	struct capiminor *next;
+	struct capincci  *nccip;
+	unsigned int      minor;
+
+	__u16		 applid;
+	__u32		 ncci;
+	__u16		 datahandle;
+	__u16		 msgid;
+
+	struct file      *file;
+	struct tty_struct *tty;
+	int                ttyinstop;
+	int                ttyoutstop;
+	struct sk_buff    *ttyskb;
+	atomic_t           ttyopencount;
+
+	struct sk_buff_head inqueue;
+	int                 inbytes;
+	struct sk_buff_head outqueue;
+	int                 outbytes;
+
+	/* for raw device */
+	struct sk_buff_head recvqueue;
+	struct wait_queue *recvwait;
+	struct wait_queue *sendwait;
+	
+	/* transmit path */
+	struct datahandle_queue {
+		    struct datahandle_queue *next;
+		    __u16                    datahandle;
+	} *ackqueue;
+	int nack;
+
+};
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+struct capincci {
+	struct capincci *next;
+	__u32		 ncci;
+	struct capidev	*cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *minorp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+};
+
+struct capidev {
+	struct capidev *next;
+	struct file    *file;
+	__u16		applid;
+	__u16		errcode;
+	unsigned int    minor;
+	unsigned        userflags;
+
+	struct sk_buff_head recvqueue;
+	struct wait_queue *recvwait;
+
+	/* Statistic */
+	unsigned long	nrecvctlpkt;
+	unsigned long	nrecvdatapkt;
+	unsigned long	nsentctlpkt;
+	unsigned long	nsentdatapkt;
+	
+	struct capincci *nccis;
+};
 
 /* -------- global variables ---------------------------------------- */
 
-static struct capidev capidevs[CAPI_MAXMINOR + 1];
-struct capi_interface *capifuncs;
+static struct capi_interface *capifuncs = 0;
+static struct capidev *capidev_openlist = 0;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+static struct capiminor *minors = 0;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
-/* -------- function called by lower level -------------------------- */
 
-static void capi_signal(__u16 applid, __u32 minor)
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- datahandles --------------------------------------------- */
+
+int capincci_add_ack(struct capiminor *mp, __u16 datahandle)
+{
+	struct datahandle_queue *n, **pp;
+
+	n = (struct datahandle_queue *)
+	kmalloc(sizeof(struct datahandle_queue), GFP_ATOMIC);
+	if (!n) {
+	   printk(KERN_ERR "capi: alloc datahandle failed\n");
+	   return -1;
+	}
+	n->next = 0;
+	n->datahandle = datahandle;
+	for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ;
+	*pp = n;
+	mp->nack++;
+	return 0;
+}
+
+int capiminor_del_ack(struct capiminor *mp, __u16 datahandle)
+{
+	struct datahandle_queue **pp, *p;
+
+	for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) {
+ 		if ((*pp)->datahandle == datahandle) {
+			p = *pp;
+			*pp = (*pp)->next;
+		        kfree(p);
+			mp->nack--;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+void capiminor_del_all_ack(struct capiminor *mp)
+{
+	struct datahandle_queue **pp, *p;
+
+	for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) {
+		p = *pp;
+		*pp = (*pp)->next;
+		kfree(p);
+		mp->nack--;
+	}
+}
+
+
+/* -------- struct capiminor ---------------------------------------- */
+
+struct capiminor *capiminor_alloc(__u16 applid, __u32 ncci)
+{
+	struct capiminor *mp, **pp;
+        unsigned int minor = 0;
+
+	MOD_INC_USE_COUNT;
+	mp = (struct capiminor *)kmalloc(sizeof(struct capiminor), GFP_ATOMIC);
+	if (!mp) {
+		MOD_DEC_USE_COUNT;
+		printk(KERN_ERR "capi: can't alloc capiminor\n");
+		return 0;
+	}
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capiminor_alloc %d\n", GET_USE_COUNT(&__this_module));
+#endif
+	memset(mp, 0, sizeof(struct capiminor));
+	mp->applid = applid;
+	mp->ncci = ncci;
+	mp->msgid = 0;
+	atomic_set(&mp->ttyopencount,0);
+
+	skb_queue_head_init(&mp->inqueue);
+	skb_queue_head_init(&mp->outqueue);
+
+	skb_queue_head_init(&mp->recvqueue);
+
+	for (pp = &minors; *pp; pp = &(*pp)->next) {
+		if ((*pp)->minor < minor)
+			continue;
+		if ((*pp)->minor > minor)
+			break;
+		minor++;
+	}
+	mp->minor = minor;
+	mp->next = *pp;
+	*pp = mp;
+	return mp;
+}
+
+void capiminor_free(struct capiminor *mp)
+{
+	struct capiminor **pp;
+	struct sk_buff *skb;
+
+	pp = &minors;
+	while (*pp) {
+		if (*pp == mp) {
+			*pp = (*pp)->next;
+			if (mp->ttyskb) kfree_skb(mp->ttyskb);
+			mp->ttyskb = 0;
+			while ((skb = skb_dequeue(&mp->recvqueue)) != 0)
+				kfree_skb(skb);
+			while ((skb = skb_dequeue(&mp->inqueue)) != 0)
+				kfree_skb(skb);
+			while ((skb = skb_dequeue(&mp->outqueue)) != 0)
+				kfree_skb(skb);
+			capiminor_del_all_ack(mp);
+			kfree(mp);
+			MOD_DEC_USE_COUNT;
+#ifdef _DEBUG_REFCOUNT
+			printk(KERN_DEBUG "capiminor_free %d\n", GET_USE_COUNT(&__this_module));
+#endif
+			return;
+		} else {
+			pp = &(*pp)->next;
+		}
+	}
+}
+
+struct capiminor *capiminor_find(unsigned int minor)
+{
+	struct capiminor *p;
+	for (p = minors; p && p->minor != minor; p = p->next)
+		;
+	return p;
+}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- struct capincci ----------------------------------------- */
+
+static struct capincci *capincci_alloc(struct capidev *cdev, __u32 ncci)
+{
+	struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *mp = 0;
+	kdev_t kdev;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+	np = (struct capincci *)kmalloc(sizeof(struct capincci), GFP_ATOMIC);
+	if (!np)
+		return 0;
+	memset(np, 0, sizeof(struct capincci));
+	np->ncci = ncci;
+	np->cdev = cdev;
+	for (pp=&cdev->nccis; *pp; pp = &(*pp)->next)
+		;
+	*pp = np;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	mp = 0;
+	if (cdev->userflags & CAPIFLAG_HIGHJACKING)
+		mp = np->minorp = capiminor_alloc(cdev->applid, ncci);
+	if (mp) {
+		mp->nccip = np;
+#ifdef _DEBUG_REFCOUNT
+		printk(KERN_DEBUG "set mp->nccip\n");
+#endif
+#ifdef CONFIG_ISDN_CAPIFS
+		kdev = MKDEV(capi_rawmajor, mp->minor);
+		capifs_new_ncci('r', mp->minor, kdev);
+		kdev = MKDEV(capi_ttymajor, mp->minor);
+		capifs_new_ncci(0, mp->minor, kdev);
+#endif
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+        return np;
+}
+
+static void capincci_free(struct capidev *cdev, __u32 ncci)
+{
+	struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+	pp=&cdev->nccis;
+	while (*pp) {
+		np = *pp;
+		if (ncci == 0xffffffff || np->ncci == ncci) {
+			*pp = (*pp)->next;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+			if ((mp = np->minorp) != 0) {
+#ifdef CONFIG_ISDN_CAPIFS
+				capifs_free_ncci('r', mp->minor);
+				capifs_free_ncci(0, mp->minor);
+#endif
+				if (mp->tty) {
+					mp->nccip = 0;
+#ifdef _DEBUG_REFCOUNT
+					printk(KERN_DEBUG "reset mp->nccip\n");
+#endif
+					tty_hangup(mp->tty);
+				} else if (mp->file) {
+					mp->nccip = 0;
+#ifdef _DEBUG_REFCOUNT
+					printk(KERN_DEBUG "reset mp->nccip\n");
+#endif
+					wake_up_interruptible(&mp->recvwait);
+					wake_up_interruptible(&mp->sendwait);
+				} else {
+					capiminor_free(mp);
+				}
+			}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			kfree(np);
+			if (*pp == 0) return;
+		} else {
+			pp = &(*pp)->next;
+		}
+	}
+}
+
+struct capincci *capincci_find(struct capidev *cdev, __u32 ncci)
+{
+	struct capincci *p;
+
+	for (p=cdev->nccis; p ; p = p->next) {
+		if (p->ncci == ncci)
+			break;
+	}
+	return p;
+}
+
+/* -------- struct capidev ------------------------------------------ */
+
+static struct capidev *capidev_alloc(struct file *file)
 {
 	struct capidev *cdev;
+	struct capidev **pp;
+
+	cdev = (struct capidev *)kmalloc(sizeof(struct capidev), GFP_KERNEL);
+	if (!cdev)
+		return 0;
+	memset(cdev, 0, sizeof(struct capidev));
+	cdev->file = file;
+	cdev->minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+	skb_queue_head_init(&cdev->recvqueue);
+	pp=&capidev_openlist;
+	while (*pp) pp = &(*pp)->next;
+	*pp = cdev;
+        return cdev;
+}
+
+static void capidev_free(struct capidev *cdev)
+{
+	struct capidev **pp;
+	struct sk_buff *skb;
+
+	if (cdev->applid)
+		(*capifuncs->capi_release) (cdev->applid);
+	cdev->applid = 0;
+
+	while ((skb = skb_dequeue(&cdev->recvqueue)) != 0) {
+		kfree_skb(skb);
+	}
+	
+	pp=&capidev_openlist;
+	while (*pp && *pp != cdev) pp = &(*pp)->next;
+	if (*pp)
+		*pp = cdev->next;
+
+	kfree(cdev);
+}
+
+static struct capidev *capidev_find(__u16 applid)
+{
+	struct capidev *p;
+	for (p=capidev_openlist; p; p = p->next) {
+		if (p->applid == applid)
+			break;
+	}
+	return p;
+}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- handle data queue --------------------------------------- */
+
+struct sk_buff *
+gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
+{
+	struct sk_buff *nskb;
+	nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
+	if (nskb) {
+		__u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
+		unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
+		capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
+		capimsg_setu16(s, 2, mp->applid);
+		capimsg_setu8 (s, 4, CAPI_DATA_B3);
+		capimsg_setu8 (s, 5, CAPI_RESP);
+		capimsg_setu16(s, 6, mp->msgid++);
+		capimsg_setu32(s, 8, mp->ncci);
+		capimsg_setu16(s, 12, datahandle);
+	}
+	return nskb;
+}
+
+int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
+{
+	struct sk_buff *nskb;
+	unsigned int datalen;
+	__u16 errcode, datahandle;
+
+	datalen = skb->len - CAPIMSG_LEN(skb->data);
+	if (mp->tty) {
+		if (mp->tty->ldisc.receive_buf == 0) {
+			printk(KERN_ERR "capi: ldisc has no receive_buf function\n");
+			return -1;
+		}
+		if (mp->ttyinstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+			printk(KERN_DEBUG "capi: recv tty throttled\n");
+#endif
+			return -1;
+		}
+		if (mp->tty->ldisc.receive_room &&
+		    mp->tty->ldisc.receive_room(mp->tty) < datalen) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+			printk(KERN_DEBUG "capi: no room in tty\n");
+#endif
+			return -1;
+		}
+		if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) {
+			printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
+			return -1;
+		}
+		datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+		errcode = (*capifuncs->capi_put_message)(mp->applid, nskb);
+		if (errcode != CAPI_NOERROR) {
+			printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
+					errcode);
+			kfree_skb(nskb);
+			return -1;
+		}
+		(void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
+					datahandle, skb->len);
+#endif
+		mp->tty->ldisc.receive_buf(mp->tty, skb->data, 0, skb->len);
+		kfree_skb(skb);
+		return 0;
+
+	} else if (mp->file) {
+		if (skb_queue_len(&mp->recvqueue) > CAPINC_MAX_RECVQUEUE) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+			printk(KERN_DEBUG "capi: no room in raw queue\n");
+#endif
+			return -1;
+		}
+		if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) {
+			printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
+			return -1;
+		}
+		datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+		errcode = (*capifuncs->capi_put_message)(mp->applid, nskb);
+		if (errcode != CAPI_NOERROR) {
+			printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
+					errcode);
+			kfree_skb(nskb);
+			return -1;
+		}
+		(void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => raw\n",
+					datahandle, skb->len);
+#endif
+		skb_queue_tail(&mp->recvqueue, skb);
+		wake_up_interruptible(&mp->recvwait);
+		return 0;
+	}
+#ifdef _DEBUG_DATAFLOW
+	printk(KERN_DEBUG "capi: currently no receiver\n");
+#endif
+	return -1;
+}
+
+void handle_minor_recv(struct capiminor *mp)
+{
+	struct sk_buff *skb;
+	while ((skb = skb_dequeue(&mp->inqueue)) != 0) {
+		unsigned int len = skb->len;
+		mp->inbytes -= len;
+		if (handle_recv_skb(mp, skb) < 0) {
+			skb_queue_head(&mp->inqueue, skb);
+			mp->inbytes += len;
+			return;
+		}
+	}
+}
+
+int handle_minor_send(struct capiminor *mp)
+{
+	struct sk_buff *skb;
+	__u16 len;
+	int count = 0;
+	__u16 errcode;
+	__u16 datahandle;
+
+	if (mp->tty && mp->ttyoutstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+		printk(KERN_DEBUG "capi: send: tty stopped\n");
+#endif
+		return 0;
+	}
+
+	while ((skb = skb_dequeue(&mp->outqueue)) != 0) {
+		datahandle = mp->datahandle;
+		len = (__u16)skb->len;
+		skb_push(skb, CAPI_DATA_B3_REQ_LEN);
+		memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+		capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+		capimsg_setu16(skb->data, 2, mp->applid);
+		capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
+		capimsg_setu8 (skb->data, 5, CAPI_REQ);
+		capimsg_setu16(skb->data, 6, mp->msgid++);
+		capimsg_setu32(skb->data, 8, mp->ncci);	/* NCCI */
+		capimsg_setu32(skb->data, 12, (__u32) skb->data); /* Data32 */
+		capimsg_setu16(skb->data, 16, len);	/* Data length */
+		capimsg_setu16(skb->data, 18, datahandle);
+		capimsg_setu16(skb->data, 20, 0);	/* Flags */
+
+		if (capincci_add_ack(mp, datahandle) < 0) {
+			skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+			skb_queue_head(&mp->outqueue, skb);
+			return count;
+		}
+		errcode = (*capifuncs->capi_put_message) (mp->applid, skb);
+		if (errcode == CAPI_NOERROR) {
+			mp->datahandle++;
+			count++;
+			mp->outbytes -= len;
+#ifdef _DEBUG_DATAFLOW
+			printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
+							datahandle, len);
+#endif
+			continue;
+		}
+		capiminor_del_ack(mp, datahandle);
+
+		if (errcode == CAPI_SENDQUEUEFULL) {
+			skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+			skb_queue_head(&mp->outqueue, skb);
+			break;
+		}
+
+		/* ups, drop packet */
+		printk(KERN_ERR "capi: put_message = %x\n", errcode);
+		mp->outbytes -= len;
+		kfree_skb(skb);
+	}
+	if (count)
+		wake_up_interruptible(&mp->sendwait);
+	return count;
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+/* -------- function called by lower level -------------------------- */
+
+static void capi_signal(__u16 applid, void *param)
+{
+	struct capidev *cdev = (struct capidev *)param;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *mp;
+	__u16 datahandle;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	struct capincci *np;
 	struct sk_buff *skb = 0;
+	__u32 ncci;
 
-	if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) {
-		printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor);
+	(void) (*capifuncs->capi_get_message) (applid, &skb);
+	if (!skb) {
+		printk(KERN_ERR "BUG: capi_signal: no skb\n");
 		return;
 	}
-	cdev = &capidevs[minor];
-	(void) (*capifuncs->capi_get_message) (applid, &skb);
-	if (skb) {
-		skb_queue_tail(&cdev->recv_queue, skb);
-		wake_up_interruptible(&cdev->recv_wait);
+
+	if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+		return;
+	}
+	ncci = CAPIMSG_CONTROL(skb->data);
+	for (np = cdev->nccis; np && np->ncci != ncci; np = np->next)
+		;
+	if (!np) {
+		printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+		return;
+	}
+#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
+	skb_queue_tail(&cdev->recvqueue, skb);
+	wake_up_interruptible(&cdev->recvwait);
+#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	mp = np->minorp;
+	if (!mp) {
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+		return;
+	}
+
+
+	if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
+		datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n",
+				datahandle, skb->len-CAPIMSG_LEN(skb->data));
+#endif
+		skb_queue_tail(&mp->inqueue, skb);
+		mp->inbytes += skb->len;
+		handle_minor_recv(mp);
+
+	} else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
+
+		datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4);
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n",
+				datahandle,
+				CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
+#endif
+		kfree_skb(skb);
+		(void)capiminor_del_ack(mp, datahandle);
+		if (mp->tty) {
+			if (mp->tty->ldisc.write_wakeup)
+				mp->tty->ldisc.write_wakeup(mp->tty);
+		} else {
+			wake_up_interruptible(&mp->sendwait);
+		}
+		(void)handle_minor_send(mp);
+
 	} else {
-		printk(KERN_ERR "BUG: capi_signal: no skb\n");
+		/* ups, let capi application handle it :-) */
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
 	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 }
 
-/* -------- file_operations ----------------------------------------- */
+/* -------- file_operations for capidev ----------------------------- */
 
-static long long capi_llseek(struct file *file,
-			     long long offset, int origin)
+static loff_t
+capi_llseek(struct file *file, loff_t offset, int origin)
 {
 	return -ESPIPE;
 }
 
-static ssize_t capi_read(struct file *file, char *buf,
-		      size_t count, loff_t *ppos)
+static ssize_t
+capi_read(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
-        struct inode *inode = file->f_dentry->d_inode;
-	unsigned int minor = MINOR(inode->i_rdev);
-	struct capidev *cdev;
+	struct capidev *cdev = (struct capidev *)file->private_data;
 	struct sk_buff *skb;
 	int retval;
 	size_t copied;
 
-       if (ppos != &file->f_pos)
+	if (ppos != &file->f_pos)
 		return -ESPIPE;
 
-	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+	if (!cdev->applid)
 		return -ENODEV;
 
-	cdev = &capidevs[minor];
-
-	if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) {
+	if ((skb = skb_dequeue(&cdev->recvqueue)) == 0) {
 
 		if (file->f_flags & O_NONBLOCK)
 			return -EAGAIN;
 
 		for (;;) {
-			interruptible_sleep_on(&cdev->recv_wait);
-			if ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
+			interruptible_sleep_on(&cdev->recvwait);
+			if ((skb = skb_dequeue(&cdev->recvqueue)) != 0)
 				break;
 			if (signal_pending(current))
 				break;
@@ -212,63 +890,58 @@
 			return -ERESTARTNOHAND;
 	}
 	if (skb->len > count) {
-		skb_queue_head(&cdev->recv_queue, skb);
+		skb_queue_head(&cdev->recvqueue, skb);
 		return -EMSGSIZE;
 	}
 	retval = copy_to_user(buf, skb->data, skb->len);
 	if (retval) {
-		skb_queue_head(&cdev->recv_queue, skb);
+		skb_queue_head(&cdev->recvqueue, skb);
 		return retval;
 	}
 	copied = skb->len;
 
-	if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
-	    && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND)
+	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) {
 		cdev->nrecvdatapkt++;
-        else cdev->nrecvctlpkt++;
+	} else {
+		cdev->nrecvctlpkt++;
+	}
+
 	kfree_skb(skb);
 
 	return copied;
 }
 
-static ssize_t capi_write(struct file *file, const char *buf,
-		       size_t count, loff_t *ppos)
+static ssize_t
+capi_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-        struct inode *inode = file->f_dentry->d_inode;
-	unsigned int minor = MINOR(inode->i_rdev);
-	struct capidev *cdev;
+	struct capidev *cdev = (struct capidev *)file->private_data;
 	struct sk_buff *skb;
 	int retval;
-	__u8 cmd;
-	__u8 subcmd;
 	__u16 mlen;
 
-       if (ppos != &file->f_pos)
+        if (ppos != &file->f_pos)
 		return -ESPIPE;
 
-	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+	if (!cdev->applid)
 		return -ENODEV;
 
-	cdev = &capidevs[minor];
-
 	skb = alloc_skb(count, GFP_USER);
 
 	if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
 		kfree_skb(skb);
 		return retval;
 	}
-	cmd = CAPIMSG_COMMAND(skb->data);
-	subcmd = CAPIMSG_SUBCOMMAND(skb->data);
 	mlen = CAPIMSG_LEN(skb->data);
-	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
-		__u16 dlen = CAPIMSG_DATALEN(skb->data);
-		if (mlen + dlen != count) {
+	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+		if (mlen + CAPIMSG_DATALEN(skb->data) != count) {
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+	} else {
+		if (mlen != count) {
 			kfree_skb(skb);
 			return -EINVAL;
 		}
-	} else if (mlen != count) {
-		kfree_skb(skb);
-		return -EINVAL;
 	}
 	CAPIMSG_SETAPPID(skb->data, cdev->applid);
 
@@ -278,63 +951,56 @@
 		kfree_skb(skb);
 		return -EIO;
 	}
-	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ)
+	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
 		cdev->nsentdatapkt++;
-	else cdev->nsentctlpkt++;
+	} else {
+		cdev->nsentctlpkt++;
+	}
 	return count;
 }
 
 static unsigned int
 capi_poll(struct file *file, poll_table * wait)
 {
+	struct capidev *cdev = (struct capidev *)file->private_data;
 	unsigned int mask = 0;
-	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
-	struct capidev *cdev;
 
-	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+	if (!cdev->applid)
 		return POLLERR;
 
-	cdev = &capidevs[minor];
-	poll_wait(file, &(cdev->recv_wait), wait);
+	poll_wait(file, &(cdev->recvwait), wait);
 	mask = POLLOUT | POLLWRNORM;
-	if (!skb_queue_empty(&cdev->recv_queue))
+	if (!skb_queue_empty(&cdev->recvqueue))
 		mask |= POLLIN | POLLRDNORM;
 	return mask;
 }
 
-static int capi_ioctl(struct inode *inode, struct file *file,
+static int
+capi_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-	struct capidev *cdev;
+	struct capidev *cdev = (struct capidev *)file->private_data;
 	capi_ioctl_struct data;
-	int retval;
-
-
-	if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open)
-		return -ENODEV;
-
-	cdev = &capidevs[minor];
+	int retval = -EINVAL;
 
 	switch (cmd) {
 	case CAPI_REGISTER:
 		{
-			if (!minor)
-				return -EINVAL;
 			retval = copy_from_user((void *) &data.rparams,
 						(void *) arg, sizeof(struct capi_register_params));
 			if (retval)
 				return -EFAULT;
-			if (cdev->is_registered)
+			if (cdev->applid)
 				return -EEXIST;
 			cdev->errcode = (*capifuncs->capi_register) (&data.rparams,
 							  &cdev->applid);
-			if (cdev->errcode)
+			if (cdev->errcode) {
+				cdev->applid = 0;
 				return -EIO;
-			(void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor);
-			cdev->is_registered = 1;
+			}
+			(void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, cdev);
 		}
-		return 0;
+		return (int)cdev->applid;
 
 	case CAPI_GET_VERSION:
 		{
@@ -440,8 +1106,6 @@
 	case CAPI_MANUFACTURER_CMD:
 		{
 			struct capi_manufacturer_cmd mcmd;
-			if (minor)
-				return -EINVAL;
 			if (!capable(CAP_SYS_ADMIN))
 				return -EPERM;
 			retval = copy_from_user((void *) &mcmd, (void *) arg,
@@ -451,116 +1115,730 @@
 			return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data);
 		}
 		return 0;
+
+	case CAPI_SET_FLAGS:
+	case CAPI_CLR_FLAGS:
+		{
+			unsigned userflags;
+			retval = copy_from_user((void *) &userflags,
+						(void *) arg,
+						sizeof(userflags));
+			if (retval)
+				return -EFAULT;
+			if (cmd == CAPI_SET_FLAGS)
+				cdev->userflags |= userflags;
+			else
+				cdev->userflags &= ~userflags;
+		}
+		return 0;
+
+	case CAPI_GET_FLAGS:
+		{
+			retval = copy_to_user((void *) arg,
+					      (void *) &cdev->userflags,
+					      sizeof(cdev->userflags));
+			if (retval)
+				return -EFAULT;
+		}
+		return 0;
+
+	case CAPI_NCCI_OPENCOUNT:
+		{
+			struct capincci *nccip;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+			struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			unsigned ncci;
+			int count = 0;
+			retval = copy_from_user((void *) &ncci,
+						(void *) arg,
+						sizeof(ncci));
+			if (retval)
+				return -EFAULT;
+			nccip = capincci_find(cdev, (__u32) ncci);
+			if (!nccip)
+				return 0;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+			if ((mp = nccip->minorp) != 0) {
+				count += atomic_read(&mp->ttyopencount);
+				if (mp->file)
+					count++;
+			}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			return count;
+		}
+		return 0;
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	case CAPI_NCCI_GETUNIT:
+		{
+			struct capincci *nccip;
+			struct capiminor *mp;
+			unsigned ncci;
+			retval = copy_from_user((void *) &ncci,
+						(void *) arg,
+						sizeof(ncci));
+			if (retval)
+				return -EFAULT;
+			nccip = capincci_find(cdev, (__u32) ncci);
+			if (!nccip || (mp = nccip->minorp) == 0)
+				return -ESRCH;
+			return mp->minor;
+		}
+		return 0;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 	}
 	return -EINVAL;
 }
 
-static int capi_open(struct inode *inode, struct file *file)
+static int
+capi_open(struct inode *inode, struct file *file)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-
-	if (minor >= CAPI_MAXMINOR)
-		return -ENXIO;
-
-	if (minor) {
-		if (capidevs[minor].is_open)
-			return -EEXIST;
-
-		capidevs[minor].is_open = 1;
-		skb_queue_head_init(&capidevs[minor].recv_queue);
-		MOD_INC_USE_COUNT;
-		capidevs[minor].nopen++;
+	if (file->private_data)
+		return -EEXIST;
 
-	} else {
-		capidevs[minor].is_open++;
-		MOD_INC_USE_COUNT;
-	}
+	if ((file->private_data = capidev_alloc(file)) == 0)
+		return -ENOMEM;
 
+	MOD_INC_USE_COUNT;
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capi_open %d\n", GET_USE_COUNT(&__this_module));
+#endif
 	return 0;
 }
 
 static int
 capi_release(struct inode *inode, struct file *file)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-	struct capidev *cdev;
-	struct sk_buff *skb;
-
-	if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) {
-		printk(KERN_ERR "capi20: release minor %d ???\n", minor);
-		return 0;
-	}
-	cdev = &capidevs[minor];
-
-	if (minor) {
-
-		if (cdev->is_registered)
-			(*capifuncs->capi_release) (cdev->applid);
-
-		cdev->is_registered = 0;
-		cdev->applid = 0;
-
-		while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) {
-			kfree_skb(skb);
-		}
-		cdev->is_open = 0;
-	} else {
-		cdev->is_open--;
-	}
+	struct capidev *cdev = (struct capidev *)file->private_data;
 
+	capincci_free(cdev, 0xffffffff);
+	capidev_free(cdev);
+	file->private_data = NULL;
+	
 	MOD_DEC_USE_COUNT;
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capi_release %d\n", GET_USE_COUNT(&__this_module));
+#endif
 	return 0;
 }
 
 static struct file_operations capi_fops =
 {
-	capi_llseek,
-	capi_read,
-	capi_write,
-	NULL,			/* capi_readdir */
-	capi_poll,
-	capi_ioctl,
-	NULL,			/* capi_mmap */
-	capi_open,
-        NULL,                   /* capi_flush */
-	capi_release,
-	NULL,			/* capi_fsync */
-	NULL,			/* capi_fasync */
+	llseek:		capi_llseek,
+	read:		capi_read,
+	write:		capi_write,
+	poll:		capi_poll,
+	ioctl:		capi_ioctl,
+	open:		capi_open,
+	release:	capi_release,
 };
 
-/* -------- /proc functions ----------------------------------- */
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- file_operations for capincci ---------------------------- */
 
-/*
- * /proc/capi/capi20:
- *  minor opencount nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
- */
-static int proc_capidev_read_proc(char *page, char **start, off_t off,
-                                       int count, int *eof, void *data)
+static int
+capinc_raw_open(struct inode *inode, struct file *file)
 {
-        struct capidev *cp;
-	int i;
-	int len = 0;
-	off_t begin = 0;
+	struct capiminor *mp;
 
-	for (i=0; i < CAPI_MAXMINOR; i++) {
-		cp = &capidevs[i+1];
-		if (cp->nopen == 0) continue;
-		len += sprintf(page+len, "%d %lu %lu %lu %lu %lu\n",
-			i+1,
-			cp->nopen,
-			cp->nrecvctlpkt,
-			cp->nrecvdatapkt,
-			cp->nsentctlpkt,
-			cp->nsentdatapkt);
-		if (len+begin > off+count)
-			goto endloop;
-		if (len+begin < off) {
-			begin += len;
+	if (file->private_data)
+		return -EEXIST;
+	if ((mp = capiminor_find(MINOR(file->f_dentry->d_inode->i_rdev))) == 0)
+		return -ENXIO;
+	if (mp->nccip == 0)
+		return -ENXIO;
+	if (mp->file)
+		return -EBUSY;
+
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capi_raw_open %d\n", GET_USE_COUNT(&__this_module));
+#endif
+
+	mp->datahandle = 0;
+	mp->file = file;
+	file->private_data = (void *)mp;
+	handle_minor_recv(mp);
+	return 0;
+}
+
+static loff_t
+capinc_raw_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+static ssize_t
+capinc_raw_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	struct capiminor *mp = (struct capiminor *)file->private_data;
+	struct sk_buff *skb;
+	int retval;
+	size_t copied = 0;
+
+        if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!mp || !mp->nccip)
+		return -EINVAL;
+
+	if ((skb = skb_dequeue(&mp->recvqueue)) == 0) {
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		for (;;) {
+			interruptible_sleep_on(&mp->recvwait);
+			if (mp->nccip == 0)
+				return 0;
+			if ((skb = skb_dequeue(&mp->recvqueue)) != 0)
+				break;
+			if (signal_pending(current))
+				break;
+		}
+		if (skb == 0)
+			return -ERESTARTNOHAND;
+	}
+	do {
+		if (count < skb->len) {
+			retval = copy_to_user(buf, skb->data, count);
+			if (retval) {
+				skb_queue_head(&mp->recvqueue, skb);
+				return retval;
+			}
+			skb_pull(skb, count);
+			skb_queue_head(&mp->recvqueue, skb);
+			copied += count;
+			return copied;
+		} else {
+			retval = copy_to_user(buf, skb->data, skb->len);
+			if (retval) {
+				skb_queue_head(&mp->recvqueue, skb);
+				return copied;
+			}
+			copied += skb->len;
+			count -= skb->len;
+			buf += skb->len;
+			kfree_skb(skb);
+		}
+	} while ((skb = skb_dequeue(&mp->recvqueue)) != 0);
+
+	return copied;
+}
+
+static ssize_t
+capinc_raw_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	struct capiminor *mp = (struct capiminor *)file->private_data;
+	struct sk_buff *skb;
+	int retval;
+
+        if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!mp || !mp->nccip)
+		return -EINVAL;
+
+	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_USER);
+
+	skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+	if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
+		kfree_skb(skb);
+		return retval;
+	}
+
+	while (skb_queue_len(&mp->outqueue) > CAPINC_MAX_SENDQUEUE) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		interruptible_sleep_on(&mp->sendwait);
+		if (mp->nccip == 0) {
+			kfree_skb(skb);
+			return -EIO;
+		}
+		if (signal_pending(current))
+			return -ERESTARTNOHAND;
+	}
+	skb_queue_tail(&mp->outqueue, skb);
+	mp->outbytes += skb->len;
+	(void)handle_minor_send(mp);
+	return count;
+}
+
+static unsigned int
+capinc_raw_poll(struct file *file, poll_table * wait)
+{
+	struct capiminor *mp = (struct capiminor *)file->private_data;
+	unsigned int mask = 0;
+
+	if (!mp || !mp->nccip)
+		return POLLERR|POLLHUP;
+
+	poll_wait(file, &(mp->recvwait), wait);
+	if (!skb_queue_empty(&mp->recvqueue))
+		mask |= POLLIN | POLLRDNORM;
+	poll_wait(file, &(mp->sendwait), wait);
+	if (skb_queue_len(&mp->outqueue) > CAPINC_MAX_SENDQUEUE)
+		mask = POLLOUT | POLLWRNORM;
+	return mask;
+}
+
+static int
+capinc_raw_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg)
+{
+	struct capiminor *mp = (struct capiminor *)file->private_data;
+	if (!mp || !mp->nccip)
+		return -EINVAL;
+
+	switch (cmd) {
+	}
+	return -EINVAL;
+}
+
+static int
+capinc_raw_release(struct inode *inode, struct file *file)
+{
+	struct capiminor *mp = (struct capiminor *)file->private_data;
+
+	if (mp) {
+		mp->file = 0;
+		if (mp->nccip == 0) {
+			capiminor_free(mp);
+			file->private_data = NULL;
+		}
+	}
+
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capinc_raw_release %d\n", GET_USE_COUNT(&__this_module));
+#endif
+	return 0;
+}
+
+static struct file_operations capinc_raw_fops =
+{
+	llseek:		capinc_raw_llseek,
+	read:		capinc_raw_read,
+	write:		capinc_raw_write,
+	poll:		capinc_raw_poll,
+	ioctl:		capinc_raw_ioctl,
+	open:		capinc_raw_open,
+	release:	capinc_raw_release,
+};
+
+/* -------- tty_operations for capincci ----------------------------- */
+
+int capinc_tty_open(struct tty_struct * tty, struct file * file)
+{
+	struct capiminor *mp;
+
+	if ((mp = capiminor_find(MINOR(file->f_dentry->d_inode->i_rdev))) == 0)
+		return -ENXIO;
+	if (mp->nccip == 0)
+		return -ENXIO;
+	if (mp->file)
+		return -EBUSY;
+
+	skb_queue_head_init(&mp->recvqueue);
+	tty->driver_data = (void *)mp;
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capi_tty_open %d\n", GET_USE_COUNT(&__this_module));
+#endif
+	if (atomic_read(&mp->ttyopencount) == 0)
+		mp->tty = tty;
+	atomic_inc(&mp->ttyopencount);
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+	handle_minor_recv(mp);
+	return 0;
+}
+
+void capinc_tty_close(struct tty_struct * tty, struct file * file)
+{
+	struct capiminor *mp;
+
+	mp = (struct capiminor *)tty->driver_data;
+	if (mp)	{
+		if (atomic_dec_and_test(&mp->ttyopencount)) {
+#ifdef _DEBUG_REFCOUNT
+			printk(KERN_DEBUG "capinc_tty_close lastclose\n");
+#endif
+			tty->driver_data = (void *)0;
+			mp->tty = 0;
+		}
+#ifdef _DEBUG_REFCOUNT
+		printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+		if (mp->nccip == 0)
+			capiminor_free(mp);
+	}
+
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capinc_tty_close\n");
+#endif
+}
+
+int capinc_tty_write(struct tty_struct * tty, int from_user,
+		      const unsigned char *buf, int count)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	struct sk_buff *skb;
+	int retval;
+
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_write(from_user=%d,count=%d)\n",
+				from_user, count);
+#endif
+
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n");
+#endif
+		return 0;
+	}
+
+	skb = mp->ttyskb;
+	if (skb) {
+		mp->ttyskb = 0;
+		skb_queue_tail(&mp->outqueue, skb);
+		mp->outbytes += skb->len;
+	}
+
+	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
+		return -ENOMEM;
+	}
+
+	skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+	if (from_user) {
+		if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
+			kfree_skb(skb);
+#ifdef _DEBUG_TTYFUNCS
+			printk(KERN_DEBUG "capinc_tty_write: copy_from_user=%d\n", retval);
+#endif
+			return retval;
+		}
+	} else {
+		memcpy(skb_put(skb, count), buf, count);
+	}
+
+	skb_queue_tail(&mp->outqueue, skb);
+	mp->outbytes += skb->len;
+	(void)handle_minor_send(mp);
+	(void)handle_minor_recv(mp);
+	return count;
+}
+
+void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
+#endif
+
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n");
+#endif
+		return;
+	}
+
+	skb = mp->ttyskb;
+	if (skb) {
+		if (skb_tailroom(skb) > 0) {
+			*(skb_put(skb, 1)) = ch;
+			return;
+		}
+		mp->ttyskb = 0;
+		skb_queue_tail(&mp->outqueue, skb);
+		mp->outbytes += skb->len;
+		(void)handle_minor_send(mp);
+	}
+	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
+	if (skb) {
+		skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+		*(skb_put(skb, 1)) = ch;
+		mp->ttyskb = skb;
+	} else {
+		printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
+	}
+}
+
+void capinc_tty_flush_chars(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_flush_chars\n");
+#endif
+
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n");
+#endif
+		return;
+	}
+
+	skb = mp->ttyskb;
+	if (skb) {
+		mp->ttyskb = 0;
+		skb_queue_tail(&mp->outqueue, skb);
+		mp->outbytes += skb->len;
+		(void)handle_minor_send(mp);
+	}
+	(void)handle_minor_recv(mp);
+}
+
+int capinc_tty_write_room(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	int room;
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n");
+#endif
+		return 0;
+	}
+	room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue);
+	room *= CAPI_MAX_BLKSIZE;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room);
+#endif
+	return room;
+}
+
+int capinc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n");
+#endif
+		return 0;
+	}
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n",
+			mp->outbytes, mp->nack,
+			skb_queue_len(&mp->outqueue),
+			skb_queue_len(&mp->inqueue));
+#endif
+	return mp->outbytes;
+}
+
+int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_set_termios\n");
+#endif
+}
+
+void capinc_tty_throttle(struct tty_struct * tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_throttle\n");
+#endif
+	if (mp)
+		mp->ttyinstop = 1;
+}
+
+void capinc_tty_unthrottle(struct tty_struct * tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_unthrottle\n");
+#endif
+	if (mp) {
+		mp->ttyinstop = 0;
+		handle_minor_recv(mp);
+	}
+}
+
+void capinc_tty_stop(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_stop\n");
+#endif
+	if (mp) {
+		mp->ttyoutstop = 1;
+	}
+}
+
+void capinc_tty_start(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_start\n");
+#endif
+	if (mp) {
+		mp->ttyoutstop = 0;
+		(void)handle_minor_send(mp);
+	}
+}
+
+void capinc_tty_hangup(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_hangup\n");
+#endif
+}
+
+void capinc_tty_break_ctl(struct tty_struct *tty, int state)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
+#endif
+}
+
+void capinc_tty_flush_buffer(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
+#endif
+}
+
+void capinc_tty_set_ldisc(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
+#endif
+}
+
+void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
+#endif
+}
+
+int capinc_tty_read_proc(char *page, char **start, off_t off,
+			  int count, int *eof, void *data)
+{
+	return 0;
+}
+
+int capinc_write_proc(struct file *file, const char *buffer,
+			  unsigned long count, void *data)
+{
+	return 0;
+}
+
+#define CAPINC_NR_PORTS 256
+static struct tty_driver capinc_tty_driver;
+static int capinc_tty_refcount;
+static struct tty_struct *capinc_tty_table[CAPINC_NR_PORTS];
+static struct termios *capinc_tty_termios[CAPINC_NR_PORTS];
+static struct termios *capinc_tty_termios_locked[CAPINC_NR_PORTS];
+
+int capinc_tty_init(void)
+{
+	struct tty_driver *drv = &capinc_tty_driver;
+
+	/* Initialize the tty_driver structure */
+	
+	memset(drv, 0, sizeof(struct tty_driver));
+	drv->magic = TTY_DRIVER_MAGIC;
+#if (LINUX_VERSION_CODE > 0x20100)
+	drv->driver_name = "capi_nc";
+#endif
+	drv->name = "capi/%d";
+	drv->major = capi_ttymajor;
+	drv->minor_start = 0;
+	drv->num = CAPINC_NR_PORTS;
+	drv->type = TTY_DRIVER_TYPE_SERIAL;
+	drv->subtype = SERIAL_TYPE_NORMAL;
+	drv->init_termios = tty_std_termios;
+	drv->init_termios.c_iflag = ICRNL;
+	drv->init_termios.c_oflag = OPOST | ONLCR;
+	drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	drv->init_termios.c_lflag = 0;
+	drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS;
+	drv->refcount = &capinc_tty_refcount;
+	drv->table = capinc_tty_table;
+	drv->termios = capinc_tty_termios;
+	drv->termios_locked = capinc_tty_termios_locked;
+
+	drv->open = capinc_tty_open;
+	drv->close = capinc_tty_close;
+	drv->write = capinc_tty_write;
+	drv->put_char = capinc_tty_put_char;
+	drv->flush_chars = capinc_tty_flush_chars;
+	drv->write_room = capinc_tty_write_room;
+	drv->chars_in_buffer = capinc_tty_chars_in_buffer;
+	drv->ioctl = capinc_tty_ioctl;
+	drv->set_termios = capinc_tty_set_termios;
+	drv->throttle = capinc_tty_throttle;
+	drv->unthrottle = capinc_tty_unthrottle;
+	drv->stop = capinc_tty_stop;
+	drv->start = capinc_tty_start;
+	drv->hangup = capinc_tty_hangup;
+#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
+	drv->break_ctl = capinc_tty_break_ctl;
+#endif
+	drv->flush_buffer = capinc_tty_flush_buffer;
+	drv->set_ldisc = capinc_tty_set_ldisc;
+#if (LINUX_VERSION_CODE >= 131343)
+	drv->send_xchar = capinc_tty_send_xchar;
+	drv->read_proc = capinc_tty_read_proc;
+#endif
+	if (tty_register_driver(drv)) {
+		printk(KERN_ERR "Couldn't register capi_nc driver\n");
+		return -1;
+	}
+	return 0;
+}
+
+void capinc_tty_exit(void)
+{
+	struct tty_driver *drv = &capinc_tty_driver;
+	int retval;
+	if ((retval = tty_unregister_driver(drv)))
+		printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval);
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- /proc functions ----------------------------------------- */
+
+/*
+ * /proc/capi/capi20:
+ *  minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidev_read_proc(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+        struct capidev *cdev;
+	int len = 0;
+	off_t begin = 0;
+
+	for (cdev=capidev_openlist; cdev; cdev = cdev->next) {
+		len += sprintf(page+len, "%d %d %lu %lu %lu %lu\n",
+			cdev->minor,
+			cdev->applid,
+			cdev->nrecvctlpkt,
+			cdev->nrecvdatapkt,
+			cdev->nsentctlpkt,
+			cdev->nsentdatapkt);
+		if (len+begin > off+count)
+			goto endloop;
+		if (len+begin < off) {
+			begin += len;
 			len = 0;
 		}
 	}
 endloop:
-	if (i >= CAPI_MAXMINOR)
+	if (cdev == 0)
 		*eof = 1;
 	if (off >= len+begin)
 		return 0;
@@ -568,6 +1846,45 @@
 	return ((count < begin+len-off) ? count : begin+len-off);
 }
 
+/*
+ * /proc/capi/capi20ncci:
+ *  applid ncci
+ */
+static int proc_capincci_read_proc(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+        struct capidev *cdev;
+        struct capincci *np;
+	int len = 0;
+	off_t begin = 0;
+
+	for (cdev=capidev_openlist; cdev; cdev = cdev->next) {
+		for (np=cdev->nccis; np; np = np->next) {
+			len += sprintf(page+len, "%d 0x%x%s\n",
+				cdev->applid,
+				np->ncci,
+#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
+				"");
+#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+				np->minorp && np->minorp->file ? " open" : "");
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			if (len+begin > off+count)
+				goto endloop;
+			if (len+begin < off) {
+				begin += len;
+				len = 0;
+			}
+		}
+	}
+endloop:
+	if (cdev == 0)
+		*eof = 1;
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
 static struct procfsentries {
   char *name;
   mode_t mode;
@@ -577,6 +1894,7 @@
 } procfsentries[] = {
    /* { "capi",		  S_IFDIR, 0 }, */
    { "capi/capi20", 	  0	 , proc_capidev_read_proc },
+   { "capi/capi20ncci",   0	 , proc_capincci_read_proc },
 };
 
 static void proc_init(void)
@@ -604,42 +1922,122 @@
 	}
     }
 }
+
 /* -------- init function and module interface ---------------------- */
 
+
+static void lower_callback(unsigned int cmd, __u32 contr, void *data)
+{
+	struct capi_ncciinfo *np;
+	struct capidev *cdev;
+
+	switch (cmd) {
+	case KCI_CONTRUP:
+		printk(KERN_INFO "capi: controller %hu up\n", contr);
+		break;
+	case KCI_CONTRDOWN:
+		printk(KERN_INFO "capi: controller %hu down\n", contr);
+		break;
+	case KCI_NCCIUP:
+		np = (struct capi_ncciinfo *)data;
+		if ((cdev = capidev_find(np->applid)) == 0)
+			return;
+		(void)capincci_alloc(cdev, np->ncci);
+		break;
+	case KCI_NCCIDOWN:
+		np = (struct capi_ncciinfo *)data;
+		if ((cdev = capidev_find(np->applid)) == 0)
+			return;
+		(void)capincci_free(cdev, np->ncci);
+		break;
+	}
+}
+
 #ifdef MODULE
 #define	 capi_init	init_module
 #endif
 
 static struct capi_interface_user cuser = {
 	"capi20",
-	0,
+	lower_callback,
 };
 
+static char rev[10];
+
 int capi_init(void)
 {
-	
-	memset(capidevs, 0, sizeof(capidevs));
+	char *p;
+
+	MOD_INC_USE_COUNT;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 2);
+		p = strchr(rev, '$');
+		*(p-1) = 0;
+	} else
+		strcpy(rev, "???");
 
 	if (register_chrdev(capi_major, "capi20", &capi_fops)) {
 		printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	if (register_chrdev(capi_rawmajor, "capi/r%d", &capinc_raw_fops)) {
+		unregister_chrdev(capi_major, "capi20");
+		printk(KERN_ERR "capi20: unable to get major %d\n", capi_rawmajor);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 	printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major);
 
 	if ((capifuncs = attach_capi_interface(&cuser)) == 0) {
+
+		MOD_DEC_USE_COUNT;
 		unregister_chrdev(capi_major, "capi20");
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+		unregister_chrdev(capi_rawmajor, "capi/r%d");
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 		return -EIO;
 	}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	if (capinc_tty_init() < 0) {
+		(void) detach_capi_interface(&cuser);
+		unregister_chrdev(capi_major, "capi20");
+		unregister_chrdev(capi_rawmajor, "capi/r%d");
+		MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+
 	(void)proc_init();
+
+	printk(KERN_NOTICE "capi20: Rev%s: started up with major %d\n",
+				rev, capi_major);
+
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
 #ifdef MODULE
 void cleanup_module(void)
 {
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#endif
 	(void)proc_exit();
+
 	unregister_chrdev(capi_major, "capi20");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	capinc_tty_exit();
+	unregister_chrdev(capi_rawmajor, "capi/r%d");
+#endif
 	(void) detach_capi_interface(&cuser);
+	printk(KERN_NOTICE "capi: Rev%s: unloaded\n", rev);
 }
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capicmd.h linux.19p1/drivers/isdn/avmb1/capicmd.h
--- linux.vanilla/drivers/isdn/avmb1/capicmd.h	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/avmb1/capicmd.h	Wed Dec 13 22:06:43 2000
@@ -1,11 +1,22 @@
 /*
- * $Id: capicmd.h,v 1.1 1997/03/04 21:50:30 calle Exp $
+ * $Id: capicmd.h,v 1.2 2000/03/03 15:50:42 calle Exp $
  * 
  * CAPI 2.0 Interface for Linux
  * 
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: capicmd.h,v $
+ * Revision 1.2  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.1  1997/03/04 21:50:30  calle
  * Frirst version in isdn4linux
  *
@@ -19,6 +30,10 @@
  */
 #ifndef __CAPICMD_H__
 #define __CAPICMD_H__
+
+#define CAPI_MSG_BASELEN		8
+#define CAPI_DATA_B3_REQ_LEN		(CAPI_MSG_BASELEN+4+4+2+2+2)
+#define CAPI_DATA_B3_RESP_LEN		(CAPI_MSG_BASELEN+4+2)
 
 /*----- CAPI commands -----*/
 #define CAPI_ALERT		    0x01
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capidev.h linux.19p1/drivers/isdn/avmb1/capidev.h
--- linux.vanilla/drivers/isdn/avmb1/capidev.h	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/avmb1/capidev.h	Wed Dec 13 22:06:43 2000
@@ -1,11 +1,22 @@
 /*
- * $Id: capidev.h,v 1.4 1999/07/01 15:26:32 calle Exp $
+ * $Id: capidev.h,v 1.5 2000/03/03 15:50:42 calle Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidev.h,v $
+ * Revision 1.5  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.4  1999/07/01 15:26:32  calle
  * complete new version (I love it):
  * + new hardware independed "capi_driver" interface that will make it easy to:
@@ -40,18 +51,18 @@
  */
 
 struct capidev {
-	int is_open;
-	int is_registered;
-	__u16 applid;
+	struct capidev *next;
+	struct file    *file;
+	__u16		applid;
+	__u16		errcode;
+	unsigned int    minor;
+
 	struct sk_buff_head recv_queue;
 	struct wait_queue *recv_wait;
-	__u16 errcode;
+
 	/* Statistic */
-	unsigned long nopen;
-	unsigned long nrecvctlpkt;
-	unsigned long nrecvdatapkt;
-	unsigned long nsentctlpkt;
-	unsigned long nsentdatapkt;
+	unsigned long	nrecvctlpkt;
+	unsigned long	nrecvdatapkt;
+	unsigned long	nsentctlpkt;
+	unsigned long	nsentdatapkt;
 };
-
-#define CAPI_MAXMINOR	CAPI_MAXAPPL
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capidrv.c linux.19p1/drivers/isdn/avmb1/capidrv.c
--- linux.vanilla/drivers/isdn/avmb1/capidrv.c	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/avmb1/capidrv.c	Wed Dec 13 22:06:44 2000
@@ -1,11 +1,46 @@
 /*
- * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $
+ * $Id: capidrv.c,v 1.36 2000/06/26 15:13:41 keil Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.36  2000/06/26 15:13:41  keil
+ * features should be or'ed
+ *
+ * Revision 1.35  2000/06/19 15:11:25  keil
+ * avoid use of freed structs
+ * changes from 2.4.0-ac21
+ *
+ * Revision 1.34  2000/06/19 13:13:55  calle
+ * Added Modemsupport!
+ *
+ * Revision 1.33  2000/05/06 00:52:36  kai
+ * merged changes from kernel tree
+ * fixed timer and net_device->name breakage
+ *
+ * Revision 1.32  2000/04/07 15:19:58  calle
+ * remove warnings
+ *
+ * Revision 1.31  2000/04/06 15:01:25  calle
+ * Bugfix: crash in capidrv.c when reseting a capi controller.
+ * - changed code order on remove of controller.
+ * - using tq_schedule for notifier in kcapi.c.
+ * - now using spin_lock_irqsave() and spin_unlock_irqrestore().
+ * strange: sometimes even MP hang on unload of isdn.o ...
+ *
+ * Revision 1.30  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.29  1999/12/06 17:13:06  calle
  * Added controller watchdog.
  *
@@ -171,8 +206,8 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.29 $";
-int debugmode = 0;
+static char *revision = "$Revision: 1.36 $";
+static int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 MODULE_PARM(debugmode, "i");
@@ -280,6 +315,7 @@
 /* -------- data definitions ----------------------------------------- */
 
 static capidrv_data global;
+static spinlock_t global_lock = SPIN_LOCK_UNLOCKED;
 static struct capi_interface *capifuncs;
 
 static void handle_dtrace_data(capidrv_contr *card,
@@ -305,6 +341,8 @@
 		return 2;
         case ISDN_PROTO_L2_FAX:
 		return 4;
+	case ISDN_PROTO_L2_MODEM:
+		return 8;
 	}
 }
 
@@ -321,6 +359,7 @@
         case ISDN_PROTO_L2_V11096:
         case ISDN_PROTO_L2_V11019:
         case ISDN_PROTO_L2_V11038:
+	case ISDN_PROTO_L2_MODEM:
 		return 1;
         case ISDN_PROTO_L2_FAX:
 		return 4;
@@ -338,6 +377,7 @@
         case ISDN_PROTO_L2_V11096:
         case ISDN_PROTO_L2_V11019:
         case ISDN_PROTO_L2_V11038:
+	case ISDN_PROTO_L2_MODEM:
 	default:
 		return 0;
         case ISDN_PROTO_L2_FAX:
@@ -439,26 +479,28 @@
 
 static inline capidrv_contr *findcontrbydriverid(int driverid)
 {
-	capidrv_contr *p = global.contr_list;
+    	unsigned long flags;
+	capidrv_contr *p;
 
-	while (p) {
+	spin_lock_irqsave(&global_lock, flags);
+	for (p = global.contr_list; p; p = p->next)
 		if (p->myid == driverid)
-			return p;
-		p = p->next;
-	}
-	return (capidrv_contr *) 0;
+			break;
+	spin_unlock_irqrestore(&global_lock, flags);
+	return p;
 }
 
 static capidrv_contr *findcontrbynumber(__u32 contr)
 {
+	unsigned long flags;
 	capidrv_contr *p = global.contr_list;
 
-	while (p) {
+	spin_lock_irqsave(&global_lock, flags);
+	for (p = global.contr_list; p; p = p->next)
 		if (p->contrnr == contr)
-			return p;
-		p = p->next;
-	}
-	return (capidrv_contr *) 0;
+			break;
+	spin_unlock_irqrestore(&global_lock, flags);
+	return p;
 }
 
 
@@ -1501,7 +1543,7 @@
 
 static _cmsg s_cmsg;
 
-static void capidrv_signal(__u16 applid, __u32 dummy)
+static void capidrv_signal(__u16 applid, void *dummy)
 {
 	struct sk_buff *skb = 0;
 
@@ -1545,47 +1587,41 @@
 static void handle_dtrace_data(capidrv_contr *card,
 			     int send, int level2, __u8 *data, __u16 len)
 {
-    long flags;
-    __u8 *p, *end;
-    isdn_ctrl cmd;
+    	__u8 *p, *end;
+    	isdn_ctrl cmd;
 
-    if (!len) {
-       printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+    	if (!len) {
+		printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
 				card->contrnr, len);
-       return;
-    }
-
-    save_flags(flags);
-    cli();
-
-    if (level2) {
-        PUTBYTE_TO_STATUS(card, 'D');
-        PUTBYTE_TO_STATUS(card, '2');
-        PUTBYTE_TO_STATUS(card, send ? '>' : '<');
-        PUTBYTE_TO_STATUS(card, ':');
-    } else {
-        PUTBYTE_TO_STATUS(card, 'D');
-        PUTBYTE_TO_STATUS(card, '3');
-        PUTBYTE_TO_STATUS(card, send ? '>' : '<');
-        PUTBYTE_TO_STATUS(card, ':');
-    }
+		return;
+	}
 
-    for (p = data, end = data+len; p < end; p++) {
-       __u8 w;
-       PUTBYTE_TO_STATUS(card, ' ');
-       w = (*p >> 4) & 0xf;
-       PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
-       w = *p & 0xf;
-       PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
-    }
-    PUTBYTE_TO_STATUS(card, '\n');
+	if (level2) {
+		PUTBYTE_TO_STATUS(card, 'D');
+		PUTBYTE_TO_STATUS(card, '2');
+        	PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        	PUTBYTE_TO_STATUS(card, ':');
+	} else {
+        	PUTBYTE_TO_STATUS(card, 'D');
+        	PUTBYTE_TO_STATUS(card, '3');
+        	PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        	PUTBYTE_TO_STATUS(card, ':');
+    	}
+
+	for (p = data, end = data+len; p < end; p++) {
+		__u8 w;
+		PUTBYTE_TO_STATUS(card, ' ');
+		w = (*p >> 4) & 0xf;
+		PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+		w = *p & 0xf;
+		PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+	}
+	PUTBYTE_TO_STATUS(card, '\n');
 
-    restore_flags(flags);
-    
-    cmd.command = ISDN_STAT_STAVAIL;
-    cmd.driver = card->myid;
-    cmd.arg = len*3+5;
-    card->interface.statcallb(&cmd);
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = len*3+5;
+	card->interface.statcallb(&cmd);
 }
 
 /* ------------------------------------------------------------------- */
@@ -1990,8 +2026,8 @@
 		return capidrv_command(c, card);
 
 	printk(KERN_ERR
-	     "capidrv-%d: if_command %d called with invalid driverId %d!\n",
-	       card->contrnr, c->command, c->driver);
+	     "capidrv: if_command %d called with invalid driverId %d!\n",
+						c->command, c->driver);
 	return -ENODEV;
 }
 
@@ -2147,50 +2183,6 @@
 	send_message(card, &cmdcmsg);
 }
 
-static void disable_dchannel_trace(capidrv_contr *card)
-{
-        __u8 manufacturer[CAPI_MANUFACTURER_LEN];
-        capi_version version;
-	__u16 contr = card->contrnr;
-	__u16 errcode;
-	__u16 avmversion[3];
-
-        errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer);
-        if (errcode != CAPI_NOERROR) {
-	   printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
-			card->name, errcode);
-	   return;
-	}
-	if (strstr(manufacturer, "AVM") == 0) {
-	   printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n",
-			card->name, manufacturer);
-	   return;
-	}
-        errcode = (*capifuncs->capi_get_version)(contr, &version);
-        if (errcode != CAPI_NOERROR) {
-	   printk(KERN_ERR "%s: can't get version (0x%x)\n",
-			card->name, errcode);
-	   return;
-	}
-	avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
-	avmversion[1] = (version.majormanuversion << 4) & 0xf0;
-	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
-	avmversion[2] |= version.minormanuversion & 0x0f;
-
-        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
-		printk(KERN_INFO "%s: D2 trace disabled\n", card->name);
-	} else {
-		printk(KERN_INFO "%s: D3 trace disabled\n", card->name);
-	}
-	capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
-				   card->msgid++,
-				   contr,
-				   0x214D5641,  /* ManuID */
-				   0,           /* Class */
-				   1,           /* Function */
-				   (_cstruct)"\004\000\000\000\000");
-	send_message(card, &cmdcmsg);
-}
 
 static void send_listen(capidrv_contr *card)
 {
@@ -2218,14 +2210,18 @@
 static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
 {
 	capidrv_contr *card;
+	long flags;
 	isdn_ctrl cmd;
 	char id[20];
 	int i;
 
+	MOD_INC_USE_COUNT;
+
 	sprintf(id, "capidrv-%d", contr);
 	if (!(card = (capidrv_contr *) kmalloc(sizeof(capidrv_contr), GFP_ATOMIC))) {
 		printk(KERN_WARNING
 		 "capidrv: (%s) Could not allocate contr-struct.\n", id);
+		MOD_DEC_USE_COUNT;
 		return -1;
 	}
 	memset(card, 0, sizeof(capidrv_contr));
@@ -2238,6 +2234,7 @@
 		printk(KERN_WARNING
 		"capidrv: (%s) Could not allocate bchan-structs.\n", id);
 		kfree(card);
+		MOD_DEC_USE_COUNT;
 		return -1;
 	}
 	card->interface.channels = profp->nbchannel;
@@ -2246,41 +2243,49 @@
 	card->interface.writebuf_skb = if_sendbuf;
 	card->interface.writecmd = 0;
 	card->interface.readstat = if_readstat;
-	card->interface.features = ISDN_FEATURE_L2_X75I |
-	    ISDN_FEATURE_L2_X75UI |
-	    ISDN_FEATURE_L2_X75BUI |
-	    ISDN_FEATURE_L2_HDLC |
-	    ISDN_FEATURE_L2_TRANS |
-	    ISDN_FEATURE_L3_TRANS |
-	    ISDN_FEATURE_L2_V11096 |
-	    ISDN_FEATURE_L2_V11019 |
-	    ISDN_FEATURE_L2_V11038 |
-	    ISDN_FEATURE_P_UNKNOWN;
+	card->interface.features = ISDN_FEATURE_L2_HDLC |
+	    			   ISDN_FEATURE_L2_TRANS |
+	    			   ISDN_FEATURE_L3_TRANS |
+				   ISDN_FEATURE_P_UNKNOWN |
+				   ISDN_FEATURE_L2_X75I |
+				   ISDN_FEATURE_L2_X75UI |
+				   ISDN_FEATURE_L2_X75BUI;
+	if (profp->support1 & (1<<2))
+		card->interface.features |= ISDN_FEATURE_L2_V11096 |
+	    				    ISDN_FEATURE_L2_V11019 |
+	    				    ISDN_FEATURE_L2_V11038;
+	if (profp->support1 & (1<<8))
+		card->interface.features |= ISDN_FEATURE_L2_MODEM;
 	card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */
 	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
-	card->next = global.contr_list;
-	global.contr_list = card;
-	global.ncontr++;
+
+
 	card->q931_read = card->q931_buf;
 	card->q931_write = card->q931_buf;
 	card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
 
 	if (!register_isdn(&card->interface)) {
-		global.contr_list = global.contr_list->next;
 		printk(KERN_ERR "capidrv: Unable to register contr %s\n", id);
 		kfree(card->bchans);
 		kfree(card);
+		MOD_DEC_USE_COUNT;
 		return -1;
 	}
 	card->myid = card->interface.channels;
 
+	spin_lock_irqsave(&global_lock, flags);
+	card->next = global.contr_list;
+	global.contr_list = card;
+	global.ncontr++;
+	spin_unlock_irqrestore(&global_lock, flags);
+
 	memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan);
 	for (i = 0; i < card->nbchan; i++) {
 		card->bchans[i].contr = card;
 	}
 
-	cmd.driver = card->myid;
 	cmd.command = ISDN_STAT_RUN;
+	cmd.driver = card->myid;
 	card->interface.statcallb(&cmd);
 
 	card->cipmask = 0x1FFF03FF;	/* any */
@@ -2303,45 +2308,82 @@
 static int capidrv_delcontr(__u16 contr)
 {
 	capidrv_contr **pp, *card;
+	unsigned long flags;
 	isdn_ctrl cmd;
-	int i;
 
-	for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
-		if ((*pp)->contrnr == contr)
+	spin_lock_irqsave(&global_lock, flags);
+	for (card = global.contr_list; card; card = card->next) {
+		if (card->contrnr == contr)
 			break;
 	}
-	if (!*pp) {
+	if (!card) {
+		spin_unlock_irqrestore(&global_lock, flags);
 		printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr);
 		return -1;
 	}
-	card = *pp;
+	spin_unlock_irqrestore(&global_lock, flags);
+
+	del_timer(&card->listentimer);
 
 	if (debugmode)
 		printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n",
 					card->contrnr, card->myid);
 
-	cmd.command = ISDN_STAT_UNLOAD;
+	cmd.command = ISDN_STAT_STOP;
 	cmd.driver = card->myid;
 	card->interface.statcallb(&cmd);
 
-	*pp = (*pp)->next;
-	global.ncontr--;
+	while (card->nbchan) {
 
-	for (i = 0; i < card->nbchan; i++) {
-		if (card->bchans[i].nccip)
-			free_ncci(card, card->bchans[i].nccip);
-		if (card->bchans[i].plcip)
-			free_plci(card, card->bchans[i].plcip);
+		cmd.command = ISDN_STAT_DISCH;
+		cmd.driver = card->myid;
+		cmd.arg = card->nbchan-1;
+	        cmd.parm.num[0] = 0;
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n",
+					card->contrnr, card->myid, cmd.arg);
+		card->interface.statcallb(&cmd);
+
+		if (card->bchans[card->nbchan-1].nccip)
+			free_ncci(card, card->bchans[card->nbchan-1].nccip);
+		if (card->bchans[card->nbchan-1].plcip)
+			free_plci(card, card->bchans[card->nbchan-1].plcip);
 		if (card->plci_list)
 			printk(KERN_ERR "capidrv: bug in free_plci()\n");
+		card->nbchan--;
 	}
 	kfree(card->bchans);
-	del_timer(&card->listentimer);
+	card->bchans = 0;
+
+	if (debugmode)
+		printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n",
+					card->contrnr, card->myid);
+
+	cmd.command = ISDN_STAT_UNLOAD;
+	cmd.driver = card->myid;
+	card->interface.statcallb(&cmd);
+
+	if (debugmode)
+		printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n",
+					card->contrnr, card->myid);
+
+	spin_lock_irqsave(&global_lock, flags);
+	for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
+		if (*pp == card) {
+			*pp = (*pp)->next;
+			card->next = 0;
+			global.ncontr--;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&global_lock, flags);
 
 	printk(KERN_INFO "%s: now down.\n", card->name);
 
 	kfree(card);
 
+	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
@@ -2438,10 +2480,14 @@
 	__u32 ncontr, contr;
 	__u16 errcode;
 
+	MOD_INC_USE_COUNT;
+
 	capifuncs = attach_capi_interface(&cuser);
 
-	if (!capifuncs)
+	if (!capifuncs) {
+		MOD_DEC_USE_COUNT;
 		return -EIO;
+	}
 
 	if ((p = strchr(revision, ':'))) {
 		strcpy(rev, p + 1);
@@ -2456,6 +2502,7 @@
 	errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
 	if (errcode) {
 		detach_capi_interface(&cuser);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -2463,6 +2510,7 @@
 	if (errcode != CAPI_NOERROR) {
 		(void) (*capifuncs->capi_release) (global.appid);
 		detach_capi_interface(&cuser);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -2477,17 +2525,19 @@
 	}
 	proc_init();
 
+	printk(KERN_NOTICE "capidrv: Rev%s: loaded\n", rev);
+	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
 #ifdef MODULE
 void cleanup_module(void)
 {
-	capidrv_contr *card, *next;
 	char rev[10];
 	char *p;
 
-	if ((p = strchr(revision, ':'))) {
+	if ((p = strchr(revision, ':')) != 0) {
 		strcpy(rev, p + 1);
 		p = strchr(rev, '$');
 		*p = 0;
@@ -2495,14 +2545,10 @@
 		strcpy(rev, " ??? ");
 	}
 
-	for (card = global.contr_list; card; card = next) {
-		next = card->next;
-		disable_dchannel_trace(card);
-		capidrv_delcontr(card->contrnr);
-	}
-
 	(void) (*capifuncs->capi_release) (global.appid);
+
 	detach_capi_interface(&cuser);
+
 	proc_exit();
 
 	printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capifs.c linux.19p1/drivers/isdn/avmb1/capifs.c
--- linux.vanilla/drivers/isdn/avmb1/capifs.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/avmb1/capifs.c	Wed Dec 13 22:06:44 2000
@@ -0,0 +1,609 @@
+/*
+ * $Id: capifs.c,v 1.7 2000/06/18 16:09:54 keil Exp $
+ * 
+ * (c) Copyright 2000 by Carsten Paeth (calle@calle.de)
+ *
+ * Heavily based on devpts filesystem from H. Peter Anvin
+ * 
+ * $Log: capifs.c,v $
+ * Revision 1.7  2000/06/18 16:09:54  keil
+ * more changes for 2.4
+ *
+ * Revision 1.6  2000/04/03 13:29:25  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
+ * Revision 1.5  2000/03/13 17:49:52  calle
+ * make it running with 2.3.51.
+ *
+ * Revision 1.4  2000/03/08 17:06:33  calle
+ * - changes for devfs and 2.3.49
+ * - capifs now configurable (no need with devfs)
+ * - New Middleware ioctl CAPI_NCCI_GETUNIT
+ * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs)
+ *
+ * Revision 1.3  2000/03/06 18:00:23  calle
+ * - Middleware extention now working with 2.3.49 (capifs).
+ * - Fixed typos in debug section of capi.c
+ * - Bugfix: Makefile corrected for b1pcmcia.c
+ *
+ * Revision 1.2  2000/03/06 09:17:07  calle
+ * - capifs: fileoperations now in inode (change for 2.3.49)
+ * - Config.in: Middleware extention not a tristate, uups.
+ *
+ * Revision 1.1  2000/03/03 16:48:38  calle
+ * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI)
+ *   It is now possible to create a connection with a CAPI2.0 applikation
+ *   and than to handle the data connection from /dev/capi/ (capifs) and also
+ *   using async or sync PPP on this connection.
+ *   The two major device number 190 and 191 are not confirmed yet,
+ *   but I want to save the code in cvs, before I go on.
+ *
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/tty.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/param.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/locks.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/ctype.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Carsten Paeth <calle@calle.de>");
+
+static char *revision = "$Revision: 1.7 $";
+
+struct capifs_ncci {
+	struct inode *inode;
+	char used;
+	char type;
+	unsigned int num;
+	kdev_t kdev;
+};
+
+struct capifs_sb_info {
+	u32 magic;
+	struct super_block *next;
+	struct super_block **back;
+	int setuid;
+	int setgid;
+	uid_t   uid;
+	gid_t   gid;
+	umode_t mode;
+
+	unsigned int max_ncci;
+	struct capifs_ncci *nccis;
+};
+
+#define CAPIFS_SUPER_MAGIC (('C'<<8)|'N')
+#define CAPIFS_SBI_MAGIC   (('C'<<24)|('A'<<16)|('P'<<8)|'I')
+
+static inline struct capifs_sb_info *SBI(struct super_block *sb)
+{
+	return (struct capifs_sb_info *)(sb->u.generic_sbp);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int capifs_root_readdir(struct file *,void *,filldir_t);
+static struct dentry *capifs_root_lookup(struct inode *,struct dentry *);
+static int capifs_revalidate(struct dentry *, int);
+
+static struct file_operations capifs_root_operations = {
+	readdir:	capifs_root_readdir,
+};
+
+struct inode_operations capifs_root_inode_operations = {
+	&capifs_root_operations, /* file operations */
+	lookup: capifs_root_lookup,
+};
+
+struct inode_operations capifs_inode_operations;
+
+static struct dentry_operations capifs_dentry_operations = {
+	d_revalidate: capifs_revalidate,
+};
+
+/*
+ * /dev/capi/%d
+ * /dev/capi/r%d
+ */
+
+static int capifs_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode * inode = filp->f_dentry->d_inode;
+	struct capifs_sb_info * sbi = SBI(filp->f_dentry->d_inode->i_sb);
+	off_t nr;
+	char numbuf[32];
+
+	nr = filp->f_pos;
+
+	switch(nr)
+	{
+	case 0:
+		if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0)
+			return 0;
+		filp->f_pos = ++nr;
+		/* fall through */
+	case 1:
+		if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0)
+			return 0;
+		filp->f_pos = ++nr;
+		/* fall through */
+	default:
+		while (nr < sbi->max_ncci) {
+			int n = nr - 2;
+			struct capifs_ncci *np = &sbi->nccis[n];
+			if (np->inode && np->used) {
+				char *p = numbuf;
+				if (np->type) *p++ = np->type;
+				sprintf(p, "%u", np->num);
+				if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr) < 0 )
+					return 0;
+			}
+			filp->f_pos = ++nr;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Revalidate is called on every cache lookup.  We use it to check that
+ * the ncci really does still exist.  Never revalidate negative dentries;
+ * for simplicity (fix later?)
+ */
+static int capifs_revalidate(struct dentry * dentry, int flags)
+{
+	struct capifs_sb_info *sbi;
+
+	if ( !dentry->d_inode )
+		return 0;
+
+	sbi = SBI(dentry->d_inode->i_sb);
+
+	return ( sbi->nccis[dentry->d_inode->i_ino - 2].inode == dentry->d_inode );
+}
+
+static struct dentry *capifs_root_lookup(struct inode * dir, struct dentry * dentry)
+{
+	struct capifs_sb_info *sbi = SBI(dir->i_sb);
+	struct capifs_ncci *np;
+	unsigned int i;
+	char numbuf[32];
+	char *p, *tmp;
+	unsigned int num;
+	char type = 0;
+
+	dentry->d_inode = NULL;	/* Assume failure */
+	dentry->d_op    = &capifs_dentry_operations;
+
+	if (dentry->d_name.len >= sizeof(numbuf) )
+		return NULL;
+	strncpy(numbuf, dentry->d_name.name, dentry->d_name.len);
+	numbuf[dentry->d_name.len] = 0;
+        p = numbuf;
+	if (!isdigit(*p)) type = *p++;
+	tmp = p;
+	num = (unsigned int)simple_strtoul(p, &tmp, 10);
+	if (tmp == p || *tmp)
+		return NULL;
+
+	for (i = 0, np = sbi->nccis ; i < sbi->max_ncci; i++, np++) {
+		if (np->used && np->num == num && np->type == type)
+			break;
+	}
+
+	if ( i >= sbi->max_ncci )
+		return NULL;
+
+	dentry->d_inode = np->inode;
+	if ( dentry->d_inode )
+		dentry->d_inode->i_count++;
+	
+	d_add(dentry, dentry->d_inode);
+
+	return NULL;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct super_block *mounts = NULL;
+
+static void capifs_put_super(struct super_block *sb)
+{
+	struct capifs_sb_info *sbi = SBI(sb);
+	struct inode *inode;
+	int i;
+
+	for ( i = 0 ; i < sbi->max_ncci ; i++ ) {
+		if ( (inode = sbi->nccis[i].inode) ) {
+			if (inode->i_count != 1 )
+				printk("capifs_put_super: badness: entry %d count %d\n",
+				       i, (unsigned)inode->i_count);
+			inode->i_nlink--;
+			iput(inode);
+		}
+	}
+
+	*sbi->back = sbi->next;
+	if ( sbi->next )
+		SBI(sbi->next)->back = sbi->back;
+
+	kfree(sbi->nccis);
+	kfree(sbi);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+static int capifs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static void capifs_write_inode(struct inode *inode) { };
+#else
+static int capifs_statfs(struct super_block *sb, struct statfs *buf);
+#endif
+static void capifs_read_inode(struct inode *inode);
+
+static struct super_operations capifs_sops = {
+	read_inode:	capifs_read_inode,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+	write_inode:	capifs_write_inode,
+#endif
+	put_super:	capifs_put_super,
+	statfs:		capifs_statfs,
+};
+
+static int capifs_parse_options(char *options, struct capifs_sb_info *sbi)
+{
+	int setuid = 0;
+	int setgid = 0;
+	uid_t uid = 0;
+	gid_t gid = 0;
+	umode_t mode = 0600;
+	unsigned int maxncci = 512;
+	char *this_char, *value;
+
+	this_char = NULL;
+	if ( options )
+		this_char = strtok(options,",");
+	for ( ; this_char; this_char = strtok(NULL,",")) {
+		if ((value = strchr(this_char,'=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(this_char,"uid")) {
+			if (!value || !*value)
+				return 1;
+			uid = simple_strtoul(value,&value,0);
+			if (*value)
+				return 1;
+			setuid = 1;
+		}
+		else if (!strcmp(this_char,"gid")) {
+			if (!value || !*value)
+				return 1;
+			gid = simple_strtoul(value,&value,0);
+			if (*value)
+				return 1;
+			setgid = 1;
+		}
+		else if (!strcmp(this_char,"mode")) {
+			if (!value || !*value)
+				return 1;
+			mode = simple_strtoul(value,&value,8);
+			if (*value)
+				return 1;
+		}
+		else if (!strcmp(this_char,"maxncci")) {
+			if (!value || !*value)
+				return 1;
+			maxncci = simple_strtoul(value,&value,8);
+			if (*value)
+				return 1;
+		}
+		else
+			return 1;
+	}
+	sbi->setuid   = setuid;
+	sbi->setgid   = setgid;
+	sbi->uid      = uid;
+	sbi->gid      = gid;
+	sbi->mode     = mode & ~S_IFMT;
+	sbi->max_ncci = maxncci;
+
+	return 0;
+}
+
+struct super_block *capifs_read_super(struct super_block *s, void *data,
+				      int silent)
+{
+	struct inode * root_inode;
+	struct dentry * root;
+	struct capifs_sb_info *sbi;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+	MOD_INC_USE_COUNT;
+	lock_super(s);
+#endif
+	/* Super block already completed? */
+	if (s->s_root)
+		goto out;
+
+	sbi = (struct capifs_sb_info *) kmalloc(sizeof(struct capifs_sb_info), GFP_KERNEL);
+	if ( !sbi )
+		goto fail;
+
+	memset(sbi, 0, sizeof(struct capifs_sb_info));
+	sbi->magic  = CAPIFS_SBI_MAGIC;
+
+	if ( capifs_parse_options(data,sbi) ) {
+		kfree(sbi);
+		printk("capifs: called with bogus options\n");
+		goto fail;
+	}
+
+	sbi->nccis = kmalloc(sizeof(struct capifs_ncci) * sbi->max_ncci, GFP_KERNEL);
+	if ( !sbi->nccis ) {
+		kfree(sbi);
+		goto fail;
+	}
+	memset(sbi->nccis, 0, sizeof(struct capifs_ncci) * sbi->max_ncci);
+
+	s->u.generic_sbp = (void *) sbi;
+	s->s_blocksize = 1024;
+	s->s_blocksize_bits = 10;
+	s->s_magic = CAPIFS_SUPER_MAGIC;
+	s->s_op = &capifs_sops;
+	s->s_root = NULL;
+
+	/*
+	 * Get the root inode and dentry, but defer checking for errors.
+	 */
+	root_inode = iget(s, 1); /* inode 1 == root directory */
+	root = d_alloc_root(root_inode, NULL);
+
+	/*
+	 * Check whether somebody else completed the super block.
+	 */
+	if (s->s_root) {
+		if (root) dput(root);
+		else iput(root_inode);
+		goto out;
+	}
+
+	if (!root) {
+		printk("capifs: get root dentry failed\n");
+		/*
+	 	* iput() can block, so we clear the super block first.
+	 	*/
+		iput(root_inode);
+		kfree(sbi->nccis);
+		kfree(sbi);
+		goto fail;
+	}
+
+	/*
+	 * Check whether somebody else completed the super block.
+	 */
+	if (s->s_root)
+		goto out;
+	
+	/*
+	 * Success! Install the root dentry now to indicate completion.
+	 */
+	s->s_root = root;
+
+	sbi->next = mounts;
+	if ( sbi->next )
+		SBI(sbi->next)->back = &(sbi->next);
+	sbi->back = &mounts;
+	mounts = s;
+
+out:	/* Success ... somebody else completed the super block for us. */ 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+	unlock_super(s);
+#endif
+	return s;
+fail:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+	unlock_super(s);
+	MOD_DEC_USE_COUNT;
+#endif
+	return NULL;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+static int capifs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+	struct statfs tmp;
+
+	tmp.f_type = CAPIFS_SUPER_MAGIC;
+	tmp.f_bsize = 1024;
+	tmp.f_blocks = 0;
+	tmp.f_bfree = 0;
+	tmp.f_bavail = 0;
+	tmp.f_files = 0;
+	tmp.f_ffree = 0;
+	tmp.f_namelen = NAME_MAX;
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+#else
+static int capifs_statfs(struct super_block *sb, struct statfs *buf)
+{
+	buf->f_type = CAPIFS_SUPER_MAGIC;
+	buf->f_bsize = 1024;
+	buf->f_blocks = 0;
+	buf->f_bfree = 0;
+	buf->f_bavail = 0;
+	buf->f_files = 0;
+	buf->f_ffree = 0;
+	buf->f_namelen = NAME_MAX;
+	return 0;
+}
+#endif
+
+static void capifs_read_inode(struct inode *inode)
+{
+	ino_t ino = inode->i_ino;
+	struct capifs_sb_info *sbi = SBI(inode->i_sb);
+
+	inode->i_op = NULL;
+	inode->i_mode = 0;
+	inode->i_nlink = 0;
+	inode->i_size = 0;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	inode->i_blocks = 0;
+	inode->i_blksize = 1024;
+	inode->i_uid = inode->i_gid = 0;
+
+	if ( ino == 1 ) {
+		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+		inode->i_op = &capifs_root_inode_operations;
+		inode->i_nlink = 2;
+		return;
+	} 
+
+
+	ino -= 2;
+	if ( ino >= sbi->max_ncci )
+		return;		/* Bogus */
+	
+	inode->i_mode = S_IFCHR;
+	inode->i_op = &chrdev_inode_operations;
+
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51)
+static struct file_system_type capifs_fs_type = {
+	"capifs",
+	0,
+	capifs_read_super,
+	NULL
+};
+#else
+static DECLARE_FSTYPE(capifs_fs_type, "capifs", capifs_read_super, 0);
+#endif
+
+void capifs_new_ncci(char type, unsigned int num, kdev_t device)
+{
+	struct super_block *sb;
+	struct capifs_sb_info *sbi;
+	struct capifs_ncci *np;
+	ino_t ino;
+
+	for ( sb = mounts ; sb ; sb = sbi->next ) {
+		sbi = SBI(sb);
+
+		for (ino = 0, np = sbi->nccis ; ino < sbi->max_ncci; ino++, np++) {
+			if (np->used == 0) {
+				np->used = 1;
+				np->type = type;
+				np->num = num;
+				np->kdev = device;
+				break;
+			}
+		}
+
+		if ((np->inode = iget(sb, ino+2)) != 0) {
+			struct inode *inode = np->inode;
+			inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid;
+			inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid;
+			inode->i_mode = sbi->mode | S_IFCHR;
+			inode->i_rdev = np->kdev;
+			inode->i_nlink++;
+		}
+	}
+}
+
+void capifs_free_ncci(char type, unsigned int num)
+{
+	struct super_block *sb;
+	struct capifs_sb_info *sbi;
+	struct inode *inode;
+	struct capifs_ncci *np;
+	ino_t ino;
+
+	for ( sb = mounts ; sb ; sb = sbi->next ) {
+		sbi = SBI(sb);
+
+		for (ino = 0, np = sbi->nccis ; ino < sbi->max_ncci; ino++, np++) {
+			if (!np->used || np->type != type || np->num != num)
+				continue;
+			if (np->inode) {
+				inode = np->inode;
+				np->inode = 0;
+				np->used = 0;
+				inode->i_nlink--;
+				iput(inode);
+				break;
+			}
+		}
+	}
+}
+
+int __init capifs_init(void)
+{
+	char rev[10];
+	char *p;
+	int err;
+
+	MOD_INC_USE_COUNT;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	err = register_filesystem(&capifs_fs_type);
+	if (err) {
+		MOD_DEC_USE_COUNT;
+		return err;
+	}
+#ifdef MODULE
+        printk(KERN_NOTICE "capifs: Rev%s: loaded\n", rev);
+#else
+	printk(KERN_NOTICE "capifs: Rev%s: started\n", rev);
+#endif
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+void capifs_exit(void)
+{
+	unregister_filesystem(&capifs_fs_type);
+}
+
+EXPORT_SYMBOL(capifs_new_ncci);
+EXPORT_SYMBOL(capifs_free_ncci);
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	return capifs_init();
+}
+
+void cleanup_module(void)
+{
+	capifs_exit();
+}
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capifs.h linux.19p1/drivers/isdn/avmb1/capifs.h
--- linux.vanilla/drivers/isdn/avmb1/capifs.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/avmb1/capifs.h	Wed Dec 13 22:06:44 2000
@@ -0,0 +1,25 @@
+/*
+ * $Id: capifs.h,v 1.2 2000/03/08 17:06:33 calle Exp $
+ * 
+ * (c) Copyright 2000 by Carsten Paeth (calle@calle.de)
+ *
+ * $Log: capifs.h,v $
+ * Revision 1.2  2000/03/08 17:06:33  calle
+ * - changes for devfs and 2.3.49
+ * - capifs now configurable (no need with devfs)
+ * - New Middleware ioctl CAPI_NCCI_GETUNIT
+ * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs)
+ *
+ * Revision 1.1  2000/03/03 16:48:38  calle
+ * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI)
+ *   It is now possible to create a connection with a CAPI2.0 applikation
+ *   and than to handle the data connection from /dev/capi/ (capifs) and also
+ *   using async or sync PPP on this connection.
+ *   The two major device number 190 and 191 are not confirmed yet,
+ *   but I want to save the code in cvs, before I go on.
+ *
+ *
+ */
+
+void capifs_new_ncci(char type, unsigned int num, kdev_t device);
+void capifs_free_ncci(char type, unsigned int num);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capiutil.c linux.19p1/drivers/isdn/avmb1/capiutil.c
--- linux.vanilla/drivers/isdn/avmb1/capiutil.c	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/avmb1/capiutil.c	Wed Dec 13 22:06:44 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: capiutil.c,v 1.10 1999/08/31 11:19:54 paul Exp $
+ * $Id: capiutil.c,v 1.11 2000/03/03 15:50:42 calle Exp $
  *
  * CAPI 2.0 convert capi message to capi message struct
  *
@@ -7,6 +7,17 @@
  * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capiutil.c,v $
+ * Revision 1.11  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.10  1999/08/31 11:19:54  paul
  * various spelling corrections (new checksums may be needed, Karsten!)
  *
@@ -794,7 +805,7 @@
     /*15 */ "Class",
     /*16 */ "ConnectedNumber",
     /*17 */ "ConnectedSubaddress",
-    /*18 */ "Data",
+    /*18 */ "Data32",
     /*19 */ "DataHandle",
     /*1a */ "DataLength",
     /*1b */ "FacilityConfirmationParameter",
@@ -892,13 +903,7 @@
 			cmsg->l += 2;
 			break;
 		case _CDWORD:
-			if (strcmp(NAME, "Data") == 0) {
-				bufprint("%-*s = ", slen, NAME);
-				printstructlen((__u8 *) * (__u32 *) (cmsg->m + cmsg->l),
-					       *(__u16 *) (cmsg->m + cmsg->l + sizeof(__u32)));
-				bufprint("\n");
-			} else
-				bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l));
+			bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l));
 			cmsg->l += 4;
 			break;
 		case _CSTRUCT:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capiutil.h linux.19p1/drivers/isdn/avmb1/capiutil.h
--- linux.vanilla/drivers/isdn/avmb1/capiutil.h	Fri Apr 21 23:23:03 2000
+++ linux.19p1/drivers/isdn/avmb1/capiutil.h	Wed Dec 13 22:06:44 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: capiutil.h,v 1.4 1999/09/15 08:16:03 calle Exp $
+ * $Id: capiutil.h,v 1.5 2000/03/03 15:50:42 calle Exp $
  * 
  * CAPI 2.0 defines & types
  * 
@@ -7,6 +7,17 @@
  * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: capiutil.h,v $
+ * Revision 1.5  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.4  1999/09/15 08:16:03  calle
  * Implementation of 64Bit extention complete.
  *
@@ -36,28 +47,47 @@
 
 #include <asm/types.h>
 
-#define	CAPIMSG_LEN(m)		(m[0] | (m[1] << 8))
-#define	CAPIMSG_APPID(m)	(m[2] | (m[3] << 8))
-#define	CAPIMSG_COMMAND(m)	(m[4])
-#define	CAPIMSG_SUBCOMMAND(m)	(m[5])
-#define	CAPIMSG_MSGID(m)	(m[6] | (m[7] << 8))
+#define CAPIMSG_BASELEN		8
+#define CAPIMSG_U8(m, off)	(m[off])
+#define CAPIMSG_U16(m, off)	(m[off]|(m[(off)+1]<<8))
+#define CAPIMSG_U32(m, off)	(m[off]|(m[(off)+1]<<8)|(m[(off)+2]<<16)|(m[(off)+3]<<24))
+#define	CAPIMSG_LEN(m)		CAPIMSG_U16(m,0)
+#define	CAPIMSG_APPID(m)	CAPIMSG_U16(m,2)
+#define	CAPIMSG_COMMAND(m)	CAPIMSG_U8(m,4)
+#define	CAPIMSG_SUBCOMMAND(m)	CAPIMSG_U8(m,5)
+#define CAPIMSG_CMD(m)		(((m[4])<<8)|(m[5]))
+#define	CAPIMSG_MSGID(m)	CAPIMSG_U16(m,6)
 #define CAPIMSG_CONTROLLER(m)	(m[8] & 0x7f)
-#define CAPIMSG_CONTROL(m)	(m[8]|(m[9]<<8)|(m[10]<<16)|(m[11]<<24))
+#define CAPIMSG_CONTROL(m)	CAPIMSG_U32(m, 8)
 #define CAPIMSG_NCCI(m)		CAPIMSG_CONTROL(m)
-#define CAPIMSG_DATA(m)		(m[12]|(m[13]<<8)|(m[14]<<16)|(m[15]<<24))
-#define CAPIMSG_DATALEN(m)	(m[16] | (m[17]<<8))
+#define CAPIMSG_DATALEN(m)	CAPIMSG_U16(m,16) /* DATA_B3_REQ */
+
+static inline void capimsg_setu8(void *m, int off, __u8 val)
+{
+	((__u8 *)m)[off] = val;
+}
+
+static inline void capimsg_setu16(void *m, int off, __u16 val)
+{
+	((__u8 *)m)[off] = val & 0xff;
+	((__u8 *)m)[off+1] = (val >> 8) & 0xff;
+}
+
+static inline void capimsg_setu32(void *m, int off, __u32 val)
+{
+	((__u8 *)m)[off] = val & 0xff;
+	((__u8 *)m)[off+1] = (val >> 8) & 0xff;
+	((__u8 *)m)[off+2] = (val >> 16) & 0xff;
+	((__u8 *)m)[off+3] = (val >> 24) & 0xff;
+}
 
-#define	CAPIMSG_SETAPPID(m, applid) \
-	do { \
-		((__u8 *)m)[2] = (__u16)(applid) & 0xff; \
-		((__u8 *)m)[3] = ((__u16)(applid) >> 8) & 0xff; \
-	} while (0)
-
-#define	CAPIMSG_SETLEN(m, len) \
-	do { \
-		((__u8 *)m)[0] = (__u16)(len) & 0xff; \
-		((__u8 *)m)[1] = ((__u16)(len) >> 8) & 0xff; \
-	} while (0)
+#define	CAPIMSG_SETLEN(m, len)		capimsg_setu16(m, 0, len)
+#define	CAPIMSG_SETAPPID(m, applid)	capimsg_setu16(m, 2, applid)
+#define	CAPIMSG_SETCOMMAND(m,cmd)	capimsg_setu8(m, 4, cmd)
+#define	CAPIMSG_SETSUBCOMMAND(m, cmd)	capimsg_setu8(m, 5, cmd)
+#define	CAPIMSG_SETMSGID(m, msgid)	capimsg_setu16(m, 6, msgid)
+#define	CAPIMSG_SETCONTROL(m, contr)	capimsg_setu32(m, 8, contr)
+#define	CAPIMSG_SETDATALEN(m, len)	capimsg_setu16(m, 16, len)
 
 /*----- basic-type definitions -----*/
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/kcapi.c linux.19p1/drivers/isdn/avmb1/kcapi.c
--- linux.vanilla/drivers/isdn/avmb1/kcapi.c	Sun Jun 11 21:44:13 2000
+++ linux.19p1/drivers/isdn/avmb1/kcapi.c	Wed Dec 13 22:06:44 2000
@@ -1,11 +1,39 @@
 /*
- * $Id: kcapi.c,v 1.12 2000/01/28 16:45:39 calle Exp $
+ * $Id: kcapi.c,v 1.17 2000/04/21 13:00:56 calle Exp $
  * 
  * Kernel CAPI 2.0 Module
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: kcapi.c,v $
+ * Revision 1.17  2000/04/21 13:00:56  calle
+ * Bugfix: driver_proc_info was also wrong.
+ *
+ * Revision 1.16  2000/04/21 12:38:42  calle
+ * Bugfix: error in proc_ functions, begin-off => off-begin
+ *
+ * Revision 1.15  2000/04/06 15:01:25  calle
+ * Bugfix: crash in capidrv.c when reseting a capi controller.
+ * - changed code order on remove of controller.
+ * - using tq_schedule for notifier in kcapi.c.
+ * - now using spin_lock_irqsave() and spin_unlock_irqrestore().
+ * strange: sometimes even MP hang on unload of isdn.o ...
+ *
+ * Revision 1.14  2000/04/03 13:29:25  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
+ * Revision 1.13  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.12  2000/01/28 16:45:39  calle
  * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard),
  * will search named driver and call the add_card function if one exist.
@@ -78,6 +106,7 @@
 #include <linux/tqueue.h>
 #include <linux/capi.h>
 #include <linux/kernelcapi.h>
+#include <linux/locks.h>
 #include <asm/uaccess.h>
 #include "capicmd.h"
 #include "capiutil.h"
@@ -86,7 +115,7 @@
 #include <linux/b1lli.h>
 #endif
 
-static char *revision = "$Revision: 1.12 $";
+static char *revision = "$Revision: 1.17 $";
 
 /* ------------------------------------------------------------- */
 
@@ -125,8 +154,8 @@
 	__u16 applid;
 	capi_register_params rparam;
 	int releasing;
-	__u32 param;
-	void (*signal) (__u16 applid, __u32 param);
+	void *param;
+	void (*signal) (__u16 applid, void *param);
 	struct sk_buff_head recv_queue;
 	int nncci;
 	struct capi_ncci *nccilist;
@@ -137,6 +166,14 @@
 	unsigned long nsentdatapkt;
 };
 
+struct capi_notifier {
+	struct capi_notifier *next;
+	unsigned int cmd;
+	__u32 controller;
+	__u16 applid;
+	__u32 ncci;
+};
+
 /* ------------------------------------------------------------- */
 
 static struct capi_version driver_version = {2, 0, 1, 1<<4};
@@ -160,9 +197,9 @@
 static int ncards = 0;
 static struct sk_buff_head recv_queue;
 static struct capi_interface_user *capi_users = 0;
+static spinlock_t capi_users_lock = SPIN_LOCK_UNLOCKED;
 static struct capi_driver *drivers;
-static long notify_up_set = 0;
-static long notify_down_set = 0;
+static spinlock_t drivers_lock = SPIN_LOCK_UNLOCKED;
 
 static struct tq_struct tq_state_notify;
 static struct tq_struct tq_recv_notify;
@@ -304,6 +341,7 @@
 	int len = 0;
 	off_t begin = 0;
 
+	spin_lock(&drivers_lock);
 	for (driver = drivers; driver; driver = driver->next) {
 		len += sprintf(page+len, "%-32s %d %s\n",
 					driver->name,
@@ -317,6 +355,7 @@
 		}
 	}
 endloop:
+	spin_unlock(&drivers_lock);
 	if (!driver)
 		*eof = 1;
 	if (off >= len+begin)
@@ -336,6 +375,7 @@
 	int len = 0;
 	off_t begin = 0;
 
+	spin_lock(&capi_users_lock);
         for (cp = capi_users; cp ; cp = cp->next) {
 		len += sprintf(page+len, "%s\n", cp->name);
 		if (len+begin > off+count)
@@ -346,6 +386,7 @@
 		}
 	}
 endloop:
+	spin_unlock(&capi_users_lock);
 	if (cp == 0)
 		*eof = 1;
 	if (off >= len+begin)
@@ -510,6 +551,167 @@
     }
 }
 
+/* -------- Notifier handling --------------------------------- */
+
+static struct capi_notifier_list{
+	struct capi_notifier *head;
+	struct capi_notifier *tail;
+} notifier_list;
+
+static spinlock_t notifier_lock = SPIN_LOCK_UNLOCKED;
+
+static inline void notify_enqueue(struct capi_notifier *np)
+{
+	struct capi_notifier_list *q = &notifier_list;
+	unsigned long flags;
+
+	spin_lock_irqsave(&notifier_lock, flags);
+	if (q->tail) {
+		q->tail->next = np;
+		q->tail = np;
+	} else {
+		q->head = q->tail = np;
+	}
+	spin_unlock_irqrestore(&notifier_lock, flags);
+}
+
+static inline struct capi_notifier *notify_dequeue(void)
+{
+	struct capi_notifier_list *q = &notifier_list;
+	struct capi_notifier *np = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&notifier_lock, flags);
+	if (q->head) {
+		np = q->head;
+		if ((q->head = np->next) == 0)
+ 			q->tail = 0;
+		np->next = 0;
+	}
+	spin_unlock_irqrestore(&notifier_lock, flags);
+	return np;
+}
+
+static int notify_push(unsigned int cmd, __u32 controller,
+				__u16 applid, __u32 ncci)
+{
+	struct capi_notifier *np;
+
+	MOD_INC_USE_COUNT;
+	np = (struct capi_notifier *)kmalloc(sizeof(struct capi_notifier), GFP_ATOMIC);
+	if (!np) {
+		MOD_DEC_USE_COUNT;
+		return -1;
+	}
+	memset(np, 0, sizeof(struct capi_notifier));
+	np->cmd = cmd;
+	np->controller = controller;
+	np->applid = applid;
+	np->ncci = ncci;
+	notify_enqueue(np);
+	/*
+	 * The notifier will result in adding/deleteing
+	 * of devices. Devices can only removed in
+	 * user process, not in bh.
+	 */
+	queue_task(&tq_state_notify, &tq_scheduler);
+	return 0;
+}
+
+/* -------- KCI_CONTRUP --------------------------------------- */
+
+static void notify_up(__u32 contr)
+{
+	struct capi_interface_user *p;
+
+        printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+	spin_lock(&capi_users_lock);
+	for (p = capi_users; p; p = p->next) {
+		if (!p->callback) continue;
+		(*p->callback) (KCI_CONTRUP, contr, &CARD(contr)->profile);
+	}
+	spin_unlock(&capi_users_lock);
+}
+
+/* -------- KCI_CONTRDOWN ------------------------------------- */
+
+static void notify_down(__u32 contr)
+{
+	struct capi_interface_user *p;
+        printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr);
+	spin_lock(&capi_users_lock);
+	for (p = capi_users; p; p = p->next) {
+		if (!p->callback) continue;
+		(*p->callback) (KCI_CONTRDOWN, contr, 0);
+	}
+	spin_unlock(&capi_users_lock);
+}
+
+/* -------- KCI_NCCIUP ---------------------------------------- */
+
+static void notify_ncciup(__u32 contr, __u16 applid, __u32 ncci)
+{
+	struct capi_interface_user *p;
+	struct capi_ncciinfo n;
+	n.applid = applid;
+	n.ncci = ncci;
+        /*printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);*/
+	spin_lock(&capi_users_lock);
+	for (p = capi_users; p; p = p->next) {
+		if (!p->callback) continue;
+		(*p->callback) (KCI_NCCIUP, contr, &n);
+	}
+	spin_unlock(&capi_users_lock);
+};
+
+/* -------- KCI_NCCIDOWN -------------------------------------- */
+
+static void notify_nccidown(__u32 contr, __u16 applid, __u32 ncci)
+{
+	struct capi_interface_user *p;
+	struct capi_ncciinfo n;
+	n.applid = applid;
+	n.ncci = ncci;
+        /*printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr);*/
+	spin_lock(&capi_users_lock);
+	for (p = capi_users; p; p = p->next) {
+		if (!p->callback) continue;
+		(*p->callback) (KCI_NCCIDOWN, contr, &n);
+	}
+	spin_unlock(&capi_users_lock);
+};
+
+/* ------------------------------------------------------------ */
+
+static void inline notify_doit(struct capi_notifier *np)
+{
+	switch (np->cmd) {
+		case KCI_CONTRUP:
+			notify_up(np->controller);
+			break;
+		case KCI_CONTRDOWN:
+			notify_down(np->controller);
+			break;
+		case KCI_NCCIUP:
+			notify_ncciup(np->controller, np->applid, np->ncci);
+			break;
+		case KCI_NCCIDOWN:
+			notify_nccidown(np->controller, np->applid, np->ncci);
+			break;
+	}
+}
+
+static void notify_handler(void *dummy)
+{
+	struct capi_notifier *np;
+
+	while ((np = notify_dequeue()) != 0) {
+		notify_doit(np);
+		kfree(np);
+		MOD_DEC_USE_COUNT;
+	}
+}
+	
 /* -------- NCCI Handling ------------------------------------- */
 
 static inline void mq_init(struct capi_ncci * np)
@@ -617,6 +819,7 @@
 	APPL(appl)->nncci++;
 	printk(KERN_INFO "kcapi: appl %d ncci 0x%x up\n", appl, ncci);
 
+	notify_push(KCI_NCCIUP, CARDNR(card), appl, ncci);
 }
 
 static void controllercb_free_ncci(struct capi_ctr * card,
@@ -634,6 +837,7 @@
 			kfree(np);
 			APPL(appl)->nncci--;
 			printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", appl, ncci);
+			notify_push(KCI_NCCIDOWN, CARDNR(card), appl, ncci);
 			return;
 		}
 	}
@@ -734,42 +938,6 @@
 	kfree_skb(skb);
 }
 
-/* -------- Notifier ------------------------------------------ */
-
-static void notify_up(__u32 contr)
-{
-	struct capi_interface_user *p;
-
-        printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
-	for (p = capi_users; p; p = p->next) {
-		if (!p->callback) continue;
-		(*p->callback) (KCI_CONTRUP, contr, &CARD(contr)->profile);
-	}
-}
-
-static void notify_down(__u32 contr)
-{
-	struct capi_interface_user *p;
-        printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr);
-	for (p = capi_users; p; p = p->next) {
-		if (!p->callback) continue;
-		(*p->callback) (KCI_CONTRDOWN, contr, 0);
-	}
-}
-
-static void notify_handler(void *dummy)
-{
-	__u32 contr;
-
-	for (contr=1; VALID_CARD(contr); contr++)
-		 if (test_and_clear_bit(contr, &notify_up_set))
-			 notify_up(contr);
-	for (contr=1; VALID_CARD(contr); contr++)
-		 if (test_and_clear_bit(contr, &notify_down_set))
-			 notify_down(contr);
-}
-
-
 static void controllercb_ready(struct capi_ctr * card)
 {
 	__u16 appl;
@@ -782,11 +950,10 @@
 		card->driver->register_appl(card, appl, &APPL(appl)->rparam);
 	}
 
-        set_bit(CARDNR(card), &notify_up_set);
-        queue_task(&tq_state_notify, &tq_scheduler);
-
         printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
 		CARDNR(card), card->name);
+
+	notify_push(KCI_CONTRUP, CARDNR(card), 0, 0);
 }
 
 static void controllercb_reseted(struct capi_ctr * card)
@@ -812,6 +979,7 @@
 				struct capi_ncci *np = *pp;
 				*pp = np->next;
 				printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down!\n", appl, np->ncci);
+				notify_push(KCI_NCCIDOWN, CARDNR(card), appl, np->ncci);
 				kfree(np);
 				nextpp = pp;
 			} else {
@@ -819,9 +987,10 @@
 			}
 		}
 	}
-	set_bit(CARDNR(card), &notify_down_set);
-	queue_task(&tq_state_notify, &tq_scheduler);
+
 	printk(KERN_NOTICE "kcapi: card %d down.\n", CARDNR(card));
+
+	notify_push(KCI_CONTRDOWN, CARDNR(card), 0, 0);
 }
 
 static void controllercb_suspend_output(struct capi_ctr *card)
@@ -937,7 +1106,7 @@
 	if (len < off) 
            return 0;
 	*eof = 1;
-	*start = page - off;
+	*start = page + off;
 	return ((count < len-off) ? count : len-off);
 }
 
@@ -952,9 +1121,12 @@
 {
 	struct capi_driver **pp;
 
+	MOD_INC_USE_COUNT;
+	spin_lock(&drivers_lock);
 	for (pp = &drivers; *pp; pp = &(*pp)->next) ;
 	driver->next = 0;
 	*pp = driver;
+	spin_unlock(&drivers_lock);
 	printk(KERN_NOTICE "kcapi: driver %s attached\n", driver->name);
 	sprintf(driver->procfn, "capi/drivers/%s", driver->name);
 	driver->procent = create_proc_entry(driver->procfn, 0, 0);
@@ -974,6 +1146,7 @@
 void detach_capi_driver(struct capi_driver *driver)
 {
 	struct capi_driver **pp;
+	spin_lock(&drivers_lock);
 	for (pp = &drivers; *pp && *pp != driver; pp = &(*pp)->next) ;
 	if (*pp) {
 		*pp = (*pp)->next;
@@ -981,10 +1154,12 @@
 	} else {
 		printk(KERN_ERR "kcapi: driver %s double detach ?\n", driver->name);
 	}
+	spin_unlock(&drivers_lock);
 	if (driver->procent) {
 	   remove_proc_entry(driver->procfn, 0);
 	   driver->procent = 0;
 	}
+	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
@@ -1041,6 +1216,7 @@
 
 	if (!VALID_APPLID(applid) || APPL(applid)->releasing)
 		return CAPI_ILLAPPNR;
+	APPL(applid)->releasing++;
 	while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
 		kfree_skb(skb);
 	for (i = 0; i < CAPI_MAXCONTR; i++) {
@@ -1049,6 +1225,7 @@
 		APPL(applid)->releasing++;
 		cards[i].driver->release_appl(&cards[i], applid);
 	}
+	APPL(applid)->releasing--;
 	if (APPL(applid)->releasing <= 0) {
 	        APPL(applid)->signal = 0;
 		APPL_MARK_FREE(applid);
@@ -1128,8 +1305,8 @@
 }
 
 static __u16 capi_set_signal(__u16 applid,
-			     void (*signal) (__u16 applid, __u32 param),
-			     __u32 param)
+			     void (*signal) (__u16 applid, void *param),
+			     void *param)
 {
 	if (!VALID_APPLID(applid))
 		return CAPI_ILLAPPNR;
@@ -1194,10 +1371,12 @@
 static struct capi_driver *find_driver(char *name)
 {
 	struct capi_driver *dp;
+	spin_lock(&drivers_lock);
 	for (dp = drivers; dp; dp = dp->next)
 		if (strcmp(dp->name, name) == 0)
-			return dp;
-	return 0;
+			break;
+	spin_unlock(&drivers_lock);
+	return dp;
 }
 
 #ifdef CONFIG_AVMB1_COMPAT
@@ -1272,6 +1451,10 @@
 		card = CARD(ldef.contr);
 		if (card->cardstate == CARD_FREE)
 			return -ESRCH;
+		if (card->driver->load_firmware == 0) {
+			printk(KERN_DEBUG "kcapi: load: driver \%s\" has no load function\n", card->driver->name);
+			return -ESRCH;
+		}
 
 		if (ldef.t4file.len <= 0) {
 			printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
@@ -1490,16 +1673,20 @@
 {
 	struct capi_interface_user *p;
 
+	MOD_INC_USE_COUNT;
+	spin_lock(&capi_users_lock);
 	for (p = capi_users; p; p = p->next) {
 		if (p == userp) {
+			spin_unlock(&capi_users_lock);
 			printk(KERN_ERR "kcapi: double attach from %s\n",
 			       userp->name);
+			MOD_DEC_USE_COUNT;
 			return 0;
 		}
 	}
 	userp->next = capi_users;
 	capi_users = userp;
-	MOD_INC_USE_COUNT;
+	spin_unlock(&capi_users_lock);
 	printk(KERN_NOTICE "kcapi: %s attached\n", userp->name);
 
 	return &avmb1_interface;
@@ -1509,15 +1696,18 @@
 {
 	struct capi_interface_user **pp;
 
+	spin_lock(&capi_users_lock);
 	for (pp = &capi_users; *pp; pp = &(*pp)->next) {
 		if (*pp == userp) {
 			*pp = userp->next;
+			spin_unlock(&capi_users_lock);
 			userp->next = 0;
-			MOD_DEC_USE_COUNT;
 			printk(KERN_NOTICE "kcapi: %s detached\n", userp->name);
+			MOD_DEC_USE_COUNT;
 			return 0;
 		}
 	}
+	spin_unlock(&capi_users_lock);
 	printk(KERN_ERR "kcapi: double detach from %s\n", userp->name);
 	return -1;
 }
@@ -1565,6 +1755,8 @@
 	char *p;
 	char rev[10];
 
+	MOD_INC_USE_COUNT;
+
 	skb_queue_head_init(&recv_queue);
 	/* init_bh(CAPI_BH, do_capi_bh); */
 
@@ -1606,6 +1798,7 @@
 	(void)c4_init();
 #endif
 #endif
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1623,7 +1816,6 @@
 		strcpy(rev, "1.0");
 	}
 
-	schedule(); /* execute queued tasks .... */
         proc_capi_exit();
 	printk(KERN_NOTICE "CAPI-driver Rev%s: unloaded\n", rev);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/t1isa.c linux.19p1/drivers/isdn/avmb1/t1isa.c
--- linux.vanilla/drivers/isdn/avmb1/t1isa.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/avmb1/t1isa.c	Wed Dec 13 22:06:52 2000
@@ -1,11 +1,15 @@
 /*
- * $Id: t1isa.c,v 1.10 2000/02/02 18:36:04 calle Exp $
+ * $Id: t1isa.c,v 1.11 2000/04/03 13:29:25 calle Exp $
  * 
  * Module for AVM T1 HEMA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: t1isa.c,v $
+ * Revision 1.11  2000/04/03 13:29:25  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
  * Revision 1.10  2000/02/02 18:36:04  calle
  * - Modules are now locked while init_module is running
  * - fixed problem with memory mapping if address is not aligned
@@ -81,7 +85,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.10 $";
+static char *revision = "$Revision: 1.11 $";
 
 /* ------------------------------------------------------------- */
 
@@ -598,6 +602,9 @@
 {
 	struct capi_driver *driver = &t1isa_driver;
 	char *p;
+	int retval = 0;
+
+	MOD_INC_USE_COUNT;
 
 	if ((p = strchr(revision, ':'))) {
 		strncpy(driver->revision, p + 1, sizeof(driver->revision));
@@ -612,9 +619,11 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
-		return -EIO;
+		retval = -EIO;
 	}
-	return 0;
+
+	MOD_DEC_USE_COUNT;
+	return retval;
 }
 
 #ifdef MODULE
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/t1pci.c linux.19p1/drivers/isdn/avmb1/t1pci.c
--- linux.vanilla/drivers/isdn/avmb1/t1pci.c	Fri Apr 21 12:46:08 2000
+++ linux.19p1/drivers/isdn/avmb1/t1pci.c	Wed Dec 13 22:06:52 2000
@@ -1,11 +1,25 @@
 /*
- * $Id: t1pci.c,v 1.5 2000/02/02 18:36:04 calle Exp $
+ * $Id: t1pci.c,v 1.9 2000/05/19 15:43:22 calle Exp $
  * 
  * Module for AVM T1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: t1pci.c,v $
+ * Revision 1.9  2000/05/19 15:43:22  calle
+ * added calls to pci_device_start().
+ *
+ * Revision 1.8  2000/05/06 00:52:36  kai
+ * merged changes from kernel tree
+ * fixed timer and net_device->name breakage
+ *
+ * Revision 1.7  2000/04/07 15:26:55  calle
+ * better error message if cabel not connected or T1 has no power.
+ *
+ * Revision 1.6  2000/04/03 13:29:25  calle
+ * make Tim Waugh happy (module unload races in 2.3.99-pre3).
+ * no real problem there, but now it is much cleaner ...
+ *
  * Revision 1.5  2000/02/02 18:36:04  calle
  * - Modules are now locked while init_module is running
  * - fixed problem with memory mapping if address is not aligned
@@ -42,12 +56,13 @@
 #include <linux/pci.h>
 #include <linux/capi.h>
 #include <asm/io.h>
+#include <linux/isdn.h>
 #include "capicmd.h"
 #include "capiutil.h"
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.9 $";
 
 #undef CONFIG_T1PCI_DEBUG
 #undef CONFIG_T1PCI_POLLDEBUG
@@ -164,7 +179,11 @@
 	b1dma_reset(card);
 
 	if ((retval = t1pci_detect(card)) != 0) {
-		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+		if (retval < 6)
+			printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+		else
+			printk(KERN_NOTICE "%s: card at 0x%x, but cabel not connected or T1 has no power (%d)\n",
 					driver->name, card->port, retval);
                 iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
 	        kfree(card->ctrlinfo);
@@ -264,6 +283,8 @@
 	char *p;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	if ((p = strchr(revision, ':'))) {
 		strncpy(driver->revision, p + 1, sizeof(driver->revision));
 		p = strchr(driver->revision, '$');
@@ -277,6 +298,7 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -284,6 +306,7 @@
 	if (!pci_present()) {
 		printk(KERN_ERR "%s: no PCI bus present\n", driver->name);
     		detach_capi_driver(driver);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -291,9 +314,21 @@
 		struct capicardparams param;
 
 		param.port = dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
-		param.irq = dev->irq;
+ 		param.irq = dev->irq;
 		param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK;
 
+		retval = pci_enable_device (dev);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: failed to enable AVM-T1-PCI at i/o %#x, irq %d, mem %#x err=%d\n",
+			driver->name, param.port, param.irq, param.membase, retval);
+#ifdef MODULE
+			cleanup_module();
+#endif
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+
 		printk(KERN_INFO
 			"%s: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
 			driver->name, param.port, param.irq, param.membase);
@@ -305,6 +340,7 @@
 #ifdef MODULE
 			cleanup_module();
 #endif
+			MOD_DEC_USE_COUNT;
 			return retval;
 		}
 		ncards++;
@@ -312,12 +348,15 @@
 	if (ncards) {
 		printk(KERN_INFO "%s: %d T1-PCI card(s) detected\n",
 				driver->name, ncards);
+		MOD_DEC_USE_COUNT;
 		return 0;
 	}
 	printk(KERN_ERR "%s: NO T1-PCI card detected\n", driver->name);
+	MOD_DEC_USE_COUNT;
 	return -ESRCH;
 #else
 	printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name);
+	MOD_DEC_USE_COUNT;
 	return -EIO;
 #endif
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/divert/divert_procfs.c linux.19p1/drivers/isdn/divert/divert_procfs.c
--- linux.vanilla/drivers/isdn/divert/divert_procfs.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/divert/divert_procfs.c	Wed Dec 13 22:06:52 2000
@@ -281,19 +281,14 @@
 
 static struct file_operations isdn_fops =
 {
-	isdn_divert_lseek,
-	isdn_divert_read,
-	isdn_divert_write,
-	NULL,			/* isdn_readdir */
-	isdn_divert_poll,	/* isdn_poll */
-	isdn_divert_ioctl,	/* isdn_ioctl */
-	NULL,			/* isdn_mmap */
-	isdn_divert_open,
-	NULL,			/* flush */
-	isdn_divert_close,
-	NULL			/* fsync */
+	llseek:         isdn_divert_lseek,
+	read:           isdn_divert_read,
+	write:          isdn_divert_write,
+	poll:           isdn_divert_poll,
+	ioctl:          isdn_divert_ioctl,
+	open:           isdn_divert_open,
+	release:        isdn_divert_close,                                      
 };
-
 struct inode_operations divert_file_inode_operations;
 
 /****************************/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/Makefile linux.19p1/drivers/isdn/hisax/Makefile
--- linux.vanilla/drivers/isdn/hisax/Makefile	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/Makefile	Wed Dec 13 22:06:52 2000
@@ -31,6 +31,8 @@
 HFC_2BDS0 :=
 JADE_OBJ :=
 W6692_OBJ :=
+NETJ_OBJ :=
+ICC_OBJ :=
 
 ifeq ($(CONFIG_HISAX_16_0),y)
 	 O_OBJS += teles0.o
@@ -124,10 +126,17 @@
 endif
 
 ifeq ($(CONFIG_HISAX_NETJET),y)
-	 O_OBJS += netjet.o
+	 O_OBJS += nj_s.o
+	 NETJ_OBJ := netjet.o
 	 ISAC_OBJ := isac.o
 endif
 
+ifeq ($(CONFIG_HISAX_NETJET_U),y)
+	 O_OBJS += nj_u.o
+	 NETJ_OBJ := netjet.o
+	 ICC_OBJ := icc.o
+endif
+
 ifeq ($(CONFIG_HISAX_HFCS),y)
 	 O_OBJS += hfcscard.o
 	 HFC_2BDS0 := hfc_2bds0.o
@@ -189,7 +198,7 @@
 endif
 
 O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(JADE_OBJ)
-O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(W6692_OBJ)
+O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(W6692_OBJ) $(NETJ_OBJ) $(ICC_OBJ)
 OX_OBJS += config.o
 
 O_TARGET :=
@@ -208,10 +217,9 @@
 
 MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \
 	    tei.c callc.c cert.c l3dss1.c l3_1tr6.c \
-	    elsa.c diva.c
+	    elsa.c diva.c sedlbauer.c
 
 CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
 
 cert.o:	$(MD5FILES) md5sums.asc
 	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D CERTIFICATION=$(CERT) -c -o cert.o cert.c
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/amd7930.c linux.19p1/drivers/isdn/hisax/amd7930.c
--- linux.vanilla/drivers/isdn/hisax/amd7930.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/amd7930.c	Wed Dec 13 22:06:52 2000
@@ -1,25 +1,9 @@
-/* $Id: amd7930.c,v 1.3 1999/07/12 21:04:52 keil Exp $
+/* $Id: amd7930.c,v 1.4 2000/06/26 08:59:12 keil Exp $
  *
  * HiSax ISDN driver - chip specific routines for AMD 7930
  *
  * Author       Brent Baccala (baccala@FreeSoft.org)
  *
- *
- *
- * $Log: amd7930.c,v $
- * Revision 1.3  1999/07/12 21:04:52  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.2  1998/02/12 23:07:10  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.1  1998/02/03 23:20:51  keil
- * New files for SPARC isdn support
- *
- * Revision 1.1  1998/01/08 04:17:12  baccala
- * ISDN comes to the Sparc.  Key points:
- *
  *    - Existing ISDN HiSax driver provides all the smarts
  *    - it compiles, runs, talks to an isolated phone switch, connects
  *      to a Cisco, pings go through
@@ -30,6 +14,7 @@
  *
  * The code is unreliable enough to be consider alpha
  *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the
  * SparcStation 1+.  The chip provides microphone and speaker interfaces
@@ -109,7 +94,7 @@
 #include "rawhdlc.h"
 #include <linux/interrupt.h>
 
-static const char *amd7930_revision = "$Revision: 1.3 $";
+static const char *amd7930_revision = "$Revision: 1.4 $";
 
 #define RCV_BUFSIZE	1024	/* Size of raw receive buffer in bytes */
 #define RCV_BUFBLKS	4	/* Number of blocks to divide buffer into
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/arcofi.c linux.19p1/drivers/isdn/hisax/arcofi.c
--- linux.vanilla/drivers/isdn/hisax/arcofi.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/arcofi.c	Wed Dec 13 22:06:52 2000
@@ -1,43 +1,10 @@
-/* $Id: arcofi.c,v 1.10 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: arcofi.c,v 1.11 2000/06/26 08:59:12 keil Exp $
+ *
  * arcofi.c   Ansteuerung ARCOFI 2165
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
- *
- *
- * $Log: arcofi.c,v $
- * Revision 1.10  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.9  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.8  1999/08/25 16:50:51  keil
- * Fix bugs which cause 2.3.14 hangs (waitqueue init)
- *
- * Revision 1.7  1999/07/01 08:11:17  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.6  1998/09/30 22:21:56  keil
- * cosmetics
- *
- * Revision 1.5  1998/09/27 12:52:57  keil
- * cosmetics
- *
- * Revision 1.4  1998/08/20 13:50:24  keil
- * More support for hybrid modem (not working yet)
- *
- * Revision 1.3  1998/05/25 12:57:38  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.2  1998/04/15 16:47:16  keil
- * new interface
- *
- * Revision 1.1  1997/10/29 18:51:20  keil
- * New files
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/arcofi.h linux.19p1/drivers/isdn/hisax/arcofi.h
--- linux.vanilla/drivers/isdn/hisax/arcofi.h	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/arcofi.h	Wed Dec 13 22:06:52 2000
@@ -1,27 +1,10 @@
-/* $Id: arcofi.h,v 1.5 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: arcofi.h,v 1.6 2000/06/26 08:59:12 keil Exp $
+ *
  * arcofi.h   Ansteuerung ARCOFI 2165
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
- *
- *
- * $Log: arcofi.h,v $
- * Revision 1.5  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.4  1999/07/01 08:11:18  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.3  1998/05/25 12:57:39  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.2  1998/04/15 16:47:17  keil
- * new interface
- *
- * Revision 1.1  1997/10/29 18:51:20  keil
- * New files
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/asuscom.c linux.19p1/drivers/isdn/hisax/asuscom.c
--- linux.vanilla/drivers/isdn/hisax/asuscom.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/asuscom.c	Wed Dec 13 22:06:59 2000
@@ -1,39 +1,12 @@
-/* $Id: asuscom.c,v 1.9 1999/12/19 13:09:41 keil Exp $
-
+/* $Id: asuscom.c,v 1.10 2000/06/26 08:59:12 keil Exp $
+ *
  * asuscom.c     low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for informations
  *
- *
- * $Log: asuscom.c,v $
- * Revision 1.9  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.8  1999/09/04 06:20:05  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.7  1999/07/12 21:04:53  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.6  1999/07/01 08:11:18  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.5  1998/11/15 23:54:19  keil
- * changes from 2.0
- *
- * Revision 1.4  1998/06/18 23:18:20  keil
- * Support for new IPAC card
- *
- * Revision 1.3  1998/04/15 16:46:53  keil
- * new init code
- *
- * Revision 1.2  1998/02/02 13:27:06  keil
- * New
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -46,7 +19,7 @@
 
 extern const char *CardType[];
 
-const char *Asuscom_revision = "$Revision: 1.9 $";
+const char *Asuscom_revision = "$Revision: 1.10 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -295,13 +268,13 @@
 		byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
 	save_flags(flags);
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	if (cs->subtyp == ASUS_IPAC)
 		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
 	else
 		byteout(cs->hw.asus.adr, 0);	/* Reset Off */
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	if (cs->subtyp == ASUS_IPAC) {
 		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/avm_a1.c linux.19p1/drivers/isdn/hisax/avm_a1.c
--- linux.vanilla/drivers/isdn/hisax/avm_a1.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/avm_a1.c	Wed Dec 13 22:06:59 2000
@@ -1,66 +1,10 @@
-/* $Id: avm_a1.c,v 2.11 1999/07/12 21:04:54 keil Exp $
-
+/* $Id: avm_a1.c,v 2.12 2000/06/26 08:59:12 keil Exp $
+ *
  * avm_a1.c     low level stuff for AVM A1 (Fritz) isdn cards
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: avm_a1.c,v $
- * Revision 2.11  1999/07/12 21:04:54  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.10  1998/11/15 23:54:21  keil
- * changes from 2.0
- *
- * Revision 2.9  1998/08/13 23:36:12  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.8  1998/04/15 16:44:27  keil
- * new init code
- *
- * Revision 2.7  1998/02/02 13:29:37  keil
- * fast io
- *
- * Revision 2.6  1998/01/13 23:09:46  keil
- * really disable timer
- *
- * Revision 2.5  1998/01/02 06:50:29  calle
- * Perodic timer of A1 now disabled, no need for linux driver.
- *
- * Revision 2.4  1997/11/08 21:35:42  keil
- * new l1 init
- *
- * Revision 2.3  1997/11/06 17:13:32  keil
- * New 2.1 init code
- *
- * Revision 2.2  1997/10/29 18:55:48  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 2.1  1997/07/27 21:47:13  keil
- * new interface structures
- *
- * Revision 2.0  1997/06/26 11:02:48  keil
- * New Layer and card interface
- *
- * Revision 1.6  1997/04/13 19:54:07  keil
- * Change in IRQ check delay for SMP
- *
- * Revision 1.5  1997/04/06 22:54:10  keil
- * Using SKB's
- *
- * Revision 1.4  1997/01/27 15:50:21  keil
- * SMP proof,cosmetics
- *
- * Revision 1.3  1997/01/21 22:14:20  keil
- * cleanups
- *
- * Revision 1.2  1996/10/27 22:07:31  keil
- * cosmetic changes
- *
- * Revision 1.1  1996/10/13 20:04:49  keil
- * Initial revision
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -70,7 +14,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-static const char *avm_revision = "$Revision: 2.11 $";
+static const char *avm_revision = "$Revision: 2.12 $";
 
 #define	 AVM_A1_STAT_ISAC	0x01
 #define	 AVM_A1_STAT_HSCX	0x02
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/avm_a1p.c linux.19p1/drivers/isdn/hisax/avm_a1p.c
--- linux.vanilla/drivers/isdn/hisax/avm_a1p.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/avm_a1p.c	Wed Dec 13 22:06:59 2000
@@ -1,4 +1,4 @@
-/* $Id: avm_a1p.c,v 2.5 1999/09/01 08:26:34 calle Exp $
+/* $Id: avm_a1p.c,v 2.6 2000/06/26 08:59:12 keil Exp $
  *
  * avm_a1p.c    low level stuff for the following AVM cards:
  *              A1 PCMCIA
@@ -7,29 +7,7 @@
  *
  * Author       Carsten Paeth (calle@calle.in-berlin.de)
  *
- * $Log: avm_a1p.c,v $
- * Revision 2.5  1999/09/01 08:26:34  calle
- * Patch from Daniel Beichl <dani@ecomag.net> to make A1 PCMCIA work again.
- *
- * Revision 2.4  1999/07/12 21:04:55  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.3  1998/11/15 23:54:22  keil
- * changes from 2.0
- *
- * Revision 2.2  1998/08/13 23:36:13  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.1  1998/07/15 15:01:23  calle
- * Support for AVM passive PCMCIA cards:
- *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
- *
- * Revision 1.1.2.1  1998/07/15 14:43:26  calle
- * Support for AVM passive PCMCIA cards:
- *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
- *
- *
+ *  This file is (c) under GNU PUBLIC LICENSE
  */
 #define __NO_VERSION__
 #include "hisax.h"
@@ -74,7 +52,7 @@
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
 
-static const char *avm_revision = "$Revision: 2.5 $";
+static const char *avm_revision = "$Revision: 2.6 $";
 
 static inline u_char
 ReadISAC(struct IsdnCardState *cs, u_char offset)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/avm_pci.c linux.19p1/drivers/isdn/hisax/avm_pci.c
--- linux.vanilla/drivers/isdn/hisax/avm_pci.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/avm_pci.c	Wed Dec 13 22:06:59 2000
@@ -1,57 +1,11 @@
-/* $Id: avm_pci.c,v 1.14 1999/12/19 13:09:41 keil Exp $
-
+/* $Id: avm_pci.c,v 1.18 2000/08/20 07:34:04 keil Exp $
+ *
  * avm_pci.c    low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
  *              Thanks to AVM, Berlin for informations
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: avm_pci.c,v $
- * Revision 1.14  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.13  1999/12/03 12:10:14  keil
- * Bugfix: Wrong channel use on hangup of channel 2
- *
- * Revision 1.12  1999/09/04 06:20:05  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.11  1999/08/11 21:01:18  keil
- * new PCI codefix
- *
- * Revision 1.10  1999/08/10 16:01:44  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.9  1999/07/12 21:04:57  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.8  1999/07/01 08:11:19  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.7  1999/02/22 18:26:30  keil
- * Argh ! ISAC address was only set with PCI
- *
- * Revision 1.6  1998/11/27 19:59:28  keil
- * set subtype for Fritz!PCI
- *
- * Revision 1.5  1998/11/27 12:56:45  keil
- * forgot to update setup function name
- *
- * Revision 1.4  1998/11/15 23:53:19  keil
- * Fritz!PnP; changes from 2.0
- *
- * Revision 1.3  1998/09/27 23:53:39  keil
- * Fix error handling
- *
- * Revision 1.2  1998/09/27 12:54:55  keil
- * bcs assign was lost in setstack, very bad results
- *
- * Revision 1.1  1998/08/20 13:47:30  keil
- * first version
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -63,13 +17,17 @@
 #include <linux/interrupt.h>
 
 extern const char *CardType[];
-static const char *avm_pci_rev = "$Revision: 1.14 $";
+static const char *avm_pci_rev = "$Revision: 1.18 $";
 
 #define  AVM_FRITZ_PCI		1
 #define  AVM_FRITZ_PNP		2
 
-#define  PCI_VENDOR_AVM		0x1244
-#define  PCI_FRITZPCI_ID	0xa00
+#ifndef PCI_VENDOR_ID_AVM
+#define PCI_VENDOR_ID_AVM	0x1244
+#endif
+#ifndef PCI_DEVICE_ID_AVM_FRITZ
+#define PCI_DEVICE_ID_AVM_FRITZ	0xa00
+#endif
 
 #define  HDLC_FIFO		0x0
 #define  HDLC_STATUS		0x4
@@ -339,7 +297,15 @@
 	if (cs->subtyp == AVM_FRITZ_PCI) {
 		outl(idx, cs->hw.avm.cfg_reg + 4);
 		while (cnt < count) {
+#ifdef __powerpc__
+#ifdef CONFIG_APUS
+			*ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#else
+			*ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#endif /* CONFIG_APUS */
+#else
 			*ptr++ = inl(cs->hw.avm.isac);
+#endif /* __powerpc__ */
 			cnt += 4;
 		}
 	} else {
@@ -395,7 +361,15 @@
 	write_ctrl(bcs, 3);  /* sets the correct index too */
 	if (cs->subtyp == AVM_FRITZ_PCI) {
 		while (cnt<count) {
+#ifdef __powerpc__
+#ifdef CONFIG_APUS
+			out_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#else
+			out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#endif /* CONFIG_APUS */
+#else
 			outl(*ptr++, cs->hw.avm.isac);
+#endif /* __powerpc__ */
 			cnt += 4;
 		}
 	} else {
@@ -751,11 +725,11 @@
 	save_flags(flags);
 	sti();
 	outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
 	outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 	printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
 }
@@ -813,17 +787,18 @@
 			printk(KERN_ERR "FritzPCI: no PCI bus present\n");
 			return(0);
 		}
-		if ((dev_avm = pci_find_device(PCI_VENDOR_AVM,
-			PCI_FRITZPCI_ID,  dev_avm))) {
+		if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM,
+			PCI_DEVICE_ID_AVM_FRITZ,  dev_avm))) {
 			cs->irq = dev_avm->irq;
 			if (!cs->irq) {
-				printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n");
+				printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n");
 				return(0);
 			}
-			cs->hw.avm.cfg_reg = dev_avm->base_address[ 1] &
-				PCI_BASE_ADDRESS_IO_MASK; 
+			if (pci_enable_device(dev_avm))
+				return(0);
+			cs->hw.avm.cfg_reg = dev_avm->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
 			if (!cs->hw.avm.cfg_reg) {
-				printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n");
+				printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n");
 				return(0);
 			}
 			cs->subtyp = AVM_FRITZ_PCI;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/bkm_a4t.c linux.19p1/drivers/isdn/hisax/bkm_a4t.c
--- linux.vanilla/drivers/isdn/hisax/bkm_a4t.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/bkm_a4t.c	Wed Dec 13 22:07:05 2000
@@ -1,4 +1,4 @@
-/* $Id: bkm_a4t.c,v 1.9 1999/12/19 13:09:41 keil Exp $
+/* $Id: bkm_a4t.c,v 1.11 2000/06/26 08:59:12 keil Exp $
  * bkm_a4t.c    low level stuff for T-Berkom A4T
  *              derived from the original file sedlbauer.c
  *              derived from the original file niccy.c
@@ -6,36 +6,7 @@
  *
  * Author       Roland Klabunde (R.Klabunde@Berkom.de)
  *
- * $Log: bkm_a4t.c,v $
- * Revision 1.9  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.8  1999/09/04 06:20:05  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.7  1999/08/22 20:26:55  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.6  1999/08/11 21:01:22  keil
- * new PCI codefix
- *
- * Revision 1.5  1999/08/10 16:01:46  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.4  1999/07/14 11:43:14  keil
- * correct PCI_SUBSYSTEM_VENDOR_ID
- *
- * Revision 1.3  1999/07/12 21:04:58  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.2  1999/07/01 08:07:53  keil
- * Initial version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -47,12 +18,12 @@
 #include "hscx.h"
 #include "jade.h"
 #include "isdnl1.h"
-#include "bkm_ax.h"
 #include <linux/pci.h>
+#include "bkm_ax.h"
 
 extern const char *CardType[];
 
-const char *bkm_a4t_revision = "$Revision: 1.9 $";
+const char *bkm_a4t_revision = "$Revision: 1.11 $";
 
 
 static inline u_char
@@ -235,11 +206,11 @@
 		sti();
 		/* Issue the I20 soft reset     */
 		pI20_Regs->i20SysControl = 0xFF;	/* all in */
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 		/* Remove the soft reset */
 		pI20_Regs->i20SysControl = sysRESET | 0xFF;
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 		/* Set our configuration */
 		pI20_Regs->i20SysControl = sysRESET | sysCFG;
@@ -250,14 +221,14 @@
 		    g_A4T_ISAC_RES |
 		    g_A4T_JADE_BOOTR |
 		    g_A4T_ISAR_BOOTR;
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 
 		/* Remove RESET state from ISDN */
 		pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES |
 						g_A4T_JADE_RES |
 						g_A4T_ISAR_RES);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 		restore_flags(flags);
 	}
@@ -316,15 +287,20 @@
 		printk(KERN_ERR "bkm_a4t: no PCI bus present\n");
 		return (0);
 	}
-	if ((dev_a4t = pci_find_device(I20_VENDOR_ID, I20_DEVICE_ID, dev_a4t))) {
-		u_int sub_sys_id = 0;
-
-		pci_read_config_dword(dev_a4t, PCI_SUBSYSTEM_VENDOR_ID,
-			&sub_sys_id);
-		if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) {
+	while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN,
+		PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) {
+		u16 sub_sys;
+		u16 sub_vendor;
+
+		pci_read_config_word(dev_a4t, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor);
+		pci_read_config_word(dev_a4t, PCI_SUBSYSTEM_ID, &sub_sys);
+		if ((sub_sys == A4T_SUBSYS_ID) && (sub_vendor == A4T_SUBVEN_ID)) {
+			if (pci_enable_device(dev_a4t))
+				return(0);
 			found = 1;
-			pci_memaddr = dev_a4t->base_address[ 0];
+			pci_memaddr = dev_a4t->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK;
 			cs->irq = dev_a4t->irq;
+			break;
 		}
 	}
 	if (!found) {
@@ -339,7 +315,6 @@
 		printk(KERN_WARNING "HiSax: %s: No Memory base address\n", CardType[card->typ]);
 		return (0);
 	}
-	pci_memaddr &= PCI_BASE_ADDRESS_MEM_MASK;
 	cs->hw.ax.base = (u_int) ioremap(pci_memaddr, 4096);
 	/* Check suspecious address */
 	pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/bkm_a8.c linux.19p1/drivers/isdn/hisax/bkm_a8.c
--- linux.vanilla/drivers/isdn/hisax/bkm_a8.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/bkm_a8.c	Wed Dec 13 22:07:05 2000
@@ -1,4 +1,4 @@
-/* $Id: bkm_a8.c,v 1.9 1999/12/19 13:09:41 keil Exp $
+/* $Id: bkm_a8.c,v 1.12 2000/06/26 08:59:12 keil Exp $
  * bkm_a8.c     low level stuff for Scitel Quadro (4*S0, passive)
  *              derived from the original file sedlbauer.c
  *              derived from the original file niccy.c
@@ -6,36 +6,7 @@
  *
  * Author       Roland Klabunde (R.Klabunde@Berkom.de)
  *
- * $Log: bkm_a8.c,v $
- * Revision 1.9  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.8  1999/09/04 06:20:05  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.7  1999/08/22 20:26:58  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.6  1999/08/11 21:01:24  keil
- * new PCI codefix
- *
- * Revision 1.5  1999/08/10 16:01:48  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.4  1999/07/14 11:43:15  keil
- * correct PCI_SUBSYSTEM_VENDOR_ID
- *
- * Revision 1.3  1999/07/12 21:04:59  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.2  1999/07/01 08:07:54  keil
- * Initial version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -46,29 +17,16 @@
 #include "ipac.h"
 #include "hscx.h"
 #include "isdnl1.h"
-#include "bkm_ax.h"
 #include <linux/pci.h>
+#include "bkm_ax.h"
+
+#if CONFIG_PCI
 
 #define	ATTEMPT_PCI_REMAPPING	/* Required for PLX rev 1 */
 
 extern const char *CardType[];
 
-const char sct_quadro_revision[] = "$Revision: 1.9 $";
-
-/* To survive the startup phase */
-typedef struct {
-	u_int active;		/* true/false */
-	u_int base;		/* ipac base address */
-} IPAC_STATE;
-
-static IPAC_STATE ipac_state[4 + 1] __initdata =
-{
-	{0, 0},			/* dummy */
-	{0, 0},			/* SCT_1 */
-	{0, 0},			/* SCT_2 */
-	{0, 0},			/* SCT_3 */
-	{0, 0}			/* SCT_4 */
-};
+const char sct_quadro_revision[] = "$Revision: 1.12 $";
 
 static const char *sct_quadro_subtypes[] =
 {
@@ -167,19 +125,13 @@
 	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value);
 }
 
-/* Check whether the specified ipac is already active or not */
-static int
-is_ipac_active(u_int ipac_nr)
-{
-	return (ipac_state[ipac_nr].active);
-}
-
 /* Set the specific ipac to active */
 static void
-set_ipac_active(u_int ipac_nr, u_int active)
+set_ipac_active(struct IsdnCardState *cs, u_int active)
 {
-	/* set activation state */
-	ipac_state[ipac_nr].active = active;
+	/* set irq mask */
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK,
+		active ? 0xc0 : 0xff);
 }
 
 /*
@@ -202,13 +154,14 @@
 {
 	struct IsdnCardState *cs = dev_id;
 	u_char ista, val, icnt = 5;
-	int i;
+
 	if (!cs) {
 		printk(KERN_WARNING "HiSax: Scitel Quadro: Spurious interrupt!\n");
 		return;
 	}
 	ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
-
+	if (!(ista & 0x3f)) /* not this IPAC */
+		return;
       Start_IPAC:
 	if (cs->debug & L1_DEB_IPAC)
 		debugl1(cs, "IPAC ISTA %02X", ista);
@@ -245,30 +198,15 @@
 		       sct_quadro_subtypes[cs->subtyp]);
 	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF);
 	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0);
-
-	/* Read out all interrupt sources from currently not active ipacs */
-	/* "Handle" all interrupts from currently not active ipac by reading the regs */
-	for (i = SCT_1; i <= SCT_4; i++)
-		if (!is_ipac_active(i)) {
-			u_int base = ipac_state[i].base;
-			if (readreg(base, base + 4, 0xC1)) {
-				readreg(base, base + 4, 0xA0);
-				readreg(base, base + 4, 0xA4);
-				readreg(base, base + 4, 0x20);
-				readreg(base, base + 4, 0x24);
-				readreg(base, base + 4, 0x60);
-				readreg(base, base + 4, 0x64);
-				readreg(base, base + 4, 0xC1);
-				readreg(base, base + 4, ISAC_CIR0 + 0x80);
-			}
-		}
 }
 
 
 void
 release_io_sct_quadro(struct IsdnCardState *cs)
 {
-	/* ?? */
+	release_region(cs->hw.ax.base & 0xffffffc0, 256);
+	if (cs->subtyp == SCT_1)
+		release_region(cs->hw.ax.plx_adr, 256);
 }
 
 static void
@@ -278,11 +216,6 @@
 		if (bEnable)
 			wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41));
 		else
-			/* Issue general di only if no ipac is active */
-			if (!is_ipac_active(SCT_1) &&
-			    !is_ipac_active(SCT_2) &&
-			    !is_ipac_active(SCT_3) &&
-			    !is_ipac_active(SCT_4))
 			wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41));
 	}
 }
@@ -292,26 +225,17 @@
 {
 	long flags;
 
-	if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
-		if (!is_ipac_active(SCT_1) &&
-		    !is_ipac_active(SCT_2) &&
-		    !is_ipac_active(SCT_3) &&
-		    !is_ipac_active(SCT_4)) {
-			/* Issue total reset only if no ipac is active */
-			wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
-
-			save_flags(flags);
-			sti();
-			current->state = TASK_UNINTERRUPTIBLE;
-			schedule_timeout((10 * HZ) / 1000);
-
-			/* Remove the soft reset */
-			wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
-
-			current->state = TASK_UNINTERRUPTIBLE;
-			schedule_timeout((10 * HZ) / 1000);
-			restore_flags(flags);
-		}
+	if (cs->subtyp == SCT_1) {
+		wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
+		save_flags(flags);
+		sti();
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((10 * HZ) / 1000);
+		/* Remove the soft reset */
+		wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((10 * HZ) / 1000);
+		restore_flags(flags);
 	}
 }
 
@@ -321,20 +245,19 @@
 	switch (mt) {
 		case CARD_RESET:
 			/* Disable ints */
-			set_ipac_active(cs->subtyp, 0);
+			set_ipac_active(cs, 0);
 			enable_bkm_int(cs, 0);
 			reset_bkm(cs);
 			return (0);
 		case CARD_RELEASE:
 			/* Sanity */
-			set_ipac_active(cs->subtyp, 0);
+			set_ipac_active(cs, 0);
 			enable_bkm_int(cs, 0);
-			reset_bkm(cs);
 			release_io_sct_quadro(cs);
 			return (0);
 		case CARD_INIT:
 			cs->debug |= L1_DEB_IPAC;
-			set_ipac_active(cs->subtyp, 1);
+			set_ipac_active(cs, 1);
 			inithscxisac(cs, 3);
 			/* Enable ints */
 			enable_bkm_int(cs, 1);
@@ -345,18 +268,38 @@
 	return (0);
 }
 
+__initfunc(int
+sct_alloc_io(u_int adr, u_int len))
+{
+	if (check_region(adr, len)) {
+		printk(KERN_WARNING
+			"HiSax: Scitel port %#x-%#x already in use\n",
+			adr, adr + len);
+		return (1);
+	} else {
+		request_region(adr, len, "scitel");
+	}
+	return(0);
+}
+
 static struct pci_dev *dev_a8 __initdata = NULL;
+static u16  sub_vendor_id __initdata = 0;
+static u16  sub_sys_id __initdata = 0;
+static u_char pci_bus __initdata = 0;
+static u_char pci_device_fn __initdata = 0;
+static u_char pci_irq __initdata = 0;
+
+#endif /* CONFIG_PCI */
 
 __initfunc(int
-	   setup_sct_quadro(struct IsdnCard *card))
+setup_sct_quadro(struct IsdnCard *card))
 {
+#if CONFIG_PCI
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
-#if CONFIG_PCI
-	u_char pci_bus = 0, pci_device_fn = 0, pci_irq = 0, pci_rev_id;
+	u_char pci_rev_id;
 	u_int found = 0;
 	u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5;
-#endif
 
 	strcpy(tmp, sct_quadro_revision);
 	printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
@@ -368,32 +311,61 @@
 	/* Identify subtype by para[0] */
 	if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4)
 		cs->subtyp = card->para[0];
-	else
+	else {
 		printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n",
-		       CardType[card->typ]);
-#if CONFIG_PCI
-	if (!pci_present()) {
-		printk(KERN_ERR "bkm_a4t: no PCI bus present\n");
+			CardType[card->typ]);
 		return (0);
 	}
-	if ((dev_a8 = pci_find_device(PLX_VENDOR_ID, PLX_DEVICE_ID, dev_a8))) {
-		u_int sub_sys_id = 0;
-
-		pci_read_config_dword(dev_a8, PCI_SUBSYSTEM_VENDOR_ID,
-			&sub_sys_id);
-		if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) {
-			found = 1;
-			pci_ioaddr1 = dev_a8->base_address[ 1];
-			pci_irq = dev_a8->irq;
-			pci_bus = dev_a8->bus->number;
-			pci_device_fn = dev_a8->devfn;
-		}
-	}
-	if (!found) {
-		printk(KERN_WARNING "HiSax: %s (%s): Card not found\n",
-		       CardType[card->typ],
-		       sct_quadro_subtypes[cs->subtyp]);
+	if ((cs->subtyp != SCT_1) && ((sub_sys_id != SCT_SUBSYS_ID) ||
+		(sub_vendor_id != SCT_SUBVEN_ID)))
 		return (0);
+	if (cs->subtyp == SCT_1) {
+		if (!pci_present()) {
+			printk(KERN_ERR "bkm_a4t: no PCI bus present\n");
+			return (0);
+		}
+		while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX,
+			PCI_DEVICE_ID_PLX_9050, dev_a8))) {
+			
+			pci_read_config_word(dev_a8, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor_id);
+			pci_read_config_word(dev_a8, PCI_SUBSYSTEM_ID, &sub_sys_id);
+			if ((sub_sys_id == SCT_SUBSYS_ID) &&
+				(sub_vendor_id == SCT_SUBVEN_ID)) {
+				if (pci_enable_device(dev_a8))
+					return(0);
+				pci_ioaddr1 = dev_a8->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
+				pci_irq = dev_a8->irq;
+				pci_bus = dev_a8->bus->number;
+				pci_device_fn = dev_a8->devfn;
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			printk(KERN_WARNING "HiSax: %s (%s): Card not found\n",
+				CardType[card->typ],
+				sct_quadro_subtypes[cs->subtyp]);
+			return (0);
+		}
+#ifdef ATTEMPT_PCI_REMAPPING
+/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+			PCI_REVISION_ID, &pci_rev_id);
+		if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) {
+			printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n",
+				CardType[card->typ],
+				sct_quadro_subtypes[cs->subtyp]);
+			/* Restart PCI negotiation */
+			pcibios_write_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_1, (u_int) - 1);
+			/* Move up by 0x80 byte */
+			pci_ioaddr1 += 0x80;
+			pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+			pcibios_write_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_1, pci_ioaddr1);
+			dev_a8->base_address[ 1] = pci_ioaddr1;
+		}
+#endif /* End HACK */
 	}
 	if (!pci_irq) {		/* IRQ range check ?? */
 		printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n",
@@ -401,25 +373,6 @@
 		       sct_quadro_subtypes[cs->subtyp]);
 		return (0);
 	}
-#ifdef ATTEMPT_PCI_REMAPPING
-/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
-	pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_REVISION_ID, &pci_rev_id);
-	if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) {
-		printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n",
-			CardType[card->typ],
-			sct_quadro_subtypes[cs->subtyp]);
-		/* Restart PCI negotiation */
-		pcibios_write_config_dword(pci_bus, pci_device_fn,
-			PCI_BASE_ADDRESS_1, (u_int) - 1);
-		/* Move up by 0x80 byte */
-		pci_ioaddr1 += 0x80;
-		pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
-		pcibios_write_config_dword(pci_bus, pci_device_fn,
-			PCI_BASE_ADDRESS_1, pci_ioaddr1);
-		dev_a8->base_address[ 1] = pci_ioaddr1;
-	}
-/* End HACK */
-#endif
 	pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
 	pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_ioaddr2);
 	pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_ioaddr3);
@@ -446,23 +399,42 @@
 	/* pci_ioaddr5 is for the first subdevice only */
 	cs->hw.ax.plx_adr = pci_ioaddr1;
 	/* Enter all ipac_base addresses */
-	ipac_state[SCT_1].base = pci_ioaddr5 + 0x00;
-	ipac_state[SCT_2].base = pci_ioaddr4 + 0x08;
-	ipac_state[SCT_3].base = pci_ioaddr3 + 0x10;
-	ipac_state[SCT_4].base = pci_ioaddr2 + 0x20;
-	/* For isac and hscx control path */
-	cs->hw.ax.base = ipac_state[cs->subtyp].base;
+	switch(cs->subtyp) {
+		case 1:
+			cs->hw.ax.base = pci_ioaddr5 + 0x00;
+			if (sct_alloc_io(pci_ioaddr1, 256))
+				return(0);
+			if (sct_alloc_io(pci_ioaddr5, 256))
+				return(0);
+			/* disable all IPAC */
+			writereg(pci_ioaddr5, pci_ioaddr5 + 4,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24,
+				IPAC_MASK, 0xFF);
+			break;
+		case 2:
+			cs->hw.ax.base = pci_ioaddr4 + 0x08;
+			if (sct_alloc_io(pci_ioaddr4, 256))
+				return(0);
+			break;
+		case 3:
+			cs->hw.ax.base = pci_ioaddr3 + 0x10;
+			if (sct_alloc_io(pci_ioaddr3, 256))
+				return(0);
+			break;
+		case 4:
+			cs->hw.ax.base = pci_ioaddr2 + 0x20;
+			if (sct_alloc_io(pci_ioaddr2, 256))
+				return(0);
+			break;
+	}	
 	/* For isac and hscx data path */
 	cs->hw.ax.data_adr = cs->hw.ax.base + 4;
-#else
-	printk(KERN_WARNING "HiSax: %s (%s): NO_PCI_BIOS\n",
-	       CardType[card->typ],
-	       sct_quadro_subtypes[cs->subtyp]);
-	printk(KERN_WARNING "HiSax: %s (%s): Unable to configure\n",
-	       CardType[card->typ],
-	       sct_quadro_subtypes[cs->subtyp]);
-	return (0);
-#endif				/* CONFIG_PCI */
+
 	printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4X, 0x%.4X, 0x%.4X and IRQ %d\n",
 	       CardType[card->typ],
 	       sct_quadro_subtypes[cs->subtyp],
@@ -473,19 +445,6 @@
 
 	test_and_set_bit(HW_IPAC, &cs->HW_Flags);
 
-	/* Disable all currently not active ipacs */
-	if (!is_ipac_active(SCT_1))
-		set_ipac_active(SCT_1, 0);
-	if (!is_ipac_active(SCT_2))
-		set_ipac_active(SCT_2, 0);
-	if (!is_ipac_active(SCT_3))
-		set_ipac_active(SCT_3, 0);
-	if (!is_ipac_active(SCT_4))
-		set_ipac_active(SCT_4, 0);
-
-	/* Perfom general reset (if possible) */
-	reset_bkm(cs);
-
 	cs->readisac = &ReadISAC;
 	cs->writeisac = &WriteISAC;
 	cs->readisacfifo = &ReadISACfifo;
@@ -502,4 +461,7 @@
 		sct_quadro_subtypes[cs->subtyp],
 		readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID));
 	return (1);
+#else
+	printk(KERN_ERR "HiSax: bkm_a8 only supported on PCI Systems\n");
+#endif /* CONFIG_PCI */
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/bkm_ax.h linux.19p1/drivers/isdn/hisax/bkm_ax.h
--- linux.vanilla/drivers/isdn/hisax/bkm_ax.h	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/bkm_ax.h	Wed Dec 13 22:07:05 2000
@@ -1,13 +1,9 @@
-/* $Id: bkm_ax.h,v 1.2 1999/07/01 08:07:55 keil Exp $
+/* $Id: bkm_ax.h,v 1.4 2000/06/26 08:59:12 keil Exp $
  * bkm_ax.h   low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive)
  *
  * Author     Roland Klabunde (R.Klabunde@Berkom.de)
  *
- * $Log: bkm_ax.h,v $
- * Revision 1.2  1999/07/01 08:07:55  keil
- * Initial version
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -23,13 +19,21 @@
 
 
 /* A4T																*/
-#define I20_DEVICE_ID       	0x6120  /* I20 PCI device ID */
-#define I20_VENDOR_ID       	0x11DE  /* I20 PCI vendor ID */
+#ifndef PCI_VENDOR_ID_ZORAN
+#define PCI_VENDOR_ID_ZORAN	0x11DE
+#endif
+#ifndef PCI_DEVICE_ID_ZORAN_36120
+#define PCI_DEVICE_ID_ZORAN_36120	0x6120
+#endif
 #define	A4T_SUBVEN_ID		0x0871	
 #define	A4T_SUBSYS_ID		0xFFA4
 /* Scitel Quadro 																*/
-#define	PLX_DEVICE_ID		0x9050	/* Scitel Quadro PLX */
-#define	PLX_VENDOR_ID		0x10B5
+#ifndef PCI_VENDOR_ID_PLX
+#define PCI_VENDOR_ID_PLX	0x10B5
+#endif
+#ifndef PCI_DEVICE_ID_PLX_9050
+#define PCI_DEVICE_ID_PLX_9050	0x9050
+#endif
 #define	SCT_SUBVEN_ID		0x0871
 #define	SCT_SUBSYS_ID		0xFFA8
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/callc.c linux.19p1/drivers/isdn/hisax/callc.c
--- linux.vanilla/drivers/isdn/hisax/callc.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/callc.c	Wed Dec 13 22:07:05 2000
@@ -1,5 +1,5 @@
-/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $
-
+/* $Id: callc.c,v 2.47 2000/06/26 08:59:12 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
@@ -10,153 +10,6 @@
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
- * $Log: callc.c,v $
- * Revision 2.40  1999/12/19 12:59:56  keil
- * fix leased line handling
- * and cosmetics
- *
- * Revision 2.39  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 2.38  1999/10/11 22:16:27  keil
- * Suspend/Resume is possible without explicit ID too
- *
- * Revision 2.37  1999/09/20 19:49:47  keil
- * Fix wrong init of PStack
- *
- * Revision 2.36  1999/09/20 12:13:13  keil
- * Fix hang if no protocol was selected
- *
- * Revision 2.35  1999/09/04 06:20:05  keil
- * Changes from kernel set_current_state()
- *
- * Revision 2.34  1999/08/25 20:02:34  werner
- * Changed return values for stat_icall(w) from 3->4 and 4->5 because of conflicts
- * with existing software definitions. (PtP incomplete called party number)
- *
- * Revision 2.33  1999/08/25 17:00:09  keil
- * Make ISAR V32bis modem running
- * Make LL->HL interface open for additional commands
- *
- * Revision 2.32  1999/08/22 20:27:01  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 2.31  1999/08/05 20:43:10  keil
- * ISAR analog modem support
- *
- * Revision 2.30  1999/07/25 16:24:04  keil
- * Fixed TEI now working again
- *
- * Revision 2.29  1999/07/13 21:05:41  werner
- * Modified set_channel_limit to use new callback ISDN_STAT_DISCH.
- *
- * Revision 2.28  1999/07/09 08:30:02  keil
- * cosmetics
- *
- * Revision 2.27  1999/07/05 23:51:38  werner
- * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl
- * hisaxctrl id 10 <nr. of chans 0-2>
- *
- * Revision 2.26  1999/07/01 08:11:21  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.25  1999/01/02 11:17:20  keil
- * Changes for 2.2
- *
- * Revision 2.24  1998/11/15 23:54:24  keil
- * changes from 2.0
- *
- * Revision 2.23  1998/09/30 22:21:57  keil
- * cosmetics
- *
- * Revision 2.22  1998/08/20 13:50:29  keil
- * More support for hybrid modem (not working yet)
- *
- * Revision 2.21  1998/08/13 23:36:15  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.20  1998/06/26 15:13:05  fritz
- * Added handling of STAT_ICALL with incomplete CPN.
- * Added AT&L for ttyI emulator.
- * Added more locking stuff in tty_write.
- *
- * Revision 2.19  1998/05/25 14:08:06  keil
- * HiSax 3.0
- * fixed X.75 and leased line to work again
- * Point2Point and fixed TEI are runtime options now:
- *    hisaxctrl <id> 7 1  set PTP
- *    hisaxctrl <id> 8 <TEIVALUE *2 >
- *    set fixed TEI to TEIVALUE (0-63)
- *
- * Revision 2.18  1998/05/25 12:57:40  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.17  1998/04/15 16:46:06  keil
- * RESUME support
- *
- * Revision 2.16  1998/04/10 10:35:17  paul
- * fixed (silly?) warnings from egcs on Alpha.
- *
- * Revision 2.15  1998/03/19 13:18:37  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 2.14  1998/03/07 22:56:54  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 2.13  1998/02/12 23:07:16  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.12  1998/02/09 10:55:54  keil
- * New leased line mode
- *
- * Revision 2.11  1998/02/02 13:35:19  keil
- * config B-channel delay
- *
- * Revision 2.10  1997/11/06 17:09:15  keil
- * New 2.1 init code
- *
- * Revision 2.9  1997/10/29 19:01:58  keil
- * new LL interface
- *
- * Revision 2.8  1997/10/10 20:56:44  fritz
- * New HL interface.
- *
- * Revision 2.7  1997/10/01 09:21:28  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 2.6  1997/09/11 17:26:58  keil
- * Open B-channel if here are incomming packets
- *
- * Revision 2.5  1997/08/07 17:46:05  keil
- * Fix Incomming Call without broadcast
- *
- * Revision 2.4  1997/08/03 14:37:58  keil
- * Activate Layer2 in PtP mode
- *
- * Revision 2.3  1997/07/31 19:23:40  keil
- * LAYER2_WATCHING for PtP
- *
- * Revision 2.2  1997/07/31 11:48:18  keil
- * experimental REJECT after ALERTING
- *
- * Revision 2.1  1997/07/30 17:12:59  keil
- * more changes for 'One TEI per card'
- *
- * Revision 2.0  1997/07/27 21:12:21  keil
- * CRef based L3; new channel handling; many other stuff
- *
- * Revision 1.31  1997/06/26 11:09:23  keil
- * New managment and minor changes
- *
- * old logs removed /KKe
- *
  */
 
 #define __NO_VERSION__
@@ -167,7 +20,7 @@
 #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module))
 #endif	/* MODULE */
 
-const char *lli_revision = "$Revision: 2.40 $";
+const char *lli_revision = "$Revision: 2.47 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -349,6 +202,8 @@
 {
 	isdn_ctrl ic;
 
+	if (!chanp->proc)
+		return;
 	if (chanp->proc->para.cause == NO_CAUSE)
 		return;
 	ic.driver = chanp->cs->myid;
@@ -511,7 +366,7 @@
 		 * No need to return "unknown" for calls without OAD,
 		 * cause that's handled in linklevel now (replaced by '0')
 		 */
-		ic.parm.setup = chanp->proc->para.setup;
+		memcpy(&ic.parm.setup, &chanp->proc->para.setup, sizeof(setup_parm));
 		ret = chanp->cs->iif.statcallb(&ic);
 		if (chanp->debug & 1)
 			link_debug(chanp, 1, "statcallb ret=%d", ret);
@@ -528,12 +383,16 @@
 				FsmChangeState(fi, ST_IN_PROCEED_SEND);
 				chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
 				if (ret == 5) {
-					chanp->setup = ic.parm.setup;
+					memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm));
 					chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
 				}
 				break;
 			case 2:	/* Rejecting Call */
 				break;
+			case 3:	/* incomplete number */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
+				break;
 			case 0:	/* OK, nobody likes this call */
 			default:	/* statcallb problems */
 				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
@@ -641,7 +500,8 @@
 		lli_leased_hup(fi, chanp);
 	} else {
 		FsmChangeState(fi, ST_WAIT_DRELEASE);
-		chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
+		if (chanp->proc)
+			chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
 		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
 			chanp->proc);
 	}
@@ -656,7 +516,8 @@
 		lli_leased_hup(fi, chanp);
 	} else {
 		FsmChangeState(fi, ST_WAIT_DRELEASE);
-		chanp->proc->para.cause = 0x15;	/* Call Rejected */
+		if (chanp->proc)
+			chanp->proc->para.cause = 0x15;	/* Call Rejected */
 		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
 			chanp->proc);
 	}
@@ -688,7 +549,8 @@
 		return;
 	}
 #ifndef ALERT_REJECT
-	chanp->proc->para.cause = 0x15;	/* Call Rejected */
+	if (chanp->proc)
+		chanp->proc->para.cause = 0x15;	/* Call Rejected */
 	chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
 	lli_dhup_close(fi, event, arg);
 #else
@@ -937,6 +799,8 @@
         {ST_IN_WAIT_LL,         EV_HANGUP,              lli_reject_req},
         {ST_IN_WAIT_LL,         EV_DISCONNECT_IND,      lli_release_req},
         {ST_IN_WAIT_LL,         EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_LL,         EV_SETUP_IND,           lli_deliver_call},
+        {ST_IN_WAIT_LL,         EV_SETUP_ERR,           lli_error},
         {ST_IN_ALERT_SENT,      EV_SETUP_CMPL_IND,      lli_init_bchan_in},
         {ST_IN_ALERT_SENT,      EV_ACCEPTD,             lli_send_dconnect},
         {ST_IN_ALERT_SENT,      EV_HANGUP,              lli_disconnect_reject},
@@ -1098,6 +962,9 @@
 		return;
 
 	switch (pr) {
+		case (CC_MORE_INFO | INDICATION):
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			break;
 		case (CC_DISCONNECT | INDICATION):
 			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
 			break;
@@ -1245,7 +1112,7 @@
 	chanp->fi.printdebug = callc_debug;
 	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
 	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
-	if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+	if (!chan || (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags) && chan < 2)) {
 		init_d_st(chanp);
 	} else {
 		chanp->d_st = csta->channel->d_st;
@@ -1318,9 +1185,12 @@
 
 	switch (pr) {
 		case (DL_DATA  | INDICATION):
-			if (chanp->data_open)
+			if (chanp->data_open) {
+				if (chanp->debug & 0x800)
+					link_debug(chanp, 0, "lldata: %d", skb->len);
 				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
-			else {
+			} else {
+				link_debug(chanp, 0, "lldata: channel not open");
 				dev_kfree_skb(skb);
 			}
 			break;
@@ -1347,10 +1217,12 @@
 
 	switch (pr) {
 		case (PH_DATA | INDICATION):
-			if (chanp->data_open)
+			if (chanp->data_open) {
+				if (chanp->debug & 0x800)
+					link_debug(chanp, 0, "lltrans: %d", skb->len);
 				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
-			else {
-				link_debug(chanp, 0, "channel not open");
+			} else {
+				link_debug(chanp, 0, "lltrans: channel not open");
 				dev_kfree_skb(skb);
 			}
 			break;
@@ -1375,6 +1247,8 @@
 	struct Channel *chanp = st->lli.userdata;
 	isdn_ctrl ic;
 
+	if (chanp->debug & 0x800)
+		link_debug(chanp, 0, "llwakeup: %d", len);
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BSENT;
 	ic.arg = chanp->chan;
@@ -1648,7 +1522,7 @@
 				link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)",
 					ic->parm.setup.eazmsn, ic->parm.setup.phone,
 					ic->parm.setup.si1, ic->parm.setup.si2);
-			chanp->setup = ic->parm.setup;
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
 			if (!strcmp(chanp->setup.eazmsn, "0"))
 				chanp->setup.eazmsn[0] = '\0';
 			/* this solution is dirty and may be change, if
@@ -1668,6 +1542,7 @@
 			break;
 		case (ISDN_CMD_ACCEPTD):
 			chanp = csta->channel + ic->arg;
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
 			if (chanp->debug & 1)
 				link_debug(chanp, 1, "ACCEPTD");
 			FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
@@ -1864,7 +1739,7 @@
 			chanp = csta->channel + ic->arg;
 			if (chanp->debug & 1)
 				link_debug(chanp, 1, "REDIR");
-			chanp->setup = ic->parm.setup;
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
 			FsmEvent(&chanp->fi, EV_REDIR, NULL);
 			break;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/cert.c linux.19p1/drivers/isdn/hisax/cert.c
--- linux.vanilla/drivers/isdn/hisax/cert.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/cert.c	Wed Dec 13 22:07:05 2000
@@ -1,22 +1,11 @@
-/* $Id: cert.c,v 2.2 1999/08/07 17:35:05 keil Exp $
-
+/* $Id: cert.c,v 2.3 2000/06/26 08:59:12 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *		This file is (c) under GNU PUBLIC LICENSE
  *		For changes and modifications please read
  *		../../../Documentation/isdn/HiSax.cert
  *
- * $Log: cert.c,v $
- * Revision 2.2  1999/08/07 17:35:05  keil
- * approval for Eicon Technology Diva 2.01 PCI
- *
- * Revision 2.1  1998/11/15 23:51:15  keil
- * certification stuff
- *
- * Revision 1.2.2.1  1998/11/03 21:46:37  keil
- * first version
- *
- *
  */
  
 #include <linux/kernel.h>
@@ -28,11 +17,9 @@
 #if CERTIFICATION == 0
 	if (output) {
 		printk(KERN_INFO "HiSax: Approval certification valid\n");
-		printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n");
-		printk(KERN_INFO "HiSax: Approval registration numbers:\n");
-		printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n");
-		printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n");
+		printk(KERN_INFO "HiSax: Approved with ELSA Microlink PCI cards\n");
 		printk(KERN_INFO "HiSax: Approved with Eicon Technology Diva 2.01 PCI cards\n");
+		printk(KERN_INFO "HiSax: Approved with Sedlbauer Speedfax + cards\n");
 	}
 	return(0);
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/config.c linux.19p1/drivers/isdn/hisax/config.c
--- linux.vanilla/drivers/isdn/hisax/config.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/hisax/config.c	Wed Dec 13 22:07:14 2000
@@ -1,161 +1,9 @@
-/* $Id: config.c,v 2.44 2000/02/26 00:35:12 keil Exp $
-
+/* $Id: config.c,v 2.52 2000/10/02 17:33:43 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
- *
- * $Log: config.c,v $
- * Revision 2.44  2000/02/26 00:35:12  keil
- * Fix skb freeing in interrupt context
- *
- * Revision 2.43  2000/01/20 19:49:36  keil
- * Support teles 13.3c vendor version 2.1
- *
- * Revision 2.42  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 2.41  1999/11/18 00:00:43  werner
- *
- * Added support for HFC-S+ and HFC-SP cards
- *
- * Revision 2.40  1999/10/30 13:09:45  keil
- * Version 3.3c
- *
- * Revision 2.39  1999/10/16 14:44:45  keil
- * Fix module parm if only NICCY was selected
- *
- * Revision 2.38  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 2.37  1999/09/20 12:11:08  keil
- * Fix hang if no protocol was selected
- *
- * Revision 2.36  1999/09/07 05:43:58  werner
- *
- * Added io as parameter 0 for HFC-PCI cards, if manual selection needed.
- *
- * Revision 2.35  1999/09/04 06:35:09  keil
- * Winbond W6692 support
- *
- * Revision 2.34  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 2.33  1999/08/30 11:57:52  keil
- * Fix broken avm pcmcia
- *
- * Revision 2.32  1999/08/28 22:11:10  keil
- * __setup function should be static
- *
- * Revision 2.31  1999/08/25 16:47:43  keil
- * Support new __setup; allow to add FEATURES after register_isdn
- *
- * Revision 2.30  1999/08/05 20:43:14  keil
- * ISAR analog modem support
- *
- * Revision 2.29  1999/07/21 14:46:00  keil
- * changes from EICON certification
- *
- * Revision 2.28  1999/07/14 12:38:36  werner
- * Added changes for echo channel handling
- *
- * Revision 2.27  1999/07/12 21:05:00  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.26  1999/07/08 21:27:17  keil
- * version 3.2
- *
- * Revision 2.25  1999/07/05 23:51:44  werner
- * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl
- * hisaxctrl id 10 <nr. of chans 0-2>
- *
- * Revision 2.24  1999/07/01 08:11:26  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.23  1999/02/17 10:53:02  cpetig
- * Added Hisax_closecard to exported symbols.
- * As indicated by Oliver Schoett <os@sdm.de>.
- *
- * If anyone is annoyed by exporting symbols deep inside the code, please
- * contact me.
- *
- * Revision 2.22  1999/02/04 21:41:53  keil
- * Fix printk msg
- *
- * Revision 2.21  1999/02/04 10:48:52  keil
- * Fix readstat bug
- *
- * Revision 2.20  1998/11/15 23:54:28  keil
- * changes from 2.0
- *
- * Revision 2.19  1998/08/13 23:36:18  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.18  1998/07/30 21:01:37  niemann
- * Fixed Sedlbauer Speed Fax PCMCIA missing isdnl3new
- *
- * Revision 2.17  1998/07/15 15:01:26  calle
- * Support for AVM passive PCMCIA cards:
- *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
- *
- * Revision 2.16  1998/05/25 14:10:03  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.15  1998/05/25 12:57:43  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.14  1998/04/15 16:38:25  keil
- * Add S0Box and Teles PCI support
- *
- * Revision 2.13  1998/03/09 23:19:23  keil
- * Changes for PCMCIA
- *
- * Revision 2.12  1998/02/11 17:28:02  keil
- * Niccy PnP/PCI support
- *
- * Revision 2.11  1998/02/09 21:26:13  keil
- * fix export module for 2.1
- *
- * Revision 2.10  1998/02/09 18:46:05  keil
- * Support for Sedlbauer PCMCIA (Marcus Niemann)
- *
- * Revision 2.9  1998/02/03 23:31:28  keil
- * add AMD7930 support
- *
- * Revision 2.8  1998/02/02 13:32:59  keil
- * New card support
- *
- * Revision 2.7  1998/01/31 21:41:44  keil
- * changes for newer 2.1 kernels
- *
- * Revision 2.6  1997/11/08 21:35:43  keil
- * new l1 init
- *
- * Revision 2.5  1997/11/06 17:15:08  keil
- * New 2.1 init; PCMCIA wrapper changes
- *
- * Revision 2.4  1997/10/29 19:07:52  keil
- * changes for 2.1
- *
- * Revision 2.3  1997/10/01 09:21:33  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 2.2  1997/09/11 17:24:46  keil
- * Add new cards
- *
- * Revision 2.1  1997/07/27 21:41:35  keil
- * version change
- *
- * Revision 2.0  1997/06/26 11:06:28  keil
- * New card and L1 interface.
- * Eicon.Diehl Diva and Dynalink IS64PH support
- *
- * old changes removed /KKe
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #include <linux/types.h>
@@ -198,7 +46,7 @@
  *   17 MIC card                p0=irq  p1=iobase
  *   18 ELSA Quickstep 1000PCI  no parameter
  *   19 Compaq ISDN S0 ISA card p0=irq  p1=IO0 (HSCX)  p2=IO1 (ISAC) p3=IO2
- *   20 Travers Technologies NETjet PCI card
+ *   20 Travers Technologies NETjet-S PCI card
  *   21 TELES PCI               no parameter
  *   22 Sedlbauer Speed Star    p0=irq p1=iobase
  *   23 reserved
@@ -216,6 +64,8 @@
  *   35 HFC 2BDS0 PCI           none
  *   36 Winbond 6692 PCI        none
  *   37 HFC 2BDS0 S+/SP         p0=irq p1=iobase
+ *   38 Travers Technologies NETspider-U PCI card
+ *   39 HFC 2BDS0-SP PCMCIA     p0=irq p1=iobase
  *
  * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
  *
@@ -227,11 +77,11 @@
  "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
  "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c",
  "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
- "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
+ "Compaq ISA", "NETjet-S", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
  "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI",
  "Sedlbauer Speed Fax +", "Siemens I-Surf", "Acer P10", "HST Saphir",
  "Telekom A4T", "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692",
- "HFC 2BDS0 SX",
+ "HFC 2BDS0 SX", "NETspider-U", "HFC-2BDS0-SP PCMCIA",
 };
 
 void HiSax_closecard(int cardnr);
@@ -257,7 +107,6 @@
 #define DEFAULT_CFG {11,0x170,0,0}
 int avm_a1_init_pcmcia(void*, int, int*, int);
 EXPORT_SYMBOL(avm_a1_init_pcmcia);
-EXPORT_SYMBOL(HiSax_closecard);
 #endif /* CONFIG_HISAX_AVM_A1_PCMCIA */
 
 #ifdef CONFIG_HISAX_FRITZPCI
@@ -349,7 +198,7 @@
 #ifdef CONFIG_HISAX_NETJET
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
-#define DEFAULT_CARD ISDN_CTYPE_NETJET
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_S
 #define DEFAULT_CFG {0,0,0,0}
 #endif
 
@@ -372,6 +221,8 @@
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_HFC_SX
 #define DEFAULT_CFG {5,0x2E0,0,0}
+int hfc_init_pcmcia(void*, int, int*, int);
+EXPORT_SYMBOL(hfc_init_pcmcia);
 #endif
 
 
@@ -431,22 +282,29 @@
 #define DEFAULT_CFG {0,0,0,0}
 #endif
 
+#ifdef CONFIG_HISAX_NETJET_U
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_U
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
 #ifdef CONFIG_HISAX_1TR6
 #define DEFAULT_PROTO ISDN_PTYPE_1TR6
 #define DEFAULT_PROTO_NAME "1TR6"
 #endif
-#ifdef CONFIG_HISAX_EURO
-#undef DEFAULT_PROTO
-#define DEFAULT_PROTO ISDN_PTYPE_EURO
-#undef DEFAULT_PROTO_NAME
-#define DEFAULT_PROTO_NAME "EURO"
-#endif
 #ifdef CONFIG_HISAX_NI1
 #undef DEFAULT_PROTO
 #define DEFAULT_PROTO ISDN_PTYPE_NI1
 #undef DEFAULT_PROTO_NAME
 #define DEFAULT_PROTO_NAME "NI1"
 #endif
+#ifdef CONFIG_HISAX_EURO
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_EURO
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "EURO"
+#endif
 #ifndef DEFAULT_PROTO
 #define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN
 #define DEFAULT_PROTO_NAME "UNKNOWN"
@@ -455,6 +313,10 @@
 #error "HiSax: No cards configured"
 #endif
 
+int hisax_init_pcmcia(void *, int *, struct IsdnCard *);
+EXPORT_SYMBOL(hisax_init_pcmcia);
+EXPORT_SYMBOL(HiSax_closecard);
+
 #define FIRST_CARD { \
 	DEFAULT_CARD, \
 	DEFAULT_PROTO, \
@@ -551,9 +413,9 @@
 
 	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
 #ifdef MODULE
-	printk(KERN_INFO "HiSax: Version 3.3e (module)\n");
+	printk(KERN_INFO "HiSax: Version 3.5 (module)\n");
 #else
-	printk(KERN_INFO "HiSax: Version 3.3e (kernel)\n");
+	printk(KERN_INFO "HiSax: Version 3.5 (kernel)\n");
 #endif
 	strcpy(tmp, l1_revision);
 	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
@@ -689,8 +551,8 @@
 extern int setup_mic(struct IsdnCard *card);
 #endif
 
-#if CARD_NETJET
-extern int setup_netjet(struct IsdnCard *card);
+#if CARD_NETJET_S
+extern int setup_netjet_s(struct IsdnCard *card);
 #endif
 
 #if CARD_HFCS
@@ -741,6 +603,10 @@
 extern int setup_w6692(struct IsdnCard *card);
 #endif
 
+#if CARD_NETJET_U
+extern int setup_netjet_u(struct IsdnCard *card);
+#endif
+
 /*
  * Find card with given driverId
  */
@@ -943,7 +809,7 @@
 	ic.command = ISDN_STAT_STOP;
 	ic.driver = cs->myid;
 	cs->iif.statcallb(&ic);
-	CallcFreeChan(cs);
+//	CallcFreeChan(cs);
 }
 
 static void
@@ -1012,7 +878,7 @@
 	while (cnt) {
 		cs->cardmsg(cs, CARD_INIT, NULL);
 		sti();
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		/* Timeout 10ms */
 		schedule_timeout((10*HZ)/1000);
 		restore_flags(flags);
@@ -1066,6 +932,8 @@
 	cs->busy_flag = busy_flag;
 	cs->irq_flags = I4L_IRQ_FLAG;
 #if TEI_PER_CARD
+	if (card->protocol == ISDN_PTYPE_NI1)
+		test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
 #else
 	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
 #endif
@@ -1210,9 +1078,9 @@
 				ret = setup_mic(card);
 				break;
 #endif
-#if CARD_NETJET
-			case ISDN_CTYPE_NETJET:
-				ret = setup_netjet(card);
+#if CARD_NETJET_S
+			case ISDN_CTYPE_NETJET_S:
+				ret = setup_netjet_s(card);
 				break;
 #endif
 #if CARD_HFCS
@@ -1276,6 +1144,11 @@
 			ret = setup_w6692(card);
 			break;
 #endif
+#if CARD_NETJET_U
+			case ISDN_CTYPE_NETJET_U:
+				ret = setup_netjet_u(card);
+				break;
+#endif
 		default:
 			printk(KERN_WARNING
 				"HiSax: Support for %s Card not selected\n",
@@ -1395,6 +1268,9 @@
 	if (cards[cardnr].cs) {
 		ll_stop(cards[cardnr].cs);
 		release_tei(cards[cardnr].cs);
+		
+		CallcFreeChan(cards[cardnr].cs);
+		
 		closecard(cardnr);
 		if (cards[cardnr].cs->irq)
 			free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
@@ -1453,10 +1329,19 @@
 __initfunc(int
 HiSax_init(void))
 {
-	int i;
+	int i,j;
 
 #ifdef MODULE
 	int nzproto = 0;
+	if (!type[0]) {
+		/* We 'll register drivers later, but init basic functions*/
+		CallcNew();
+		Isdnl3New();
+		Isdnl2New();
+		TeiNew();
+		Isdnl1New();
+		return 0;
+	}
 #ifdef CONFIG_HISAX_ELSA
 	if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) {
 		/* we have exported  and return in this case */
@@ -1475,47 +1360,53 @@
 		return 0;
 	}
 #endif
+#ifdef CONFIG_HISAX_HFC_SX
+	if (type[0] == ISDN_CTYPE_HFC_SP_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
 #endif
 	nrcards = 0;
 	HiSaxVersion();
 #ifdef MODULE
 	if (id)			/* If id= string used */
 		HiSax_id = id;
-	for (i = 0; i < HISAX_MAX_CARDS; i++) {
-		cards[i].typ = type[i];
+	for (i = j = 0; j < HISAX_MAX_CARDS; i++) {
+		cards[j].typ = type[i];
 		if (protocol[i]) {
-			cards[i].protocol = protocol[i];
+			cards[j].protocol = protocol[i];
 			nzproto++;
 		}
 		switch (type[i]) {
 			case ISDN_CTYPE_16_0:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = mem[i];
-				cards[i].para[2] = io[i];
+				cards[j].para[0] = irq[i];
+				cards[j].para[1] = mem[i];
+				cards[j].para[2] = io[i];
 				break;
 
 			case ISDN_CTYPE_8_0:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = mem[i];
+				cards[j].para[0] = irq[i];
+				cards[j].para[1] = mem[i];
 				break;
 
 #ifdef IO0_IO1
 			case ISDN_CTYPE_PNP:
 			case ISDN_CTYPE_NICCY:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io0[i];
-				cards[i].para[2] = io1[i];
+				cards[j].para[0] = irq[i];
+				cards[j].para[1] = io0[i];
+				cards[j].para[2] = io1[i];
 				break;
 			case ISDN_CTYPE_COMPAQ_ISA:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io0[i];
-				cards[i].para[2] = io1[i];
-				cards[i].para[3] = io[i];
+				cards[j].para[0] = irq[i];
+				cards[j].para[1] = io0[i];
+				cards[j].para[2] = io1[i];
+				cards[j].para[3] = io[i];
 				break;
 #endif
 			case ISDN_CTYPE_ELSA:
 			case ISDN_CTYPE_HFC_PCI:
-				cards[i].para[0] = io[i];
+				cards[j].para[0] = io[i];
 				break;
 			case ISDN_CTYPE_16_3:
 			case ISDN_CTYPE_TELESPCMCIA:
@@ -1539,26 +1430,43 @@
 			case ISDN_CTYPE_HSTSAPHIR:
 			case ISDN_CTYPE_GAZEL:
 		        case ISDN_CTYPE_HFC_SX:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io[i];
+		        case ISDN_CTYPE_HFC_SP_PCMCIA:
+				cards[j].para[0] = irq[i];
+				cards[j].para[1] = io[i];
 				break;
 			case ISDN_CTYPE_ISURF:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io[i];
-				cards[i].para[2] = mem[i];
+				cards[j].para[0] = irq[i];
+				cards[j].para[1] = io[i];
+				cards[j].para[2] = mem[i];
 				break;
 			case ISDN_CTYPE_ELSA_PCI:
-			case ISDN_CTYPE_NETJET:
+			case ISDN_CTYPE_NETJET_S:
 			case ISDN_CTYPE_AMD7930:
 			case ISDN_CTYPE_TELESPCI:
 			case ISDN_CTYPE_W6692:
+			case ISDN_CTYPE_NETJET_U:
 				break;
 			case ISDN_CTYPE_BKM_A4T:
-	  		   	break;
+	  			break;
 			case ISDN_CTYPE_SCT_QUADRO:
-	          	cards[i].para[0] = irq[i];
+				if (irq[i]) {
+					cards[j].para[0] = irq[i];
+				} else {
+				        /* QUADRO is a 4 BRI card */
+					cards[j++].para[0] = 1;
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO; 
+					cards[j].protocol = protocol[i];
+					cards[j++].para[0] = 2;
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO; 
+					cards[j].protocol = protocol[i];
+					cards[j++].para[0] = 3;
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO; 
+					cards[j].protocol = protocol[i];
+					cards[j].para[0] = 4;
+				}
 				break;
 		}
+		j++;
 	}
 	if (!nzproto) {
 		printk(KERN_WARNING "HiSax: Warning - no protocol specified\n");
@@ -1666,6 +1574,55 @@
 }
 #endif
 
+#ifdef CONFIG_HISAX_HFC_SX
+int hfc_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+#ifdef MODULE
+	int i;
+	int nzproto = 0;
+
+	nrcards = 0;
+	HiSaxVersion();
+	/* Initialize all structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = ISDN_CTYPE_HFC_SP_PCMCIA;
+	nzproto = 1;
+
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	Isdnl1New();
+	CallcNew();
+	Isdnl3New();
+	Isdnl2New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+	return (0);
+}
+#endif
+
 #ifdef CONFIG_HISAX_SEDLBAUER
 int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
 {
@@ -1763,3 +1720,21 @@
 	return (0);
 }
 #endif
+
+int hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card)
+{
+	u_char ids[16];
+	int ret = -1;
+
+	cards[nrcards] = *card;
+	if (nrcards)
+		sprintf(ids, "HiSax%d", nrcards);
+	else
+		sprintf(ids, "HiSax");
+	if (!checkcard(nrcards, ids, busy_flag)) {
+		return(-1);
+	}
+	ret = nrcards;
+	nrcards++;
+	return (ret);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/diva.c linux.19p1/drivers/isdn/hisax/diva.c
--- linux.vanilla/drivers/isdn/hisax/diva.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/diva.c	Wed Dec 13 22:07:14 2000
@@ -1,5 +1,5 @@
-/* $Id: diva.c,v 1.18 1999/12/19 13:09:41 keil Exp $
-
+/* $Id: diva.c,v 1.21 2000/06/26 08:59:12 keil Exp $
+ *
  * diva.c     low level stuff for Eicon.Diehl Diva Family ISDN cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
@@ -10,67 +10,6 @@
  *
  * Thanks to Eicon Technology for documents and informations
  *
- *
- * $Log: diva.c,v $
- * Revision 1.18  1999/12/19 13:09:41  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.17  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.16  1999/08/11 21:01:25  keil
- * new PCI codefix
- *
- * Revision 1.15  1999/08/10 16:01:49  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.14  1999/08/07 17:35:08  keil
- * approval for Eicon Technology Diva 2.01 PCI
- *
- * Revision 1.13  1999/07/21 14:46:07  keil
- * changes from EICON certification
- *
- * Revision 1.12  1999/07/12 21:05:04  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.11  1999/07/01 08:11:29  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.10  1998/11/15 23:54:31  keil
- * changes from 2.0
- *
- * Revision 1.9  1998/06/27 22:52:03  keil
- * support for Diva 2.01
- *
- * Revision 1.8  1998/05/25 12:57:46  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.7  1998/04/15 16:42:36  keil
- * new init code
- * new PCI init (2.1.94)
- *
- * Revision 1.6  1998/03/07 22:56:57  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 1.5  1998/02/02 13:29:38  keil
- * fast io
- *
- * Revision 1.4  1997/11/08 21:35:44  keil
- * new l1 init
- *
- * Revision 1.3  1997/11/06 17:13:33  keil
- * New 2.1 init code
- *
- * Revision 1.2  1997/10/29 18:55:55  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 1.1  1997/09/18 17:11:20  keil
- * first version
- *
- *
  */
 
 #define __NO_VERSION__
@@ -84,7 +23,7 @@
 
 extern const char *CardType[];
 
-const char *Diva_revision = "$Revision: 1.18 $";
+const char *Diva_revision = "$Revision: 1.21 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -108,12 +47,18 @@
 #define DIVA_IPAC_PCI	4
 
 /* PCI stuff */
-#define PCI_VENDOR_EICON_DIEHL	0x1133
-#define PCI_DIVA20PRO_ID	0xe001
-#define PCI_DIVA20_ID		0xe002
-#define PCI_DIVA20PRO_U_ID	0xe003
-#define PCI_DIVA20_U_ID		0xe004
-#define PCI_DIVA_201		0xe005
+#ifndef PCI_VENDOR_ID_EICON
+#define PCI_VENDOR_ID_EICON	0x1133
+#endif
+#ifndef PCI_DEVICE_ID_EICON_DIVA20
+#define PCI_DEVICE_ID_EICON_DIVA20	0xe002
+#endif
+#ifndef PCI_DEVICE_ID_EICON_DIVA20_U
+#define PCI_DEVICE_ID_EICON_DIVA20_U	0xe004
+#endif
+#ifndef PCI_DEVICE_ID_EICON_DIVA201
+#define PCI_DEVICE_ID_EICON_DIVA201	0xe005
+#endif
 
 /* CTRL (Read) */
 #define DIVA_IRQ_STAT	0x01
@@ -131,10 +76,16 @@
 
 /* Siemens PITA */
 #define PITA_MISC_REG		0x1c
+#ifdef __BIG_ENDIAN
+#define PITA_PARA_SOFTRESET	0x00000001
+#define PITA_PARA_MPX_MODE	0x00000004
+#define PITA_INT0_ENABLE	0x00000200
+#else
 #define PITA_PARA_SOFTRESET	0x01000000
 #define PITA_PARA_MPX_MODE	0x04000000
 #define PITA_INT0_ENABLE	0x00020000
-#define PITA_INT0_STATUS	0x00000002
+#endif
+#define PITA_INT0_STATUS	0x02
 
 static inline u_char
 readreg(unsigned int ale, unsigned int adr, u_char off)
@@ -183,7 +134,7 @@
 static inline u_char
 memreadreg(unsigned long adr, u_char off)
 {
-	return(0xff & *((unsigned int *)
+	return(*((unsigned char *)
 		(((unsigned int *)adr) + off)));
 }
 
@@ -754,30 +705,30 @@
 	sti();
 	if (cs->subtyp == DIVA_IPAC_ISA) {
 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
 	} else if (cs->subtyp == DIVA_IPAC_PCI) {
 		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
 					PITA_MISC_REG);
 		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		*ireg = PITA_PARA_MPX_MODE;
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
 	} else { /* DIVA 2.0 */
 		cs->hw.diva.ctrl_reg = 0;        /* Reset On */
 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		if (cs->subtyp == DIVA_ISA)
 			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
@@ -931,28 +882,30 @@
 		}
 
 		cs->subtyp = 0;
-		if ((dev_diva = pci_find_device(PCI_VENDOR_EICON_DIEHL,
-			PCI_DIVA20_ID, dev_diva))) {
+		if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) {
+			if (pci_enable_device(dev_diva))
+				return(0);
 			cs->subtyp = DIVA_PCI;
 			cs->irq = dev_diva->irq;
-			cs->hw.diva.cfg_reg = dev_diva->base_address[ 2]
-				& PCI_BASE_ADDRESS_IO_MASK;
-		} else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL,
-			PCI_DIVA20_U_ID, dev_diva_u))) {
+			cs->hw.diva.cfg_reg = dev_diva->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK;
+		} else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) {
+			if (pci_enable_device(dev_diva_u))
+				return(0);
 			cs->subtyp = DIVA_PCI;
 			cs->irq = dev_diva_u->irq;
-			cs->hw.diva.cfg_reg = dev_diva_u->base_address[ 2]
-				& PCI_BASE_ADDRESS_IO_MASK;
-		} else if ((dev_diva201 = pci_find_device(PCI_VENDOR_EICON_DIEHL,
-			PCI_DIVA_201, dev_diva201))) {
+			cs->hw.diva.cfg_reg = dev_diva_u->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK;
+		} else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) {
+			if (pci_enable_device(dev_diva201))
+				return(0);
 			cs->subtyp = DIVA_IPAC_PCI;
 			cs->irq = dev_diva201->irq;
 			cs->hw.diva.pci_cfg =
-				(ulong) ioremap((dev_diva201->base_address[ 0]
-					& PCI_BASE_ADDRESS_IO_MASK), 4096);
+				(ulong) ioremap(dev_diva201->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK, 4096);
 			cs->hw.diva.cfg_reg =
-				(ulong) ioremap((dev_diva201->base_address[ 1]
-					& PCI_BASE_ADDRESS_IO_MASK), 4096);
+				(ulong) ioremap(dev_diva201->base_address[ 1] & PCI_BASE_ADDRESS_MEM_MASK, 4096);
 		} else {
 			printk(KERN_WARNING "Diva: No PCI card found\n");
 			return(0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/elsa.c linux.19p1/drivers/isdn/hisax/elsa.c
--- linux.vanilla/drivers/isdn/hisax/elsa.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/elsa.c	Wed Dec 13 22:07:14 2000
@@ -1,5 +1,5 @@
-/* $Id: elsa.c,v 2.20 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: elsa.c,v 2.23 2000/06/26 08:59:12 keil Exp $
+ *
  * elsa.c     low level stuff for Elsa isdn cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
@@ -13,80 +13,6 @@
  *              Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
  *              for ELSA PCMCIA support
  *
- * $Log: elsa.c,v $
- * Revision 2.20  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 2.19  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 2.18  1999/08/25 16:50:54  keil
- * Fix bugs which cause 2.3.14 hangs (waitqueue init)
- *
- * Revision 2.17  1999/08/11 20:57:40  keil
- * bugfix IPAC version 1.1
- * new PCI codefix
- *
- * Revision 2.16  1999/08/10 16:01:51  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 2.15  1999/08/09 19:25:21  keil
- * Support (alpha version) for the '98 model of ELSA Microlink ISDN/MC
- * by Christer Weinigel, Cendio Systems AB <wingel@cendio.se>
- * Add support for IPAC 1.2
- *
- * Revision 2.14  1999/07/12 21:05:07  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.13  1999/07/01 08:11:31  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.12  1998/11/15 23:54:35  keil
- * changes from 2.0
- *
- * Revision 2.11  1998/08/20 13:50:34  keil
- * More support for hybrid modem (not working yet)
- *
- * Revision 2.10  1998/08/13 23:36:22  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.9  1998/05/25 12:57:48  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.8  1998/04/15 16:41:42  keil
- * QS3000 PCI support
- * new init code
- * new PCI init (2.1.94)
- *
- * Revision 2.7  1998/03/07 22:56:58  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 2.6  1998/02/02 13:29:40  keil
- * fast io
- *
- * Revision 2.5  1998/01/31 21:41:45  keil
- * changes for newer 2.1 kernels
- *
- * Revision 2.4  1997/11/08 21:35:46  keil
- * new l1 init
- *
- * Revision 2.3  1997/11/06 17:15:09  keil
- * New 2.1 init; PCMCIA wrapper changes
- *
- * Revision 2.2  1997/10/29 18:57:09  keil
- * changes for 2.1.60, arcofi support
- *
- * Revision 2.1  1997/07/27 21:47:08  keil
- * new interface structures
- *
- * Revision 2.0  1997/06/26 11:02:40  keil
- * New Layer and card interface
- *
- * old changes removed KKe
- *
  */
 
 #define __NO_VERSION__
@@ -103,10 +29,10 @@
 
 extern const char *CardType[];
 
-const char *Elsa_revision = "$Revision: 2.20 $";
+const char *Elsa_revision = "$Revision: 2.23 $";
 const char *Elsa_Types[] =
 {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
- "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", 
+ "PCMCIA", "QS 1000", "QS 3000", "Microlink PCI", "QS 3000 PCI", 
  "PCMCIA-IPAC" };
 
 const char *ITACVer[] =
@@ -140,9 +66,15 @@
 #define ELSA_PCMCIA_IPAC 11
 
 /* PCI stuff */
-#define PCI_VENDOR_ELSA	0x1048
-#define PCI_QS1000_ID	0x1000
-#define PCI_QS3000_ID	0x3000
+#ifndef PCI_VENDOR_ID_ELSA
+#define PCI_VENDOR_ID_ELSA	0x1048
+#endif
+#ifndef PCI_DEVICE_ID_ELSA_MIRCOLINK
+#define PCI_DEVICE_ID_ELSA_MIRCOLINK	0x1000
+#endif
+#ifndef PCI_DEVICE_ID_ELSA_QS3000
+#define PCI_DEVICE_ID_ELSA_QS3000	0x3000
+#endif
 #define ELSA_PCI_IRQ_MASK	0x04
 
 /* ITAC Registeradressen (only Microlink PC) */
@@ -582,10 +514,10 @@
 		save_flags(flags);
 		sti();
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
-		current->state = TASK_UNINTERRUPTIBLE;
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
 		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 		restore_flags(flags);
@@ -789,7 +721,7 @@
 				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
 				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
 				byteout(cs->hw.elsa.timer, 0);
-				current->state = TASK_UNINTERRUPTIBLE;
+				set_current_state(TASK_UNINTERRUPTIBLE);
 				schedule_timeout((110*HZ)/1000);
 				restore_flags(flags);
 				cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
@@ -797,7 +729,8 @@
 				cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
 				printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
 				       cs->hw.elsa.counter);
-				if (abs(cs->hw.elsa.counter - 13) < 3) {
+				if ((cs->hw.elsa.counter > 10) &&
+					(cs->hw.elsa.counter < 16)) {
 					printk(KERN_INFO "Elsa: timer and irq OK\n");
 					ret = 0;
 				} else {
@@ -1056,22 +989,22 @@
 			return(0);
 		}
 		cs->subtyp = 0;
-		if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS1000_ID,
-			 dev_qs1000))) {
-				cs->subtyp = ELSA_QS1000PCI;
+		if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+			PCI_DEVICE_ID_ELSA_MIRCOLINK, dev_qs1000))) {
+			if (pci_enable_device(dev_qs1000))
+				return(0);
+			cs->subtyp = ELSA_QS1000PCI;
 			cs->irq = dev_qs1000->irq;
-			cs->hw.elsa.cfg = dev_qs1000->base_address[ 1] & 
-				PCI_BASE_ADDRESS_IO_MASK;
-			cs->hw.elsa.base = dev_qs1000->base_address[ 3] & 
-				PCI_BASE_ADDRESS_IO_MASK;
-		} else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA,
-			PCI_QS3000_ID, dev_qs3000))) {
+			cs->hw.elsa.cfg = dev_qs1000->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
+			cs->hw.elsa.base = dev_qs1000->base_address[ 3] & PCI_BASE_ADDRESS_IO_MASK;
+		} else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+			PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) {
+			if (pci_enable_device(dev_qs3000))
+				return(0);
 			cs->subtyp = ELSA_QS3000PCI;
 			cs->irq = dev_qs3000->irq;
-			cs->hw.elsa.cfg = dev_qs3000->base_address[ 1] & 
-				PCI_BASE_ADDRESS_IO_MASK;
-			cs->hw.elsa.base = dev_qs3000->base_address[ 3] & 
-				PCI_BASE_ADDRESS_IO_MASK;
+			cs->hw.elsa.cfg = dev_qs3000->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
+			cs->hw.elsa.base = dev_qs3000->base_address[ 3] & PCI_BASE_ADDRESS_IO_MASK;
 		} else {
 			printk(KERN_WARNING "Elsa: No PCI card found\n");
 			return(0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/elsa_ser.c linux.19p1/drivers/isdn/hisax/elsa_ser.c
--- linux.vanilla/drivers/isdn/hisax/elsa_ser.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/elsa_ser.c	Wed Dec 13 22:07:14 2000
@@ -1,3 +1,10 @@
+/* $Id: elsa_ser.c,v 2.9 2000/06/26 08:59:12 keil Exp $
+ *
+ * stuff for the serial modem on ELSA cards
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ *
+ */
 #include <linux/config.h>
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/fsm.c linux.19p1/drivers/isdn/hisax/fsm.c
--- linux.vanilla/drivers/isdn/hisax/fsm.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/fsm.c	Wed Dec 13 22:07:14 2000
@@ -1,48 +1,12 @@
-/* $Id: fsm.c,v 1.11 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: fsm.c,v 1.13 2000/06/26 08:59:12 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
- * $Log: fsm.c,v $
- * Revision 1.11  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.10  1998/11/15 23:54:39  keil
- * changes from 2.0
- *
- * Revision 1.9  1998/03/26 07:10:02  paul
- * The jumpmatrix table in struct Fsm was an array of "int". This is not
- * large enough for pointers to functions on Linux/Alpha (instant crash
- * on "insmod hisax). Now there is a typedef for the pointer to function.
- * This also prevents warnings about "incompatible pointer types".
- *
- * Revision 1.8  1998/03/07 22:56:59  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 1.7  1997/11/06 17:09:13  keil
- * New 2.1 init code
- *
- * Revision 1.6  1997/07/27 21:42:25  keil
- * proof Fsm routines
- *
- * Revision 1.5  1997/06/26 11:10:05  keil
- * Restart timer function added
- *
- * Revision 1.4  1997/04/06 22:56:42  keil
- * Some cosmetic changes
- *
- * Revision 1.3  1997/02/16 01:04:08  fritz
- * Bugfix: Changed timer handling caused hang with 2.1.X
- *
- * Revision 1.2  1997/01/09 20:57:27  keil
- * cleanup & FSM_TIMER_DEBUG
- *
- * Revision 1.1  1996/10/13 20:04:52  keil
- * Initial revision
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -156,7 +120,7 @@
 			(long) ft, millisec, where);
 #endif
 
-	if (ft->tl.next || ft->tl.prev) {
+	if (timer_pending(&ft->tl)) {
 		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
 		ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
 		return -1;
@@ -180,7 +144,7 @@
 			(long) ft, millisec, where);
 #endif
 
-	if (ft->tl.next || ft->tl.prev)
+	if (timer_pending(&ft->tl))
 		del_timer(&ft->tl);
 	init_timer(&ft->tl);
 	ft->event = event;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/gazel.c linux.19p1/drivers/isdn/hisax/gazel.c
--- linux.vanilla/drivers/isdn/hisax/gazel.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/gazel.c	Wed Dec 13 22:07:14 2000
@@ -1,32 +1,11 @@
-/* $Id: gazel.c,v 2.6 1999/08/22 20:27:03 calle Exp $
-
+/* $Id: gazel.c,v 2.8 2000/06/26 08:59:12 keil Exp $
+ *
  * gazel.c     low level stuff for Gazel isdn cards
  *
  * Author       BeWan Systems
  *              based on source code from Karsten Keil
  *
- * $Log: gazel.c,v $
- * Revision 2.6  1999/08/22 20:27:03  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 2.5  1999/08/11 21:01:26  keil
- * new PCI codefix
- *
- * Revision 2.4  1999/08/10 16:01:54  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 2.3  1999/07/12 21:05:09  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.1  1999/07/08 21:26:17  keil
- * new card
- *
- * Revision 1.0  1999/28/06
- * Initial revision
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #include <linux/config.h>
@@ -39,7 +18,7 @@
 #include <linux/pci.h>
 
 extern const char *CardType[];
-const char *gazel_revision = "$Revision: 2.6 $";
+const char *gazel_revision = "$Revision: 2.8 $";
 
 #define R647      1
 #define R685      2
@@ -47,10 +26,18 @@
 #define R742      4
 
 /* Gazel R685 stuff */
-#define GAZEL_MANUFACTURER     0x10b5
-#define GAZEL_R685             0x1030
-#define GAZEL_R753             0x1152
-#define GAZEL_DJINN_ITOO       0x1151
+#ifndef PCI_VENDOR_ID_PLX
+#define PCI_VENDOR_ID_PLX	0x10b5
+#endif
+#ifndef PCI_DEVICE_ID_PLX_R685
+#define PCI_DEVICE_ID_PLX_R685	0x1030
+#endif
+#ifndef PCI_DEVICE_ID_PLX_R753
+#define PCI_DEVICE_ID_PLX_R753	0x1152
+#endif
+#ifndef PCI_DEVICE_ID_PLX_DJINN_ITOO
+#define PCI_DEVICE_ID_PLX_DJINN_ITOO	0x1151
+#endif
 
 #define PLX_CNTRL    0x50	/* registre de controle PLX */
 #define RESET_GAZEL  0x4
@@ -586,24 +573,25 @@
 		printk(KERN_WARNING "Gazel: No PCI bus present\n");
 		return 1;
 	}
-	seekcard = GAZEL_R685;
+	seekcard = PCI_DEVICE_ID_PLX_R685;
 	for (nbseek = 0; nbseek < 3; nbseek++) {
-		if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) {
-
+		if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX, seekcard, dev_tel))) {
+			if (pci_enable_device(dev_tel))
+				return 1;
 			pci_irq = dev_tel->irq;
-			pci_ioaddr0 = dev_tel->base_address[ 1];
-			pci_ioaddr1 = dev_tel->base_address[ 2];
+			pci_ioaddr0 = dev_tel->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
+			pci_ioaddr1 = dev_tel->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK;
 			found = 1;
 		}
 		if (found)
 			break;
 		else {
 			switch (seekcard) {
-				case GAZEL_R685:
-					seekcard = GAZEL_R753;
+				case PCI_DEVICE_ID_PLX_R685:
+					seekcard = PCI_DEVICE_ID_PLX_R753;
 					break;
-				case GAZEL_R753:
-					seekcard = GAZEL_DJINN_ITOO;
+				case PCI_DEVICE_ID_PLX_R753:
+					seekcard = PCI_DEVICE_ID_PLX_DJINN_ITOO;
 					break;
 			}
 		}
@@ -632,7 +620,7 @@
 	cs->irq_flags |= SA_SHIRQ;
 
 	switch (seekcard) {
-		case GAZEL_R685:
+		case PCI_DEVICE_ID_PLX_R685:
 			printk(KERN_INFO "Gazel: Card PCI R685 found\n");
 			cs->subtyp = R685;
 			cs->dc.isac.adf2 = 0x87;
@@ -643,8 +631,8 @@
 			       "Gazel: hscx A:0x%X  hscx B:0x%X\n",
 			     cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
 			break;
-		case GAZEL_R753:
-		case GAZEL_DJINN_ITOO:
+		case PCI_DEVICE_ID_PLX_R753:
+		case PCI_DEVICE_ID_PLX_DJINN_ITOO:
 			printk(KERN_INFO "Gazel: Card PCI R753 found\n");
 			cs->subtyp = R753;
 			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_2bds0.c linux.19p1/drivers/isdn/hisax/hfc_2bds0.c
--- linux.vanilla/drivers/isdn/hisax/hfc_2bds0.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_2bds0.c	Wed Dec 13 22:07:14 2000
@@ -1,43 +1,10 @@
-/* $Id: hfc_2bds0.c,v 1.11 1999/12/23 15:09:32 keil Exp $
+/* $Id: hfc_2bds0.c,v 1.13 2000/06/26 08:59:12 keil Exp $
  *
  *  specific routines for CCD's HFC 2BDS0
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hfc_2bds0.c,v $
- * Revision 1.11  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.10  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 1.9  1999/07/01 08:11:35  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.8  1998/11/15 23:54:40  keil
- * changes from 2.0
- *
- * Revision 1.7  1998/09/30 22:24:45  keil
- * Fix missing line in setstack*
- *
- * Revision 1.6  1998/08/13 23:36:26  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 1.5  1998/06/27 22:52:58  keil
- * make 16.3c working with 3.0
- *
- * Revision 1.4  1998/05/25 12:57:52  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.3  1998/02/12 23:07:22  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.2  1998/02/02 13:26:13  keil
- * New
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_2bds0.h linux.19p1/drivers/isdn/hisax/hfc_2bds0.h
--- linux.vanilla/drivers/isdn/hisax/hfc_2bds0.h	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_2bds0.h	Wed Dec 13 22:07:14 2000
@@ -1,18 +1,10 @@
-/* $Id: hfc_2bds0.h,v 1.3 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: hfc_2bds0.h,v 1.4 2000/06/26 08:59:12 keil Exp $
+ *
  *  specific defines for CCD's HFC 2BDS0
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hfc_2bds0.h,v $
- * Revision 1.3  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.2  1998/02/02 13:26:15  keil
- * New
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_2bs0.c linux.19p1/drivers/isdn/hisax/hfc_2bs0.c
--- linux.vanilla/drivers/isdn/hisax/hfc_2bs0.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_2bs0.c	Wed Dec 13 22:07:49 2000
@@ -1,49 +1,10 @@
-/* $Id: hfc_2bs0.c,v 1.12 1999/12/19 14:17:12 keil Exp $
-
+/* $Id: hfc_2bs0.c,v 1.15 2000/07/26 20:46:47 keil Exp $
+ *
  *  specific routines for CCD's HFC 2BS0
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hfc_2bs0.c,v $
- * Revision 1.12  1999/12/19 14:17:12  keil
- * fix compiler warning
- *
- * Revision 1.11  1999/11/21 12:41:18  werner
- *
- * Implemented full audio support
- *
- * Revision 1.10  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 1.9  1999/07/01 08:11:36  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.8  1998/11/15 23:54:43  keil
- * changes from 2.0
- *
- * Revision 1.7  1998/09/30 22:24:46  keil
- * Fix missing line in setstack*
- *
- * Revision 1.6  1998/08/13 23:36:28  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 1.5  1998/05/25 12:57:54  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.4  1998/02/12 23:07:29  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.3  1997/11/06 17:13:35  keil
- * New 2.1 init code
- *
- * Revision 1.2  1997/10/29 19:04:47  keil
- * changes for 2.1
- *
- * Revision 1.1  1997/09/11 17:31:33  keil
- * Common part for HFC 2BS0 based cards
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -242,7 +203,7 @@
 		ptr = skb_put(skb, count);
 		idx = 0;
 		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
-		while ((idx < count - 3) && WaitNoBusy(cs)) {
+		while ((idx < count) && WaitNoBusy(cs)) {
 			*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
 			idx++;
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_2bs0.h linux.19p1/drivers/isdn/hisax/hfc_2bs0.h
--- linux.vanilla/drivers/isdn/hisax/hfc_2bs0.h	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_2bs0.h	Wed Dec 13 22:07:49 2000
@@ -1,17 +1,10 @@
-/* $Id: hfc_2bs0.h,v 1.2 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: hfc_2bs0.h,v 1.3 2000/06/26 08:59:13 keil Exp $
+ *
  *  specific defines for CCD's HFC 2BS0
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hfc_2bs0.h,v $
- * Revision 1.2  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.1  1997/09/11 17:31:34  keil
- * Common part for HFC 2BS0 based cards
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_pci.c linux.19p1/drivers/isdn/hisax/hfc_pci.c
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_pci.c	Wed Dec 13 22:07:49 2000
@@ -1,4 +1,4 @@
-/* $Id: hfc_pci.c,v 1.27 2000/02/26 00:35:12 keil Exp $
+/* $Id: hfc_pci.c,v 1.31 2000/08/20 07:32:55 keil Exp $
 
  * hfc_pci.c     low level driver for CCD´s hfc-pci based cards
  *
@@ -22,101 +22,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: hfc_pci.c,v $
- * Revision 1.27  2000/02/26 00:35:12  keil
- * Fix skb freeing in interrupt context
- *
- * Revision 1.26  2000/02/09 20:22:55  werner
- *
- * Updated PCI-ID table
- *
- * Revision 1.25  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.24  1999/11/17 23:59:55  werner
- *
- * removed unneeded data
- *
- * Revision 1.23  1999/11/07 17:01:55  keil
- * fix for 2.3 pci structs
- *
- * Revision 1.22  1999/10/10 20:14:27  werner
- *
- * Correct B2-chan usage in conjuntion with echo mode. First implementation of NT-leased line mode.
- *
- * Revision 1.21  1999/10/02 17:47:49  werner
- *
- * Changed init order, added correction for page alignment with shared mem
- *
- * Revision 1.20  1999/09/07 06:18:55  werner
- *
- * Added io parameter for HFC-PCI based cards. Needed only with multiple cards
- * when initialisation/selection order needs to be set.
- *
- * Revision 1.19  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.18  1999/08/29 17:05:44  werner
- * corrected tx_lo line setup. Datasheet is not correct.
- *
- * Revision 1.17  1999/08/28 21:04:27  werner
- * Implemented full audio support (transparent mode)
- *
- * Revision 1.16  1999/08/25 17:01:27  keil
- * Use new LL->HL auxcmd call
- *
- * Revision 1.15  1999/08/22 20:27:05  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.14  1999/08/12 18:59:45  werner
- * Added further manufacturer and device ids to PCI list
- *
- * Revision 1.13  1999/08/11 21:01:28  keil
- * new PCI codefix
- *
- * Revision 1.12  1999/08/10 16:01:58  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.11  1999/08/09 19:13:32  werner
- * moved constant pci ids to pci id table
- *
- * Revision 1.10  1999/08/08 10:17:34  werner
- * added new PCI vendor and card ids for Manufacturer 0x1043
- *
- * Revision 1.9  1999/08/07 21:09:10  werner
- * Fixed another memcpy problem in fifo handling.
- * Thanks for debugging aid by Olaf Kordwittenborg.
- *
- * Revision 1.8  1999/07/23 14:25:15  werner
- * Some smaller bug fixes and prepared support for GCI/IOM bus
- *
- * Revision 1.7  1999/07/14 21:24:20  werner
- * fixed memcpy problem when using E-channel feature
- *
- * Revision 1.6  1999/07/13 21:08:08  werner
- * added echo channel logging feature.
- *
- * Revision 1.5  1999/07/12 21:05:10  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.4  1999/07/04 21:51:39  werner
- * Changes to solve problems with irq sharing and smp machines
- * Thanks to Karsten Keil and Alex Holden for giving aid with
- * testing and debugging
- *
- * Revision 1.3  1999/07/01 09:43:19  keil
- * removed additional schedules in timeouts
- *
- * Revision 1.2  1999/07/01 08:07:51  keil
- * Initial version
- *
- *
- *
  */
 
 #include <linux/config.h>
@@ -129,7 +34,7 @@
 
 extern const char *CardType[];
 
-static const char *hfcpci_revision = "$Revision: 1.27 $";
+static const char *hfcpci_revision = "$Revision: 1.31 $";
 
 /* table entry in the PCI devices list */
 typedef struct {
@@ -139,7 +44,9 @@
 	char *card_name;
 } PCI_ENTRY;
 
-#define NT_T1_COUNT 20		/* number of 3.125ms interrupts for G2 timeout */
+#define NT_T1_COUNT	20	/* number of 3.125ms interrupts for G2 timeout */
+#define CLKDEL_TE	0x0e	/* CLKDEL in TE mode */
+#define CLKDEL_NT	0x6c	/* CLKDEL in NT mode */
 
 static const PCI_ENTRY id_list[] =
 {
@@ -158,7 +65,10 @@
 	{0x1051, 0x0100, "Motorola MC145575", "MC145575"},
 	{0x1397, 0xB100, "Seyeon", "B100"},
 	{0x15B0, 0x2BD0, "Zoltrix", "2BD0"},
-	{0x114f, 0x71,   "Digi intl.","Digicom"}, 
+	{0x114F, 0x70,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"},
+	{0x114F, 0x71,"Digi International", "Digi DataFire Micro V (Europe)"},
+	{0x114F, 0x72,"Digi International", "Digi DataFire Micro V IOM2 (North America)"},
+	{0x114F, 0x73,"Digi International", "Digi DataFire Micro V (North America)"},
 	{0, 0, NULL, NULL},
 };
 
@@ -180,7 +90,7 @@
 	restore_flags(flags);
 	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
 	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
 #if CONFIG_PCI
@@ -211,10 +121,10 @@
 	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER);	/* enable memory ports + busmaster */
 	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
 	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
 	if (Read_hfc(cs, HFCPCI_STATUS) & 2)
 		printk(KERN_WARNING "HFC-PCI init bit busy\n");
@@ -225,7 +135,7 @@
 	cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK;	/* no echo connect , threshold */
 	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
 
-	Write_hfc(cs, HFCPCI_CLKDEL, 0x0e);	/* ST-Bit delay for TE-Mode */
+	Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_TE); /* ST-Bit delay for TE-Mode */
 	cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE;
 	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);	/* S/T Auto awake */
 	cs->hw.hfcpci.bswapped = 0;	/* no exchange */
@@ -349,6 +259,9 @@
 	    (*(bdata + (zp->z1 - B_SUB_VAL)))) {
 		if (cs->debug & L1_DEB_WARN)
 			debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
 		bz->za[new_f2].z2 = new_z2;
 		bz->f2 = new_f2;	/* next buffer */
 		skb = NULL;
@@ -415,6 +328,9 @@
 		    (df->data[zp->z1])) {
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]);
+#ifdef ERROR_STATISTIC
+			cs->err_rx++;
+#endif
 			df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1);	/* next buffer */
 			df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1);
 		} else if ((skb = dev_alloc_skb(rcnt - 3))) {
@@ -601,6 +517,9 @@
 	if (fcnt > (MAX_D_FRAMES - 1)) {
 		if (cs->debug & L1_DEB_ISAC)
 			debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames");
+#ifdef ERROR_STATISTIC
+		cs->err_tx++;
+#endif
 		return;
 	}
 	/* now determine free bytes in FIFO buffer */
@@ -835,6 +754,7 @@
 	    (!(cs->hw.hfcpci.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) {
 		save_flags(flags);
 		cli();
+		Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */
 		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0);	/* HFC ST G0 */
 		udelay(10);
 		cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT;
@@ -1690,7 +1610,7 @@
 			inithfcpci(cs);
 			save_flags(flags);
 			sti();
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((80 * HZ) / 1000);	/* Timeout 80ms */
 			/* now switch timer interrupt off */
 			cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
@@ -1720,6 +1640,9 @@
 	int i;
 	struct pci_dev *tmp_hfcpci = NULL;
 
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
 	strcpy(tmp, hfcpci_revision);
 	printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
 #if CONFIG_PCI
@@ -1738,6 +1661,8 @@
 						     dev_hfcpci);
 			i++;
 			if (tmp_hfcpci) {
+				if (pci_enable_device(tmp_hfcpci))
+					continue;
 				if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK)))
 					continue;
 				else
@@ -1761,7 +1686,6 @@
 			printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
 			return (0);
 		}
-#ifdef notdef
 		if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) {
 			printk(KERN_WARNING "HFC-PCI shared mem address will be corrected\n");
 			pcibios_write_config_word(cs->hw.hfcpci.pci_bus,
@@ -1795,7 +1719,6 @@
 			}
 			dev_hfcpci->base_address[1] = (int) cs->hw.hfcpci.pci_io;
 		}
-#endif
 		if (!cs->hw.hfcpci.pci_io) {
 			printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
 			return (0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_pci.h linux.19p1/drivers/isdn/hisax/hfc_pci.h
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_pci.h	Wed Dec 13 22:07:49 2000
@@ -1,5 +1,5 @@
-/* $Id: hfc_pci.h,v 1.7 1999/10/10 20:13:06 werner Exp $
-
+/* $Id: hfc_pci.h,v 1.8 2000/06/26 08:59:13 keil Exp $
+ *
  *  specific defines for CCD's HFC 2BDS0 PCI chips
  *
  * Author     Werner Cornelius (werner@isdn4linux.de)      
@@ -19,28 +19,6 @@
  * 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.
- *
- * $Log: hfc_pci.h,v $
- * Revision 1.7  1999/10/10 20:13:06  werner
- *
- * Corrected timer constant
- *
- * Revision 1.6  1999/08/28 21:04:29  werner
- * Implemented full audio support (transparent mode)
- *
- * Revision 1.5  1999/08/09 19:13:34  werner
- * moved constant pci ids to pci id table
- *
- * Revision 1.4  1999/08/08 10:17:33  werner
- * added new PCI vendor and card ids for Manufacturer 0x1043
- *
- * Revision 1.3  1999/07/14 12:39:34  werner
- * Added changes for echo handling.
- *
- * Revision 1.2  1999/07/01 08:07:52  keil
- * Initial version
- *
- *
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_sx.c linux.19p1/drivers/isdn/hisax/hfc_sx.c
--- linux.vanilla/drivers/isdn/hisax/hfc_sx.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_sx.c	Wed Dec 13 22:07:49 2000
@@ -1,4 +1,4 @@
-/* $Id: hfc_sx.c,v 1.3 2000/01/20 19:49:36 keil Exp $
+/* $Id: hfc_sx.c,v 1.7 2000/09/08 22:55:59 werner Exp $
 
  * hfc_sx.c     low level driver for CCD´s hfc-s+/sp based cards
  *
@@ -21,24 +21,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: hfc_sx.c,v $
- * Revision 1.3  2000/01/20 19:49:36  keil
- * Support teles 13.3c vendor version 2.1
- *
- * Revision 1.2  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.1  1999/11/18 00:09:18  werner
- *
- * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM.
- * Audio and Echo are supported.
- *
- *
- *
  */
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include "hisax.h"
 #include "hfc_sx.h"
@@ -47,7 +31,7 @@
 
 extern const char *CardType[];
 
-static const char *hfcsx_revision = "$Revision: 1.3 $";
+static const char *hfcsx_revision = "$Revision: 1.7 $";
 
 /***************************************/
 /* IRQ-table for CCDs demo board       */
@@ -361,7 +345,7 @@
 	restore_flags(flags);
 	Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET);	/* Reset On */
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
 	Write_hfc(cs, HFCSX_CIRM, 0);	/* Reset Off */
 	del_timer(&cs->hw.hfcsx.timer);
@@ -408,10 +392,10 @@
 	while (1) {
 	  Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */
 	  sti();
-	  current->state = TASK_UNINTERRUPTIBLE;
+	  set_current_state(TASK_UNINTERRUPTIBLE);
 	  schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
 	  Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */
-	  current->state = TASK_UNINTERRUPTIBLE;
+	  set_current_state(TASK_UNINTERRUPTIBLE);
 	  schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
 	  if (Read_hfc(cs, HFCSX_STATUS) & 2)
 	    printk(KERN_WARNING "HFC-SX init bit busy\n");
@@ -1471,7 +1455,7 @@
 			inithfcsx(cs);
 			save_flags(flags);
 			sti();
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((80 * HZ) / 1000);	/* Timeout 80ms */
 			/* now switch timer interrupt off */
 			cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
@@ -1502,7 +1486,8 @@
 	cs->hw.hfcsx.int_s1 = 0;
 	cs->dc.hfcsx.ph_state = 0;
 	cs->hw.hfcsx.fifo = 255;
-	if (cs->typ == ISDN_CTYPE_HFC_SX) {
+	if ((cs->typ == ISDN_CTYPE_HFC_SX) || 
+	    (cs->typ == ISDN_CTYPE_HFC_SP_PCMCIA)) {
 	        if ((!cs->hw.hfcsx.base) || 
 		    check_region((cs->hw.hfcsx.base), 2)) {
 		  printk(KERN_WARNING
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_sx.h linux.19p1/drivers/isdn/hisax/hfc_sx.h
--- linux.vanilla/drivers/isdn/hisax/hfc_sx.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/hfc_sx.h	Wed Dec 13 22:07:49 2000
@@ -1,5 +1,5 @@
-/* $Id: hfc_sx.h,v 1.1 1999/11/18 00:09:18 werner Exp $
-
+/* $Id: hfc_sx.h,v 1.2 2000/06/26 08:59:13 keil Exp $
+ *
  *  specific defines for CCD's HFC 2BDS0 S+,SP chips
  *
  * Author     Werner Cornelius (werner@isdn4linux.de)      
@@ -19,14 +19,6 @@
  * 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.
- *
- * $Log: hfc_sx.h,v $
- * Revision 1.1  1999/11/18 00:09:18  werner
- *
- * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM.
- * Audio and Echo are supported.
- *
- *
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfcscard.c linux.19p1/drivers/isdn/hisax/hfcscard.c
--- linux.vanilla/drivers/isdn/hisax/hfcscard.c	Fri Apr 21 12:46:09 2000
+++ linux.19p1/drivers/isdn/hisax/hfcscard.c	Wed Dec 13 22:07:56 2000
@@ -1,29 +1,10 @@
-/* $Id: hfcscard.c,v 1.6 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: hfcscard.c,v 1.7 2000/06/26 08:59:13 keil Exp $
+ *
  * hfcscard.c     low level stuff for hfcs based cards (Teles3c, ACER P10)
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hfcscard.c,v $
- * Revision 1.6  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.5  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.4  1999/08/09 18:59:59  keil
- * Fix S0 init - Thanks to Stefan Gybas
- *
- * Revision 1.3  1999/07/12 21:05:12  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.2  1999/07/01 08:16:03  keil
- * teles3c ---> hfcscard
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -34,7 +15,7 @@
 
 extern const char *CardType[];
 
-static const char *hfcs_revision = "$Revision: 1.6 $";
+static const char *hfcs_revision = "$Revision: 1.7 $";
 
 static void
 hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs)
@@ -89,13 +70,13 @@
 	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset On */
 	save_flags(flags);
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);
 	cs->hw.hfcD.cirm = 0;
 	if (cs->typ == ISDN_CTYPE_TELES3C)
 		cs->hw.hfcD.cirm |= HFCD_MEM8K;
 	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset Off */
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	if (cs->typ == ISDN_CTYPE_TELES3C)
 		cs->hw.hfcD.cirm |= HFCD_INTB;
@@ -142,7 +123,7 @@
 			init2bds0(cs);
 			save_flags(flags);
 			sti();
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((80*HZ)/1000);
 			cs->hw.hfcD.ctmt |= HFCD_TIM800;
 			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hisax.h linux.19p1/drivers/isdn/hisax/hisax.h
--- linux.vanilla/drivers/isdn/hisax/hisax.h	Wed May  3 22:33:45 2000
+++ linux.19p1/drivers/isdn/hisax/hisax.h	Thu Dec 14 16:54:28 2000
@@ -1,143 +1,8 @@
-/* $Id: hisax.h,v 2.41 2000/02/26 00:35:13 keil Exp $
-
- *   Basic declarations, defines and prototypes
- *
- * $Log: hisax.h,v $
- * Revision 2.41  2000/02/26 00:35:13  keil
- * Fix skb freeing in interrupt context
- *
- * Revision 2.40  2000/01/20 19:51:46  keil
- * Fix AddTimer message
- * Change CONFIG defines
- *
- * Revision 2.39  1999/11/18 00:00:43  werner
- *
- * Added support for HFC-S+ and HFC-SP cards
- *
- * Revision 2.38  1999/11/14 23:37:03  keil
- * new ISA memory mapped IO
- *
- * Revision 2.37  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 2.36  1999/10/10 20:16:15  werner
- *
- * Added variable to hfcpci union.
- *
- * Revision 2.35  1999/09/04 06:35:09  keil
- * Winbond W6692 support
- *
- * Revision 2.34  1999/08/25 17:00:04  keil
- * Make ISAR V32bis modem running
- * Make LL->HL interface open for additional commands
- *
- * Revision 2.33  1999/08/05 20:43:16  keil
- * ISAR analog modem support
- *
- * Revision 2.31  1999/07/21 14:46:11  keil
- * changes from EICON certification
- *
- * Revision 2.30  1999/07/14 12:38:38  werner
- * Added changes for echo channel handling
- *
- * Revision 2.29  1999/07/12 21:05:14  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.28  1999/07/05 23:51:46  werner
- * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl
- * hisaxctrl id 10 <nr. of chans 0-2>
- *
- * Revision 2.27  1999/07/01 08:11:38  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.26  1998/11/15 23:54:45  keil
- * changes from 2.0
- *
- * Revision 2.25  1998/09/30 22:28:42  keil
- * More work for ISAR support
- *
- * Revision 2.24  1998/08/20 13:50:39  keil
- * More support for hybrid modem (not working yet)
- *
- * Revision 2.23  1998/08/13 23:36:31  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.22  1998/07/15 15:01:28  calle
- * Support for AVM passive PCMCIA cards:
- *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
- *
- * Revision 2.21  1998/05/25 14:10:05  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.20  1998/05/25 12:57:57  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.19  1998/04/15 16:39:15  keil
- * Add S0Box and Teles PCI support
- *
- * Revision 2.18  1998/03/26 07:10:04  paul
- * The jumpmatrix table in struct Fsm was an array of "int". This is not
- * large enough for pointers to functions on Linux/Alpha (instant crash
- * on "insmod hisax). Now there is a typedef for the pointer to function.
- * This also prevents warnings about "incompatible pointer types".
- *
- * Revision 2.17  1998/03/19 13:18:43  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 2.16  1998/03/09 23:19:25  keil
- * Changes for PCMCIA
- *
- * Revision 2.14  1998/02/11 17:28:04  keil
- * Niccy PnP/PCI support
- *
- * Revision 2.13  1998/02/09 18:46:02  keil
- * Support for Sedlbauer PCMCIA (Marcus Niemann)
- *
- * Revision 2.12  1998/02/03 23:31:30  keil
- * add AMD7930 support
- *
- * Revision 2.11  1998/02/02 13:33:00  keil
- * New card support
- *
- * Revision 2.10  1997/11/08 21:37:52  keil
- * new l1 init;new Compaq card
- *
- * Revision 2.9  1997/11/06 17:09:09  keil
- * New 2.1 init code
- *
- * Revision 2.8  1997/10/29 19:04:13  keil
- * new L1; changes for 2.1
- *
- * Revision 2.7  1997/10/10 20:56:47  fritz
- * New HL interface.
- *
- * Revision 2.6  1997/09/11 17:25:51  keil
- * Add new cards
+/* $Id: hisax.h,v 2.49 2000/09/08 22:55:59 werner Exp $
  *
- * Revision 2.5  1997/08/03 14:36:31  keil
- * Implement RESTART procedure
- *
- * Revision 2.4  1997/07/31 19:25:20  keil
- * PTP_DATA_LINK support
- *
- * Revision 2.3  1997/07/31 11:50:17  keil
- * ONE TEI and FIXED TEI handling
- *
- * Revision 2.2  1997/07/30 17:13:02  keil
- * more changes for 'One TEI per card'
- *
- * Revision 2.1  1997/07/27 21:45:13  keil
- * new main structures
- *
- * Revision 2.0  1997/06/26 11:06:27  keil
- * New card and L1 interface.
- * Eicon.Diehl Diva and Dynalink IS64PH support
+ *   Basic declarations, defines and prototypes
  *
- * old changes removed KKe
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #include <linux/config.h>
@@ -173,8 +38,11 @@
 #define HW_POWERUP	0x0008
 #define HW_ACTIVATE	0x0010
 #define HW_DEACTIVATE	0x0018
+
+#define HW_INFO1	0x0010
 #define HW_INFO2	0x0020
 #define HW_INFO3	0x0030
+#define HW_INFO4	0x0040
 #define HW_INFO4_P8	0x0040
 #define HW_INFO4_P10	0x0048
 #define HW_RSYNC	0x0060
@@ -224,6 +92,7 @@
 #define CC_SUSPEND	0x0370
 #define CC_PROCEED_SEND 0x0374
 #define CC_REDIR        0x0378
+#define CC_T302		0x0382
 #define CC_T303		0x0383
 #define CC_T304		0x0384
 #define CC_T305		0x0385
@@ -234,6 +103,7 @@
 #define CC_T313		0x0393
 #define CC_T318		0x0398
 #define CC_T319		0x0399
+#define CC_TSPID	0x03A0
 #define CC_NOSETUP_RSP	0x03E0
 #define CC_SETUP_ERR	0x03E1
 #define CC_SUSPEND_ERR	0x03E2
@@ -242,6 +112,7 @@
 #define CC_RELEASE_ERR	0x03E5
 #define CC_RESTART	0x03F4
 #define CC_TDSS1_IO     0x13F4    /* DSS1 IO user timer */
+#define CC_TNI1_IO      0x13F5    /* NI1 IO user timer */
 
 /* define maximum number of possible waiting incoming calls */
 #define MAX_WAITING_CALLS 2
@@ -249,13 +120,19 @@
 
 #ifdef __KERNEL__
 
-/* include only l3dss1 specific process structures, but no other defines */
+/* include l3dss1 & ni1 specific process structures, but no other defines */
 #ifdef CONFIG_HISAX_EURO
   #define l3dss1_process
   #include "l3dss1.h" 
   #undef  l3dss1_process
 #endif CONFIG_HISAX_EURO
 
+#ifdef CONFIG_HISAX_NI1
+  #define l3ni1_process
+  #include "l3ni1.h" 
+  #undef  l3ni1_process
+#endif CONFIG_HISAX_NI1
+
 #define MAX_DFRAME_LEN	260
 #define MAX_DFRAME_LEN_L1	300
 #define HSCX_BUFMAX	4096
@@ -318,6 +195,7 @@
 #define FLG_L1_ACTTIMER		4
 #define FLG_L1_T3RUN		5
 #define FLG_L1_PULL_REQ		6
+#define FLG_L1_UINT		7
 
 struct Layer1 {
 	void *hardware;
@@ -432,7 +310,7 @@
 	struct Layer3 l3;
 	struct LLInterface lli;
 	struct Management ma;
-	int protocol;		/* EDSS1 or 1TR6 */
+	int protocol;		/* EDSS1, 1TR6 or NI1 */
 
         /* protocol specific data fields */
         union
@@ -440,6 +318,9 @@
 #ifdef CONFIG_HISAX_EURO
            dss1_stk_priv dss1; /* private dss1 data */
 #endif CONFIG_HISAX_EURO              
+#ifdef CONFIG_HISAX_NI1
+           ni1_stk_priv ni1; /* private ni1 data */
+#endif CONFIG_HISAX_NI1              
 	 } prot;
 };
 
@@ -461,6 +342,9 @@
 #ifdef CONFIG_HISAX_EURO 
            dss1_proc_priv dss1; /* private dss1 data */
 #endif CONFIG_HISAX_EURO            
+#ifdef CONFIG_HISAX_NI1
+           ni1_proc_priv ni1; /* private ni1 data */
+#endif CONFIG_HISAX_NI1              
 	 } prot;
 };
 
@@ -499,6 +383,7 @@
 	u_char mod;
 	u_char newcmd;
 	u_char newmod;
+	char try_mod;
 	struct timer_list ftimer;
 	u_char *rcvbuf;         /* B-Channel receive Buffer */
 	u_char conmsg[16];
@@ -506,10 +391,17 @@
 };
 
 struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+	u_char fill __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char cmd  __attribute__((packed));
+#else
 	u_char cmd  __attribute__((packed));
 	u_char xml  __attribute__((packed));
 	u_char mode __attribute__((packed));
 	u_char fill __attribute__((packed));
+#endif
 };
 
 struct hdlc_hw {
@@ -938,6 +830,22 @@
 	int ph_state;
 };
 
+struct icc_chip {
+	int ph_state;
+	u_char *mon_tx;
+	u_char *mon_rx;
+	int mon_txp;
+	int mon_txc;
+	int mon_rxp;
+	struct arcofi_msg *arcofi_list;
+	struct timer_list arcofitimer;
+	struct wait_queue *arcofi_wait;
+	u_char arcofi_bc;
+	u_char arcofi_state;
+	u_char mocr;
+	u_char adf2;
+};
+
 #define HW_IOM1			0
 #define HW_IPAC			1
 #define HW_ISAR			2
@@ -948,6 +856,7 @@
 #define FLG_LOCK_ATOMIC 	7
 #define FLG_ARCOFI_TIMER	8
 #define FLG_ARCOFI_ERROR	9
+#define FLG_HW_L1_UINT		10
 
 struct IsdnCardState {
 	unsigned char typ;
@@ -1016,6 +925,7 @@
 		struct hfcpci_chip hfcpci;
 		struct hfcsx_chip hfcsx;
 		struct w6692_chip w6692;
+		struct icc_chip icc;
 	} dc;
 	u_char *rcvbuf;
 	int rcvidx;
@@ -1057,7 +967,7 @@
 #define  ISDN_CTYPE_MIC		17
 #define  ISDN_CTYPE_ELSA_PCI	18
 #define  ISDN_CTYPE_COMPAQ_ISA	19
-#define  ISDN_CTYPE_NETJET	20
+#define  ISDN_CTYPE_NETJET_S	20
 #define  ISDN_CTYPE_TELESPCI	21
 #define  ISDN_CTYPE_SEDLBAUER_PCMCIA	22
 #define  ISDN_CTYPE_AMD7930	23
@@ -1075,7 +985,9 @@
 #define  ISDN_CTYPE_HFC_PCI	35
 #define  ISDN_CTYPE_W6692	36
 #define  ISDN_CTYPE_HFC_SX      37
-#define  ISDN_CTYPE_COUNT	37
+#define  ISDN_CTYPE_NETJET_U	38
+#define  ISDN_CTYPE_HFC_SP_PCMCIA      39
+#define  ISDN_CTYPE_COUNT	39
 
 
 #ifdef ISDN_CHIP_ISAC
@@ -1224,12 +1136,12 @@
 #endif
 
 #ifdef  CONFIG_HISAX_NETJET
-#define CARD_NETJET 1
+#define CARD_NETJET_S 1
 #ifndef ISDN_CHIP_ISAC
 #define ISDN_CHIP_ISAC 1
 #endif
 #else
-#define CARD_NETJET 0
+#define CARD_NETJET_S 0
 #endif
 
 #ifdef	CONFIG_HISAX_HFCS
@@ -1337,17 +1249,19 @@
 #define	CARD_W6692	0
 #endif
 
-#define TEI_PER_CARD 0
-
-#ifdef CONFIG_HISAX_1TR6
-#undef TEI_PER_CARD
-#define TEI_PER_CARD 1
+#ifdef  CONFIG_HISAX_NETJET_U
+#define CARD_NETJET_U 1
+#ifndef ISDN_CHIP_ICC
+#define ISDN_CHIP_ICC 1
+#endif
+#ifndef HISAX_UINTERFACE
+#define HISAX_UINTERFACE 1
+#endif
+#else
+#define CARD_NETJET_U 0
 #endif
 
-#ifdef CONFIG_HISAX_EURO
-#undef TEI_PER_CARD
 #define TEI_PER_CARD 1
-#endif
 
 /* L1 Debug */
 #define	L1_DEB_WARN		0x01
@@ -1371,7 +1285,7 @@
 
 struct IsdnCard {
 	int typ;
-	int protocol;		/* EDSS1 or 1TR6 */
+	int protocol;		/* EDSS1, 1TR6 or NI1 */
 	unsigned int para[4];
 	struct IsdnCardState *cs;
 };
@@ -1448,3 +1362,17 @@
 void TeiNew(void);
 void TeiFree(void);
 int certification_check(int output);
+#ifdef __powerpc__
+#include <linux/pci.h>
+static inline int pci_enable_device(struct pci_dev *dev)
+{
+	u16 cmd;
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_SERR;
+	cmd &= ~PCI_COMMAND_FAST_BACK;
+	pci_write_config_word(dev, PCI_COMMAND, cmd);
+	return(0);
+}
+#else
+#define pci_enable_device(dev)	!dev
+#endif /* __powerpc__ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hscx.c linux.19p1/drivers/isdn/hisax/hscx.c
--- linux.vanilla/drivers/isdn/hisax/hscx.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/hscx.c	Wed Dec 13 22:07:56 2000
@@ -1,61 +1,10 @@
-/* $Id: hscx.c,v 1.17 1999/07/01 08:11:41 keil Exp $
-
+/* $Id: hscx.c,v 1.19 2000/06/26 08:59:13 keil Exp $
+ *
  * hscx.c   HSCX specific routines
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hscx.c,v $
- * Revision 1.17  1999/07/01 08:11:41  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.16  1998/11/15 23:54:48  keil
- * changes from 2.0
- *
- * Revision 1.15  1998/08/20 13:50:42  keil
- * More support for hybrid modem (not working yet)
- *
- * Revision 1.14  1998/08/13 23:36:33  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 1.13  1998/06/26 22:03:28  keil
- * send flags between hdlc frames
- *
- * Revision 1.12  1998/06/09 18:26:01  keil
- * PH_DEACTIVATE B-channel every time signaled to higher layer
- *
- * Revision 1.11  1998/05/25 14:10:07  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 1.10  1998/05/25 12:57:59  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.9  1998/04/15 16:45:33  keil
- * new init code
- *
- * Revision 1.8  1998/03/19 13:16:24  keil
- * fix the correct release of the hscx
- *
- * Revision 1.7  1998/02/12 23:07:36  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.6  1998/02/02 13:41:12  keil
- * new init
- *
- * Revision 1.5  1997/11/06 17:09:34  keil
- * New 2.1 init code
- *
- * Revision 1.4  1997/10/29 19:01:06  keil
- * changes for 2.1
- *
- * Revision 1.3  1997/07/27 21:38:34  keil
- * new B-channel interface
- *
- * Revision 1.2  1997/06/26 11:16:17  keil
- * first version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hscx.h linux.19p1/drivers/isdn/hisax/hscx.h
--- linux.vanilla/drivers/isdn/hisax/hscx.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/hscx.h	Wed Dec 13 22:07:56 2000
@@ -1,23 +1,10 @@
-/* $Id: hscx.h,v 1.5 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: hscx.h,v 1.6 2000/06/26 08:59:13 keil Exp $
+ *
  * hscx.h   HSCX specific defines
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: hscx.h,v $
- * Revision 1.5  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.4  1998/04/15 16:45:34  keil
- * new init code
- *
- * Revision 1.3  1997/07/27 21:38:35  keil
- * new B-channel interface
- *
- * Revision 1.2  1997/06/26 11:16:18  keil
- * first version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hscx_irq.c linux.19p1/drivers/isdn/hisax/hscx_irq.c
--- linux.vanilla/drivers/isdn/hisax/hscx_irq.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/hscx_irq.c	Wed Dec 13 22:07:56 2000
@@ -1,50 +1,12 @@
-/* $Id: hscx_irq.c,v 1.13 1999/10/14 20:25:28 keil Exp $
-
+/* $Id: hscx_irq.c,v 1.15 2000/06/26 08:59:13 keil Exp $
+ *
  * hscx_irq.c     low level b-channel stuff for Siemens HSCX
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * This is an include file for fast inline IRQ stuff
  *
- * $Log: hscx_irq.c,v $
- * Revision 1.13  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 1.12  1999/07/01 08:11:42  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.11  1998/11/15 23:54:49  keil
- * changes from 2.0
- *
- * Revision 1.10  1998/08/13 23:36:35  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 1.9  1998/06/24 14:44:51  keil
- * Fix recovery of TX IRQ loss
- *
- * Revision 1.8  1998/04/10 10:35:22  paul
- * fixed (silly?) warnings from egcs on Alpha.
- *
- * Revision 1.7  1998/02/12 23:07:37  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.6  1997/10/29 19:01:07  keil
- * changes for 2.1
- *
- * Revision 1.5  1997/10/01 09:21:35  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.4  1997/08/15 17:48:02  keil
- * cosmetic
- *
- * Revision 1.3  1997/07/27 21:38:36  keil
- * new B-channel interface
- *
- * Revision 1.2  1997/06/26 11:16:19  keil
- * first version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/icc.c linux.19p1/drivers/isdn/hisax/icc.c
--- linux.vanilla/drivers/isdn/hisax/icc.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/icc.c	Wed Dec 13 22:08:04 2000
@@ -0,0 +1,685 @@
+// $Id: icc.c,v 1.3 2000/08/20 07:34:04 keil Exp $
+//-----------------------------------------------------------------------------
+//
+// ICC specific routines
+//
+// Author       Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd
+//              www.traverse.com.au
+//
+// 1999.6.25 Initial implementation of routines for Siemens ISDN
+// Communication Controler PEB 2070 based on the ISAC routines
+// written by Karsten Keil.
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+//-----------------------------------------------------------------------------
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "icc.h"
+// #include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 0
+
+static char *ICCVer[] HISAX_INITDATA =
+{"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"};
+
+void
+ICCVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ICC_RBCH);
+	printk(KERN_INFO "%s ICC version (%x): %s\n", s, val, ICCVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, ICC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+icc_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.icc.ph_state) {
+		case (ICC_IND_EI1):
+			ph_command(cs, ICC_CMD_DI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+		case (ICC_IND_DC):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (ICC_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (ICC_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (ICC_IND_FJ):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (ICC_IND_AR):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (ICC_IND_AI):
+			l1_msg(cs, HW_INFO4 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+icc_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		icc_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+	if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+		return;
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+void
+icc_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "icc_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "icc_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeisac(cs, ICC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	save_flags(flags);
+	cli();
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ICC_CMDR, 0x80);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "icc_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+icc_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "icc_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ICC_CMDR, more ? 0x8 : 0xa);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "icc_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "icc_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+void
+icc_sched_event(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	queue_task(&cs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+icc_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+	long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ICC interrupt %x", val);
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ICC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ICC RDO");
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			}
+			if (!(exval & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ICC CRC error");
+#ifdef ERROR_STATISTIC
+				cs->err_crc++;
+#endif
+			}
+			cs->writeisac(cs, ICC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ICC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			icc_empty_fifo(cs, count);
+			save_flags(flags);
+			cli();
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+			restore_flags(flags);
+		}
+		cs->rcvidx = 0;
+		icc_sched_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		icc_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			icc_sched_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				icc_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			icc_fill_fifo(cs);
+		} else
+			icc_sched_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		exval = cs->readisac(cs, ICC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ICC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->dc.icc.ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->dc.icc.ph_state);
+			icc_sched_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ICC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ICC CIR1 %02X", exval );
+		}
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ICC_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC EXIR %02x", exval);
+		if (exval & 0x80) {  /* XMR */
+			debugl1(cs, "ICC XMR");
+			printk(KERN_WARNING "HiSax: ICC XMR\n");
+		}
+		if (exval & 0x40) {  /* XDU */
+			debugl1(cs, "ICC XDU");
+			printk(KERN_WARNING "HiSax: ICC XDU\n");
+#ifdef ERROR_STATISTIC
+			cs->err_tx++;
+#endif
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				icc_sched_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) { /* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				icc_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: ICC XDU no skb\n");
+				debugl1(cs, "ICC XDU no skb");
+			}
+		}
+		if (exval & 0x04) {  /* MOS */
+			v1 = cs->readisac(cs, ICC_MOSR);
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ICC MOSR %02x", v1);
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->dc.icc.mon_rx) {
+					if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ICC MON RX out of memory!");
+						cs->dc.icc.mocr &= 0xf0;
+						cs->dc.icc.mocr |= 0x0a;
+						cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+						goto afterMONR0;
+					} else
+						cs->dc.icc.mon_rxp = 0;
+				}
+				if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.icc.mocr &= 0xf0;
+					cs->dc.icc.mocr |= 0x0a;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ICC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR0);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC MOR0 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+				if (cs->dc.icc.mon_rxp == 1) {
+					cs->dc.icc.mocr |= 0x04;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->dc.icc.mon_rx) {
+					if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ICC MON RX out of memory!");
+						cs->dc.icc.mocr &= 0x0f;
+						cs->dc.icc.mocr |= 0xa0;
+						cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+						goto afterMONR1;
+					} else
+						cs->dc.icc.mon_rxp = 0;
+				}
+				if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.icc.mocr &= 0x0f;
+					cs->dc.icc.mocr |= 0xa0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ICC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR1);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC MOR1 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+				cs->dc.icc.mocr |= 0x40;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->dc.icc.mocr &= 0xf0;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				cs->dc.icc.mocr |= 0x0a;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				icc_sched_event(cs, D_RX_MON0);
+			}
+			if (v1 & 0x40) {
+				cs->dc.icc.mocr &= 0x0f;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				cs->dc.icc.mocr |= 0xa0;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				icc_sched_event(cs, D_RX_MON1);
+			}
+			if (v1 & 0x02) {
+				if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && 
+					(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && 
+					!(v1 & 0x08))) {
+					cs->dc.icc.mocr &= 0xf0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mocr |= 0x0a;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					if (cs->dc.icc.mon_txc &&
+						(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+						icc_sched_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+					icc_sched_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ICC_MOX0,
+					cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC %02x -> MOX0", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && 
+					(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && 
+					!(v1 & 0x80))) {
+					cs->dc.icc.mocr &= 0x0f;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mocr |= 0xa0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					if (cs->dc.icc.mon_txc &&
+						(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+						icc_sched_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+					icc_sched_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ICC_MOX1,
+					cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC %02x -> MOX1", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static void
+ICC_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	int  val;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				icc_fill_fifo(cs);
+			}
+			break;
+		case (PH_PULL |INDICATION):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			icc_fill_fifo(cs);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			if ((cs->dc.icc.ph_state == ICC_IND_EI1) ||
+				(cs->dc.icc.ph_state == ICC_IND_DR))
+			        ph_command(cs, ICC_CMD_DI);
+			else
+				ph_command(cs, ICC_CMD_RES);
+			break;
+		case (HW_ENABLE | REQUEST):
+			ph_command(cs, ICC_CMD_DI);
+			break;
+		case (HW_INFO1 | REQUEST):
+			ph_command(cs, ICC_CMD_AR);
+			break;
+		case (HW_INFO3 | REQUEST):
+			ph_command(cs, ICC_CMD_AI);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ICC_SPCR, 0xa);
+					cs->writeisac(cs, ICC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ICC_SPCR, val);
+					cs->writeisac(cs, ICC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ICC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ICC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ICC_ADF1, 0x0);
+			}
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			discard_queue(&cs->rq);
+			discard_queue(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				icc_sched_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "icc_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_icc(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = ICC_l1hw;
+}
+
+void 
+DC_Close_icc(struct IsdnCardState *cs) {
+	if (cs->dc.icc.mon_rx) {
+		kfree(cs->dc.icc.mon_rx);
+		cs->dc.icc.mon_rx = NULL;
+	}
+	if (cs->dc.icc.mon_tx) {
+		kfree(cs->dc.icc.mon_tx);
+		cs->dc.icc.mon_tx = NULL;
+	}
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int	rbch, star;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readisac(cs, ICC_RBCH);
+		star = cs->readisac(cs, ICC_STAR);
+		if (cs->debug) 
+			debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+				rbch, star);
+		if (rbch & ICC_RBCH_XAC) { /* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ICC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, ICC_CMDR, 0x01); /* Transmitter reset */
+			cs->irq_func(cs->irq, cs, NULL);
+		}
+	}
+}
+
+HISAX_INITFUNC(void
+initicc(struct IsdnCardState *cs))
+{
+	cs->tqueue.routine = (void *) (void *) icc_bh;
+	cs->setstack_d = setstack_icc;
+	cs->DC_Close = DC_Close_icc;
+	cs->dc.icc.mon_tx = NULL;
+	cs->dc.icc.mon_rx = NULL;
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+  	cs->writeisac(cs, ICC_MASK, 0xff);
+  	cs->dc.icc.mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ICC_ADF2, 0x0);
+		cs->writeisac(cs, ICC_SPCR, 0xa);
+		cs->writeisac(cs, ICC_ADF1, 0x2);
+		cs->writeisac(cs, ICC_STCR, 0x70);
+		cs->writeisac(cs, ICC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!cs->dc.icc.adf2)
+			cs->dc.icc.adf2 = 0x80;
+		cs->writeisac(cs, ICC_ADF2, cs->dc.icc.adf2);
+		cs->writeisac(cs, ICC_SQXR, 0xa0);
+		cs->writeisac(cs, ICC_SPCR, 0x20);
+		cs->writeisac(cs, ICC_STCR, 0x70);
+		cs->writeisac(cs, ICC_MODE, 0xca);
+		cs->writeisac(cs, ICC_TIMR, 0x00);
+		cs->writeisac(cs, ICC_ADF1, 0x20);
+	}
+	ph_command(cs, ICC_CMD_RES);
+	cs->writeisac(cs, ICC_MASK, 0x0);
+	ph_command(cs, ICC_CMD_DI);
+}
+
+HISAX_INITFUNC(void
+clear_pending_icc_ints(struct IsdnCardState *cs))
+{
+	int val, eval;
+
+	val = cs->readisac(cs, ICC_STAR);
+	debugl1(cs, "ICC STAR %x", val);
+	val = cs->readisac(cs, ICC_MODE);
+	debugl1(cs, "ICC MODE %x", val);
+	val = cs->readisac(cs, ICC_ADF2);
+	debugl1(cs, "ICC ADF2 %x", val);
+	val = cs->readisac(cs, ICC_ISTA);
+	debugl1(cs, "ICC ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->readisac(cs, ICC_EXIR);
+		debugl1(cs, "ICC EXIR %x", eval);
+	}
+	val = cs->readisac(cs, ICC_CIR0);
+	debugl1(cs, "ICC CIR0 %x", val);
+	cs->dc.icc.ph_state = (val >> 2) & 0xf;
+	icc_sched_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
+	cs->writeisac(cs, ICC_MASK, 0xFF);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/icc.h linux.19p1/drivers/isdn/hisax/icc.h
--- linux.vanilla/drivers/isdn/hisax/icc.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/icc.h	Wed Dec 13 22:08:04 2000
@@ -0,0 +1,73 @@
+// $Id: icc.h,v 1.2 2000/06/26 08:59:13 keil Exp $
+//-----------------------------------------------------------------------------
+//
+// ICC specific routines
+//
+// Author       Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd
+//              www.traverse.com.au
+//
+// 1999.7.14 Initial implementation of routines for Siemens ISDN 
+// Communication Controler PEB 2070 based on the ISAC routines 
+// written by Karsten Keil.
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+//-----------------------------------------------------------------------------
+
+
+/* All Registers original Siemens Spec  */
+
+#define ICC_MASK 0x20
+#define ICC_ISTA 0x20
+#define ICC_STAR 0x21
+#define ICC_CMDR 0x21
+#define ICC_EXIR 0x24
+#define ICC_ADF2 0x39
+#define ICC_SPCR 0x30
+#define ICC_ADF1 0x38
+#define ICC_CIR0 0x31
+#define ICC_CIX0 0x31
+#define ICC_CIR1 0x33
+#define ICC_CIX1 0x33
+#define ICC_STCR 0x37
+#define ICC_MODE 0x22
+#define ICC_RSTA 0x27
+#define ICC_RBCL 0x25
+#define ICC_RBCH 0x2A
+#define ICC_TIMR 0x23
+#define ICC_SQXR 0x3b
+#define ICC_MOSR 0x3a
+#define ICC_MOCR 0x3a
+#define ICC_MOR0 0x32
+#define ICC_MOX0 0x32
+#define ICC_MOR1 0x34
+#define ICC_MOX1 0x34
+
+#define ICC_RBCH_XAC 0x80
+
+#define ICC_CMD_TIM    0x0
+#define ICC_CMD_RES    0x1
+#define ICC_CMD_DU     0x3
+#define ICC_CMD_EI1    0x4
+#define ICC_CMD_SSP    0x5
+#define ICC_CMD_DT     0x6
+#define ICC_CMD_AR     0x8
+#define ICC_CMD_ARL    0xA
+#define ICC_CMD_AI     0xC
+#define ICC_CMD_DI     0xF
+
+#define ICC_IND_DR     0x0
+#define ICC_IND_FJ     0x2
+#define ICC_IND_EI1    0x4
+#define ICC_IND_INT    0x6
+#define ICC_IND_PU     0x7
+#define ICC_IND_AR     0x8
+#define ICC_IND_ARL    0xA
+#define ICC_IND_AI     0xC
+#define ICC_IND_AIL    0xE
+#define ICC_IND_DC     0xF
+
+extern void ICCVersion(struct IsdnCardState *cs, char *s);
+extern void initicc(struct IsdnCardState *cs);
+extern void icc_interrupt(struct IsdnCardState *cs, u_char val);
+extern void clear_pending_icc_ints(struct IsdnCardState *cs);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/ipac.h linux.19p1/drivers/isdn/hisax/ipac.h
--- linux.vanilla/drivers/isdn/hisax/ipac.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/ipac.h	Wed Dec 13 22:08:04 2000
@@ -1,24 +1,10 @@
-/* $Id: ipac.h,v 1.4 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: ipac.h,v 1.5 2000/06/26 08:59:13 keil Exp $
+ *
  * ipac.h   IPAC specific defines
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: ipac.h,v $
- * Revision 1.4  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.3  1998/04/15 16:48:09  keil
- * IPAC_ATX added
- *
- * Revision 1.2  1997/10/29 18:51:21  keil
- * New files
- *
- * Revision 1.1.2.1  1997/10/17 22:10:48  keil
- * new files on 2.0
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isac.c linux.19p1/drivers/isdn/hisax/isac.c
--- linux.vanilla/drivers/isdn/hisax/isac.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isac.c	Wed Dec 13 22:08:04 2000
@@ -1,5 +1,5 @@
-/* $Id: isac.c,v 1.24 1999/10/14 20:25:28 keil Exp $
-
+/* $Id: isac.c,v 1.26 2000/06/26 08:59:13 keil Exp $
+ *
  * isac.c   ISAC specific routines
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
@@ -7,82 +7,6 @@
  *		This file is (c) under GNU PUBLIC LICENSE
  *		For changes and modifications please read
  *		../../../Documentation/isdn/HiSax.cert
- *
- * $Log: isac.c,v $
- * Revision 1.24  1999/10/14 20:25:28  keil
- * add a statistic for error monitoring
- *
- * Revision 1.23  1999/08/25 16:50:52  keil
- * Fix bugs which cause 2.3.14 hangs (waitqueue init)
- *
- * Revision 1.22  1999/08/09 19:04:40  keil
- * Fix race condition - Thanks to Christer Weinigel
- *
- * Revision 1.21  1999/07/12 21:05:17  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.20  1999/07/09 08:23:06  keil
- * Fix ISAC lost TX IRQ handling
- *
- * Revision 1.19  1999/07/01 08:11:43  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.18  1998/11/15 23:54:51  keil
- * changes from 2.0
- *
- * Revision 1.17  1998/08/13 23:36:37  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 1.16  1998/05/25 12:58:01  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.15  1998/04/15 16:45:32  keil
- * new init code
- *
- * Revision 1.14  1998/04/10 10:35:26  paul
- * fixed (silly?) warnings from egcs on Alpha.
- *
- * Revision 1.13  1998/03/07 22:57:01  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 1.12  1998/02/12 23:07:40  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.11  1998/02/09 10:54:49  keil
- * fixes for leased mode
- *
- * Revision 1.10  1998/02/02 13:37:37  keil
- * new init
- *
- * Revision 1.9  1997/11/06 17:09:07  keil
- * New 2.1 init code
- *
- * Revision 1.8  1997/10/29 19:00:03  keil
- * new layer1,changes for 2.1
- *
- * Revision 1.7  1997/10/01 09:21:37  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.6  1997/08/15 17:47:08  keil
- * avoid oops because a uninitialised timer
- *
- * Revision 1.5  1997/08/07 17:48:49  keil
- * fix wrong parenthesis
- *
- * Revision 1.4  1997/07/30 17:11:59  keil
- * fixed Timer3
- *
- * Revision 1.3  1997/07/27 21:37:40  keil
- * T3 implemented; supervisor l1timer; B-channel TEST_LOOP
- *
- * Revision 1.2  1997/06/26 11:16:15  keil
- * first version
- *
- *
  */
 
 #define __NO_VERSION__
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isac.h linux.19p1/drivers/isdn/hisax/isac.h
--- linux.vanilla/drivers/isdn/hisax/isac.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isac.h	Wed Dec 13 22:08:04 2000
@@ -1,27 +1,10 @@
-/* $Id: isac.h,v 1.6 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: isac.h,v 1.7 2000/06/26 08:59:13 keil Exp $
+ *
  * isac.h   ISAC specific defines
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: isac.h,v $
- * Revision 1.6  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.5  1998/05/25 12:58:03  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.4  1997/10/29 19:09:34  keil
- * new L1
- *
- * Revision 1.3  1997/07/27 21:37:41  keil
- * T3 implemented; supervisor l1timer; B-channel TEST_LOOP
- *
- * Revision 1.2  1997/06/26 11:16:16  keil
- * first version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isar.c linux.19p1/drivers/isdn/hisax/isar.c
--- linux.vanilla/drivers/isdn/hisax/isar.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isar.c	Wed Dec 13 22:08:04 2000
@@ -1,39 +1,10 @@
-/* $Id: isar.c,v 1.9 2000/01/20 19:47:45 keil Exp $
-
+/* $Id: isar.c,v 1.15 2000/06/26 08:59:13 keil Exp $
+ *
  * isar.c   ISAR (Siemens PSB 7110) specific routines
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: isar.c,v $
- * Revision 1.9  2000/01/20 19:47:45  keil
- * Add Fax Class 1 support
- *
- * Revision 1.8  1999/12/19 13:00:56  keil
- * Fix races in setting a new mode
- *
- * Revision 1.7  1999/10/14 20:25:29  keil
- * add a statistic for error monitoring
- *
- * Revision 1.6  1999/08/31 11:20:20  paul
- * various spelling corrections (new checksums may be needed, Karsten!)
- *
- * Revision 1.5  1999/08/25 16:59:55  keil
- * Make ISAR V32bis modem running
- * Make LL->HL interface open for additional commands
- *
- * Revision 1.4  1999/08/05 20:43:18  keil
- * ISAR analog modem support
- *
- * Revision 1.3  1999/07/01 08:11:45  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.2  1998/11/15 23:54:53  keil
- * changes from 2.0
- *
- * Revision 1.1  1998/08/13 23:33:47  keil
- * First version, only init
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -272,6 +243,14 @@
 			printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
 			goto reterror;
 		}
+#ifdef __BIG_ENDIAN
+		sadr = (blk_head.sadr & 0xff)*256 + blk_head.sadr/256;
+		blk_head.sadr = sadr;
+		sadr = (blk_head.len & 0xff)*256 + blk_head.len/256;
+		blk_head.len = sadr;
+		sadr = (blk_head.d_key & 0xff)*256 + blk_head.d_key/256;
+		blk_head.d_key = sadr;
+#endif /* __BIG_ENDIAN */
 		cnt += BLK_HEAD_SIZE;
 		p += BLK_HEAD_SIZE;
 		printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
@@ -313,8 +292,13 @@
 #endif
 			sadr += noc;
 			while(noc) {
+#ifdef __BIG_ENDIAN
+				*mp++ = *sp % 256;
+				*mp++ = *sp / 256;
+#else
 				*mp++ = *sp / 256;
 				*mp++ = *sp % 256;
+#endif /* __BIG_ENDIAN */
 				sp++;
 				noc--;
 			}
@@ -557,8 +541,9 @@
 			rcv_mbox(cs, ireg, ptr);
 			if (ireg->cmsb & HDLC_FED) {
 				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
-					printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
-						bcs->hw.isar.rcvidx);
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar frame to short %d",
+							bcs->hw.isar.rcvidx);
 				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) {
 					printk(KERN_WARNING "ISAR: receive out of memory\n");
 				} else {
@@ -567,6 +552,7 @@
 					skb_queue_tail(&bcs->rqueue, skb);
 					isar_sched_event(bcs, B_RCVBUFREADY);
 				}
+				bcs->hw.isar.rcvidx = 0;
 			}
 		}
 		break;
@@ -635,13 +621,16 @@
 			bcs->hw.isar.rcvidx += ireg->clsb;
 			rcv_mbox(cs, ireg, ptr);
 			if (ireg->cmsb & HDLC_FED) {
+				int len = bcs->hw.isar.rcvidx +
+					dle_count(bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx);
 				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
-					printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
-						bcs->hw.isar.rcvidx);
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar frame to short %d",
+							bcs->hw.isar.rcvidx);
 				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
 					printk(KERN_WARNING "ISAR: receive out of memory\n");
 				} else {
-					memcpy(skb_put(skb, bcs->hw.isar.rcvidx),
+					insert_dle((u_char *)skb_put(skb, len),
 						bcs->hw.isar.rcvbuf,
 						bcs->hw.isar.rcvidx);
 					skb_queue_tail(&bcs->rqueue, skb);
@@ -649,8 +638,20 @@
 					send_DLE_ETX(bcs);
 					isar_sched_event(bcs, B_LL_OK);
 				}
+				bcs->hw.isar.rcvidx = 0;
 			}
 		}
+		if (ireg->cmsb & SART_NMD) { /* ABORT */
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: no more data");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			send_DLE_ETX(bcs);
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+				ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			isar_sched_event(bcs, B_LL_NOCARRIER);
+		}
 		break;
 	default:
 		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
@@ -1073,6 +1074,9 @@
 				debugl1(cs, "pump stev RSP_DISC");
 			if (bcs->hw.isar.state == STFAX_ESCAPE) {
 				switch(bcs->hw.isar.newcmd) {
+					case 0:
+						bcs->hw.isar.state = STFAX_READY;
+						break;
 					case PCTRL_CMD_FTH:
 					case PCTRL_CMD_FTM:
 						p1 = 10;
@@ -1082,13 +1086,14 @@
 						break;
 					case PCTRL_CMD_FRH:
 					case PCTRL_CMD_FRM:
-						p1 = bcs->hw.isar.newmod;
+						p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
 						bcs->hw.isar.newmod = 0;
 						bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
 						bcs->hw.isar.newcmd = 0;
 						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
 							bcs->hw.isar.cmd, 1, &p1);
 						bcs->hw.isar.state = STFAX_LINE;
+						bcs->hw.isar.try_mod = 3;
 						break;
 					default:
 						if (cs->debug & L1_DEB_HSCX)
@@ -1114,13 +1119,14 @@
 			if (cs->debug & L1_DEB_HSCX)
 				debugl1(cs, "pump stev RSP_SILDET");
 			if (bcs->hw.isar.state == STFAX_SILDET) {
-				p1 = bcs->hw.isar.newmod;
+				p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
 				bcs->hw.isar.newmod = 0;
 				bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
 				bcs->hw.isar.newcmd = 0;
 				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
 					bcs->hw.isar.cmd, 1, &p1);
 				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.try_mod = 3;
 			}
 			break;
 		case PSEV_RSP_SILOFF:
@@ -1128,6 +1134,17 @@
 				debugl1(cs, "pump stev RSP_SILOFF");
 			break;
 		case PSEV_RSP_FCERR:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_FCERR try %d",
+						bcs->hw.isar.try_mod);
+				if (bcs->hw.isar.try_mod--) {
+					sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+						bcs->hw.isar.cmd, 1,
+						&bcs->hw.isar.mod);
+					break;
+				}
+			}
 			if (cs->debug & L1_DEB_HSCX)
 				debugl1(cs, "pump stev RSP_FCERR");
 			bcs->hw.isar.state = STFAX_ESCAPE;
@@ -1438,6 +1455,7 @@
 				bcs->hw.isar.mod = para;
 				bcs->hw.isar.newmod = 0;
 				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
 			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
 				(bcs->hw.isar.cmd == PCTRL_CMD_FTM) &&
 				(bcs->hw.isar.mod == para)) {
@@ -1460,6 +1478,7 @@
 				bcs->hw.isar.mod = para;
 				bcs->hw.isar.newmod = 0;
 				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
 			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
 				(bcs->hw.isar.cmd == PCTRL_CMD_FTH) &&
 				(bcs->hw.isar.mod == para)) {
@@ -1482,6 +1501,7 @@
 				bcs->hw.isar.mod = para;
 				bcs->hw.isar.newmod = 0;
 				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
 			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
 				(bcs->hw.isar.cmd == PCTRL_CMD_FRM) &&
 				(bcs->hw.isar.mod == para)) {
@@ -1504,6 +1524,7 @@
 				bcs->hw.isar.mod = para;
 				bcs->hw.isar.newmod = 0;
 				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
 			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
 				(bcs->hw.isar.cmd == PCTRL_CMD_FRH) &&
 				(bcs->hw.isar.mod == para)) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isar.h linux.19p1/drivers/isdn/hisax/isar.h
--- linux.vanilla/drivers/isdn/hisax/isar.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isar.h	Wed Dec 13 22:08:04 2000
@@ -1,32 +1,10 @@
-/* $Id: isar.h,v 1.7 2000/01/20 19:47:45 keil Exp $
+/* $Id: isar.h,v 1.9 2000/06/26 08:59:13 keil Exp $
+ *
  * isar.h   ISAR (Siemens PSB 7110) specific defines
  *
  * Author Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: isar.h,v $
- * Revision 1.7  2000/01/20 19:47:45  keil
- * Add Fax Class 1 support
- *
- * Revision 1.6  1999/10/14 20:25:29  keil
- * add a statistic for error monitoring
- *
- * Revision 1.5  1999/08/25 16:59:59  keil
- * Make ISAR V32bis modem running
- * Make LL->HL interface open for additional commands
- *
- * Revision 1.4  1999/08/05 20:43:20  keil
- * ISAR analog modem support
- *
- * Revision 1.3  1999/07/01 08:11:46  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.2  1998/11/15 23:54:54  keil
- * changes from 2.0
- *
- * Revision 1.1  1998/08/13 23:33:48  keil
- * First version, only init
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
  
@@ -208,7 +186,7 @@
 #define HDLC_ERROR	0x1c
 #define HDLC_ERR_FAD	0x10
 #define HDLC_ERR_RER	0x08
-#define HDLC_ERR_CER	0x01
+#define HDLC_ERR_CER	0x04
 #define SART_NMD	0x01
 
 #define BSTAT_RDM0	0x1
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isdnl1.c linux.19p1/drivers/isdn/hisax/isdnl1.c
--- linux.vanilla/drivers/isdn/hisax/isdnl1.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isdnl1.c	Wed Dec 13 22:08:04 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnl1.c,v 2.37 2000/01/20 19:51:46 keil Exp $
-
+/* $Id: isdnl1.c,v 2.39 2000/06/26 08:59:13 keil Exp $
+ *
  * isdnl1.c     common low level stuff for Siemens Chipsetbased isdn cards
  *              based on the teles driver from Jan den Ouden
  *
@@ -13,136 +13,9 @@
  *              Fritz Elfert
  *              Beat Doebeli
  *
- *
- * $Log: isdnl1.c,v $
- * Revision 2.37  2000/01/20 19:51:46  keil
- * Fix AddTimer message
- * Change CONFIG defines
- *
- * Revision 2.36  1999/08/25 16:50:57  keil
- * Fix bugs which cause 2.3.14 hangs (waitqueue init)
- *
- * Revision 2.35  1999/08/22 20:27:07  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 2.34  1999/07/09 13:50:15  keil
- * remove unused variable
- *
- * Revision 2.33  1999/07/09 13:34:33  keil
- * remove debug code
- *
- * Revision 2.32  1999/07/01 08:11:47  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.31  1998/11/15 23:54:56  keil
- * changes from 2.0
- *
- * Revision 2.30  1998/09/30 22:27:00  keil
- * Add init of l1.Flags
- *
- * Revision 2.29  1998/09/27 23:54:43  keil
- * cosmetics
- *
- * Revision 2.28  1998/09/27 12:52:23  keil
- * Fix against segfault, if the driver cannot allocate an IRQ channel
- *
- * Revision 2.27  1998/08/13 23:36:39  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.26  1998/07/15 15:01:31  calle
- * Support for AVM passive PCMCIA cards:
- *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
- *
- * Revision 2.25  1998/05/25 14:10:09  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.24  1998/05/25 12:58:04  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.22  1998/04/15 16:40:13  keil
- * Add S0Box and Teles PCI support
- * Fix cardnr overwrite bug
- *
- * Revision 2.21  1998/04/10 10:35:28  paul
- * fixed (silly?) warnings from egcs on Alpha.
- *
- * Revision 2.20  1998/03/09 23:19:27  keil
- * Changes for PCMCIA
- *
- * Revision 2.18  1998/02/12 23:07:42  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.17  1998/02/11 17:28:07  keil
- * Niccy PnP/PCI support
- *
- * Revision 2.16  1998/02/09 18:46:08  keil
- * Support for Sedlbauer PCMCIA (Marcus Niemann)
- *
- * Revision 2.15  1998/02/09 10:54:51  keil
- * fixes for leased mode
- *
- * Revision 2.14  1998/02/03 23:31:31  keil
- * add AMD7930 support
- *
- * Revision 2.13  1998/02/02 13:33:02  keil
- * New card support
- *
- * Revision 2.12  1998/01/31 21:41:48  keil
- * changes for newer 2.1 kernels
- *
- * Revision 2.11  1997/11/12 15:01:23  keil
- * COMPAQ_ISA changes
- *
- * Revision 2.10  1997/11/08 21:35:48  keil
- * new l1 init
- *
- * Revision 2.9  1997/11/06 17:09:18  keil
- * New 2.1 init code
- *
- * Revision 2.8  1997/10/29 19:00:05  keil
- * new layer1,changes for 2.1
- *
- * Revision 2.7  1997/10/10 20:56:50  fritz
- * New HL interface.
- *
- * Revision 2.6  1997/09/12 10:05:16  keil
- * ISDN_CTRL_DEBUG define
- *
- * Revision 2.5  1997/09/11 17:24:45  keil
- * Add new cards
- *
- * Revision 2.4  1997/08/15 17:47:09  keil
- * avoid oops because a uninitialised timer
- *
- * Revision 2.3  1997/08/01 11:16:40  keil
- * cosmetics
- *
- * Revision 2.2  1997/07/30 17:11:08  keil
- * L1deactivated exported
- *
- * Revision 2.1  1997/07/27 21:35:38  keil
- * new layer1 interface
- *
- * Revision 2.0  1997/06/26 11:02:53  keil
- * New Layer and card interface
- *
- * Revision 1.15  1997/05/27 15:17:55  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
- *
- * old changes removed KKe
- *
  */
 
-const char *l1_revision = "$Revision: 2.37 $";
+const char *l1_revision = "$Revision: 2.39 $";
 
 #define __NO_VERSION__
 #include "hisax.h"
@@ -155,7 +28,7 @@
 {NULL, 0, 0, NULL, NULL};
 
 static
-struct Fsm l1fsm_d =
+struct Fsm l1fsm_s =
 {NULL, 0, 0, NULL, NULL};
 
 enum {
@@ -168,9 +41,9 @@
 	ST_L1_F8,
 };
 
-#define L1D_STATE_COUNT (ST_L1_F8+1)
+#define L1S_STATE_COUNT (ST_L1_F8+1)
 
-static char *strL1DState[] =
+static char *strL1SState[] =
 {
 	"ST_L1_F2",
 	"ST_L1_F3",
@@ -181,6 +54,29 @@
 	"ST_L1_F8",
 };
 
+#ifdef HISAX_UINTERFACE
+static
+struct Fsm l1fsm_u =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L1_RESET,
+	ST_L1_DEACT,
+	ST_L1_SYNC2,
+	ST_L1_TRANS,
+};
+
+#define L1U_STATE_COUNT (ST_L1_TRANS+1)
+
+static char *strL1UState[] =
+{
+	"ST_L1_RESET",
+	"ST_L1_DEACT",
+	"ST_L1_SYNC2",
+	"ST_L1_TRANS",
+};
+#endif
+
 enum {
 	ST_L1_NULL,
 	ST_L1_WAIT_ACT,
@@ -559,7 +455,7 @@
 }
 
 static void
-l1_deact_req(struct FsmInst *fi, int event, void *arg)
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 
@@ -569,7 +465,7 @@
 }
 
 static void
-l1_power_up(struct FsmInst *fi, int event, void *arg)
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 
@@ -599,7 +495,12 @@
 {
 	struct PStack *st = fi->userdata;
 
-	FsmChangeState(fi, ST_L1_F6);
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+		FsmChangeState(fi, ST_L1_SYNC2);
+	else
+#endif
+		FsmChangeState(fi, ST_L1_F6);
 	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
 }
 
@@ -608,7 +509,12 @@
 {
 	struct PStack *st = fi->userdata;
 
-	FsmChangeState(fi, ST_L1_F7);
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+		FsmChangeState(fi, ST_L1_TRANS);
+	else
+#endif
+		FsmChangeState(fi, ST_L1_F7);
 	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
 	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
 		FsmDelTimer(&st->l1.timer, 4);
@@ -628,6 +534,10 @@
 	test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);	
 	if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
 		L1deactivated(st->l1.hardware);
+
+#ifdef HISAX_UINTERFACE
+	if (!test_bit(FLG_L1_UINT, &st->l1.Flags))
+#endif
 	if (st->l1.l1m.state != ST_L1_F6) {
 		FsmChangeState(fi, ST_L1_F3);
 		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
@@ -656,7 +566,7 @@
 }
 
 static void
-l1_activate(struct FsmInst *fi, int event, void *arg)
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
                 
@@ -674,9 +584,9 @@
 	}
 }
 
-static struct FsmNode L1DFnList[] HISAX_INITDATA =
+static struct FsmNode L1SFnList[] HISAX_INITDATA =
 {
-	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate},
+	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
 	{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
 	{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
 	{ST_L1_F3, EV_RESET_IND, l1_reset},
@@ -691,10 +601,10 @@
 	{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
 	{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
 	{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
-	{ST_L1_F6, EV_DEACT_IND, l1_deact_req},
-	{ST_L1_F7, EV_DEACT_IND, l1_deact_req},
-	{ST_L1_F8, EV_DEACT_IND, l1_deact_req},
-	{ST_L1_F3, EV_POWER_UP, l1_power_up},
+	{ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F3, EV_POWER_UP, l1_power_up_s},
 	{ST_L1_F4, EV_RSYNC_IND, l1_go_F5},
 	{ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
 	{ST_L1_F7, EV_RSYNC_IND, l1_go_F8},
@@ -722,7 +632,68 @@
 	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
 };
 
-#define L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode))
+#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode))
+
+#ifdef HISAX_UINTERFACE
+static void
+l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_power_up_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+}
+
+static void
+l1_info0_ind(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+l1_activate_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+                
+	st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL);
+}
+
+static struct FsmNode L1UFnList[] HISAX_INITDATA =
+{
+	{ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u},
+	{ST_L1_DEACT, EV_POWER_UP, l1_power_up_u},
+	{ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind},
+	{ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_RESET, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_DEACT, EV_TIMER3, l1_timer3},
+	{ST_L1_SYNC2, EV_TIMER3, l1_timer3},
+	{ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act},
+	{ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode))
+
+#endif
 
 static void
 l1b_activate(struct FsmInst *fi, int event, void *arg)
@@ -772,11 +743,18 @@
 
 HISAX_INITFUNC(void Isdnl1New(void))
 {
-	l1fsm_d.state_count = L1D_STATE_COUNT;
-	l1fsm_d.event_count = L1_EVENT_COUNT;
-	l1fsm_d.strEvent = strL1Event;
-	l1fsm_d.strState = strL1DState;
-	FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT);
+#ifdef HISAX_UINTERFACE
+	l1fsm_u.state_count = L1U_STATE_COUNT;
+	l1fsm_u.event_count = L1_EVENT_COUNT;
+	l1fsm_u.strEvent = strL1Event;
+	l1fsm_u.strState = strL1UState;
+	FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
+#endif
+	l1fsm_s.state_count = L1S_STATE_COUNT;
+	l1fsm_s.event_count = L1_EVENT_COUNT;
+	l1fsm_s.strEvent = strL1Event;
+	l1fsm_s.strState = strL1SState;
+	FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
 	l1fsm_b.state_count = L1B_STATE_COUNT;
 	l1fsm_b.event_count = L1_EVENT_COUNT;
 	l1fsm_b.strEvent = strL1Event;
@@ -786,7 +764,10 @@
 
 void Isdnl1Free(void)
 {
-	FsmFree(&l1fsm_d);
+#ifdef HISAX_UINTERFACE
+	FsmFree(&l1fsm_u);
+#endif
+	FsmFree(&l1fsm_s);
 	FsmFree(&l1fsm_b);
 }
 
@@ -804,7 +785,7 @@
 		case (PH_ACTIVATE | REQUEST):
 			if (cs->debug)
 				debugl1(cs, "PH_ACTIVATE_REQ %s",
-					strL1DState[st->l1.l1m.state]);
+					st->l1.l1m.fsm->strState[st->l1.l1m.state]);
 			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
 				st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
 			else {
@@ -884,8 +865,16 @@
 {
 	st->l1.hardware = cs;
 	st->protocol = cs->protocol;
-	st->l1.l1m.fsm = &l1fsm_d;
+	st->l1.l1m.fsm = &l1fsm_s;
 	st->l1.l1m.state = ST_L1_F3;
+	st->l1.Flags = 0;
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) {
+		st->l1.l1m.fsm = &l1fsm_u;
+		st->l1.l1m.state = ST_L1_RESET;
+		st->l1.Flags = FLG_L1_UINT;
+	}
+#endif
 	st->l1.l1m.debug = cs->debug;
 	st->l1.l1m.userdata = st;
 	st->l1.l1m.userint = 0;
@@ -895,7 +884,6 @@
 	setstack_manager(st);
 	st->l1.stlistp = &(cs->stlist);
 	st->l2.l2l1  = dch_l2l1;
-	st->l1.Flags = 0;
 	cs->setstack_d(st, cs);
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isdnl1.h linux.19p1/drivers/isdn/hisax/isdnl1.h
--- linux.vanilla/drivers/isdn/hisax/isdnl1.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isdnl1.h	Wed Dec 13 22:08:04 2000
@@ -1,34 +1,8 @@
-/* $Id: isdnl1.h,v 2.8 1998/11/15 23:54:59 keil Exp $
-
- * $Log: isdnl1.h,v $
- * Revision 2.8  1998/11/15 23:54:59  keil
- * changes from 2.0
+/* $Id: isdnl1.h,v 2.9 2000/06/26 08:59:13 keil Exp $
  *
- * Revision 2.7  1998/09/30 22:21:55  keil
- * cosmetics
- *
- * Revision 2.6  1998/05/25 12:58:06  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.5  1998/02/02 13:36:58  keil
- * more debug
- *
- * Revision 2.4  1997/11/08 21:35:49  keil
- * new l1 init
- *
- * Revision 2.3  1997/10/29 19:07:53  keil
- * changes for 2.1
- *
- * Revision 2.2  1997/07/30 17:11:09  keil
- * L1deactivated exported
- *
- * Revision 2.1  1997/07/27 21:43:58  keil
- * new l1 interface
- *
- * Revision 2.0  1997/06/26 11:02:55  keil
- * New Layer and card interface
+ * Layer 1 defines
  *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isdnl2.c linux.19p1/drivers/isdn/hisax/isdnl2.c
--- linux.vanilla/drivers/isdn/hisax/isdnl2.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isdnl2.c	Wed Dec 13 22:08:04 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnl2.c,v 2.20 1999/08/25 16:52:04 keil Exp $
-
+/* $Id: isdnl2.c,v 2.23 2000/06/26 08:59:13 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
@@ -10,80 +10,12 @@
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
- * $Log: isdnl2.c,v $
- * Revision 2.20  1999/08/25 16:52:04  keil
- * Make gcc on AXP happy
- *
- * Revision 2.19  1999/08/05 20:40:26  keil
- * Fix interlayer communication
- *
- * Revision 2.18  1999/07/21 14:46:16  keil
- * changes from EICON certification
- *
- * Revision 2.17  1999/07/01 08:11:50  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.16  1998/11/15 23:55:01  keil
- * changes from 2.0
- *
- * Revision 2.15  1998/08/13 23:36:42  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.14  1998/06/19 15:19:18  keil
- * fix LAPB tx_cnt for none I-frames
- *
- * Revision 2.13  1998/06/18 23:17:20  keil
- * LAPB bugfix
- *
- * Revision 2.12  1998/05/25 14:10:12  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.11  1998/05/25 12:58:08  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.9  1998/04/10 10:35:30  paul
- * fixed (silly?) warnings from egcs on Alpha.
- *
- * Revision 2.8  1998/03/07 22:57:04  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 2.7  1998/02/12 23:07:47  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.6  1998/02/02 13:36:15  keil
- * bugfix X.75 win calculation
- *
- * Revision 2.5  1997/11/06 17:09:22  keil
- * New 2.1 init code
- *
- * Revision 2.4  1997/10/29 19:02:01  keil
- * new LL interface
- *
- * Revision 2.3  1997/10/01 09:21:39  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 2.2  1997/07/31 11:49:05  keil
- * Error handling for no TEI assign
- *
- * Revision 2.1  1997/07/27 21:34:38  keil
- * cosmetics
- *
- * Revision 2.0  1997/06/26 11:07:29  keil
- * New q.921 and X.75 Layer2
- *
- *
- *  Old log removed KKe
- *
  */
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl2.h"
 
-const char *l2_revision = "$Revision: 2.20 $";
+const char *l2_revision = "$Revision: 2.23 $";
 
 static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
 
@@ -361,7 +293,7 @@
 int
 iframe_error(struct PStack *st, struct sk_buff *skb)
 {
-	int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 1 : 0);
+	int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1);
 	int rsp = *skb->data & 0x2;
 
 	if (test_bit(FLG_ORIG, &st->l2.flag))
@@ -371,7 +303,7 @@
 		return 'L';
 
 
-	if (skb->len <= i)
+	if (skb->len < i)
 		return 'N';
 
 	if ((skb->len - i) > st->l2.maxlen)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isdnl2.h linux.19p1/drivers/isdn/hisax/isdnl2.h
--- linux.vanilla/drivers/isdn/hisax/isdnl2.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isdnl2.h	Wed Dec 13 22:08:04 2000
@@ -1,4 +1,10 @@
-/* isdnl2.h */
+/* $Id: isdnl2.h,v 1.3 2000/06/26 08:59:13 keil Exp $
+ *
+ * Layer 2 defines
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ *
+ */
 
 #define RR     0x01
 #define RNR    0x05
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isdnl3.c linux.19p1/drivers/isdn/hisax/isdnl3.c
--- linux.vanilla/drivers/isdn/hisax/isdnl3.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isdnl3.c	Wed Dec 13 22:08:13 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $
-
+/* $Id: isdnl3.c,v 2.14 2000/06/26 08:59:13 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
@@ -10,65 +10,13 @@
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
- * $Log: isdnl3.c,v $
- * Revision 2.10  1999/07/21 14:46:19  keil
- * changes from EICON certification
- *
- * Revision 2.9  1999/07/01 08:11:53  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.8  1998/11/15 23:55:04  keil
- * changes from 2.0
- *
- * Revision 2.7  1998/05/25 14:10:15  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.6  1998/05/25 12:58:11  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.5  1998/02/12 23:07:52  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.4  1997/11/06 17:09:25  keil
- * New 2.1 init code
- *
- * Revision 2.3  1997/10/29 19:07:53  keil
- * changes for 2.1
- *
- * Revision 2.2  1997/10/01 09:21:41  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 2.1  1997/08/03 14:36:32  keil
- * Implement RESTART procedure
- *
- * Revision 2.0  1997/07/27 21:15:42  keil
- * New Callref based layer3
- *
- * Revision 1.11  1997/06/26 11:11:44  keil
- * SET_SKBFREE now on creation of a SKB
- *
- * Revision 1.10  1997/04/06 22:54:16  keil
- * Using SKB's
- *
- * Revision 1.9  1997/03/25 23:11:25  keil
- * US NI-1 protocol
- *
- * Revision 1.8  1997/03/21 18:53:44  keil
- * Report no protocol error to syslog too
- *
- * Remove old logs /KKe
- *
  */
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl3.h"
 #include <linux/config.h>
 
-const char *l3_revision = "$Revision: 2.10 $";
+const char *l3_revision = "$Revision: 2.14 $";
 
 static
 struct Fsm l3fsm =
@@ -234,7 +182,7 @@
 L3AddTimer(struct L3Timer *t,
 	   int millisec, int event)
 {
-	if (t->tl.next || t->tl.prev) {
+	if (timer_pending(&t->tl)) {
 		printk(KERN_WARNING "L3AddTimer: timer already active!\n");
 		return -1;
 	}
@@ -286,7 +234,7 @@
 extern void setstack_dss1(struct PStack *st);
 #endif
 
-#ifdef        CONFIG_HISAX_NI1
+#ifdef  CONFIG_HISAX_NI1
 extern void setstack_ni1(struct PStack *st);
 #endif
 
@@ -375,10 +323,13 @@
 l3ml3p(struct PStack *st, int pr)
 {
 	struct l3_process *p = st->l3.proc;
+	struct l3_process *np;
 
 	while (p) {
+		/* p might be kfreed under us, so we need to save where we want to go on */
+		np = p->next;
 		st->l3.l3ml3(st, pr, p);
-		p = p->next;
+		p = np;
 	}
 }
 
@@ -405,7 +356,7 @@
 		setstack_dss1(st);
 	} else
 #endif
-#ifdef        CONFIG_HISAX_NI1
+#ifdef  CONFIG_HISAX_NI1
 	if (st->protocol == ISDN_PTYPE_NI1) {
 		setstack_ni1(st);
 	} else
@@ -593,7 +544,6 @@
 void
 l3_msg(struct PStack *st, int pr, void *arg)
 {
-	
 	switch (pr) {
 		case (DL_DATA | REQUEST):
 			if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isdnl3.h linux.19p1/drivers/isdn/hisax/isdnl3.h
--- linux.vanilla/drivers/isdn/hisax/isdnl3.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isdnl3.h	Wed Dec 13 22:08:13 2000
@@ -1,38 +1,6 @@
-/* $Id: isdnl3.h,v 2.5 1999/07/25 16:18:32 keil Exp $
-
- * $Log: isdnl3.h,v $
- * Revision 2.5  1999/07/25 16:18:32  keil
- * Fix Suspend/Resume
- *
- * Revision 2.4  1999/07/01 08:11:54  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.3  1998/11/15 23:55:06  keil
- * changes from 2.0
- *
- * Revision 2.2  1998/05/25 14:10:17  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.1  1998/05/25 12:58:13  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.0  1997/07/27 21:15:42  keil
- * New Callref based layer3
- *
- * Revision 1.4  1997/06/26 11:20:57  keil
- * ?
- *
- * Revision 1.3  1997/04/06 22:54:17  keil
- * Using SKB's
- *
- * Revision 1.2  1997/01/21 22:31:28  keil
- * new statemachine; L3 timers
- *
- * Revision 1.1  1996/10/13 20:03:47  keil
- * Initial revision
+/* $Id: isdnl3.h,v 2.6 2000/06/26 08:59:13 keil Exp $
  *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/isurf.c linux.19p1/drivers/isdn/hisax/isurf.c
--- linux.vanilla/drivers/isdn/hisax/isurf.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/isurf.c	Wed Dec 13 22:08:13 2000
@@ -1,38 +1,10 @@
-/* $Id: isurf.c,v 1.8 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: isurf.c,v 1.9 2000/06/26 08:59:13 keil Exp $
+ *
  * isurf.c  low level stuff for Siemens I-Surf/I-Talk cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
- * $Log: isurf.c,v $
- * Revision 1.8  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.7  1999/11/14 23:37:03  keil
- * new ISA memory mapped IO
- *
- * Revision 1.6  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.5  1999/08/25 17:00:02  keil
- * Make ISAR V32bis modem running
- * Make LL->HL interface open for additional commands
- *
- * Revision 1.4  1999/08/22 20:27:09  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.3  1999/07/12 21:05:18  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.2  1999/07/01 08:07:56  keil
- * Initial version
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -44,7 +16,7 @@
 
 extern const char *CardType[];
 
-static const char *ISurf_revision = "$Revision: 1.8 $";
+static const char *ISurf_revision = "$Revision: 1.9 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -164,10 +136,10 @@
 	byteout(cs->hw.isurf.reset, chips); /* Reset On */
 	save_flags(flags);
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	restore_flags(flags);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/ix1_micro.c linux.19p1/drivers/isdn/hisax/ix1_micro.c
--- linux.vanilla/drivers/isdn/hisax/ix1_micro.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/ix1_micro.c	Wed Dec 13 22:08:13 2000
@@ -1,5 +1,5 @@
-/* $Id: ix1_micro.c,v 2.8 1999/07/12 21:05:19 keil Exp $
-
+/* $Id: ix1_micro.c,v 2.9 2000/06/26 08:59:13 keil Exp $
+ *
  * ix1_micro.c  low level stuff for ITK ix1-micro Rev.2 isdn cards
  *              derived from the original file teles3.c from Karsten Keil
  *
@@ -10,45 +10,6 @@
  *              Fritz Elfert
  *              Beat Doebeli
  *
- * $Log: ix1_micro.c,v $
- * Revision 2.8  1999/07/12 21:05:19  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.7  1998/04/15 16:44:31  keil
- * new init code
- *
- * Revision 2.6  1998/02/11 17:28:09  keil
- * Niccy PnP/PCI support
- *
- * Revision 2.5  1998/02/02 13:29:42  keil
- * fast io
- *
- * Revision 2.4  1997/11/08 21:35:50  keil
- * new l1 init
- *
- * Revision 2.3  1997/11/06 17:09:35  keil
- * New 2.1 init code
- *
- * Revision 2.2  1997/10/29 18:55:51  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 2.1  1997/07/27 21:47:09  keil
- * new interface structures
- *
- * Revision 2.0  1997/06/26 11:02:50  keil
- * New Layer and card interface
- *
- * Revision 1.3  1997/04/13 19:54:02  keil
- * Change in IRQ check delay for SMP
- *
- * Revision 1.2  1997/04/06 22:54:21  keil
- * Using SKB's
- *
- * Revision 1.1  1997/01/27 15:43:10  keil
- * first version
- *
- *
  */
 
 /*
@@ -88,7 +49,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *ix1_revision = "$Revision: 2.8 $";
+const char *ix1_revision = "$Revision: 2.9 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/jade.c linux.19p1/drivers/isdn/hisax/jade.c
--- linux.vanilla/drivers/isdn/hisax/jade.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/jade.c	Wed Dec 13 22:08:13 2000
@@ -1,13 +1,10 @@
-/* $Id: jade.c,v 1.2 1999/07/01 08:07:57 keil Exp $
+/* $Id: jade.c,v 1.4 2000/06/26 08:59:14 keil Exp $
  *
  * jade.c   JADE stuff (derived from original hscx.c)
  *
  * Author   Roland Klabunde (R.Klabunde@Berkom.de)
  *
- * $Log: jade.c,v $
- * Revision 1.2  1999/07/01 08:07:57  keil
- * Initial version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/jade.h linux.19p1/drivers/isdn/hisax/jade.h
--- linux.vanilla/drivers/isdn/hisax/jade.h	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/jade.h	Wed Dec 13 22:08:13 2000
@@ -1,13 +1,9 @@
-/* $Id: jade.h,v 1.2 1999/07/01 08:07:58 keil Exp $
+/* $Id: jade.h,v 1.3 2000/06/26 08:59:14 keil Exp $
  * jade.h   JADE specific defines
  *
  * Author   Roland Klabunde (R.Klabunde@Berkom.de)
  *
- *
- * $Log: jade.h,v $
- * Revision 1.2  1999/07/01 08:07:58  keil
- * Initial version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/jade_irq.c linux.19p1/drivers/isdn/hisax/jade_irq.c
--- linux.vanilla/drivers/isdn/hisax/jade_irq.c	Fri Apr 21 12:46:10 2000
+++ linux.19p1/drivers/isdn/hisax/jade_irq.c	Wed Dec 13 22:08:13 2000
@@ -1,13 +1,10 @@
-/* $Id: jade_irq.c,v 1.2 1999/07/01 08:07:59 keil Exp $
+/* $Id: jade_irq.c,v 1.4 2000/06/26 08:59:14 keil Exp $
  *
  * jade_irq.c   Low level JADE IRQ stuff (derived from original hscx_irq.c)
  *
  * Author   Roland Klabunde (R.Klabunde@Berkom.de)
  *
- * $Log: jade_irq.c,v $
- * Revision 1.2  1999/07/01 08:07:59  keil
- * Initial version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/l3_1tr6.c linux.19p1/drivers/isdn/hisax/l3_1tr6.c
--- linux.vanilla/drivers/isdn/hisax/l3_1tr6.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/l3_1tr6.c	Wed Dec 13 22:08:13 2000
@@ -1,5 +1,5 @@
-/* $Id: l3_1tr6.c,v 2.10 2000/01/20 19:42:01 keil Exp $
-
+/* $Id: l3_1tr6.c,v 2.12 2000/08/20 07:31:30 keil Exp $
+ *
  *  German 1TR6 D-channel protocol
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
@@ -8,51 +8,6 @@
  *		For changes and modifications please read
  *		../../../Documentation/isdn/HiSax.cert
  *
- *
- * $Log: l3_1tr6.c,v $
- * Revision 2.10  2000/01/20 19:42:01  keil
- * Fixed uninitialiesed location
- *
- * Revision 2.9  1999/07/01 08:11:55  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.8  1998/11/15 23:55:08  keil
- * changes from 2.0
- *
- * Revision 2.7  1998/08/13 23:36:45  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.6  1998/05/25 14:10:18  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.5  1998/05/25 12:58:14  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.4  1998/02/12 23:07:57  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.3  1997/11/06 17:12:24  keil
- * KERN_NOTICE --> KERN_INFO
- *
- * Revision 2.2  1997/10/29 19:03:00  keil
- * changes for 2.1
- *
- * Revision 2.1  1997/08/03 15:28:09  keil
- * release L3 empty processes
- *
- * Revision 2.0  1997/07/27 21:15:45  keil
- * New Callref based layer3
- *
- * Revision 1.12  1997/06/26 11:11:45  keil
- * SET_SKBFREE now on creation of a SKB
- *
- * Revision 1.11  1997/04/06 22:54:18  keil
- * Using SKB's
- *
- * Old Log removed /KKe
- *
  */
 
 #define __NO_VERSION__
@@ -62,7 +17,7 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 2.10 $";
+const char *l3_1tr6_revision = "$Revision: 2.12 $";
 
 #define MsgHead(ptr, cref, mty, dis) \
 	*ptr++ = dis; \
@@ -928,7 +883,7 @@
 		} else {
 			proc->chan = chan;
 			chan->proc = proc;
-			proc->para.setup = chan->setup;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
 			proc->callref = cr;
 		}
 	} else {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/l3_1tr6.h linux.19p1/drivers/isdn/hisax/l3_1tr6.h
--- linux.vanilla/drivers/isdn/hisax/l3_1tr6.h	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/l3_1tr6.h	Wed Dec 13 22:08:13 2000
@@ -1,16 +1,8 @@
-/* $Id: l3_1tr6.h,v 2.1 1998/08/13 23:36:48 keil Exp $
+/* $Id: l3_1tr6.h,v 2.2 2000/06/26 08:59:14 keil Exp $
  *
  *  German 1TR6 D-channel protocol defines
  *
- * $Log: l3_1tr6.h,v $
- * Revision 2.1  1998/08/13 23:36:48  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.0  1997/07/27 21:15:47  keil
- * New Callref based layer3
- *
- * Revision 1.1  1996/10/13 20:03:48  keil
- * Initial revision
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #ifndef l3_1tr6
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/l3dss1.c linux.19p1/drivers/isdn/hisax/l3dss1.c
--- linux.vanilla/drivers/isdn/hisax/l3dss1.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/l3dss1.c	Wed Dec 13 22:08:13 2000
@@ -1,5 +1,5 @@
-/* $Id: l3dss1.c,v 2.23 2000/02/26 01:38:14 keil Exp $
-
+/* $Id: l3dss1.c,v 2.29 2000/06/26 08:59:14 keil Exp $
+ *
  * EURO/DSS1 D-channel protocol
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
@@ -12,91 +12,6 @@
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
- * $Log: l3dss1.c,v $
- * Revision 2.23  2000/02/26 01:38:14  keil
- * Fixes for V.110 encoding LLC from Jens Jakobsen
- *
- * Revision 2.22  2000/01/20 19:44:20  keil
- * Fixed uninitialiesed location
- * Fixed redirecting number IE in Setup
- * Changes from certification
- * option for disabling use of KEYPAD protocol
- *
- * Revision 2.21  1999/12/19 20:25:17  keil
- * fixed LLC for outgoing analog calls
- * IE Signal is valid on older local switches
- *
- * Revision 2.20  1999/10/11 22:16:27  keil
- * Suspend/Resume is possible without explicit ID too
- *
- * Revision 2.19  1999/08/25 16:55:23  keil
- * Fix for test case TC10011
- *
- * Revision 2.18  1999/08/11 20:54:39  keil
- * High layer compatibility is valid in SETUP
- *
- * Revision 2.17  1999/07/25 16:18:25  keil
- * Fix Suspend/Resume
- *
- * Revision 2.16  1999/07/21 14:46:23  keil
- * changes from EICON certification
- *
- * Revision 2.14  1999/07/09 08:30:08  keil
- * cosmetics
- *
- * Revision 2.13  1999/07/01 08:11:58  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.12  1998/11/15 23:55:10  keil
- * changes from 2.0
- *
- * Revision 2.11  1998/08/13 23:36:51  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 2.10  1998/05/25 14:10:20  keil
- * HiSax 3.0
- * X.75 and leased are working again.
- *
- * Revision 2.9  1998/05/25 12:58:17  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.8  1998/03/19 13:18:47  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 2.7  1998/02/12 23:08:01  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.6  1998/02/03 23:26:35  keil
- * V110 extensions from Thomas Pfeiffer
- *
- * Revision 2.5  1998/02/02 13:34:28  keil
- * Support australian Microlink net and german AOCD
- *
- * Revision 2.4  1997/11/06 17:12:25  keil
- * KERN_NOTICE --> KERN_INFO
- *
- * Revision 2.3  1997/10/29 19:03:01  keil
- * changes for 2.1
- *
- * Revision 2.2  1997/08/07 17:44:36  keil
- * Fix RESTART
- *
- * Revision 2.1  1997/08/03 14:36:33  keil
- * Implement RESTART procedure
- *
- * Revision 2.0  1997/07/27 21:15:43  keil
- * New Callref based layer3
- *
- * Revision 1.17  1997/06/26 11:11:46  keil
- * SET_SKBFREE now on creation of a SKB
- *
- * Revision 1.15  1997/04/17 11:50:48  keil
- * pa->loc was undefined, if it was not send by the exchange
- *
- * Old log removed /KKe
- *
  */
 
 #define __NO_VERSION__
@@ -107,7 +22,7 @@
 #include <linux/config.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 2.23 $";
+const char *dss1_revision = "$Revision: 2.29 $";
 
 #define EXT_BEARER_CAPS 1
 
@@ -806,6 +721,9 @@
 	u_char *p, ie;
 	int l, newpos, oldpos;
 	int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+	u_char codeset = 0;
+	u_char old_codeset = 0;
+	u_char codelock = 1;
 	
 	p = skb->data;
 	/* skip cr */
@@ -814,20 +732,34 @@
 	p += l;
 	mt = *p++;
 	oldpos = 0;
-/* shift codeset procedure not implemented in the moment */
 	while ((p - skb->data) < skb->len) {
-		if ((newpos = ie_in_set(pc, *p, cl))) {
-			if (newpos > 0) {
-				if (newpos < oldpos)
-					err_seq++;
+		if ((*p & 0xf0) == 0x90) { /* shift codeset */
+			old_codeset = codeset;
+			codeset = *p & 7;
+			if (*p & 0x08)
+				codelock = 0;
+			else
+				codelock = 1;
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+					codelock ? " locking ": " ", old_codeset, codeset);
+			p++;
+			continue;
+		}
+		if (!codeset) { /* only codeset 0 */
+			if ((newpos = ie_in_set(pc, *p, cl))) {
+				if (newpos > 0) {
+					if (newpos < oldpos)
+						err_seq++;
+					else
+						oldpos = newpos;
+				}
+			} else {
+				if (ie_in_set(pc, *p, comp_required))
+					err_compr++;
 				else
-					oldpos = newpos;
+					err_ureg++;
 			}
-		} else {
-			if (ie_in_set(pc, *p, comp_required))
-				err_compr++;
-			else
-				err_ureg++;
 		}
 		ie = *p++;
 		if (ie & 0x80) {
@@ -837,12 +769,19 @@
 			p += l;
 			l += 2;
 		}
-		if (l > getmax_ie_len(ie))
+		if (!codeset && (l > getmax_ie_len(ie)))
 			err_len++;
+		if (!codelock) {
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift back codeset %d->%d",
+					codeset, old_codeset);
+			codeset = old_codeset;
+			codelock = 1;
+		}
 	}
 	if (err_compr | err_ureg | err_len | err_seq) {
 		if (pc->debug & L3_DEB_CHECK)
-			l3_debug(pc->st, "check_infoelements mt %x %d/%d/%d/%d",
+			l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
 				mt, err_compr, err_ureg, err_len, err_seq);
 		if (err_compr)
 			return(ERR_IE_COMPREHENSION);
@@ -1044,7 +983,7 @@
 
 #if EXT_BEARER_CAPS
 
-u_char *
+static u_char *
 EncodeASyncParams(u_char * p, u_char si2)
 {				// 7c 06 88  90 21 42 00 bb
 
@@ -1109,7 +1048,7 @@
 	return p + 3;
 }
 
-u_char
+static  u_char
 EncodeSyncParams(u_char si2, u_char ai)
 {
 
@@ -1313,39 +1252,31 @@
 	/*
 	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
 	 */
-        if (!send_keypad)
-	  switch (pc->para.setup.si1) {
-		case 1:	/* Telephony                               */
-			*p++ = 0x4;	/* BC-IE-code                              */
-			*p++ = 0x3;	/* Length                                  */
-			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio     */
-			*p++ = 0x90;	/* Circuit-Mode 64kbps                     */
-			*p++ = 0xa3;	/* A-Law Audio                             */
-			break;
-		case 5:	/* Datatransmission 64k, BTX               */
-		case 7:	/* Datatransmission 64k                    */
-		default:
-			*p++ = 0x4;	/* BC-IE-code                              */
-			*p++ = 0x2;	/* Length                                  */
-			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
-			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
-			break;
-	  }
-	else {  *p++ = 0x4; /* assumptions for bearer services with keypad  */
-		*p++ = 0x3;
-		*p++ = 0x80;
-                *p++ = 0x90;
-                *p++ = 0xa3;
-		*p++ = 0x18; /* no specific channel */
-                *p++ = 0x01;
-                *p++ = 0x83;
-		*p++ = 0x2C; /* IE keypad */
+	switch (pc->para.setup.si1) {
+	case 1:	                  /* Telephony                                */
+		*p++ = IE_BEARER;
+		*p++ = 0x3;	  /* Length                                   */
+		*p++ = 0x90;	  /* Coding Std. CCITT, 3.1 kHz audio         */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		*p++ = 0xa3;	  /* A-Law Audio                              */
+		break;
+	case 5:	                  /* Datatransmission 64k, BTX                */
+	case 7:	                  /* Datatransmission 64k                     */
+	default:
+		*p++ = IE_BEARER;
+		*p++ = 0x2;	  /* Length                                   */
+		*p++ = 0x88;	  /* Coding Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		break;
+	}
+
+	if (send_keypad) {
+		*p++ = IE_KEYPAD;
 		*p++ = strlen(teln);
 		while (*teln)
-		  *p++ = (*teln++) & 0x7F;
-	  }
+			*p++ = (*teln++) & 0x7F;
+	}
 	  
-       
 	/*
 	 * What about info2? Mapping to High-Layer-Compatibility?
 	 */
@@ -1394,7 +1325,7 @@
 			sp++;
 	}
 	if (*msn) {
-		*p++ = 0x6c;
+		*p++ = IE_CALLING_PN;
 		*p++ = strlen(msn) + (screen ? 2 : 1);
 		/* Classify as AnyPref. */
 		if (screen) {
@@ -1407,7 +1338,7 @@
 	}
 	if (sub) {
 		*sub++ = '.';
-		*p++ = 0x6d;	/* Calling party subaddress */
+		*p++ = IE_CALLING_SUB;
 		*p++ = strlen(sub) + 2;
 		*p++ = 0x80;	/* NSAP coded */
 		*p++ = 0x50;	/* local IDI format */
@@ -1425,33 +1356,27 @@
 	}
 	
         if (!send_keypad) {      
-	  *p++ = 0x70;
-	  *p++ = strlen(teln) + 1;
-	  /* Classify as AnyPref. */
-	  *p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-	  while (*teln)
-		  *p++ = *teln++ & 0x7f;
-
-	  if (sub) {
-		  *sub++ = '.';
-		  *p++ = 0x71;	/* Called party subaddress */
-		  *p++ = strlen(sub) + 2;
-		  *p++ = 0x80;	/* NSAP coded */
-		  *p++ = 0x50;	/* local IDI format */
-		  while (*sub)
-			  *p++ = *sub++ & 0x7f;
-	  }
+		*p++ = IE_CALLED_PN;
+		*p++ = strlen(teln) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+		
+		if (sub) {
+			*sub++ = '.';
+			*p++ = IE_CALLED_SUB;
+			*p++ = strlen(sub) + 2;
+			*p++ = 0x80;	/* NSAP coded */
+			*p++ = 0x50;	/* local IDI format */
+			while (*sub)
+				*p++ = *sub++ & 0x7f;
+		}
         }
 #if EXT_BEARER_CAPS
-        if (send_keypad) { /* special handling independant of si2 */
-                *p++ = 0x7c;
-                *p++ = 0x03;
-                *p++ = 0x80;
-                *p++ = 0x90;
-                *p++ = 0xa3;
-	} else if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
 
-		*p++ = 0x7c;
+		*p++ = IE_LLC;
 		*p++ = 0x04;
 		*p++ = 0x88;
 		*p++ = 0x90;
@@ -1459,7 +1384,7 @@
 		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
 	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
 
-		*p++ = 0x7c;
+		*p++ = IE_LLC;
 		*p++ = 0x05;
 		*p++ = 0x88;
 		*p++ = 0x90;
@@ -1468,7 +1393,7 @@
 		*p++ = 0x82;
 	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
 
-		*p++ = 0x7c;
+		*p++ = IE_LLC;
 		*p++ = 0x06;
 		*p++ = 0x88;
 		*p++ = 0x90;
@@ -1477,18 +1402,18 @@
 #ifndef CONFIG_HISAX_NO_LLC
 	} else {
 	  switch (pc->para.setup.si1) {
-		case 1:	/* Telephony                               */
-			*p++ = 0x7c;	/* BC-IE-code                              */
-			*p++ = 0x3;	/* Length                                  */
-			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio     */
-			*p++ = 0x90;	/* Circuit-Mode 64kbps                     */
-			*p++ = 0xa3;	/* A-Law Audio                             */
+		case 1:	                /* Telephony                                */
+			*p++ = IE_LLC;
+			*p++ = 0x3;	/* Length                                   */
+			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio         */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			*p++ = 0xa3;	/* A-Law Audio                              */
 			break;
-		case 5:	/* Datatransmission 64k, BTX               */
-		case 7:	/* Datatransmission 64k                    */
+		case 5:	                /* Datatransmission 64k, BTX                */
+		case 7:	                /* Datatransmission 64k                     */
 		default:
-			*p++ = 0x7c;	/* BC-IE-code                              */
-			*p++ = 0x2;	/* Length                                  */
+			*p++ = IE_LLC;
+			*p++ = 0x2;	/* Length                                   */
 			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
 			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
 			break;
@@ -1988,6 +1913,16 @@
 	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
 }
 
+static void
+l3dss1_setup_ack_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 25);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T302, CC_T302);
+	l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
 /********************************************/
 /* deliver a incoming display message to HL */
 /********************************************/
@@ -2025,7 +1960,7 @@
 		if (p[1] != 2) {
 			err = 1;
 			pc->para.cause = 100;
-		} else if (p[2] & 0x60) {
+		} else if (!(p[2] & 0x70)) {
 			switch (p[2]) {
 				case 0x80:
 				case 0x81:
@@ -2129,9 +2064,22 @@
 {
 	int ret;
 	struct sk_buff *skb = arg;
+	u_char *p;
+	char tmp[32];
 
 	ret = check_infoelements(pc, skb, ie_INFORMATION);
-	l3dss1_std_ie_err(pc, ret);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	if (pc->state == 25) { /* overlap receiving */
+		L3DelTimer(&pc->timer);
+		p = skb->data;
+		if ((p = findie(p, skb->len, 0x70, 0))) {
+			iecpy(tmp, p, 1);
+			strcat(pc->para.setup.eazmsn, tmp);
+			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+		}
+		L3AddTimer(&pc->timer, T302, CC_T302);
+	}
 }
 
 /******************************/
@@ -2357,6 +2305,16 @@
 }
 
 static void
+l3dss1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 28; /* invalid number */
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
 l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
 {
 	if (pc->N303 > 0) {
@@ -2375,6 +2333,7 @@
 l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3dss1_disconnect_req(pc, pr, NULL);
 	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
@@ -2414,6 +2373,7 @@
 l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3dss1_disconnect_req(pc, pr, NULL);
 	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
@@ -2423,6 +2383,7 @@
 l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3dss1_disconnect_req(pc, pr, NULL);
 	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
@@ -2812,32 +2773,36 @@
 	 CC_SETUP | REQUEST, l3dss1_setup_req},
 	{SBIT(0),
 	 CC_RESUME | REQUEST, l3dss1_resume_req},
-	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10),
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25),
 	 CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
 	{SBIT(12),
 	 CC_RELEASE | REQUEST, l3dss1_release_req},
 	{ALL_STATES,
 	 CC_RESTART | REQUEST, l3dss1_restart},
-	{SBIT(6),
+	{SBIT(6) | SBIT(25),
 	 CC_IGNORE | REQUEST, l3dss1_reset},
-	{SBIT(6),
+	{SBIT(6) | SBIT(25),
 	 CC_REJECT | REQUEST, l3dss1_reject_req},
-	{SBIT(6),
+	{SBIT(6) | SBIT(25),
 	 CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req},
-	{SBIT(6) | SBIT(9),
+	{SBIT(6),
+	 CC_MORE_INFO | REQUEST, l3dss1_setup_ack_req},
+	{SBIT(25),
+	 CC_MORE_INFO | REQUEST, l3dss1_dummy},
+	{SBIT(6) | SBIT(9) | SBIT(25),
 	 CC_ALERTING | REQUEST, l3dss1_alert_req},
-	{SBIT(6) | SBIT(7) | SBIT(9),
+	{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
 	 CC_SETUP | RESPONSE, l3dss1_setup_rsp},
 	{SBIT(10),
 	 CC_SUSPEND | REQUEST, l3dss1_suspend_req},
-        {SBIT(6),
-         CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req},
-        {SBIT(7) | SBIT(9),
+        {SBIT(7) | SBIT(9) | SBIT(25),
          CC_REDIR | REQUEST, l3dss1_redir_req},
         {SBIT(6),
          CC_REDIR | REQUEST, l3dss1_redir_req_early},
         {SBIT(9) | SBIT(25),
          CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
+	{SBIT(25),
+	 CC_T302, l3dss1_t302},
 	{SBIT(1),
 	 CC_T303, l3dss1_t303},
 	{SBIT(2),
@@ -3180,7 +3145,7 @@
 		if ((proc = dss1_new_l3_process(st, cr))) {
 			proc->chan = chan;
 			chan->proc = proc;
-			proc->para.setup = chan->setup;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
 			proc->callref = cr;
 		}
 	} else {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/l3dss1.h linux.19p1/drivers/isdn/hisax/l3dss1.h
--- linux.vanilla/drivers/isdn/hisax/l3dss1.h	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/l3dss1.h	Wed Dec 13 22:08:13 2000
@@ -1,39 +1,14 @@
-/* $Id: l3dss1.h,v 1.8 2000/01/20 19:46:15 keil Exp $
+/* $Id: l3dss1.h,v 1.10 2000/06/26 08:59:14 keil Exp $
  *
  *  DSS1 (Euro) D-channel protocol defines
  *
- * $Log: l3dss1.h,v $
- * Revision 1.8  2000/01/20 19:46:15  keil
- * Changes from certification
- *
- * Revision 1.7  1999/07/01 08:12:02  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.6  1998/03/19 13:18:50  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 1.5  1998/02/02 13:34:30  keil
- * Support australian Microlink net and german AOCD
- *
- * Revision 1.4  1997/10/29 19:07:54  keil
- * changes for 2.1
- *
- * Revision 1.3  1997/08/07 17:44:37  keil
- * Fix RESTART
- *
- * Revision 1.2  1997/08/03 14:36:34  keil
- * Implement RESTART procedure
- *
- * Revision 1.1  1997/07/27 21:08:38  keil
- * new
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
 #ifndef l3dss1_process
 
+#define T302	15000
 #define T303	4000
 #define T304	30000
 #define T305	30000
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/l3ni1.c linux.19p1/drivers/isdn/hisax/l3ni1.c
--- linux.vanilla/drivers/isdn/hisax/l3ni1.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/l3ni1.c	Wed Dec 13 22:08:13 2000
@@ -0,0 +1,3172 @@
+// $Id: l3ni1.c,v 2.3 2000/06/26 08:59:14 keil Exp $
+//-----------------------------------------------------------------------------
+//
+// NI1 D-channel protocol
+//
+// Author                Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd
+//                                www.traverse.com.au
+//
+// 2000.6.6 Initial implementation of routines for US NI1 
+// Layer 3 protocol based on the EURO/DSS1 D-channel protocol 
+// driver written by Karsten Keil et al.  Thanks also for the 
+// code provided by Ragnar Paulson and Will Scales.
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+//-----------------------------------------------------------------------------
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3ni1.h"
+#include <linux/ctype.h>
+#include <linux/config.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *ni1_revision = "$Revision: 2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	if (cref == -1) { \
+		*ptr++ = 0x0; \
+	} else { \
+		*ptr++ = 0x1; \
+		*ptr++ = cref^0x80; \
+	} \
+	*ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid          */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+	unsigned char retval;
+	int flags,i;
+  
+	i = 32; /* maximum search depth */
+
+	save_flags(flags);
+	cli();
+
+	retval = p->prot.ni1.last_invoke_id + 1; /* try new id */
+	while ((i) && (p->prot.ni1.invoke_used[retval >> 3] == 0xFF)) {
+		p->prot.ni1.last_invoke_id = (retval & 0xF8) + 8;
+		i--;
+	}  
+	if (i) {
+		while (p->prot.ni1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+		retval++; 
+	} else
+		retval = 0;
+	p->prot.ni1.last_invoke_id = retval;
+	p->prot.ni1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+	restore_flags(flags);
+
+	return(retval);  
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{ int flags;
+
+  if (!id) return; /* 0 = invalid value */
+
+  save_flags(flags);
+  cli();
+  p->prot.ni1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+  restore_flags(flags);
+} /* free_invoke_id */  
+
+
+/**********************************************************/
+/* create a new l3 process and fill in ni1 specific data */
+/**********************************************************/
+static struct l3_process
+*ni1_new_l3_process(struct PStack *st, int cr)
+{  struct l3_process *proc;
+
+   if (!(proc = new_l3_process(st, cr))) 
+     return(NULL);
+
+   proc->prot.ni1.invoke_id = 0;
+   proc->prot.ni1.remote_operation = 0;
+   proc->prot.ni1.uus1_data[0] = '\0';
+   
+   return(proc);
+} /* ni1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all ni1 specific data */
+/************************************************/ 
+static void
+ni1_release_l3_process(struct l3_process *p)
+{
+   free_invoke_id(p->st,p->prot.ni1.invoke_id);
+   release_l3_process(p);
+} /* ni1_release_l3_process */
+ 
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3ni1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+  if (!id) return(NULL);
+
+  while (pc)
+   { if ((pc->callref == -1) && (pc->prot.ni1.invoke_id == id))
+       return(pc);
+     pc = pc->next;
+   } 
+  return(NULL);
+} /* l3ni1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id.   */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3ni1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = NI1_STAT_INVOKE_RES;
+     ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+     ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+     ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+     ic.parm.ni1_io.timeout= 0;
+     ic.parm.ni1_io.datalen = nlen;
+     ic.parm.ni1_io.data = p;
+     free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+     pc->prot.ni1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     ni1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3ni1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id.    */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3ni1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = NI1_STAT_INVOKE_ERR;
+     ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+     ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+     ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+     ic.parm.ni1_io.timeout= error;
+     ic.parm.ni1_io.datalen = 0;
+     ic.parm.ni1_io.data = NULL;
+     free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+     pc->prot.ni1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     ni1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3ni1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id.          */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_invoke(struct PStack *st, int cr, int id, 
+                    int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+
+  l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+               (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+  if (cr >= -1) return; /* ignore local data */
+
+  cs = st->l1.hardware;
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = NI1_STAT_INVOKE_BRD;
+  ic.parm.ni1_io.hl_id = id;
+  ic.parm.ni1_io.ll_id = 0;
+  ic.parm.ni1_io.proc = ident;
+  ic.parm.ni1_io.timeout= 0;
+  ic.parm.ni1_io.datalen = nlen;
+  ic.parm.ni1_io.data = p;
+
+  cs->iif.statcallb(&ic);
+} /* l3ni1_dummy_invoke */
+
+static void
+l3ni1_parse_facility(struct PStack *st, struct l3_process *pc,
+                      int cr, u_char * p)
+{
+	int qd_len = 0;
+	unsigned char nlen = 0, ilen, cp_tag;
+	int ident, id;
+	ulong err_ret;
+
+	if (pc) 
+		st = pc->st; /* valid Stack */
+	else
+		if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(st, "qd_len == 0");
+		return;
+	}
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(st, "supplementary service != 0x11");
+		return;
+	}
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
+		l3_debug(st, "qd_len < 2");
+		return;
+	}
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(st, "class and form != 0xA0");
+		return;
+	}
+       
+        cp_tag = *p & 0x1F; /* remember tag value */
+
+        p++;
+	qd_len--;
+	if (qd_len < 1) 
+          { l3_debug(st, "qd_len < 1");
+	    return;
+	  }
+	if (*p & 0x80) 
+          { /* length format indefinite or limited */
+	    nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+            if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+                (nlen > 1))   
+	     { l3_debug(st, "length format error or not implemented");
+	       return;
+             }
+            if (nlen == 1)
+	     { nlen = *p++; /* complete length */
+               qd_len--;
+             } 
+            else
+	     { qd_len -= 2; /* trailing null bytes */
+               if ((*(p+qd_len)) || (*(p+qd_len+1)))
+		{ l3_debug(st,"length format indefinite error");
+                  return;
+                }
+               nlen = qd_len;
+             }
+	  }
+        else
+	  { nlen = *p++;
+	    qd_len--;
+          } 
+	if (qd_len < nlen) 
+          { l3_debug(st, "qd_len < nlen");
+	    return;
+	  }
+	qd_len -= nlen;
+
+	if (nlen < 2) 
+          { l3_debug(st, "nlen < 2");
+	    return;
+	  }
+        if (*p != 0x02) 
+          {  /* invoke identifier tag */
+	     l3_debug(st, "invoke identifier tag !=0x02");
+	     return;
+	  }
+	p++;
+	nlen--;
+	if (*p & 0x80) 
+          { /* length format */
+	    l3_debug(st, "invoke id length format 2");
+	    return;
+	  }
+	ilen = *p++;
+	nlen--;
+	if (ilen > nlen || ilen == 0) 
+          { l3_debug(st, "ilen > nlen || ilen == 0");
+	    return;
+	  }
+	nlen -= ilen;
+	id = 0;
+	while (ilen > 0) 
+          { id = (id << 8) | (*p++ & 0xFF);	/* invoke identifier */
+	    ilen--;
+	  }
+
+	switch (cp_tag) {	/* component tag */
+		case 1:	/* invoke */
+				if (nlen < 2) {
+					l3_debug(st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+				if (!pc) 
+				{
+					l3ni1_dummy_invoke(st, cr, id, ident, p, nlen);
+					return;
+				} 
+				l3_debug(st, "invoke break");
+				break;
+		case 2:	/* return result */
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1) 
+                             l3ni1_dummy_return_result(st, id, p, nlen);
+                           return; 
+                         }   
+                        if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+                          { /* Diversion successfull */
+                            free_invoke_id(st,pc->prot.ni1.invoke_id);
+                            pc->prot.ni1.remote_result = 0; /* success */     
+                            pc->prot.ni1.invoke_id = 0;
+                            pc->redir_result = pc->prot.ni1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successfull */
+                        else
+                          l3_debug(st,"return error unknown identifier");
+			break;
+		case 3:	/* return error */
+                            err_ret = 0;
+	                    if (nlen < 2) 
+                              { l3_debug(st, "return error nlen < 2");
+	                        return;
+	                      }
+                            if (*p != 0x02) 
+                              { /* result tag */
+	                        l3_debug(st, "invoke error tag !=0x02");
+	                        return;
+	                      }
+	                    p++;
+	                    nlen--;
+	                    if (*p > 4) 
+                              { /* length format */
+	                        l3_debug(st, "invoke return errlen > 4 ");
+	                        return;
+	                      }
+	                    ilen = *p++;
+	                    nlen--;
+	                    if (ilen > nlen || ilen == 0) 
+                              { l3_debug(st, "error return ilen > nlen || ilen == 0");
+	                        return;
+	                       }
+	                    nlen -= ilen;
+	                    while (ilen > 0) 
+                             { err_ret = (err_ret << 8) | (*p++ & 0xFF);	/* error value */
+	                       ilen--;
+	                     }
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1)
+                             l3ni1_dummy_error_return(st, id, err_ret);
+                           return; 
+                         }   
+                        if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+                          { /* Deflection error */
+                            free_invoke_id(st,pc->prot.ni1.invoke_id);
+                            pc->prot.ni1.remote_result = err_ret; /* result */
+                            pc->prot.ni1.invoke_id = 0; 
+                            pc->redir_result = pc->prot.ni1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                          } /* Deflection error */
+                        else
+                          l3_debug(st,"return result unknown identifier");
+			break;
+		default:
+			l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+			break;
+	}
+}
+
+static void
+l3ni1_message(struct l3_process *pc, u_char mt)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = pc->para.cause | 0x80;
+
+	*p++ = IE_CALL_STATE;
+	*p++ = 0x1;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	/* This routine is called if here was no SETUP made (checks in ni1up and in
+	 * l3ni1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+	 * MT_STATUS_ENQUIRE in the NULL state is handled too
+	 */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+		case 81:	/* invalid callreference */
+		case 88:	/* incomp destination */
+		case 96:	/* mandory IE missing */
+		case 100:       /* invalid IE contents */
+		case 101:	/* incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax l3ni1_msg_without_setup wrong cause %d\n",
+				pc->para.cause);
+			return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	ni1_release_l3_process(pc);
+}
+
+static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC,
+		IE_USER_USER, -1};
+static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1};
+static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, 
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL,
+		IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1};
+static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL,
+		IE_CALLED_PN, -1};
+static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS |
+		IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
+static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY,
+		IE_SIGNAL, IE_USER_USER, -1};
+/* a RELEASE_COMPLETE with errors don't require special actions 
+static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+*/
+static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY,
+		IE_DISPLAY, -1};
+static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER  | IE_MANDATORY,
+		IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
+		IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN,
+		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR,
+		IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1};
+static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
+		IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1};
+static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_DISPLAY, IE_FACILITY, -1};
+static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+/* not used 
+ * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY,
+ *		IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+ * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1};
+ * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND |
+ *		IE_MANDATORY, -1};
+ */
+static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1};
+static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1};
+static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1};
+
+struct ie_len {
+	int ie;
+	int len;
+};
+
+static
+struct ie_len max_ie_len[] = {
+	{IE_SEGMENT, 4},
+	{IE_BEARER, 12},
+	{IE_CAUSE, 32},
+	{IE_CALL_ID, 10},
+	{IE_CALL_STATE, 3},
+	{IE_CHANNEL_ID,	34},
+	{IE_FACILITY, 255},
+	{IE_PROGRESS, 4},
+	{IE_NET_FAC, 255},
+	{IE_NOTIFY, 3},
+	{IE_DISPLAY, 82},
+	{IE_DATE, 8},
+	{IE_KEYPAD, 34},
+	{IE_SIGNAL, 3},
+	{IE_INFORATE, 6},
+	{IE_E2E_TDELAY, 11},
+	{IE_TDELAY_SEL, 5},
+	{IE_PACK_BINPARA, 3},
+	{IE_PACK_WINSIZE, 4},
+	{IE_PACK_SIZE, 4},
+	{IE_CUG, 7},
+	{IE_REV_CHARGE, 3},
+	{IE_CALLING_PN, 24},
+	{IE_CALLING_SUB, 23},
+	{IE_CALLED_PN, 24},
+	{IE_CALLED_SUB, 23},
+	{IE_REDIR_NR, 255},
+	{IE_TRANS_SEL, 255},
+	{IE_RESTART_IND, 3},
+	{IE_LLC, 18},
+	{IE_HLC, 5},
+	{IE_USER_USER, 131},
+	{-1,0},
+};
+
+static int
+getmax_ie_len(u_char ie) {
+	int i = 0;
+	while (max_ie_len[i].ie != -1) {
+		if (max_ie_len[i].ie == ie)
+			return(max_ie_len[i].len);
+		i++;
+	}
+	return(255);
+}
+
+static int
+ie_in_set(struct l3_process *pc, u_char ie, int *checklist) {
+	int ret = 1;
+
+	while (*checklist != -1) {
+		if ((*checklist & 0xff) == ie) {
+			if (ie & 0x80)
+				return(-ret);
+			else
+				return(ret);
+		}
+		ret++;
+		checklist++;
+	}
+	return(0);
+}
+
+static int
+check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+	int *cl = checklist;
+	u_char mt;
+	u_char *p, ie;
+	int l, newpos, oldpos;
+	int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+	u_char codeset = 0;
+	u_char old_codeset = 0;
+	u_char codelock = 1;
+	
+	p = skb->data;
+	/* skip cr */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	mt = *p++;
+	oldpos = 0;
+	while ((p - skb->data) < skb->len) {
+		if ((*p & 0xf0) == 0x90) { /* shift codeset */
+			old_codeset = codeset;
+			codeset = *p & 7;
+			if (*p & 0x08)
+				codelock = 0;
+			else
+				codelock = 1;
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+					codelock ? " locking ": " ", old_codeset, codeset);
+			p++;
+			continue;
+		}
+		if (!codeset) { /* only codeset 0 */
+			if ((newpos = ie_in_set(pc, *p, cl))) {
+				if (newpos > 0) {
+					if (newpos < oldpos)
+						err_seq++;
+					else
+						oldpos = newpos;
+				}
+			} else {
+				if (ie_in_set(pc, *p, comp_required))
+					err_compr++;
+				else
+					err_ureg++;
+			}
+		}
+		ie = *p++;
+		if (ie & 0x80) {
+			l = 1;
+		} else {
+			l = *p++;
+			p += l;
+			l += 2;
+		}
+		if (!codeset && (l > getmax_ie_len(ie)))
+			err_len++;
+		if (!codelock) {
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift back codeset %d->%d",
+					codeset, old_codeset);
+			codeset = old_codeset;
+			codelock = 1;
+		}
+	}
+	if (err_compr | err_ureg | err_len | err_seq) {
+		if (pc->debug & L3_DEB_CHECK)
+			l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+				mt, err_compr, err_ureg, err_len, err_seq);
+		if (err_compr)
+			return(ERR_IE_COMPREHENSION);
+		if (err_ureg)
+			return(ERR_IE_UNRECOGNIZED);
+		if (err_len)
+			return(ERR_IE_LENGTH);
+		if (err_seq)
+			return(ERR_IE_SEQUENCE);
+	} 
+	return(0);
+}
+
+/* verify if a message type exists and contain no IE error */
+static int
+l3ni1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg)
+{
+	switch (mt) {
+		case MT_ALERTING:
+		case MT_CALL_PROCEEDING:
+		case MT_CONNECT:
+		case MT_CONNECT_ACKNOWLEDGE:
+		case MT_DISCONNECT:
+		case MT_INFORMATION:
+		case MT_FACILITY:
+		case MT_NOTIFY:
+		case MT_PROGRESS:
+		case MT_RELEASE:
+		case MT_RELEASE_COMPLETE:
+		case MT_SETUP:
+		case MT_SETUP_ACKNOWLEDGE:
+		case MT_RESUME_ACKNOWLEDGE:
+		case MT_RESUME_REJECT:
+		case MT_SUSPEND_ACKNOWLEDGE:
+		case MT_SUSPEND_REJECT:
+		case MT_USER_INFORMATION:
+		case MT_RESTART:
+		case MT_RESTART_ACKNOWLEDGE:
+		case MT_CONGESTION_CONTROL:
+		case MT_STATUS:
+		case MT_STATUS_ENQUIRY:
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "l3ni1_check_messagetype_validity mt(%x) OK", mt);
+			break;
+		case MT_RESUME: /* RESUME only in user->net */
+		case MT_SUSPEND: /* SUSPEND only in user->net */
+		default:
+			if (pc->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+				l3_debug(pc->st, "l3ni1_check_messagetype_validity mt(%x) fail", mt);
+			pc->para.cause = 97;
+			l3ni1_status_send(pc, 0, NULL);
+			return(1);
+	}
+	return(0);
+}
+
+static void
+l3ni1_std_ie_err(struct l3_process *pc, int ret) {
+
+	if (pc->debug & L3_DEB_CHECK)
+		l3_debug(pc->st, "check_infoelements ret %d", ret);
+	switch(ret) {
+		case 0: 
+			break;
+		case ERR_IE_COMPREHENSION:
+			pc->para.cause = 96;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_UNRECOGNIZED:
+			pc->para.cause = 99;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_LENGTH:
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_SEQUENCE:
+		default:
+			break;
+	}
+}
+
+static int
+l3ni1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		p++;
+		if (*p != 1) { /* len for BRI = 1 */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid len %d", *p);
+			return (-2);
+		}
+		p++;
+		if (*p & 0x60) { /* only base rate interface */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid %x", *p);
+			return (-3);
+		}
+		return(*p & 0x3);
+	} else
+		return(-1);
+}
+
+static int
+l3ni1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+	u_char l, i=0;
+	u_char *p;
+
+	p = skb->data;
+	pc->para.cause = 31;
+	pc->para.loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		if (l>30)
+			return(1);
+		if (l) {
+			pc->para.loc = *p++;
+			l--;
+		} else {
+			return(2);
+		}
+		if (l && !(pc->para.loc & 0x80)) {
+			l--;
+			p++; /* skip recommendation */
+		}
+		if (l) {
+			pc->para.cause = *p++;
+			l--;
+			if (!(pc->para.cause & 0x80))
+				return(3);
+		} else
+			return(4);
+		while (l && (i<6)) {
+			pc->para.diag[i++] = *p++;
+			l--;
+		}
+	} else
+		return(-1);
+	return(0);
+}
+
+static void
+l3ni1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, cmd);
+
+        if (pc->prot.ni1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.ni1.uus1_data);
+           p += strlen(pc->prot.ni1.uus1_data);
+           pc->prot.ni1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_msg_with_uus */
+
+static void
+l3ni1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	if (!pc->prot.ni1.uus1_data[0]) 
+		l3ni1_message(pc, MT_RELEASE);
+	else
+		l3ni1_msg_with_uus(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+	} else if (ret < 0)
+		pc->para.cause = NO_CAUSE;
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	ni1_release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+static u_char *
+EncodeASyncParams(u_char * p, u_char si2)
+{				// 7c 06 88  90 21 42 00 bb
+
+	p[0] = 0;
+	p[1] = 0x40;		// Intermediate rate: 16 kbit/s jj 2000.02.19
+	p[2] = 0x80;
+	if (si2 & 32)		// 7 data bits
+
+		p[2] += 16;
+	else			// 8 data bits
+
+		p[2] += 24;
+
+	if (si2 & 16)		// 2 stop bits
+
+		p[2] += 96;
+	else			// 1 stop bit
+
+		p[2] += 32;
+
+	if (si2 & 8)		// even parity
+
+		p[2] += 2;
+	else			// no parity
+
+		p[2] += 3;
+
+	switch (si2 & 0x07) {
+		case 0:
+			p[0] = 66;	// 1200 bit/s
+
+			break;
+		case 1:
+			p[0] = 88;	// 1200/75 bit/s
+
+			break;
+		case 2:
+			p[0] = 87;	// 75/1200 bit/s
+
+			break;
+		case 3:
+			p[0] = 67;	// 2400 bit/s
+
+			break;
+		case 4:
+			p[0] = 69;	// 4800 bit/s
+
+			break;
+		case 5:
+			p[0] = 72;	// 9600 bit/s
+
+			break;
+		case 6:
+			p[0] = 73;	// 14400 bit/s
+
+			break;
+		case 7:
+			p[0] = 75;	// 19200 bit/s
+
+			break;
+	}
+	return p + 3;
+}
+
+static u_char
+EncodeSyncParams(u_char si2, u_char ai)
+{
+
+	switch (si2) {
+		case 0:
+			return ai + 2;	// 1200 bit/s
+
+		case 1:
+			return ai + 24;		// 1200/75 bit/s
+
+		case 2:
+			return ai + 23;		// 75/1200 bit/s
+
+		case 3:
+			return ai + 3;	// 2400 bit/s
+
+		case 4:
+			return ai + 5;	// 4800 bit/s
+
+		case 5:
+			return ai + 8;	// 9600 bit/s
+
+		case 6:
+			return ai + 9;	// 14400 bit/s
+
+		case 7:
+			return ai + 11;		// 19200 bit/s
+
+		case 8:
+			return ai + 14;		// 48000 bit/s
+
+		case 9:
+			return ai + 15;		// 56000 bit/s
+
+		case 15:
+			return ai + 40;		// negotiate bit/s
+
+		default:
+			break;
+	}
+	return ai;
+}
+
+
+static u_char
+DecodeASyncParams(u_char si2, u_char * p)
+{
+	u_char info;
+
+	switch (p[5]) {
+		case 66:	// 1200 bit/s
+
+			break;	// si2 don't change
+
+		case 88:	// 1200/75 bit/s
+
+			si2 += 1;
+			break;
+		case 87:	// 75/1200 bit/s
+
+			si2 += 2;
+			break;
+		case 67:	// 2400 bit/s
+
+			si2 += 3;
+			break;
+		case 69:	// 4800 bit/s
+
+			si2 += 4;
+			break;
+		case 72:	// 9600 bit/s
+
+			si2 += 5;
+			break;
+		case 73:	// 14400 bit/s
+
+			si2 += 6;
+			break;
+		case 75:	// 19200 bit/s
+
+			si2 += 7;
+			break;
+	}
+
+	info = p[7] & 0x7f;
+	if ((info & 16) && (!(info & 8)))	// 7 data bits
+
+		si2 += 32;	// else 8 data bits
+
+	if ((info & 96) == 96)	// 2 stop bits
+
+		si2 += 16;	// else 1 stop bit
+
+	if ((info & 2) && (!(info & 1)))	// even parity
+
+		si2 += 8;	// else no parity
+
+	return si2;
+}
+
+
+static u_char
+DecodeSyncParams(u_char si2, u_char info)
+{
+	info &= 0x7f;
+	switch (info) {
+		case 40:	// bit/s negotiation failed  ai := 165 not 175!
+
+			return si2 + 15;
+		case 15:	// 56000 bit/s failed, ai := 0 not 169 !
+
+			return si2 + 9;
+		case 14:	// 48000 bit/s
+
+			return si2 + 8;
+		case 11:	// 19200 bit/s
+
+			return si2 + 7;
+		case 9:	// 14400 bit/s
+
+			return si2 + 6;
+		case 8:	// 9600  bit/s
+
+			return si2 + 5;
+		case 5:	// 4800  bit/s
+
+			return si2 + 4;
+		case 3:	// 2400  bit/s
+
+			return si2 + 3;
+		case 23:	// 75/1200 bit/s
+
+			return si2 + 2;
+		case 24:	// 1200/75 bit/s
+
+			return si2 + 1;
+		default:	// 1200 bit/s
+
+			return si2;
+	}
+}
+
+static u_char
+DecodeSI2(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		switch (p[4] & 0x0f) {
+			case 0x01:
+				if (p[1] == 0x04)	// sync. Bitratenadaption
+
+					return DecodeSyncParams(160, p[5]);	// V.110/X.30
+
+				else if (p[1] == 0x06)	// async. Bitratenadaption
+
+					return DecodeASyncParams(192, p);	// V.110/X.30
+
+				break;
+			case 0x08:	// if (p[5] == 0x02) // sync. Bitratenadaption
+				if (p[1] > 3) 
+					return DecodeSyncParams(176, p[5]);	// V.120
+				break;
+		}
+	}
+	return 0;
+}
+
+#endif
+
+
+static void
+l3ni1_setup_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+
+	u_char *teln;
+	u_char *sub;
+	u_char *sp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_SETUP);
+
+	teln = pc->para.setup.phone;
+
+	*p++ = 0xa1;		/* complete indicator */
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to NI1
+	 */
+	switch (pc->para.setup.si1) {
+	case 1:	                  /* Telephony                                */
+		*p++ = IE_BEARER;
+		*p++ = 0x3;	  /* Length                                   */
+		*p++ = 0x90;	  /* Coding Std. CCITT, 3.1 kHz audio         */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		*p++ = 0xa3;	  /* A-Law Audio                              */
+		break;
+	case 5:	                  /* Datatransmission 64k, BTX                */
+	case 7:	                  /* Datatransmission 64k                     */
+	default:
+		*p++ = IE_BEARER;
+		*p++ = 0x2;	  /* Length                                   */
+		*p++ = 0x88;	  /* Coding Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		break;
+	}
+
+	sub = NULL;
+	sp = teln;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	
+	*p++ = IE_KEYPAD;
+	*p++ = strlen(teln);
+	while (*teln)
+		*p++ = (*teln++) & 0x7F;
+
+	if (sub)
+		*sub++ = '.';
+	
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = IE_LLC;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+	} else {
+	  switch (pc->para.setup.si1) {
+		case 1:	                /* Telephony                                */
+			*p++ = IE_LLC;
+			*p++ = 0x3;	/* Length                                   */
+			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio         */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			*p++ = 0xa3;	/* A-Law Audio                              */
+			break;
+		case 5:	                /* Datatransmission 64k, BTX                */
+		case 7:	                /* Datatransmission 64k                     */
+		default:
+			*p++ = IE_LLC;
+			*p++ = 0x2;	/* Length                                   */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	  }
+	}
+#endif
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+{
+		return;
+}
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 3);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 2);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3ni1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret;
+	u_char cause = 0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	} 
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+		l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	ret = check_infoelements(pc, skb, ie_DISCONNECT);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+		cause = 99;
+	ret = pc->state;
+	newl3state(pc, 12);
+	if (cause)
+		newl3state(pc, 19);
+       	if (11 != ret)
+		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+       	else if (!cause)
+		   l3ni1_release_req(pc, pr, NULL);
+	if (cause) {
+		l3ni1_message_cause(pc, MT_RELEASE, cause);
+		L3AddTimer(&pc->timer, T308, CC_T308_1);
+	}
+}
+
+static void
+l3ni1_connect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_CONNECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	/* here should inserted COLP handling KKe */
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3ni1_alerting(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_ALERTING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+	int id;
+	int err = 0;
+
+	/*
+	 * Bearer Capabilities
+	 */
+	p = skb->data;
+	/* only the first occurence 'll be detected ! */
+	if ((p = findie(p, skb->len, 0x04, 0))) {
+		if ((p[1] < 2) || (p[1] > 11))
+			err = 1;
+		else {
+			pc->para.setup.si2 = 0;
+			switch (p[2] & 0x7f) {
+				case 0x00: /* Speech */
+				case 0x10: /* 3.1 Khz audio */
+					pc->para.setup.si1 = 1;
+					break;
+				case 0x08: /* Unrestricted digital information */
+					pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+					pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+					break;
+				case 0x09: /* Restricted digital information */
+					pc->para.setup.si1 = 2;
+					break;
+				case 0x11:
+					/* Unrestr. digital information  with 
+					 * tones/announcements ( or 7 kHz audio
+					 */
+					pc->para.setup.si1 = 3;
+					break;
+				case 0x18: /* Video */
+					pc->para.setup.si1 = 4;
+					break;
+				default:
+					err = 2;
+					break;
+			}
+			switch (p[3] & 0x7f) {
+				case 0x40: /* packed mode */
+					pc->para.setup.si1 = 8;
+					break;
+				case 0x10: /* 64 kbit */
+				case 0x11: /* 2*64 kbit */
+				case 0x13: /* 384 kbit */
+				case 0x15: /* 1536 kbit */
+				case 0x17: /* 1920 kbit */
+					pc->para.moderate = p[3] & 0x7f;
+					break;
+				default:
+					err = 3;
+					break;
+			}
+		}
+		if (pc->debug & L3_DEB_SI)
+			l3_debug(pc->st, "SI=%d, AI=%d",
+				pc->para.setup.si1, pc->para.setup.si2);
+		if (err) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+					p[1], p[2], p[3]);
+			pc->para.cause = 100;
+			l3ni1_msg_without_setup(pc, pr, NULL);
+			return;
+		}
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+		pc->para.cause = 96;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/*
+	 * Channel Identification
+	 */
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((pc->para.bchannel = id)) {
+			if ((3 == id) && (0x10 == pc->para.moderate)) {
+				if (pc->debug & L3_DEB_WARN)
+					l3_debug(pc->st, "setup with wrong chid %x",
+						id);
+				pc->para.cause = 100;
+				l3ni1_msg_without_setup(pc, pr, NULL);
+				return;
+			}
+			bcfound++;
+		} else 
+                   { if (pc->debug & L3_DEB_WARN)
+			 l3_debug(pc->st, "setup without bchannel, call waiting");
+                     bcfound++;
+                   } 
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup with wrong chid ret %d", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_SETUP);
+	if (ERR_IE_COMPREHENSION == err) {
+		pc->para.cause = 96;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x70, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6c, 0))) {
+		pc->para.setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
+		} else {
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
+		}
+	} else {
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
+	}
+	newl3state(pc, 6);
+	if (err) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, err);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3ni1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 16;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	StopAllL3Timer(pc);
+
+	MsgHead(p, pc->callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+        if (pc->prot.ni1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.ni1.uus1_data);
+           p += strlen(pc->prot.ni1.uus1_data);
+           pc->prot.ni1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 11);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3ni1_setup_rsp(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+        if (!pc->para.bchannel) 
+	 { if (pc->debug & L3_DEB_WARN)
+	       l3_debug(pc->st, "D-chan connect for waiting call");
+           l3ni1_disconnect_req(pc, pr, arg);
+           return;
+         }
+	newl3state(pc, 8);
+	l3ni1_message(pc, MT_CONNECT);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3ni1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3ni1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 21;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret, cause=0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3ni1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+	} else if (ret<0)
+		pc->para.cause = NO_CAUSE;
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+		l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+	if ((ret<0) && (pc->state != 11))
+		cause = 96;
+	else if (ret>0)
+		cause = 100;
+	ret = check_infoelements(pc, skb, ie_RELEASE);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+		cause = 99;  
+	if (cause)
+		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+	else
+		l3ni1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_alert_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	newl3state(pc, 7);
+	if (!pc->prot.ni1.uus1_data[0]) 
+		l3ni1_message(pc, MT_ALERTING);
+	else
+		l3ni1_msg_with_uus(pc, MT_ALERTING); 
+}
+
+static void
+l3ni1_proceed_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 9);
+	l3ni1_message(pc, MT_CALL_PROCEEDING);
+	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+}
+
+static void
+l3ni1_setup_ack_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 25);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T302, CC_T302);
+	l3ni1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3ni1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{       u_char len;
+        isdn_ctrl ic; 
+	struct IsdnCardState *cs;
+        char *p; 
+
+        if (*infp++ != IE_DISPLAY) return;
+        if ((len = *infp++) > 80) return; /* total length <= 82 */
+	if (!pc->chan) return;
+
+	p = ic.parm.display; 
+        while (len--)
+	  *p++ = *infp++;
+	*p = '\0';
+	ic.command = ISDN_STAT_DISPLAY;
+	cs = pc->st->l1.hardware;
+	ic.driver = cs->myid;
+	ic.arg = pc->chan->chan; 
+	cs->iif.statcallb(&ic);
+} /* l3ni1_deliver_display */
+
+
+static void
+l3ni1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+		if (p[1] != 2) {
+			err = 1;
+			pc->para.cause = 100;
+		} else if (!(p[2] & 0x70)) {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+				case 0x84:
+				case 0x85:
+				case 0x87:
+				case 0x8a:
+					switch (p[3]) {
+						case 0x81:
+						case 0x82:
+						case 0x83:
+						case 0x84:
+						case 0x88:
+							break;
+						default:
+							err = 2;
+							pc->para.cause = 100;
+							break;
+					}
+					break;
+				default:
+					err = 3;
+					pc->para.cause = 100;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 4;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "progress error %d", err);
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_PROGRESS);
+	if (err)
+		l3ni1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3ni1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+		if (p[1] != 1) {
+			err = 1;
+			pc->para.cause = 100;
+		} else {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+					break;
+				default:
+					pc->para.cause = 100;
+					err = 2;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 3;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "notify error %d", err);
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_NOTIFY);
+	if (err)
+		l3ni1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3ni1_status_enq(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+
+	ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
+	l3ni1_std_ie_err(pc, ret);
+	pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+        l3ni1_status_send(pc, pr, NULL);
+}
+
+static void
+l3ni1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+	u_char *p;
+	char tmp[32];
+
+	ret = check_infoelements(pc, skb, ie_INFORMATION);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	if (pc->state == 25) { /* overlap receiving */
+		L3DelTimer(&pc->timer);
+		p = skb->data;
+		if ((p = findie(p, skb->len, 0x70, 0))) {
+			iecpy(tmp, p, 1);
+			strcat(pc->para.setup.eazmsn, tmp);
+			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+		}
+		L3AddTimer(&pc->timer, T302, CC_T302);
+	}
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3ni1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+        u_char *subp;
+        u_char len_phone = 0;
+        u_char len_sub = 0;
+	int l; 
+
+
+        strcpy(pc->prot.ni1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+        if (!pc->chan->setup.phone[0])
+          { pc->para.cause = -1;
+            l3ni1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+            return;
+          } /* only uus */
+ 
+        if (pc->prot.ni1.invoke_id) 
+          free_invoke_id(pc->st,pc->prot.ni1.invoke_id);
+ 
+        if (!(pc->prot.ni1.invoke_id = new_invoke_id(pc->st))) 
+          return;
+
+        MsgHead(p, pc->callref, MT_FACILITY);
+
+        for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+        if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subadress element */ 
+
+	*p++ = 0x1c;   /* Facility info element */
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+        *p++ = 0x91;  /* remote operations protocol */
+        *p++ = 0xa1;  /* invoke component */
+	  
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+        *p++ = 0x02;  /* invoke id tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = pc->prot.ni1.invoke_id;  /* invoke id */ 
+        *p++ = 0x02;  /* operation value tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = 0x0D;  /* Call Deflect */
+	  
+        *p++ = 0x30;  /* sequence phone number */
+        *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+	  
+        *p++ = 0x30;  /* Deflected to UserNumber */
+        *p++ = len_phone+2+len_sub; /* length */
+        *p++ = 0x80; /* NumberDigits */
+	*p++ = len_phone; /* length */
+        for (l = 0; l < len_phone; l++)
+	 *p++ = pc->chan->setup.phone[l];
+
+        if (len_sub)
+	  { *p++ = 0x04; /* called party subadress */
+            *p++ = len_sub - 2;
+            while (*subp) *p++ = *subp++;
+          }
+
+        *p++ = 0x01; /* screening identifier */
+        *p++ = 0x01;
+        *p++ = pc->chan->setup.screen;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l))) return;
+	memcpy(skb_put(skb, l), tmp, l);
+
+        l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3ni1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+  l3ni1_proceed_req(pc,pr,arg);
+  l3ni1_redir_req(pc,pr,arg);
+} /* l3ni1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol.  */
+/* Examples are call independant services like */
+/* remote operations with dummy  callref.      */
+/***********************************************/
+static int l3ni1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+  u_char temp[265];
+  u_char *p = temp;
+  int i, l, proc_len; 
+  struct sk_buff *skb;
+  struct l3_process *pc = NULL;
+
+  switch (ic->arg)
+   { case NI1_CMD_INVOKE:
+       if (ic->parm.ni1_io.datalen < 0) return(-2); /* invalid parameter */ 
+
+       for (proc_len = 1, i = ic->parm.ni1_io.proc >> 8; i; i++) 
+         i = i >> 8; /* add one byte */    
+       l = ic->parm.ni1_io.datalen + proc_len + 8; /* length excluding ie header */
+       if (l > 255) 
+         return(-2); /* too long */
+
+       if (!(id = new_invoke_id(st))) 
+         return(0); /* first get a invoke id -> return if no available */
+       
+       i = -1; 
+       MsgHead(p, i, MT_FACILITY); /* build message head */
+       *p++ = 0x1C; /* Facility IE */
+       *p++ = l; /* length of ie */
+       *p++ = 0x91; /* remote operations */
+       *p++ = 0xA1; /* invoke */
+       *p++ = l - 3; /* length of invoke */
+       *p++ = 0x02; /* invoke id tag */
+       *p++ = 0x01; /* length is 1 */
+       *p++ = id; /* invoke id */
+       *p++ = 0x02; /* operation */
+       *p++ = proc_len; /* length of operation */
+       
+       for (i = proc_len; i; i--)
+         *p++ = (ic->parm.ni1_io.proc >> (i-1)) & 0xFF;
+       memcpy(p, ic->parm.ni1_io.data, ic->parm.ni1_io.datalen); /* copy data */
+       l = (p - temp) + ic->parm.ni1_io.datalen; /* total length */         
+
+       if (ic->parm.ni1_io.timeout > 0)
+        if (!(pc = ni1_new_l3_process(st, -1)))
+          { free_invoke_id(st, id);
+            return(-2);
+          } 
+       pc->prot.ni1.ll_id = ic->parm.ni1_io.ll_id; /* remember id */ 
+       pc->prot.ni1.proc = ic->parm.ni1_io.proc; /* and procedure */
+
+       if (!(skb = l3_alloc_skb(l))) 
+         { free_invoke_id(st, id);
+           if (pc) ni1_release_l3_process(pc);
+           return(-2);
+         }
+       memcpy(skb_put(skb, l), temp, l);
+       
+       if (pc)
+        { pc->prot.ni1.invoke_id = id; /* remember id */
+          L3AddTimer(&pc->timer, ic->parm.ni1_io.timeout, CC_TNI1_IO | REQUEST);
+        }
+       
+       l3_msg(st, DL_DATA | REQUEST, skb);
+       ic->parm.ni1_io.hl_id = id; /* return id */
+       return(0);
+
+     case NI1_CMD_INVOKE_ABORT:
+       if ((pc = l3ni1_search_dummy_proc(st, ic->parm.ni1_io.hl_id)))
+	{ L3DelTimer(&pc->timer); /* remove timer */
+          ni1_release_l3_process(pc);
+          return(0); 
+        } 
+       else
+	{ l3_debug(st, "l3ni1_cmd_global abort unknown id");
+          return(-2);
+        } 
+       break;
+    
+     default: 
+       l3_debug(st, "l3ni1_cmd_global unknown cmd 0x%lx", ic->arg);
+       return(-1);  
+   } /* switch ic-> arg */
+  return(-1);
+} /* l3ni1_cmd_global */
+
+static void 
+l3ni1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs = pc->st->l1.hardware;
+
+  L3DelTimer(&pc->timer); /* remove timer */
+
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = NI1_STAT_INVOKE_ERR;
+  ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+  ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+  ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+  ic.parm.ni1_io.timeout= -1;
+  ic.parm.ni1_io.datalen = 0;
+  ic.parm.ni1_io.data = NULL;
+  free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+  pc->prot.ni1.invoke_id = 0; /* reset id */
+
+  cs->iif.statcallb(&ic);
+
+  ni1_release_l3_process(pc); 
+} /* l3ni1_io_timer */
+
+static void
+l3ni1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++)
+			callState = *p;
+	}
+	if (callState == 0) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
+		 * set down layer 3 without sending any message
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		ni1_release_l3_process(pc);
+	} else {
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+	}
+}
+
+static void
+l3ni1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3ni1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 28; /* invalid number */
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3ni1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		ni1_release_l3_process(pc);
+	}
+}
+
+static void
+l3ni1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3ni1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+	u_char cause = 16;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 19);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3ni1_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 19);
+	L3DelTimer(&pc->timer);
+	l3ni1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+}
+
+static void
+l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3ni1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3ni1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int ret; 
+	u_char cause = 0, callState = 0;
+	
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	}
+	if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++) {
+			callState = *p;
+			if (!ie_in_set(pc, *p, l3_valid_states))
+				cause = 100;
+		} else
+			cause = 100;
+	} else
+		cause = 96;
+	if (!cause) { /*  no error before */
+		ret = check_infoelements(pc, skb, ie_STATUS);
+		if (ERR_IE_COMPREHENSION == ret)
+			cause = 96;
+		else if (ERR_IE_UNRECOGNIZED == ret)
+			cause = 99;
+	}
+	if (cause) {
+		u_char tmp;
+		
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+		tmp = pc->para.cause;
+		pc->para.cause = cause;
+		l3ni1_status_send(pc, 0, NULL);
+		if (cause == 99)
+			pc->para.cause = tmp;
+		else
+			return;
+	}
+	cause = pc->para.cause;
+	if (((cause & 0x7f) == 111) && (callState == 0)) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
+		 * if received MT_STATUS with cause == 111 and call
+		 * state == 0, then we must set down layer 3
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		ni1_release_l3_process(pc);
+	}
+}
+
+static void
+l3ni1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+	
+	ret = check_infoelements(pc, skb, ie_FACILITY);
+	l3ni1_std_ie_err(pc, ret);
+ 	  {
+		u_char *p;
+		if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+			l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+}
+
+static void
+l3ni1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3ni1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	/* We don't handle suspend_ack for IE errors now */
+	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3ni1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) > 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "resume ack with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+		pc->para.cause = 96;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 0);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, ch = 0, chan = 0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
+	} else {
+		l3_debug(pc->st, "Restart without restart IE");
+		ri = 0x86;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
+	}
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		else if (up->para.bchannel == chan)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = ch | 0x80;
+	}
+	*p++ = 0x79;		/* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = 0x29;          /* Temporary failure */
+        pc->para.loc = 0;
+        l3ni1_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+static void
+l3ni1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+        L3DelTimer(&pc->timer);
+        L3AddTimer(&pc->timer, T309, CC_T309);
+        l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+ 
+static void
+l3ni1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+ 
+ 	pc->para.cause = 0x1F; /* normal, unspecified */
+	l3ni1_status_send(pc, 0, NULL);
+}
+
+static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *skb, int iNewState )
+{
+	u_char         * p;
+	char           * pSPID;
+	struct Channel * pChan = pc->st->lli.userdata;
+	int              l;
+
+	if ( skb )
+		dev_kfree_skb( skb);
+
+	if ( !( pSPID = strchr( pChan->setup.eazmsn, ':' ) ) )
+	{
+		printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn );
+		newl3state( pc, 0 );
+		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+		return;
+	}
+
+	l = strlen( ++pSPID );
+	if ( !( skb = l3_alloc_skb( 5+l ) ) )
+	{
+		printk( KERN_ERR "HiSax can't get memory to send SPID\n" );
+		return;
+	}
+
+	p = skb_put( skb, 5 );
+	*p++ = PROTO_DIS_EURO;
+	*p++ = 0;
+	*p++ = MT_INFORMATION;
+	*p++ = IE_SPID;
+	*p++ = l;
+
+	memcpy( skb_put( skb, l ), pSPID, l );
+
+	newl3state( pc, iNewState );
+
+	L3DelTimer( &pc->timer );
+	L3AddTimer( &pc->timer, TSPID, CC_TSPID );
+
+	pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb );
+}
+
+static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg )
+{
+	l3ni1_SendSpid( pc, pr, arg, 20 );
+}
+
+void l3ni1_spid_epid( struct l3_process *pc, u_char pr, void *arg )
+{
+	struct sk_buff *skb = arg;
+
+	if ( skb->data[ 1 ] == 0 )
+		if ( skb->data[ 3 ] == IE_ENDPOINT_ID )
+		{
+			L3DelTimer( &pc->timer );
+			newl3state( pc, 0 );
+			l3_msg( pc->st, DL_ESTABLISH | CONFIRM, NULL );
+		}
+	dev_kfree_skb( skb);
+}
+
+static void l3ni1_spid_tout( struct l3_process *pc, u_char pr, void *arg )
+{
+	if ( pc->state < 22 )
+		l3ni1_SendSpid( pc, pr, arg, pc->state+1 );
+	else
+	{
+		L3DelTimer( &pc->timer );
+		dev_kfree_skb( arg);
+
+		printk( KERN_ERR "SPID not accepted\n" );
+		newl3state( pc, 0 );
+		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+	}
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3ni1_setup_req},
+	{SBIT(0),
+	 CC_RESUME | REQUEST, l3ni1_resume_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25),
+	 CC_DISCONNECT | REQUEST, l3ni1_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE | REQUEST, l3ni1_release_req},
+	{ALL_STATES,
+	 CC_RESTART | REQUEST, l3ni1_restart},
+	{SBIT(6) | SBIT(25),
+	 CC_IGNORE | REQUEST, l3ni1_reset},
+	{SBIT(6) | SBIT(25),
+	 CC_REJECT | REQUEST, l3ni1_reject_req},
+	{SBIT(6) | SBIT(25),
+	 CC_PROCEED_SEND | REQUEST, l3ni1_proceed_req},
+	{SBIT(6),
+	 CC_MORE_INFO | REQUEST, l3ni1_setup_ack_req},
+	{SBIT(25),
+	 CC_MORE_INFO | REQUEST, l3ni1_dummy},
+	{SBIT(6) | SBIT(9) | SBIT(25),
+	 CC_ALERTING | REQUEST, l3ni1_alert_req},
+	{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+	 CC_SETUP | RESPONSE, l3ni1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3ni1_suspend_req},
+        {SBIT(7) | SBIT(9) | SBIT(25),
+         CC_REDIR | REQUEST, l3ni1_redir_req},
+        {SBIT(6),
+         CC_REDIR | REQUEST, l3ni1_redir_req_early},
+        {SBIT(9) | SBIT(25),
+         CC_DISCONNECT | REQUEST, l3ni1_disconnect_req},
+	{SBIT(25),
+	 CC_T302, l3ni1_t302},
+	{SBIT(1),
+	 CC_T303, l3ni1_t303},
+	{SBIT(2),
+	 CC_T304, l3ni1_t304},
+	{SBIT(3),
+	 CC_T310, l3ni1_t310},
+	{SBIT(8),
+	 CC_T313, l3ni1_t313},
+	{SBIT(11),
+	 CC_T305, l3ni1_t305},
+	{SBIT(15),
+	 CC_T319, l3ni1_t319},
+	{SBIT(17),
+	 CC_T318, l3ni1_t318},
+	{SBIT(19),
+	 CC_T308_1, l3ni1_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3ni1_t308_2},
+	{SBIT(10),
+	 CC_T309, l3ni1_dl_release},
+	{ SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ),
+	 CC_TSPID, l3ni1_spid_tout },
+};
+
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct stateentry))
+
+static struct stateentry datastatelist[] =
+{
+	{ALL_STATES,
+	 MT_STATUS_ENQUIRY, l3ni1_status_enq},
+	{ALL_STATES,
+	 MT_FACILITY, l3ni1_facility},
+	{SBIT(19),
+	 MT_STATUS, l3ni1_release_ind},
+	{ALL_STATES,
+	 MT_STATUS, l3ni1_status},
+	{SBIT(0),
+	 MT_SETUP, l3ni1_setup},
+	{SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) |
+	 SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_SETUP, l3ni1_dummy},
+	{SBIT(1) | SBIT(2),
+	 MT_CALL_PROCEEDING, l3ni1_call_proc},
+	{SBIT(1),
+	 MT_SETUP_ACKNOWLEDGE, l3ni1_setup_ack},
+	{SBIT(2) | SBIT(3),
+	 MT_ALERTING, l3ni1_alerting},
+	{SBIT(2) | SBIT(3),
+	 MT_PROGRESS, l3ni1_progress},
+	{SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_INFORMATION, l3ni1_information},
+	{SBIT(10) | SBIT(11) | SBIT(15),
+	 MT_NOTIFY, l3ni1_notify},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_RELEASE_COMPLETE, l3ni1_release_cmpl},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25),
+	 MT_RELEASE, l3ni1_release},
+	{SBIT(19),  MT_RELEASE, l3ni1_release_ind},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25),
+	 MT_DISCONNECT, l3ni1_disconnect},
+	{SBIT(19),
+	 MT_DISCONNECT, l3ni1_dummy},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_CONNECT, l3ni1_connect},
+	{SBIT(8),
+	 MT_CONNECT_ACKNOWLEDGE, l3ni1_connect_ack},
+	{SBIT(15),
+	 MT_SUSPEND_ACKNOWLEDGE, l3ni1_suspend_ack},
+	{SBIT(15),
+	 MT_SUSPEND_REJECT, l3ni1_suspend_rej},
+	{SBIT(17),
+	 MT_RESUME_ACKNOWLEDGE, l3ni1_resume_ack},
+	{SBIT(17),
+	 MT_RESUME_REJECT, l3ni1_resume_rej},
+};
+
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct stateentry))
+
+static struct stateentry globalmes_list[] =
+{
+	{ALL_STATES,
+	 MT_STATUS, l3ni1_status},
+	{SBIT(0),
+	 MT_RESTART, l3ni1_global_restart},
+/*	{SBIT(1),
+	 MT_RESTART_ACKNOWLEDGE, l3ni1_restart_ack},
+*/
+	{ SBIT( 0 ), MT_DL_ESTABLISHED, l3ni1_spid_send },
+	{ SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), MT_INFORMATION, l3ni1_spid_epid },
+};
+#define GLOBALM_LEN \
+	(sizeof(globalmes_list) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+        {SBIT(2),
+         DL_ESTABLISH | INDICATION, l3ni1_dl_reset},
+        {SBIT(10),
+         DL_ESTABLISH | CONFIRM, l3ni1_dl_reest_status},
+        {SBIT(10),
+         DL_RELEASE | INDICATION, l3ni1_dl_reestablish},
+        {ALL_STATES,
+         DL_RELEASE | INDICATION, l3ni1_dl_release},
+};
+
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	int i;
+	struct l3_process *proc = st->l3.global;
+
+	if ( skb )	
+		proc->callref = skb->data[2]; /* cr flag */
+	else
+		proc->callref = 0;
+	for (i = 0; i < GLOBALM_LEN; i++)
+		if ((mt == globalmes_list[i].primitive) &&
+		    ((1 << proc->state) & globalmes_list[i].state))
+			break;
+	if (i == GLOBALM_LEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1 global state %d mt %x unhandled",
+				proc->state, mt);
+		}
+		MsgHead(p, proc->callref, MT_STATUS);
+		*p++ = IE_CAUSE;
+		*p++ = 0x2;
+		*p++ = 0x80;
+		*p++ = 81 |0x80;	/* invalid cr */
+		*p++ = 0x14;		/* CallState */
+		*p++ = 0x1;
+		*p++ = proc->state & 0x3f;
+		l = p - tmp;
+		if (!(skb = l3_alloc_skb(l)))
+			return;
+		memcpy(skb_put(skb, l), tmp, l);
+		l3_msg(proc->st, DL_DATA | REQUEST, skb);
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1 global %d mt %x",
+				proc->state, mt);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
+
+static void
+ni1up(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr, cause, callState;
+	char *ptr;
+	u_char *p;
+	struct sk_buff *skb = arg;
+	struct l3_process *proc;
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+
+		case (DL_ESTABLISH | CONFIRM):
+			global_handler( st, MT_DL_ESTABLISHED, NULL );
+			return;
+
+		default:
+			printk(KERN_ERR "HiSax ni1up unknown pr=%04x\n", pr);
+			return;
+	}
+	if (skb->len < 3) {
+		l3_debug(st, "ni1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (skb->data[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			l3_debug(st, "ni1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = getcallref(skb->data);
+	if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+		l3_debug(st, "ni1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	if (st->l3.debug & L3_DEB_STATE)
+		l3_debug(st, "ni1up cr %d", cr);
+	if (cr == -2) {  /* wrong Callref */
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "ni1up wrong Callref");
+		dev_kfree_skb(skb);
+		return;
+	} else if (cr == -1) {	/* Dummy Callref */
+		if (mt == MT_FACILITY)
+		{
+			if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+				l3ni1_parse_facility(st, NULL, 
+					(pr == (DL_DATA | INDICATION)) ? -1 : -2, p); 
+				dev_kfree_skb(skb);
+				return;  
+			}
+		}
+		else
+		{
+			global_handler(st, mt, skb);
+			return;
+		}
+				
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "ni1up dummy Callref (no facility msg or ie)");
+		dev_kfree_skb(skb);
+		return;
+	} else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+		(((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) {	/* Global CallRef */
+		if (st->l3.debug & L3_DEB_STATE)
+			l3_debug(st, "ni1up Global CallRef");
+		global_handler(st, mt, skb);
+		dev_kfree_skb(skb);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+			/* Setup creates a new transaction process */
+			if (skb->data[2] & 0x80) {
+				/* Setup with wrong CREF flag */
+				if (st->l3.debug & L3_DEB_STATE)
+					l3_debug(st, "ni1up wrong CRef flag");
+				dev_kfree_skb(skb);
+				return;
+			}
+			if (!(proc = ni1_new_l3_process(st, cr))) {
+				/* May be to answer with RELEASE_COMPLETE and
+				 * CAUSE 0x2f "Resource unavailable", but this
+				 * need a new_l3_process too ... arghh
+				 */
+				dev_kfree_skb(skb);
+				return;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			/* ETS 300-104 part 2.4.1
+			 * if setup has not been made and a message type
+			 * MT_STATUS is received with call state == 0,
+			 * we must send nothing
+			 */
+			if (callState != 0) {
+				/* ETS 300-104 part 2.4.2
+				 * if setup has not been made and a message type
+				 * MT_STATUS is received with call state != 0,
+				 * we must send MT_RELEASE_COMPLETE cause 101
+				 */
+				if ((proc = ni1_new_l3_process(st, cr))) {
+					proc->para.cause = 101;
+					l3ni1_msg_without_setup(proc, 0, NULL);
+				}
+			}
+			dev_kfree_skb(skb);
+			return;
+		} else if (mt == MT_RELEASE_COMPLETE) {
+			dev_kfree_skb(skb);
+			return;
+		} else {
+			/* ETS 300-104 part 2
+			 * if setup has not been made and a message type
+			 * (except MT_SETUP and RELEASE_COMPLETE) is received,
+			 * we must send MT_RELEASE_COMPLETE cause 81 */
+			dev_kfree_skb(skb);
+			if ((proc = ni1_new_l3_process(st, cr))) {
+				proc->para.cause = 81;
+				l3ni1_msg_without_setup(proc, 0, NULL);
+			}
+			return;
+		}
+	}
+	if (l3ni1_check_messagetype_validity(proc, mt, skb)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
+	  l3ni1_deliver_display(proc, pr, p); /* Display IE included */
+	for (i = 0; i < DATASLLEN; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << proc->state) & datastatelist[i].state))
+			break;
+	if (i == DATASLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1up%sstate %d mt %#x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+			proc->para.cause = 101;
+			l3ni1_status_send(proc, pr, skb);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		datastatelist[i].rout(proc, pr, skb);
+	}
+	dev_kfree_skb(skb);
+	return;
+}
+
+static void
+ni1down(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+
+	if ((DL_ESTABLISH | REQUEST) == pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = ni1_new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax ni1down without proc pr=%04x\n", pr);
+		return;
+	}
+
+	if ( pr == (CC_TNI1_IO | REQUEST)) {
+		l3ni1_io_timer(proc); /* timer expires */ 
+		return;
+	}  
+
+	for (i = 0; i < DOWNSLLEN; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << proc->state) & downstatelist[i].state))
+			break;
+	if (i == DOWNSLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1down state %d prim %#x unhandled",
+				proc->state, pr);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1down state %d prim %#x",
+				proc->state, pr);
+		}
+		downstatelist[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+ni1man(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+
+        if (!proc) {
+                printk(KERN_ERR "HiSax ni1man without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d ni1man state %d prim %#x unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d ni1man state %d prim %#x",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_ni1(struct PStack *st)
+{
+	char tmp[64];
+	int i;
+
+	st->lli.l4l3 = ni1down;
+	st->lli.l4l3_proto = l3ni1_cmd_global;
+	st->l2.l2l3 = ni1up;
+	st->l3.l3ml3 = ni1man;
+	st->l3.N303 = 1;
+	st->prot.ni1.last_invoke_id = 0;
+	st->prot.ni1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+	i = 1;
+	while (i < 32) 
+		st->prot.ni1.invoke_used[i++] = 0;   
+
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for ni1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		st->l3.global->prot.ni1.invoke_id = 0; 
+
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
+	}
+	strcpy(tmp, ni1_revision);
+	printk(KERN_INFO "HiSax: National ISDN-1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/l3ni1.h linux.19p1/drivers/isdn/hisax/l3ni1.h
--- linux.vanilla/drivers/isdn/hisax/l3ni1.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/l3ni1.h	Wed Dec 13 22:08:26 2000
@@ -0,0 +1,136 @@
+// $Id: l3ni1.h,v 2.2 2000/06/26 08:59:14 keil Exp $
+//-----------------------------------------------------------------------------
+//
+// NI1 D-channel protocol
+//
+// Author       Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd
+//              www.traverse.com.au
+//
+// 2000.6.6 Initial implementation of routines for US NI1 
+// Layer 3 protocol based on the EURO/DSS1 D-channel protocol 
+// driver written by Karsten Keil et al.  Thanks also for the 
+// code provided by Ragnar Paulson.
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+//-----------------------------------------------------------------------------
+
+#ifndef l3ni1_process
+
+#define T302	15000
+#define T303	4000
+#define T304	30000
+#define T305	30000
+#define T308	4000
+/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
+/* This makes some tests easier and quicker */
+#define T309	40000
+#define T310	30000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+#define TSPID	2000
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING		0x01
+#define MT_CALL_PROCEEDING	0x02
+#define MT_CONNECT		0x07
+#define MT_CONNECT_ACKNOWLEDGE	0x0f
+#define MT_PROGRESS		0x03
+#define MT_SETUP		0x05
+#define MT_SETUP_ACKNOWLEDGE	0x0d
+#define MT_RESUME		0x26
+#define MT_RESUME_ACKNOWLEDGE	0x2e
+#define MT_RESUME_REJECT	0x22
+#define MT_SUSPEND		0x25
+#define MT_SUSPEND_ACKNOWLEDGE	0x2d
+#define MT_SUSPEND_REJECT	0x21
+#define MT_USER_INFORMATION	0x20
+#define MT_DISCONNECT		0x45
+#define MT_RELEASE		0x4d
+#define MT_RELEASE_COMPLETE	0x5a
+#define MT_RESTART		0x46
+#define MT_RESTART_ACKNOWLEDGE	0x4e
+#define MT_SEGMENT		0x60
+#define MT_CONGESTION_CONTROL	0x79
+#define MT_INFORMATION		0x7b
+#define MT_FACILITY		0x62
+#define MT_NOTIFY		0x6e
+#define MT_STATUS		0x7d
+#define MT_STATUS_ENQUIRY	0x75
+#define MT_DL_ESTABLISHED	0xfe
+
+#define IE_SEGMENT	0x00
+#define IE_BEARER	0x04
+#define IE_CAUSE	0x08
+#define IE_CALL_ID	0x10
+#define IE_CALL_STATE	0x14
+#define IE_CHANNEL_ID	0x18
+#define IE_FACILITY	0x1c
+#define IE_PROGRESS	0x1e
+#define IE_NET_FAC	0x20
+#define IE_NOTIFY	0x27
+#define IE_DISPLAY	0x28
+#define IE_DATE		0x29
+#define IE_KEYPAD	0x2c
+#define IE_SIGNAL	0x34
+#define IE_SPID		0x3a
+#define IE_ENDPOINT_ID	0x3b
+#define IE_INFORATE	0x40
+#define IE_E2E_TDELAY	0x42
+#define IE_TDELAY_SEL	0x43
+#define IE_PACK_BINPARA	0x44
+#define IE_PACK_WINSIZE	0x45
+#define IE_PACK_SIZE	0x46
+#define IE_CUG		0x47
+#define	IE_REV_CHARGE	0x4a
+#define IE_CONNECT_PN	0x4c
+#define IE_CONNECT_SUB	0x4d
+#define IE_CALLING_PN	0x6c
+#define IE_CALLING_SUB	0x6d
+#define IE_CALLED_PN	0x70
+#define IE_CALLED_SUB	0x71
+#define IE_REDIR_NR	0x74
+#define IE_TRANS_SEL	0x78
+#define IE_RESTART_IND	0x79
+#define IE_LLC		0x7c
+#define IE_HLC		0x7d
+#define IE_USER_USER	0x7e
+#define IE_ESCAPE	0x7f
+#define IE_SHIFT	0x90
+#define IE_MORE_DATA	0xa0
+#define IE_COMPLETE	0xa1
+#define IE_CONGESTION	0xb0
+#define IE_REPEAT	0xd0
+
+#define IE_MANDATORY	0x0100
+/* mandatory not in every case */
+#define IE_MANDATORY_1	0x0200
+
+#define ERR_IE_COMPREHENSION	 1
+#define ERR_IE_UNRECOGNIZED	-1
+#define ERR_IE_LENGTH		-2
+#define ERR_IE_SEQUENCE		-3
+
+#else /* only l3ni1_process */
+
+/* l3ni1 specific data in l3 process */
+typedef struct
+  { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */
+    ulong ll_id; /* remebered ll id */
+    u_char remote_operation; /* handled remote operation, 0 = not active */ 
+    int proc; /* rememered procedure */  
+    ulong remote_result; /* result of remote operation for statcallb */
+    char uus1_data[35]; /* data send during alerting or disconnect */
+  } ni1_proc_priv;
+
+/* l3dni1 specific data in protocol stack */
+typedef struct
+  { unsigned char last_invoke_id; /* last used value for invoking */
+    unsigned char invoke_used[32]; /* 256 bits for 256 values */
+  } ni1_stk_priv;        
+
+#endif /* only l3dni1_process */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/lmgr.c linux.19p1/drivers/isdn/hisax/lmgr.c
--- linux.vanilla/drivers/isdn/hisax/lmgr.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/lmgr.c	Wed Dec 13 22:08:26 2000
@@ -1,30 +1,10 @@
-/* $Id: lmgr.c,v 1.6 1999/07/01 08:12:04 keil Exp $
-
- * Author       Karsten Keil (keil@isdn4linux.de)
+/* $Id: lmgr.c,v 1.7 2000/06/26 08:59:14 keil Exp $
  *
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *  Layermanagement module
  *
- * $Log: lmgr.c,v $
- * Revision 1.6  1999/07/01 08:12:04  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.5  1998/11/15 23:55:12  keil
- * changes from 2.0
- *
- * Revision 1.4  1998/05/25 12:58:19  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.3  1998/03/07 22:57:06  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 1.2  1997/10/29 19:09:34  keil
- * new L1
- *
- * Revision 1.1  1997/06/26 11:17:25  keil
- * first version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/md5sums.asc linux.19p1/drivers/isdn/hisax/md5sums.asc
--- linux.vanilla/drivers/isdn/hisax/md5sums.asc	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/md5sums.asc	Wed Dec 13 22:08:26 2000
@@ -2,30 +2,32 @@
 
 # This are valid md5sums for certificated HiSax driver.
 # The certification is valid only if the md5sums of all files match.
-# The certification is valid only for ELSA QuickStep cards and
-# Eicon Technology Diva 2.01 PCI cards in the moment.
+# The certification is valid only for ELSA Microlink PCI,
+# Eicon Technology Diva 2.01 PCI and Sedlbauer SpeedFax +
+# cards in the moment.
 # Read ../../../Documentation/isdn/HiSax.cert for more informations.
 # 
-3c2b1c96274cba97a8261d1cecc662b8  isac.c
-dd3955847bbf680b41233478fe521d88  isdnl1.c
-bb51bd223040b511c18f091da5ab6456  isdnl2.c
-b7aa7f97b2374967a4aca7c52991142c  isdnl3.c
-a23fbf8879c1432b04640b8b04bdf419  tei.c
-ce248e56c2e1326012d0b25f92bbf99b  callc.c
-bf9605b36429898f7be6630034e83230  cert.c
-6ce0a184127be1a44747e2017ed24ad9  l3dss1.c
-a3a570781f828b6d59e6b231653133de  l3_1tr6.c
-8188deeb4a1b34c574cd51638cd214d0  elsa.c
-a3c2b8e9d2c623658888b5f1d4317c2a  diva.c
+4131693d878d465faf0a4a4b4c6b811e  isac.c
+a29f5270c0c89626d8d6fa5dd09e7005  isdnl1.c
+fbe41751c8130a8c3c607bfe1b41cb4e  isdnl2.c
+7915b7e802b98f6f4f05b931c4736ad4  isdnl3.c
+7c31c12b3c2cfde33596bd2c406f775c  tei.c
+f1fbd532016f005e01decf36e5197d8f  callc.c
+a1834e9b2ec068440cff2e899eff4710  cert.c
+a1f908f8b4f225c5c2f2a13842549b72  l3dss1.c
+3d0a3f88fe48f3151813fafb22ea6119  l3_1tr6.c
+6f3420dca0f50084f82471a238545a95  elsa.c
+65da7b33bbfa454df2f99636b83f0610  diva.c
+4bdb7e35627b7e25f17fccb669fda835  sedlbauer.c
 # end of md5sums
 
 -----BEGIN PGP SIGNATURE-----
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBONEHHTpxHvX/mS9tAQG/pAP/UyLx23V9mOgiYaId1Gm6xthyM8TP4bfE
-ov4cNmUHQtHINLXtkAP+eacw/kCiiYhdxBIrNaUC+KQkyiFPOs8frx6espxTfDte
-VZDhKDY5NYch2n3OKPLX26pNNJeu7DELMpFR6vvGByokv1RRYVs2wBk0Q5dnfPaq
-WsW0HeL/vBA=
-=fSCb
+iQCVAwUBOVctcDpxHvX/mS9tAQFOhAP+OpOSxDz46cEHjy4jLsYz44/yKZzFAXz4
+s0tYmixrFbbS5XoT1GZqzEF0n/EycO9jsp6d0eanDCg25UX7ehu9dtOJw0o6qRr2
+4M/EbloHK2G1aW4gI8W2eWRNRqTZQJ2cjPJD/V/27jDbxBBP31NvOSYvVxrbBzJG
+qZHWUDiQxRo=
+=mYrg
 -----END PGP SIGNATURE-----
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/mic.c linux.19p1/drivers/isdn/hisax/mic.c
--- linux.vanilla/drivers/isdn/hisax/mic.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/mic.c	Wed Dec 13 22:08:26 2000
@@ -1,38 +1,12 @@
-/* $Id: mic.c,v 1.8 1999/07/12 21:05:20 keil Exp $
-
+/* $Id: mic.c,v 1.9 2000/06/26 08:59:14 keil Exp $
+ *
  * mic.c  low level stuff for mic cards
  *
  * Copyright (C) 1997 
  *
  * Author  Stephan von Krawczynski <skraw@ithnet.com>
  *
- *
- * $Log: mic.c,v $
- * Revision 1.8  1999/07/12 21:05:20  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.7  1998/04/15 16:44:32  keil
- * new init code
- *
- * Revision 1.6  1998/02/17 15:39:57  keil
- * fix reset problem
- *
- * Revision 1.5  1998/02/02 13:29:43  keil
- * fast io
- *
- * Revision 1.4  1997/11/08 21:35:51  keil
- * new l1 init
- *
- * Revision 1.3  1997/11/06 17:09:11  keil
- * New 2.1 init code
- *
- * Revision 1.2  1997/10/29 18:51:17  keil
- * New files
- *
- * Revision 1.1.2.1  1997/10/17 22:10:54  keil
- * new files on 2.0
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -44,7 +18,7 @@
 
 extern const char *CardType[];
 
-const char *mic_revision = "$Revision: 1.8 $";
+const char *mic_revision = "$Revision: 1.9 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/netjet.c linux.19p1/drivers/isdn/hisax/netjet.c
--- linux.vanilla/drivers/isdn/hisax/netjet.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/netjet.c	Wed Dec 13 22:08:26 2000
@@ -1,67 +1,12 @@
-/* $Id: netjet.c,v 1.17 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: netjet.c,v 1.20 2000/06/26 08:59:14 keil Exp $
+ *
  * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to Traverse Technologie Australia for documents and informations
  *
- * $Log: netjet.c,v $
- * Revision 1.17  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.16  1999/10/14 20:25:29  keil
- * add a statistic for error monitoring
- *
- * Revision 1.15  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.14  1999/08/31 11:20:25  paul
- * various spelling corrections (new checksums may be needed, Karsten!)
- *
- * Revision 1.13  1999/08/11 21:01:31  keil
- * new PCI codefix
- *
- * Revision 1.12  1999/08/10 16:02:00  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.11  1999/08/07 17:32:00  keil
- * Asymetric buffers for improved ping times.  Interframe spacing
- * fix for NJ<->NJ thoughput.  Matt Henderson - www.traverse.com.au
- *
- *
- * Revision 1.10  1999/07/12 21:05:22  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.9  1999/07/01 08:12:05  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.8  1998/11/15 23:55:14  keil
- * changes from 2.0
- *
- * Revision 1.7  1998/09/30 22:24:48  keil
- * Fix missing line in setstack*
- *
- * Revision 1.6  1998/08/13 23:36:54  keil
- * HiSax 3.1 - don't work stable with current LinkLevel
- *
- * Revision 1.5  1998/05/25 12:58:21  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 1.4  1998/04/15 16:42:35  keil
- * new init code
- * new PCI init (2.1.94)
- *
- * Revision 1.3  1998/02/12 23:08:05  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 1.2  1998/02/02 13:32:06  keil
- * New
- *
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -74,6 +19,7 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/ppp_defs.h>
+#include "netjet.h"
 
 #ifndef bus_to_virt
 #define bus_to_virt (u_int *)
@@ -83,61 +29,12 @@
 #define virt_to_bus (u_int)
 #endif
 
-extern const char *CardType[];
-
-const char *NETjet_revision = "$Revision: 1.17 $";
-
-#define byteout(addr,val) outb(val,addr)
-#define bytein(addr) inb(addr)
-
-/* PCI stuff */
-#define PCI_VENDOR_TRAVERSE_TECH 0xe159
-#define PCI_NETJET_ID	0x0001
-
-#define NETJET_CTRL	0x00
-#define NETJET_DMACTRL	0x01
-#define NETJET_AUXCTRL	0x02
-#define NETJET_AUXDATA	0x03
-#define NETJET_IRQMASK0 0x04
-#define NETJET_IRQMASK1 0x05
-#define NETJET_IRQSTAT0 0x06
-#define NETJET_IRQSTAT1 0x07
-#define NETJET_DMA_READ_START	0x08
-#define NETJET_DMA_READ_IRQ	0x0c
-#define NETJET_DMA_READ_END	0x10
-#define NETJET_DMA_READ_ADR	0x14
-#define NETJET_DMA_WRITE_START	0x18
-#define NETJET_DMA_WRITE_IRQ	0x1c
-#define NETJET_DMA_WRITE_END	0x20
-#define NETJET_DMA_WRITE_ADR	0x24
-#define NETJET_PULSE_CNT	0x28
-
-#define NETJET_ISAC_OFF	0xc0
-#define NETJET_ISACIRQ	0x10
-#define NETJET_IRQM0_READ	0x0c
-#define NETJET_IRQM0_READ_1	0x04
-#define NETJET_IRQM0_READ_2	0x08
-#define NETJET_IRQM0_WRITE	0x03
-#define NETJET_IRQM0_WRITE_1	0x01
-#define NETJET_IRQM0_WRITE_2	0x02
-
-#define NETJET_DMA_TXSIZE 512
-#define NETJET_DMA_RXSIZE 128
-
-#define HDLC_ZERO_SEARCH 0
-#define HDLC_FLAG_SEARCH 1
-#define HDLC_FLAG_FOUND  2
-#define HDLC_FRAME_FOUND 3
-#define HDLC_NULL 4
-#define HDLC_PART 5
-#define HDLC_FULL 6
-
-#define HDLC_FLAG_VALUE	0x7e
+const char *NETjet_revision = "$Revision: 1.20 $";
 
 /* Interface functions */
 
-static u_char
-ReadISAC(struct IsdnCardState *cs, u_char offset)
+u_char
+NETjet_ReadIC(struct IsdnCardState *cs, u_char offset)
 {
 	long flags;
 	u_char ret;
@@ -152,8 +49,8 @@
 	return(ret);
 }
 
-static void
-WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+void
+NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
 	long flags;
 	
@@ -166,8 +63,8 @@
 	restore_flags(flags);
 }
 
-static void
-ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+void
+NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size)
 {
 	cs->hw.njet.auxd &= 0xfc;
 	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
@@ -210,8 +107,8 @@
 	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
 
-static void 
-WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+void 
+NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size)
 {
 	cs->hw.njet.auxd &= 0xfc;
 	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
@@ -297,15 +194,6 @@
 			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
 }
 
-static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
-{
-	return(5);
-}
-
-static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
-{
-}
-
 static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) {
 	char tmp[128];
 	char *t = tmp;
@@ -596,7 +484,7 @@
 	bcs->hw.tiger.r_bitcnt = bitcnt;
 }
 
-static void read_tiger(struct IsdnCardState *cs) {
+void read_tiger(struct IsdnCardState *cs) {
 	u_int *p;
 	int cnt = NETJET_DMA_RXSIZE/2;
 	
@@ -627,7 +515,7 @@
 
 static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
 
-static void fill_dma(struct BCState *bcs)
+void netjet_fill_dma(struct BCState *bcs)
 {
 	register u_int *p, *sp;
 	register int cnt;
@@ -742,7 +630,7 @@
 				test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
 			}
 			if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
-				fill_dma(bcs);
+				netjet_fill_dma(bcs);
 			} else {
 				mask ^= 0xffffffff;
 				if (s_cnt < cnt) {
@@ -774,7 +662,7 @@
 	}
 }
 
-static void write_tiger(struct IsdnCardState *cs) {
+void write_tiger(struct IsdnCardState *cs) {
 	u_int *p, cnt = NETJET_DMA_TXSIZE/2;
 	
 	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
@@ -990,88 +878,6 @@
 	}
 }
 
-static void
-netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs)
-{
-	struct IsdnCardState *cs = dev_id;
-	u_char val, sval;
-	long flags;
-
-	if (!cs) {
-		printk(KERN_WARNING "NETjet: Spurious interrupt!\n");
-		return;
-	}
-	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
-		NETJET_ISACIRQ)) {
-		val = ReadISAC(cs, ISAC_ISTA);
-		if (cs->debug & L1_DEB_ISAC)
-			debugl1(cs, "tiger: i1 %x %x", sval, val);
-		if (val) {
-			isac_interrupt(cs, val);
-			WriteISAC(cs, ISAC_MASK, 0xFF);
-			WriteISAC(cs, ISAC_MASK, 0x0);
-		}
-	}
-	save_flags(flags);
-	cli();
-	if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
-		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
-			restore_flags(flags);
-			return;
-		}
-		cs->hw.njet.irqstat0 = sval;
-		restore_flags(flags);
-/*		debugl1(cs, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
-			sval, 
-			bytein(cs->hw.njet.base + NETJET_DMACTRL),
-			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
-			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
-			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
-			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
-*/
-/*		cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
-*/		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
-/*		byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
-*/		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ)
-			read_tiger(cs);
-		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE)
-			write_tiger(cs);
-		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-	} else
-		restore_flags(flags);
-
-/*	if (!testcnt--) {
-		cs->hw.njet.dmactrl = 0;
-		byteout(cs->hw.njet.base + NETJET_DMACTRL,
-			cs->hw.njet.dmactrl);
-		byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
-	}
-*/
-}
-
-static void
-reset_netjet(struct IsdnCardState *cs)
-{
-	long flags;
-
-	save_flags(flags);
-	sti();
-	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
-	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
-	current->state = TASK_UNINTERRUPTIBLE;
-	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
-	cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
-	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
-	current->state = TASK_UNINTERRUPTIBLE;
-	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
-	restore_flags(flags);
-	cs->hw.njet.auxd = 0;
-	cs->hw.njet.dmactrl = 0;
-	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
-	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
-	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
-}
-
 void
 release_io_netjet(struct IsdnCardState *cs)
 {
@@ -1081,99 +887,3 @@
 	release_region(cs->hw.njet.base, 256);
 }
 
-
-static int
-NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg)
-{
-	switch (mt) {
-		case CARD_RESET:
-			reset_netjet(cs);
-			return(0);
-		case CARD_RELEASE:
-			release_io_netjet(cs);
-			return(0);
-		case CARD_INIT:
-			inittiger(cs);
-			clear_pending_isac_ints(cs);
-			initisac(cs);
-			/* Reenable all IRQ */
-			cs->writeisac(cs, ISAC_MASK, 0);
-			return(0);
-		case CARD_TEST:
-			return(0);
-	}
-	return(0);
-}
-
-static 	struct pci_dev *dev_netjet __initdata = NULL;
-
-__initfunc(int
-setup_netjet(struct IsdnCard *card))
-{
-	int bytecnt;
-	struct IsdnCardState *cs = card->cs;
-	char tmp[64];
-#if CONFIG_PCI
-#endif
-	strcpy(tmp, NETjet_revision);
-	printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp));
-	if (cs->typ != ISDN_CTYPE_NETJET)
-		return(0);
-	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-#if CONFIG_PCI
-	if (!pci_present()) {
-		printk(KERN_ERR "Netjet: no PCI bus present\n");
-		return(0);
-	}
-	if ((dev_netjet = pci_find_device(PCI_VENDOR_TRAVERSE_TECH,
-		PCI_NETJET_ID,  dev_netjet))) {
-		cs->irq = dev_netjet->irq;
-		if (!cs->irq) {
-			printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
-			return(0);
-		}
-		cs->hw.njet.base = dev_netjet->base_address[ 0]
-			& PCI_BASE_ADDRESS_IO_MASK; 
-		if (!cs->hw.njet.base) {
-			printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
-			return(0);
-		}
-	} else {
-		printk(KERN_WARNING "NETjet: No PCI card found\n");
-		return(0);
-	}
-	cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
-	cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
-	bytecnt = 256;
-#else
-	printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n");
-	printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n");
-	return (0);
-#endif /* CONFIG_PCI */
-	printk(KERN_INFO
-		"NETjet: PCI card configured at 0x%x IRQ %d\n",
-		cs->hw.njet.base, cs->irq);
-	if (check_region(cs->hw.njet.base, bytecnt)) {
-		printk(KERN_WARNING
-		       "HiSax: %s config port %x-%x already in use\n",
-		       CardType[card->typ],
-		       cs->hw.njet.base,
-		       cs->hw.njet.base + bytecnt);
-		return (0);
-	} else {
-		request_region(cs->hw.njet.base, bytecnt, "netjet isdn");
-	}
-	reset_netjet(cs);
-	cs->readisac  = &ReadISAC;
-	cs->writeisac = &WriteISAC;
-	cs->readisacfifo  = &ReadISACfifo;
-	cs->writeisacfifo = &WriteISACfifo;
-	cs->BC_Read_Reg  = &dummyrr;
-	cs->BC_Write_Reg = &dummywr;
-	cs->BC_Send_Data = &fill_dma;
-	cs->cardmsg = &NETjet_card_msg;
-	cs->irq_func = &netjet_interrupt;
-	cs->irq_flags |= SA_SHIRQ;
-	ISACVersion(cs, "NETjet:");
-	return (1);
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/netjet.h linux.19p1/drivers/isdn/hisax/netjet.h
--- linux.vanilla/drivers/isdn/hisax/netjet.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/netjet.h	Wed Dec 13 22:08:26 2000
@@ -0,0 +1,77 @@
+// $Id: netjet.h,v 2.3 2000/06/26 08:59:14 keil Exp $
+//-----------------------------------------------------------------------------
+//
+// NETjet common header file
+//
+// Author       Kerstern Keil repackaged by
+//              Matt Henderson - Traverse Technologies P/L www.traverse.com.au
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+//-----------------------------------------------------------------------------
+
+extern const char *CardType[];
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/* PCI stuff */
+#ifndef PCI_VENDOR_ID_TIGERJET
+#define PCI_VENDOR_ID_TIGERJET	0xe159
+#endif
+#ifndef PCI_DEVICE_ID_TIGERJET_300
+#define PCI_DEVICE_ID_TIGERJET_300	0x0001
+#endif
+#define NETJET_CTRL	0x00
+#define NETJET_DMACTRL	0x01
+#define NETJET_AUXCTRL	0x02
+#define NETJET_AUXDATA	0x03
+#define NETJET_IRQMASK0 0x04
+#define NETJET_IRQMASK1 0x05
+#define NETJET_IRQSTAT0 0x06
+#define NETJET_IRQSTAT1 0x07
+#define NETJET_DMA_READ_START	0x08
+#define NETJET_DMA_READ_IRQ	0x0c
+#define NETJET_DMA_READ_END	0x10
+#define NETJET_DMA_READ_ADR	0x14
+#define NETJET_DMA_WRITE_START	0x18
+#define NETJET_DMA_WRITE_IRQ	0x1c
+#define NETJET_DMA_WRITE_END	0x20
+#define NETJET_DMA_WRITE_ADR	0x24
+#define NETJET_PULSE_CNT	0x28
+
+#define NETJET_ISAC_OFF	0xc0
+#define NETJET_ISACIRQ	0x10
+#define NETJET_IRQM0_READ	0x0c
+#define NETJET_IRQM0_READ_1	0x04
+#define NETJET_IRQM0_READ_2	0x08
+#define NETJET_IRQM0_WRITE	0x03
+#define NETJET_IRQM0_WRITE_1	0x01
+#define NETJET_IRQM0_WRITE_2	0x02
+
+#define NETJET_DMA_TXSIZE 512
+#define NETJET_DMA_RXSIZE 128
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND  2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE	0x7e
+
+u_char NETjet_ReadIC(struct IsdnCardState *cs, u_char offset);
+void NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value);
+void NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size);
+void NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size);
+
+void read_tiger(struct IsdnCardState *cs);
+void write_tiger(struct IsdnCardState *cs);
+
+void netjet_fill_dma(struct BCState *bcs);
+void netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs);
+__initfunc(void inittiger(struct IsdnCardState *cs));
+void release_io_netjet(struct IsdnCardState *cs);
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/niccy.c linux.19p1/drivers/isdn/hisax/niccy.c
--- linux.vanilla/drivers/isdn/hisax/niccy.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/niccy.c	Wed Dec 13 22:08:26 2000
@@ -1,5 +1,5 @@
-/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $
-
+/* $Id: niccy.c,v 1.12 2000/06/26 08:59:14 keil Exp $
+ *
  * niccy.c  low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
  *          compatible (SAGEM cybermodem)
  *
@@ -7,28 +7,7 @@
  * 
  * Thanks to Dr. Neuhaus and SAGEM for informations
  *
- * $Log: niccy.c,v $
- * Revision 1.8  1999/08/11 21:01:33  keil
- * new PCI codefix
- *
- * Revision 1.7  1999/08/10 16:02:04  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.6  1999/07/12 21:05:23  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.5  1999/07/01 08:12:07  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.4  1998/04/16 19:16:48  keil
- * need config.h
- *
- * Revision 1.3  1998/04/15 16:42:59  keil
- * new init code
- *
- * Revision 1.2  1998/02/11 17:31:04  keil
- * new file
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -42,7 +21,7 @@
 #include <linux/pci.h>
 
 extern const char *CardType[];
-const char *niccy_revision = "$Revision: 1.8 $";
+const char *niccy_revision = "$Revision: 1.12 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -59,8 +38,12 @@
 #define NICCY_PCI	2
 
 /* PCI stuff */
-#define PCI_VENDOR_DR_NEUHAUS	0x1267
-#define PCI_NICCY_ID		0x1016
+#ifndef PCI_VENDOR_ID_SATSAGEM
+#define PCI_VENDOR_ID_SATSAGEM	0x1267
+#endif
+#ifndef PCI_DEVICE_ID_SATSAGEM_NICCY
+#define PCI_DEVICE_ID_SATSAGEM_NICCY	0x1016
+#endif
 #define PCI_IRQ_CTRL_REG	0x38
 #define PCI_IRQ_ENABLE		0x1f00
 #define PCI_IRQ_DISABLE		0xff0000
@@ -228,12 +211,13 @@
 static void
 niccy_reset(struct IsdnCardState *cs)
 {
-	int val, nval;
-	
-	val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
-	nval = val | PCI_IRQ_ENABLE;
-	outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
 
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val |= PCI_IRQ_ENABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
 	inithscxisac(cs, 3);
 }
 
@@ -304,24 +288,26 @@
 			return(0);
 		}
 		cs->subtyp = 0;
-		if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS,
-			   PCI_NICCY_ID, niccy_dev))) {
+		if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM,
+			PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) {
+			if (pci_enable_device(niccy_dev))
+				return(0);
 			/* get IRQ */
 			if (!niccy_dev->irq) {
 				printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
 				return(0);
 			}
 			cs->irq = niccy_dev->irq;
-			if (!niccy_dev->base_address[ 0]) {
+			cs->hw.niccy.cfg_reg = niccy_dev->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK;
+			if (!cs->hw.niccy.cfg_reg) {
 				printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
 				return(0);
 			}
-			cs->hw.niccy.cfg_reg = niccy_dev->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK;
-			if (!niccy_dev->base_address[ 1]) {
+			pci_ioaddr = niccy_dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
+			if (!pci_ioaddr) {
 				printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
 				return(0);
 			}
-			pci_ioaddr = niccy_dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
 			cs->subtyp = NICCY_PCI;
 		} else {
 			printk(KERN_WARNING "Niccy: No PCI card found\n");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/nj_s.c linux.19p1/drivers/isdn/hisax/nj_s.c
--- linux.vanilla/drivers/isdn/hisax/nj_s.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/nj_s.c	Wed Dec 13 22:08:26 2000
@@ -0,0 +1,257 @@
+// $Id: nj_s.c,v 2.3 2000/06/26 08:59:14 keil Exp $
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+const char *NETjet_S_revision = "$Revision: 2.3 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static void
+netjet_s_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	long flags;
+
+	if (!cs) {
+		printk(KERN_WARNING "NETjet-S: Spurious interrupt!\n");
+		return;
+	}
+	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+		NETJET_ISACIRQ)) {
+		val = NETjet_ReadIC(cs, ISAC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", sval, val);
+		if (val) {
+			isac_interrupt(cs, val);
+			NETjet_WriteIC(cs, ISAC_MASK, 0xFF);
+			NETjet_WriteIC(cs, ISAC_MASK, 0x0);
+		}
+	}
+	save_flags(flags);
+	cli();
+	if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			restore_flags(flags);
+			return;
+		}
+		cs->hw.njet.irqstat0 = sval;
+		restore_flags(flags);
+/*		debugl1(cs, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
+			sval, 
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+*/
+/*		cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
+*/		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
+/*		byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
+*/		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ)
+			read_tiger(cs);
+		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE)
+			write_tiger(cs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		restore_flags(flags);
+
+/*	if (!testcnt--) {
+		cs->hw.njet.dmactrl = 0;
+		byteout(cs->hw.njet.base + NETJET_DMACTRL,
+			cs->hw.njet.dmactrl);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	}
+*/
+}
+
+static void
+reset_netjet_s(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	sti();
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+	cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+	restore_flags(flags);
+	cs->hw.njet.auxd = 0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_S_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_netjet_s(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_INIT:
+			inittiger(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	struct pci_dev *dev_netjet __initdata = NULL;
+
+__initfunc(int
+setup_netjet_s(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	long flags;
+#if CONFIG_PCI
+#endif
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, NETjet_S_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETjet-S driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET_S)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+	for ( ;; )
+	{
+
+#if CONFIG_PCI
+
+		if (!pci_present()) {
+			printk(KERN_ERR "Netjet: no PCI bus present\n");
+			return(0);
+		}
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "NETjet-S: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = dev_netjet->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK;
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "NETjet-S: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+		} else {
+			printk(KERN_WARNING "NETjet-S: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+
+		save_flags(flags);
+		sti();
+
+		cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+
+		restore_flags(flags);
+
+		cs->hw.njet.auxd = 0xC0;
+		cs->hw.njet.dmactrl = 0;
+
+		byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+		byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		switch ( ( ( NETjet_ReadIC( cs, ISAC_RBCH ) >> 5 ) & 3 ) )
+		{
+			case 0 :
+				break;
+
+			case 3 :
+				printk( KERN_WARNING "NETjet-S: NETspider-U PCI card found\n" );
+				continue;
+
+			default :
+				printk( KERN_WARNING "NETjet-S: No PCI card found\n" );
+				return 0;
+                }
+                break;
+	}
+#else
+
+	printk(KERN_WARNING "NETjet-S: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETjet-S: unable to config NETJET-S PCI\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"NETjet-S: PCI card configured at 0x%x IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (check_region(cs->hw.njet.base, bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.njet.base, bytecnt, "netjet-s isdn");
+	}
+	reset_netjet_s(cs);
+	cs->readisac  = &NETjet_ReadIC;
+	cs->writeisac = &NETjet_WriteIC;
+	cs->readisacfifo  = &NETjet_ReadICfifo;
+	cs->writeisacfifo = &NETjet_WriteICfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	cs->cardmsg = &NETjet_S_card_msg;
+	cs->irq_func = &netjet_s_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "NETjet-S:");
+	return (1);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/nj_u.c linux.19p1/drivers/isdn/hisax/nj_u.c
--- linux.vanilla/drivers/isdn/hisax/nj_u.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/isdn/hisax/nj_u.c	Wed Dec 13 22:08:26 2000
@@ -0,0 +1,260 @@
+/* $Id: nj_u.c,v 2.4 2000/06/26 11:42:16 keil Exp $ 
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "icc.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+const char *NETjet_U_revision = "$Revision: 2.4 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static void
+netjet_u_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	long flags;
+
+	if (!cs) {
+		printk(KERN_WARNING "NETspider-U: Spurious interrupt!\n");
+		return;
+	}
+	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+		NETJET_ISACIRQ)) {
+		val = NETjet_ReadIC(cs, ICC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", sval, val);
+		if (val) {
+			icc_interrupt(cs, val);
+			NETjet_WriteIC(cs, ICC_MASK, 0xFF);
+			NETjet_WriteIC(cs, ICC_MASK, 0x0);
+		}
+	}
+	save_flags(flags);
+	cli();
+	if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			restore_flags(flags);
+			return;
+		}
+		cs->hw.njet.irqstat0 = sval;
+		restore_flags(flags);
+/*		debugl1(cs, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
+			sval, 
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+*/
+/*		cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
+*/		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
+/*		byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
+*/		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ)
+			read_tiger(cs);
+		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE)
+			write_tiger(cs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		restore_flags(flags);
+
+/*	if (!testcnt--) {
+		cs->hw.njet.dmactrl = 0;
+		byteout(cs->hw.njet.base + NETJET_DMACTRL,
+			cs->hw.njet.dmactrl);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	}
+*/
+}
+
+static void
+reset_netjet_u(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	sti();
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+	cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+	restore_flags(flags);
+	cs->hw.njet.auxd = 0xC0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.auxa, 0);
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_U_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_netjet_u(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_INIT:
+			inittiger(cs);
+			clear_pending_icc_ints(cs);
+			initicc(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ICC_MASK, 0);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	struct pci_dev *dev_netjet __initdata = NULL;
+
+__initfunc(int
+setup_netjet_u(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	long flags;
+#if CONFIG_PCI
+#endif
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, NETjet_U_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETspider-U driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET_U)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+	for ( ;; )
+	{
+
+#if CONFIG_PCI
+
+		if (!pci_present()) {
+			printk(KERN_ERR "NETspider-U: no PCI bus present\n");
+			return(0);
+		}
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "NETspider-U: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = dev_netjet->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK;
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "NETspider-U: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+		} else {
+			printk(KERN_WARNING "NETspider-U: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+
+		save_flags(flags);
+		sti();
+
+		cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+
+		restore_flags(flags);
+
+		cs->hw.njet.auxd = 0xC0;
+		cs->hw.njet.dmactrl = 0;
+
+		byteout(cs->hw.njet.auxa, 0);
+		byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+		byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		switch ( ( ( NETjet_ReadIC( cs, ICC_RBCH ) >> 5 ) & 3 ) )
+		{
+			case 3 :
+				break;
+
+			case 0 :
+				printk( KERN_WARNING "NETspider-U: NETjet-S PCI card found\n" );
+				continue;
+
+			default :
+				printk( KERN_WARNING "NETspider-U: No PCI card found\n" );
+				return 0;
+                }
+                break;
+	}
+#else
+
+	printk(KERN_WARNING "NETspider-U: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETspider-U: unable to config NETspider-U PCI\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"NETspider-U: PCI card configured at 0x%x IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (check_region(cs->hw.njet.base, bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.njet.base, bytecnt, "netjet-u isdn");
+	}
+	reset_netjet_u(cs);
+	cs->readisac  = &NETjet_ReadIC;
+	cs->writeisac = &NETjet_WriteIC;
+	cs->readisacfifo  = &NETjet_ReadICfifo;
+	cs->writeisacfifo = &NETjet_WriteICfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	cs->cardmsg = &NETjet_U_card_msg;
+	cs->irq_func = &netjet_u_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ICCVersion(cs, "NETspider-U:");
+	return (1);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/q931.c linux.19p1/drivers/isdn/hisax/q931.c
--- linux.vanilla/drivers/isdn/hisax/q931.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/q931.c	Wed Dec 13 22:08:26 2000
@@ -1,9 +1,11 @@
-/* $Id: q931.c,v 1.7 1998/11/15 23:55:17 keil Exp $
-
+/* $Id: q931.c,v 1.10 2000/06/26 08:59:14 keil Exp $
+ *
  * q931.c       code to decode ITU Q.931 call control messages
  *
  * Author       Jan den Ouden
  *
+ * This file is (c) under GNU PUBLIC LICENSE
+ *
  * Changelog
  *
  * Pauline Middelink    general improvements
@@ -12,30 +14,6 @@
  *
  * Karsten Keil         cause texts, display information element for 1TR6
  *
- *
- * $Log: q931.c,v $
- * Revision 1.7  1998/11/15 23:55:17  keil
- * changes from 2.0
- *
- * Revision 1.6  1997/07/27 21:09:44  keil
- * move functions to isdnl3.c
- *
- * Revision 1.5  1997/04/06 22:56:43  keil
- * Some cosmetic changes
- *
- * Revision 1.4  1997/02/09 00:29:11  keil
- * new interface handling, one interface per card
- *
- * Revision 1.3  1997/01/21 22:24:59  keil
- * cleanups
- *
- * Revision 1.2  1996/10/27 22:12:45  keil
- * reporting unknown level 3 protocol ids
- *
- * Revision 1.1  1996/10/13 20:04:56  keil
- * Initial revision
- *
- *
  */
 
 
@@ -87,6 +65,24 @@
 		0xd, "SETUP ACKNOWLEDGE"
 	},
 	{
+		0x24, "HOLD"
+	},
+	{
+		0x28, "HOLD ACKNOWLEDGE"
+	},
+	{
+		0x30, "HOLD REJECT"
+	},
+	{
+		0x31, "RETRIEVE"
+	},
+	{
+		0x33, "RETRIEVE ACKNOWLEDGE"
+	},
+	{
+		0x37, "RETRIEVE REJECT"
+	},
+	{
 		0x26, "RESUME"
 	},
 	{
@@ -201,29 +197,6 @@
 
 #define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
 
-static struct MessageType fac_1tr6[] =
-{
-	{FAC_Sperre, "Sperre"},
-	{FAC_Forward1, "Forward 1"},
-	{FAC_Forward2, "Forward 2"},
-	{FAC_Konferenz, "Konferenz"},
-	{FAC_GrabBchan, "Grab Bchannel"},
-	{FAC_Reactivate, "Reactivate"},
-	{FAC_Konferenz3, "Dreier Konferenz"},
-	{FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"},
-	{FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"},
-	{FAC_NummernIdent, "Rufnummer-Identifizierung"},
-	{FAC_GBG, "GBG"},
-	{FAC_DisplayUebergeben, "Display Uebergeben"},
-	{FAC_DisplayUmgeleitet, "Display Umgeleitet"},
-	{FAC_Unterdruecke, "Unterdruecke Rufnummer"},
-	{FAC_Deactivate, "Deactivate"},
-	{FAC_Activate, "Activate"},
-	{FAC_SPV, "SPV"},
-	{FAC_Rueckwechsel, "Rueckwechsel"},
-	{FAC_Umleitung, "Umleitung"}
-};
-#define FAC_1TR6_LEN (sizeof(fac_1tr6) / sizeof(struct MessageType))
 
 static int
 prbits(char *dest, u_char b, int start, int len)
@@ -683,6 +656,65 @@
 	return (dp - dest);
 }
 
+
+static
+int
+prbearer_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	u_char len;
+
+	p++;
+	len = *p++;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p, 8, 8);
+	switch (*p++) {
+		case 0x80:
+			dp += sprintf(dp, " Speech");
+			break;
+		case 0x88:
+			dp += sprintf(dp, " Unrestricted digital information");
+			break;
+		case 0x90:
+			dp += sprintf(dp, " 3.1 kHz audio");
+			break;
+		default:
+			dp += sprintf(dp, " Unknown information-transfer capability");
+	}
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	switch (*p++) {
+		case 0x90:
+			dp += sprintf(dp, " 64 kbps, circuit mode");
+			break;
+		case 0xc0:
+			dp += sprintf(dp, " Packet mode");
+			break;
+		default:
+			dp += sprintf(dp, " Unknown transfer mode");
+	}
+	*dp++ = '\n';
+	if (len > 2) {
+		dp += sprintf(dp, "    octet 5  ");
+		dp += prbits(dp, *p, 8, 8);
+		switch (*p++) {
+			case 0x21:
+				dp += sprintf(dp, " Rate adaption\n");
+				dp += sprintf(dp, "    octet 5a ");
+				dp += prbits(dp, *p, 8, 8);
+				break;
+			case 0xa2:
+				dp += sprintf(dp, " u-law");
+				break;
+			default:
+				dp += sprintf(dp, " Unknown UI layer 1 protocol");
+		}
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
 static int
 general(char *dest, u_char * p)
 {
@@ -711,6 +743,33 @@
 }
 
 static int
+general_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p++ & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
 prcharge(char *dest, u_char * p)
 {
 	char *dp = dest;
@@ -742,6 +801,112 @@
 	*dp++ = '\n';
 	return (dp - dest);
 }
+
+static int
+prfeatureind(char *dest, u_char * p)
+{
+	char *dp = dest;
+
+	p += 2; /* skip id, len */
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p++ & 80)) {
+		dp += sprintf(dp, "    octet 4  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	dp += sprintf(dp, "    Status:  ");
+	switch (*p) {
+		case 0:
+			dp += sprintf(dp, "Idle");
+			break;
+		case 1:
+			dp += sprintf(dp, "Active");
+			break;
+		case 2:
+			dp += sprintf(dp, "Prompt");
+			break;
+		case 3:
+			dp += sprintf(dp, "Pending");
+			break;
+		default:
+			dp += sprintf(dp, "(Reserved)");
+			break;
+	}
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+struct DTag { /* Display tags */
+	u_char nr;
+	char *descr;
+} dtaglist[] = {
+	{ 0x82, "Continuation" },
+	{ 0x83, "Called address" },
+	{ 0x84, "Cause" },
+	{ 0x85, "Progress indicator" },
+	{ 0x86, "Notification indicator" },
+	{ 0x87, "Prompt" },
+	{ 0x88, "Accumlated digits" },
+	{ 0x89, "Status" },
+	{ 0x8a, "Inband" },
+	{ 0x8b, "Calling address" },
+	{ 0x8c, "Reason" },
+	{ 0x8d, "Calling party name" },
+	{ 0x8e, "Called party name" },
+	{ 0x8f, "Orignal called name" },
+	{ 0x90, "Redirecting name" },
+	{ 0x91, "Connected name" },
+	{ 0x92, "Originating restrictions" },
+	{ 0x93, "Date & time of day" },
+	{ 0x94, "Call Appearance ID" },
+	{ 0x95, "Feature address" },
+	{ 0x96, "Redirection name" },
+	{ 0x9e, "Text" },
+};
+#define DTAGSIZE sizeof(dtaglist)/sizeof(struct DTag)
+
+static int
+disptext_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l, tag, len, i;
+
+	p++;
+	l = *p++ - 1;
+	if (*p++ != 0x80) {
+		dp += sprintf(dp, "    Unknown display type\n");
+		return (dp - dest);
+	}
+	/* Iterate over all tag,length,text fields */
+	while (l > 0) {
+		tag = *p++;
+		len = *p++;
+		l -= len + 2;
+		/* Don't space or skip */
+		if ((tag == 0x80) || (tag == 0x81)) p++;
+		else {
+			for (i = 0; i < DTAGSIZE; i++)
+				if (tag == dtaglist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != DTAGSIZE) {
+				dp += sprintf(dp, "    %s: ", dtaglist[i].descr);
+				while (len--)
+					*dp++ = *p++;
+			} else {
+				dp += sprintf(dp, "    (unknown display tag %2x): ", tag);
+				while (len--)
+					*dp++ = *p++;
+			}
+			dp += sprintf(dp, "\n");
+                }
+	}
+	return (dp - dest);
+}
 static int
 display(char *dest, u_char * p)
 {
@@ -912,6 +1077,49 @@
 
 #define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
 
+static
+struct InformationElement ielist_ni1[] = {
+	{ 0x04, "Bearer Capability", prbearer_ni1 },
+	{ 0x08, "Cause", prcause },
+	{ 0x14, "Call State", general_ni1 },
+	{ 0x18, "Channel Identification", prchident },
+	{ 0x1e, "Progress Indicator", general_ni1 },
+	{ 0x27, "Notification Indicator", general_ni1 },
+	{ 0x2c, "Keypad Facility", prtext },
+	{ 0x32, "Information Request", general_ni1 },
+	{ 0x34, "Signal", general_ni1 },
+	{ 0x38, "Feature Activation", general_ni1 },
+	{ 0x39, "Feature Indication", prfeatureind },
+	{ 0x3a, "Service Profile Identification (SPID)", prtext },
+	{ 0x3b, "Endpoint Identifier", general_ni1 },
+	{ 0x6c, "Calling Party Number", prcalling },
+	{ 0x6d, "Calling Party Subaddress", general_ni1 },
+	{ 0x70, "Called Party Number", prcalled },
+	{ 0x71, "Called Party Subaddress", general_ni1 },
+	{ 0x74, "Redirecting Number", general_ni1 },
+	{ 0x78, "Transit Network Selection", general_ni1 },
+	{ 0x7c, "Low Layer Compatibility", general_ni1 },
+	{ 0x7d, "High Layer Compatibility", general_ni1 },
+};
+
+
+#define IESIZE_NI1 sizeof(ielist_ni1)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs5[] = {
+	{ 0x1d, "Operator system access", general_ni1 },
+	{ 0x2a, "Display text", disptext_ni1 },
+};
+
+#define IESIZE_NI1_CS5 sizeof(ielist_ni1_cs5)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs6[] = {
+	{ 0x7b, "Call appearance", general_ni1 },
+};
+
+#define IESIZE_NI1_CS6 sizeof(ielist_ni1_cs6)/sizeof(struct InformationElement)
+
 static struct InformationElement we_0[] =
 {
 	{WE0_cause, "Cause", prcause_1tr6},
@@ -1152,7 +1360,93 @@
 			}
 			buf += buf[1] + 2;
 		}
-	} else if (buf[0] == 8) {	/* EURO */
+	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) {	/* NI-1 */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cset;
+						cset = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					default:
+						dp += sprintf(dp, "  Unknown single-octet IE %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cset == 0) {
+				for (i = 0; i < IESIZE; i++)
+					if (*buf == ielist_ni1[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1[i].descr);
+					dp += ielist_ni1[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else if (cset == 5) {
+				for (i = 0; i < IESIZE_NI1_CS5; i++)
+					if (*buf == ielist_ni1_cs5[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE_NI1_CS5) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1_cs5[i].descr);
+					dp += ielist_ni1_cs5[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else if (cset == 6) {
+				for (i = 0; i < IESIZE_NI1_CS6; i++)
+					if (*buf == ielist_ni1_cs6[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE_NI1_CS6) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1_cs6[i].descr);
+					dp += ielist_ni1_cs6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cset = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */
 		/* locate message type */
 		buf++;
 		cr_l = *buf++;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/rawhdlc.c linux.19p1/drivers/isdn/hisax/rawhdlc.c
--- linux.vanilla/drivers/isdn/hisax/rawhdlc.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/rawhdlc.c	Wed Dec 13 22:08:26 2000
@@ -1,10 +1,11 @@
-/* $Id: rawhdlc.c,v 1.4 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: rawhdlc.c,v 1.5 2000/06/26 08:59:14 keil Exp $
+ *
  * rawhdlc.c     support routines for cards that don't support HDLC
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *            Brent Baccala <baccala@FreeSoft.org>
  *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  * Some passive ISDN cards, such as the Traverse NETJet and the AMD 7930,
  * don't perform HDLC encapsulation over the B channel.  Drivers for
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/rawhdlc.h linux.19p1/drivers/isdn/hisax/rawhdlc.h
--- linux.vanilla/drivers/isdn/hisax/rawhdlc.h	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/rawhdlc.h	Wed Dec 13 22:08:26 2000
@@ -1,8 +1,10 @@
-/* $Id: rawhdlc.h,v 1.2 1998/02/09 10:53:53 keil Exp $
-
+/* $Id: rawhdlc.h,v 1.3 2000/06/26 08:59:14 keil Exp $
+ *
  * rawhdlc.h     support routines for cards that don't support HDLC
  *
  * Author     Brent Baccala <baccala@FreeSoft.org>
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/s0box.c linux.19p1/drivers/isdn/hisax/s0box.c
--- linux.vanilla/drivers/isdn/hisax/s0box.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/s0box.c	Wed Dec 13 22:08:26 2000
@@ -1,9 +1,10 @@
-/* $Id: s0box.c,v 2.2 1999/07/12 21:05:25 keil Exp $
-
+/* $Id: s0box.c,v 2.3 2000/06/26 08:59:14 keil Exp $
+ *
  * s0box.c      low level stuff for Creatix S0BOX
  *
  * Author       S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de)
  *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -13,7 +14,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *s0box_revision = "$Revision: 2.2 $";
+const char *s0box_revision = "$Revision: 2.3 $";
 
 static inline void
 writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/saphir.c linux.19p1/drivers/isdn/hisax/saphir.c
--- linux.vanilla/drivers/isdn/hisax/saphir.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/saphir.c	Wed Dec 13 22:08:26 2000
@@ -1,27 +1,12 @@
-/* $Id: saphir.c,v 1.5 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: saphir.c,v 1.7 2000/06/26 08:59:14 keil Exp $
+ *
  * saphir.c low level stuff for HST Saphir 1
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to    HST High Soft Tech GmbH
  *
- *
- * $Log: saphir.c,v $
- * Revision 1.5  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.4  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.3  1999/07/12 21:05:26  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.2  1999/07/01 08:07:55  keil
- * Initial version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -33,7 +18,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-static char *saphir_rev = "$Revision: 1.5 $";
+static char *saphir_rev = "$Revision: 1.7 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -173,11 +158,9 @@
 		goto Start_ISAC;
 	}
 	/* Watchdog */
-	if (cs->hw.saphir.timer.function) {
-		del_timer(&cs->hw.saphir.timer);
-		cs->hw.saphir.timer.expires = jiffies + 1*HZ;
-		add_timer(&cs->hw.saphir.timer);
-	} else
+	if (cs->hw.saphir.timer.function) 
+		mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+	else
 		printk(KERN_WARNING "saphir: Spurious timer!\n");
 	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF);
 	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF);
@@ -192,9 +175,7 @@
 {
         /* 5 sec WatchDog, so read at least every 4 sec */
 	cs->readisac(cs, ISAC_RBCH);
-	del_timer(&cs->hw.saphir.timer);
-	cs->hw.saphir.timer.expires = jiffies + 1*HZ;
-	add_timer(&cs->hw.saphir.timer);
+	mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
 }
 
 void
@@ -241,10 +222,10 @@
 	save_flags(flags);
 	sti();
 	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1);
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);	/* Timeout 30ms */
 	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0);
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);	/* Timeout 30ms */
 	restore_flags(flags);
 	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/sedlbauer.c linux.19p1/drivers/isdn/hisax/sedlbauer.c
--- linux.vanilla/drivers/isdn/hisax/sedlbauer.c	Fri Apr 21 12:46:11 2000
+++ linux.19p1/drivers/isdn/hisax/sedlbauer.c	Wed Dec 13 22:08:26 2000
@@ -1,5 +1,5 @@
-/* $Id: sedlbauer.c,v 1.20 2000/01/20 19:47:45 keil Exp $
-
+/* $Id: sedlbauer.c,v 1.23 2000/06/26 08:59:14 keil Exp $
+ *
  * sedlbauer.c  low level stuff for Sedlbauer cards
  *              includes support for the Sedlbauer speed star (speed star II),
  *              support for the Sedlbauer speed fax+,
@@ -16,71 +16,7 @@
  *            Sedlbauer AG for informations
  *            Edgar Toernig
  *
- * $Log: sedlbauer.c,v $
- * Revision 1.20  2000/01/20 19:47:45  keil
- * Add Fax Class 1 support
- *
- * Revision 1.19  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.18  1999/11/13 21:25:03  keil
- * Support for Speedfax+ PCI
- *
- * Revision 1.17  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.16  1999/08/29 18:23:01  niemann
- * Fixed typo in errormsg
- *
- * Revision 1.15  1999/08/25 17:00:00  keil
- * Make ISAR V32bis modem running
- * Make LL->HL interface open for additional commands
- *
- * Revision 1.14  1999/08/11 20:59:22  keil
- * new PCI codefix
- * fix IRQ problem while unload
- *
- * Revision 1.13  1999/08/10 16:02:08  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 1.12  1999/08/05 20:43:22  keil
- * ISAR analog modem support
- *
- * Revision 1.11  1999/07/12 21:05:27  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.10  1999/07/01 08:12:09  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.9  1998/11/15 23:55:20  keil
- * changes from 2.0
- *
- * Revision 1.8  1998/08/13 23:34:51  keil
- * starting speedfax+ (ISAR) support
- *
- * Revision 1.7  1998/04/15 16:44:33  keil
- * new init code
- *
- * Revision 1.6  1998/02/09 18:46:06  keil
- * Support for Sedlbauer PCMCIA (Marcus Niemann)
- *
- * Revision 1.5  1998/02/02 13:29:45  keil
- * fast io
- *
- * Revision 1.4  1997/11/08 21:35:52  keil
- * new l1 init
- *
- * Revision 1.3  1997/11/06 17:09:28  keil
- * New 2.1 init code
- *
- * Revision 1.2  1997/10/29 18:55:52  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 1.1  1997/09/11 17:32:04  keil
- * new
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -103,8 +39,6 @@
  * For example: hisaxctrl <DriverID> 9 ISAR.BIN
 */
 
-#define SEDLBAUER_PCI 1
-
 #define __NO_VERSION__
 #include <linux/config.h>
 #include "hisax.h"
@@ -117,19 +51,23 @@
 
 extern const char *CardType[];
 
-const char *Sedlbauer_revision = "$Revision: 1.20 $";
+const char *Sedlbauer_revision = "$Revision: 1.23 $";
 
 const char *Sedlbauer_Types[] =
 	{"None", "speed card/win", "speed star", "speed fax+", 
 	"speed win II / ISDN PC/104", "speed star II", "speed pci",
-	"speed fax+ pci"};
+	"speed fax+ pyramid", "speed fax+ pci"};
 
-#ifdef SEDLBAUER_PCI
-#define PCI_VENDOR_SEDLBAUER	0xe159
-#define PCI_SPEEDPCI_ID		0x02
-#define PCI_SUBVENDOR_SEDLBAUER	0x51
-#define PCI_SUB_ID_SPEEDFAXP	0x01
+#ifndef PCI_VENDOR_ID_TIGERJET
+#define PCI_VENDOR_ID_TIGERJET		0xe159
 #endif
+#ifndef PCI_DEVICE_ID_TIGERJET_100
+#define PCI_DEVICE_ID_TIGERJET_100	0x0002
+#endif
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID	0x51
+#define PCI_SUBVENDOR_SEDLBAUER_PCI	0x53
+#define PCI_SUBVENDOR_SPEEDFAX_PCI	0x54
+#define PCI_SUB_ID_SEDLBAUER		0x01
  
 #define SEDL_SPEED_CARD_WIN	1
 #define SEDL_SPEED_STAR 	2
@@ -137,7 +75,8 @@
 #define SEDL_SPEED_WIN2_PC104 	4
 #define SEDL_SPEED_STAR2 	5
 #define SEDL_SPEED_PCI   	6
-#define SEDL_SPEEDFAX_PCI	7
+#define SEDL_SPEEDFAX_PYRAMID	7
+#define SEDL_SPEEDFAX_PCI	8
 
 #define SEDL_CHIP_TEST		0
 #define SEDL_CHIP_ISAC_HSCX	1
@@ -349,10 +288,10 @@
 	}
 
 	if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
-          /* The card tends to generate interrupts while being removed
-             causing us to just crash the kernel. bad. */
-          printk(KERN_WARNING "Sedlbauer: card not available!\n");
-          return;
+		/* The card tends to generate interrupts while being removed
+		   causing us to just crash the kernel. bad. */
+		printk(KERN_WARNING "Sedlbauer: card not available!\n");
+		return;
         }
 
 	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
@@ -424,7 +363,8 @@
 		goto Start_IPAC;
 	}
 	if (!icnt)
-		printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Sedlbauer IRQ LOOP");
 	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
 	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
 }
@@ -462,7 +402,8 @@
 		goto Start_ISAC;
 	}
 	if (!cnt)
-		printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Sedlbauer IRQ LOOP");
 
 	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
 	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
@@ -497,10 +438,10 @@
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
 			save_flags(flags);
 			sti();
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
@@ -523,10 +464,10 @@
 			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
 			save_flags(flags);
 			sti();
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
-			current->state = TASK_UNINTERRUPTIBLE;
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			restore_flags(flags);
 		}
@@ -572,7 +513,7 @@
 		case CARD_TEST:
 			return(0);
 		case MDL_INFO_CONN:
-			if (cs->subtyp != SEDL_SPEEDFAX_PCI)
+			if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
 				return(0);
 			if ((long) arg)
 				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2;
@@ -581,7 +522,7 @@
 			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
 			break;
 		case MDL_INFO_REL:
-			if (cs->subtyp != SEDL_SPEEDFAX_PCI)
+			if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
 				return(0);
 			if ((long) arg)
 				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2;
@@ -593,9 +534,7 @@
 	return(0);
 }
 
-#ifdef SEDLBAUER_PCI
 static 	struct pci_dev *dev_sedl __initdata = NULL;
-#endif
 
 __initfunc(int
 setup_sedlbauer(struct IsdnCard *card))
@@ -633,42 +572,50 @@
 		}
 	} else {
 /* Probe for Sedlbauer speed pci */
-#if SEDLBAUER_PCI
 #if CONFIG_PCI
 		if (!pci_present()) {
 			printk(KERN_ERR "Sedlbauer: no PCI bus present\n");
 			return(0);
 		}
-		if ((dev_sedl = pci_find_device(PCI_VENDOR_SEDLBAUER,
-				PCI_SPEEDPCI_ID, dev_sedl))) {
+		if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+				PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) {
+			if (pci_enable_device(dev_sedl))
+				return(0);
 			cs->irq = dev_sedl->irq;
 			if (!cs->irq) {
 				printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
 				return(0);
 			}
-			cs->hw.sedl.cfg_reg = dev_sedl->base_address[ 0] &
-				PCI_BASE_ADDRESS_IO_MASK; 
+			cs->hw.sedl.cfg_reg = dev_sedl->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK;
 		} else {
 			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
 			return(0);
 		}
 		cs->irq_flags |= SA_SHIRQ;
 		cs->hw.sedl.bus = SEDL_BUS_PCI;
-		pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_VENDOR_ID,
-			&sub_vendor_id);
-		pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_ID,
-			&sub_id);
+		pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor_id);
+		pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_ID, &sub_id);
 		printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
 			sub_vendor_id, sub_id);
 		printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
 			cs->hw.sedl.cfg_reg);
-		if ((sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER) &&
-			(sub_id == PCI_SUB_ID_SPEEDFAXP)) {
+		if (sub_id != PCI_SUB_ID_SEDLBAUER) {
+			printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id);
+			return(0);
+		}
+		if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) {
+			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+			cs->subtyp = SEDL_SPEEDFAX_PYRAMID;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) {
 			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
 			cs->subtyp = SEDL_SPEEDFAX_PCI;
-		} else {
+		} else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) {
 			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
 			cs->subtyp = SEDL_SPEED_PCI;
+		} else {
+			printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n",
+				sub_vendor_id);
+			return(0);
 		}
 		bytecnt = 256;
 		cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON;
@@ -688,7 +635,6 @@
 		printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
 		return (0);
 #endif /* CONFIG_PCI */
-#endif /* SEDLBAUER_PCI */
 	}	
 	
        	/* In case of the sedlbauer pcmcia card, this region is in use,
@@ -748,7 +694,6 @@
 
 
 	if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
-	/* IPAC */
 		if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
 	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
         	        cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/sportster.c linux.19p1/drivers/isdn/hisax/sportster.c
--- linux.vanilla/drivers/isdn/hisax/sportster.c	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/sportster.c	Wed Dec 13 22:08:26 2000
@@ -1,49 +1,12 @@
-/* $Id: sportster.c,v 1.12 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: sportster.c,v 1.13 2000/06/26 08:59:14 keil Exp $
+ *
  * sportster.c     low level stuff for USR Sportster internal TA
  *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
  *
- * $Log: sportster.c,v $
- * Revision 1.12  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 1.11  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.10  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.9  1999/07/12 21:05:29  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.8  1999/07/01 08:12:10  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.7  1998/11/15 23:55:22  keil
- * changes from 2.0
- *
- * Revision 1.6  1998/04/15 16:44:35  keil
- * new init code
- *
- * Revision 1.5  1998/02/02 13:29:46  keil
- * fast io
- *
- * Revision 1.4  1997/11/08 21:35:52  keil
- * new l1 init
- *
- * Revision 1.3  1997/11/06 17:09:29  keil
- * New 2.1 init code
- *
- * Revision 1.2  1997/10/29 18:51:18  keil
- * New files
- *
- * Revision 1.1.2.1  1997/10/17 22:10:58  keil
- * new files on 2.0
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -53,7 +16,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *sportster_revision = "$Revision: 1.12 $";
+const char *sportster_revision = "$Revision: 1.13 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -187,11 +150,11 @@
 	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
 	save_flags(flags);
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
 	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	restore_flags(flags);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/tei.c linux.19p1/drivers/isdn/hisax/tei.c
--- linux.vanilla/drivers/isdn/hisax/tei.c	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/tei.c	Wed Dec 13 22:08:26 2000
@@ -1,5 +1,5 @@
-/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $
-
+/* $Id: tei.c,v 2.15 2000/06/26 08:59:14 keil Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
@@ -10,75 +10,13 @@
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
- * $Log: tei.c,v $
- * Revision 2.13  1999/07/21 14:46:28  keil
- * changes from EICON certification
- *
- * Revision 2.12  1999/07/01 08:12:11  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.11  1998/11/15 23:55:24  keil
- * changes from 2.0
- *
- * Revision 2.10  1998/05/25 14:08:10  keil
- * HiSax 3.0
- * fixed X.75 and leased line to work again
- * Point2Point and fixed TEI are runtime options now:
- *    hisaxctrl <id> 7 1  set PTP
- *    hisaxctrl <id> 8 <TEIVALUE *2 >
- *    set fixed TEI to TEIVALUE (0-63)
- *
- * Revision 2.9  1998/05/25 12:58:23  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.8  1998/03/07 22:57:07  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 2.7  1998/02/12 23:08:11  keil
- * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
- *
- * Revision 2.6  1998/02/02 13:41:42  keil
- * fix MDL_ASSIGN for PtP
- *
- * Revision 2.5  1997/11/06 17:09:12  keil
- * New 2.1 init code
- *
- * Revision 2.4  1997/10/29 19:04:46  keil
- * changes for 2.1
- *
- * Revision 2.3  1997/10/01 09:21:43  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 2.2  1997/07/31 19:24:39  keil
- * fixed a warning
- *
- * Revision 2.1  1997/07/31 11:50:16  keil
- * ONE TEI and FIXED TEI handling
- *
- * Revision 2.0  1997/07/27 21:13:30  keil
- * New TEI managment
- *
- * Revision 1.9  1997/06/26 11:18:02  keil
- * New managment
- *
- * Revision 1.8  1997/04/07 22:59:08  keil
- * GFP_KERNEL --> GFP_ATOMIC
- *
- * Revision 1.7  1997/04/06 22:54:03  keil
- * Using SKB's
- *
- * Old log removed/ KKe
- *
  */
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl2.h"
 #include <linux/random.h>
 
-const char *tei_revision = "$Revision: 2.13 $";
+const char *tei_revision = "$Revision: 2.15 $";
 
 #define ID_REQUEST	1
 #define ID_ASSIGNED	2
@@ -216,7 +154,7 @@
 	if (st->ma.debug)
 		st->ma.tei_m.printdebug(&st->ma.tei_m,
 			"identity assign ri %d tei %d", ri, tei);
-	if ((ost = findtei(st, tei))) {		/* same tei is in use */
+	if ((ost = findtei(st, tei))) {	/* same tei is in use */
 		if (ri != ost->ma.ri) {
 			st->ma.tei_m.printdebug(&st->ma.tei_m,
 				"possible duplicate assignment tei %d", tei);
@@ -243,10 +181,12 @@
 	if (st->ma.debug)
 		st->ma.tei_m.printdebug(&st->ma.tei_m,
 			"foreign identity assign ri %d tei %d", ri, tei);
-	if ((ost = findtei(st, tei))) {		/* same tei is in use */
-		st->ma.tei_m.printdebug(&st->ma.tei_m,
-			"possible duplicate assignment tei %d", tei);
-		FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL);
+	if ((ost = findtei(st, tei))) {	/* same tei is in use */
+		if (ri != ost->ma.ri) {	/* and it wasn't our request */
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL);
+		}
 	} 
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/teleint.c linux.19p1/drivers/isdn/hisax/teleint.c
--- linux.vanilla/drivers/isdn/hisax/teleint.c	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/teleint.c	Wed Dec 13 22:08:26 2000
@@ -1,49 +1,10 @@
-/* $Id: teleint.c,v 1.12 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: teleint.c,v 1.13 2000/06/26 08:59:14 keil Exp $
+ *
  * teleint.c     low level stuff for TeleInt isdn cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: teleint.c,v $
- * Revision 1.12  1999/12/19 13:09:42  keil
- * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
- * signal proof delays
- *
- * Revision 1.11  1999/09/04 06:20:06  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.10  1999/08/31 11:20:27  paul
- * various spelling corrections (new checksums may be needed, Karsten!)
- *
- * Revision 1.9  1999/07/12 21:05:30  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 1.8  1999/07/01 08:12:12  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 1.7  1998/11/15 23:55:26  keil
- * changes from 2.0
- *
- * Revision 1.6  1998/04/15 16:45:31  keil
- * new init code
- *
- * Revision 1.5  1998/02/02 13:40:47  keil
- * fast io
- *
- * Revision 1.4  1997/11/08 21:35:53  keil
- * new l1 init
- *
- * Revision 1.3  1997/11/06 17:09:30  keil
- * New 2.1 init code
- *
- * Revision 1.2  1997/10/29 18:55:53  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 1.1  1997/09/11 17:32:32  keil
- * new
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
@@ -55,7 +16,7 @@
 
 extern const char *CardType[];
 
-const char *TeleInt_revision = "$Revision: 1.12 $";
+const char *TeleInt_revision = "$Revision: 1.13 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -264,11 +225,11 @@
 	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset On */
 	save_flags(flags);
 	sti();
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);
 	cs->hw.hfc.cirm &= ~HFC_RESET;
 	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset Off */
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	restore_flags(flags);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/teles0.c linux.19p1/drivers/isdn/hisax/teles0.c
--- linux.vanilla/drivers/isdn/hisax/teles0.c	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/teles0.c	Wed Dec 13 22:08:26 2000
@@ -1,5 +1,5 @@
-/* $Id: teles0.c,v 2.11 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: teles0.c,v 2.12 2000/06/26 08:59:14 keil Exp $
+ *
  * teles0.c     low level stuff for Teles Memory IO isdn cards
  *              based on the teles driver from Jan den Ouden
  *
@@ -9,51 +9,7 @@
  *              Fritz Elfert
  *              Beat Doebeli
  *
- * $Log: teles0.c,v $
- * Revision 2.11  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 2.10  1999/11/14 23:37:03  keil
- * new ISA memory mapped IO
- *
- * Revision 2.9  1999/07/12 21:05:31  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.8  1998/04/15 16:44:28  keil
- * new init code
- *
- * Revision 2.7  1998/03/07 22:57:08  tsbogend
- * made HiSax working on Linux/Alpha
- *
- * Revision 2.6  1998/02/03 23:27:47  keil
- * IRQ 9
- *
- * Revision 2.5  1998/02/02 13:29:47  keil
- * fast io
- *
- * Revision 2.4  1997/11/08 21:35:54  keil
- * new l1 init
- *
- * Revision 2.3  1997/11/06 17:09:31  keil
- * New 2.1 init code
- *
- * Revision 2.2  1997/10/29 18:55:57  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 2.1  1997/07/27 21:47:10  keil
- * new interface structures
- *
- * Revision 2.0  1997/06/26 11:02:43  keil
- * New Layer and card interface
- *
- * Revision 1.8  1997/04/13 19:54:04  keil
- * Change in IRQ check delay for SMP
- *
- * Revision 1.7  1997/04/06 22:54:04  keil
- * Using SKB's
- *
- * removed old log info /KKe
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -64,7 +20,7 @@
 
 extern const char *CardType[];
 
-const char *teles0_revision = "$Revision: 2.11 $";
+const char *teles0_revision = "$Revision: 2.12 $";
 
 #define TELES_IOMEM_SIZE	0x400
 #define byteout(addr,val) outb(val,addr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/teles3.c linux.19p1/drivers/isdn/hisax/teles3.c
--- linux.vanilla/drivers/isdn/hisax/teles3.c	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/teles3.c	Wed Dec 13 22:08:26 2000
@@ -1,5 +1,5 @@
-/* $Id: teles3.c,v 2.15 2000/02/03 16:40:10 keil Exp $
-
+/* $Id: teles3.c,v 2.16 2000/06/26 08:59:15 keil Exp $
+ *
  * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
  *
  *              based on the teles driver from Jan den Ouden
@@ -10,81 +10,7 @@
  *              Fritz Elfert
  *              Beat Doebeli
  *
- * $Log: teles3.c,v $
- * Revision 2.15  2000/02/03 16:40:10  keil
- * Fix teles pcmcia
- *
- * Revision 2.14  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 2.13  1999/08/30 12:01:28  keil
- * HW version v1.3 support
- *
- * Revision 2.12  1999/07/12 21:05:32  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.11  1999/07/01 08:12:14  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.10  1999/02/15 14:37:15  cpetig
- * oops, missed something in last commit
- *
- * Revision 2.9  1999/02/15 14:11:02  cpetig
- * fixed a bug with Teles PCMCIA, it doesn't have a config register
- *
- * Revision 2.8  1998/04/15 16:44:30  keil
- * new init code
- *
- * Revision 2.7  1998/02/02 13:29:48  keil
- * fast io
- *
- * Revision 2.6  1997/11/13 16:22:44  keil
- * COMPAQ_ISA reset
- *
- * Revision 2.5  1997/11/12 15:01:25  keil
- * COMPAQ_ISA changes
- *
- * Revision 2.4  1997/11/08 21:35:56  keil
- * new l1 init
- *
- * Revision 2.3  1997/11/06 17:09:33  keil
- * New 2.1 init code
- *
- * Revision 2.2  1997/10/29 18:55:59  keil
- * changes for 2.1.60 (irq2dev_map)
- *
- * Revision 2.1  1997/07/27 21:47:12  keil
- * new interface structures
- *
- * Revision 2.0  1997/06/26 11:02:46  keil
- * New Layer and card interface
- *
- * Revision 1.11  1997/04/13 19:54:05  keil
- * Change in IRQ check delay for SMP
- *
- * Revision 1.10  1997/04/06 22:54:05  keil
- * Using SKB's
- *
- * Revision 1.9  1997/03/22 02:01:07  fritz
- * -Reworked toplevel Makefile. From now on, no different Makefiles
- *  for standalone- and in-kernel-compilation are needed any more.
- * -Added local Rules.make for above reason.
- * -Experimental changes in teles3.c for enhanced IRQ-checking with
- *  2.1.X and SMP kernels.
- * -Removed diffstd-script, same functionality is in stddiff -r.
- * -Enhanced scripts std2kern and stddiff.
- *
- * Revision 1.8  1997/02/23 18:43:55  fritz
- * Added support for Teles-Vision.
- *
- * Revision 1.7  1997/01/28 22:48:33  keil
- * fixes for Teles PCMCIA (Christof Petig)
- *
- * Revision 1.6  1997/01/27 15:52:55  keil
- * SMP proof,cosmetics, PCMCIA added
- *
- * removed old log info /KKe
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -94,7 +20,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *teles3_revision = "$Revision: 2.15 $";
+const char *teles3_revision = "$Revision: 2.16 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/telespci.c linux.19p1/drivers/isdn/hisax/telespci.c
--- linux.vanilla/drivers/isdn/hisax/telespci.c	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/telespci.c	Wed Dec 13 22:08:26 2000
@@ -1,44 +1,11 @@
-/* $Id: telespci.c,v 2.11 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: telespci.c,v 2.13 2000/06/26 08:59:15 keil Exp $
+ *
  * telespci.c     low level stuff for Teles PCI isdn cards
  *
  * Author       Ton van Rosmalen 
  *              Karsten Keil (keil@isdn4linux.de)
  *
- *
- * $Log: telespci.c,v $
- * Revision 2.11  1999/12/23 15:09:32  keil
- * change email
- *
- * Revision 2.10  1999/11/15 14:20:05  keil
- * 64Bit compatibility
- *
- * Revision 2.9  1999/08/11 21:01:34  keil
- * new PCI codefix
- *
- * Revision 2.8  1999/08/10 16:02:10  calle
- * struct pci_dev changed in 2.3.13. Made the necessary changes.
- *
- * Revision 2.7  1999/07/12 21:05:34  keil
- * fix race in IRQ handling
- * added watchdog for lost IRQs
- *
- * Revision 2.6  1999/07/01 08:12:15  keil
- * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
- *
- * Revision 2.5  1998/11/15 23:55:28  keil
- * changes from 2.0
- *
- * Revision 2.4  1998/10/05 09:38:08  keil
- * Fix register addressing
- *
- * Revision 2.3  1998/05/25 12:58:26  keil
- * HiSax golden code from certification, Don't use !!!
- * No leased lines, no X75, but many changes.
- *
- * Revision 2.1  1998/04/15 16:38:23  keil
- * Add S0Box and Teles PCI support
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 #define __NO_VERSION__
@@ -50,7 +17,7 @@
 #include <linux/pci.h>
 
 extern const char *CardType[];
-const char *telespci_revision = "$Revision: 2.11 $";
+const char *telespci_revision = "$Revision: 2.13 $";
 
 #define ZORAN_PO_RQ_PEN	0x02000000
 #define ZORAN_PO_WR	0x00800000
@@ -60,6 +27,12 @@
 #define ZORAN_PO_GREG1	0x00010000
 #define ZORAN_PO_DMASK	0xFF
 
+#ifndef PCI_VENDOR_ID_ZORAN
+#define PCI_VENDOR_ID_ZORAN	0x11DE
+#endif
+#ifndef PCI_DEVICE_ID_ZORAN_36120
+#define PCI_DEVICE_ID_ZORAN_36120	0x6120
+#endif
 #define WRITE_ADDR_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
 #define READ_DATA_ISAC	(ZORAN_PO_GID0 | ZORAN_PO_GREG1)
 #define WRITE_DATA_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
@@ -315,6 +288,9 @@
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
 	strcpy(tmp, telespci_revision);
 	printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
 	if (cs->typ != ISDN_CTYPE_TELESPCI)
@@ -324,16 +300,18 @@
 		printk(KERN_ERR "TelesPCI: no PCI bus present\n");
 		return(0);
 	}
-	if ((dev_tel = pci_find_device (0x11DE, 0x6120, dev_tel))) {
+	if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) {
+		if (pci_enable_device(dev_tel))
+			return(0);
 		cs->irq = dev_tel->irq;
 		if (!cs->irq) {
 			printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
 			return(0);
 		}
-		cs->hw.teles0.membase = (u_long) ioremap(dev_tel->base_address[ 0],
+		cs->hw.teles0.membase = (u_long) ioremap(dev_tel->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK,
 			PAGE_SIZE);
 		printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
-			dev_tel->base_address[ 0], dev_tel->irq);
+			dev_tel->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK, dev_tel->irq);
 	} else {
 		printk(KERN_WARNING "TelesPCI: No PCI card found\n");
 		return(0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/w6692.c linux.19p1/drivers/isdn/hisax/w6692.c
--- linux.vanilla/drivers/isdn/hisax/w6692.c	Sun Jun 11 21:44:14 2000
+++ linux.19p1/drivers/isdn/hisax/w6692.c	Wed Dec 13 22:08:26 2000
@@ -1,5 +1,5 @@
-/* $Id: w6692.c,v 1.4 2000/03/16 23:24:11 werner Exp $
-
+/* $Id: w6692.c,v 1.8 2000/09/07 20:33:30 werner Exp $
+ *
  * w6692.c   Winbond W6692 specific routines
  *
  * Author       Petr Novak <petr.novak@i.cz>
@@ -7,23 +7,6 @@
  *
  *              This file is (c) under GNU PUBLIC LICENSE
  *
- * $Log: w6692.c,v $
- * Revision 1.4  2000/03/16 23:24:11  werner
- *
- * Fixed an additional location
- *
- * Revision 1.3  2000/03/16 22:41:36  werner
- *
- * Tried to fix second B-channel problem (still not tested)
- *
- * Revision 1.2  2000/02/26 00:35:13  keil
- * Fix skb freeing in interrupt context
- *
- * Revision 1.1  1999/09/04 06:28:58  keil
- * first revision
- *
- *
- *
  */
 
 #include <linux/config.h>
@@ -34,13 +17,18 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 
-#define	PCI_VEND_ASUSCOM	0x675
-#define	PCI_DEV_ASUSCOMPCI1	0x1702
+#ifndef PCI_VENDOR_ID_ASUSCOM
+#define PCI_VENDOR_ID_ASUSCOM	0x675
+#endif
+#ifndef PCI_DEVICE_ID_ASUSCOM_TA1
+#define PCI_DEVICE_ID_ASUSCOM_TA1	0x1702
+#endif
 #ifndef PCI_VENDOR_ID_WINBOND2
 #define PCI_VENDOR_ID_WINBOND2	0x1050
 #endif
-#define	PCI_DEVICE_W6692	0x6692
-
+#ifndef PCI_DEVICE_ID_WINBOND_6692
+#define	PCI_DEVICE_ID_WINBOND_6692	0x6692
+#endif
 /* table entry in the PCI devices list */
 typedef struct {
 	int vendor_id;
@@ -51,14 +39,14 @@
 
 static const PCI_ENTRY id_list[] =
 {
-	{PCI_VEND_ASUSCOM, PCI_DEV_ASUSCOMPCI1, "AsusCom", "TA XXX"},
-	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_W6692, "Winbond", "W6692"},
+	{PCI_VENDOR_ID_ASUSCOM, PCI_DEVICE_ID_ASUSCOM_TA1, "AsusCom", "TA XXX"},
+	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND_6692, "Winbond", "W6692"},
 	{0, 0, NULL, NULL}
 };
 
 extern const char *CardType[];
 
-const char *w6692_revision = "$Revision: 1.4 $";
+const char *w6692_revision = "$Revision: 1.8 $";
 
 #define DBUSY_TIMER_VALUE 80
 
@@ -256,7 +244,7 @@
 	if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) {
 		if (cs->debug & L1_DEB_WARN)
 			debugl1(cs, "W6692B_empty_fifo: incoming packet too large");
-		cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+		cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
 		bcs->hw.w6692.rcvidx = 0;
 		return;
 	}
@@ -264,14 +252,14 @@
 	bcs->hw.w6692.rcvidx += count;
 	save_flags(flags);
 	cli();
-	READW6692BFIFO(cs, bcs->hw.w6692.bchan, ptr, count);
-	cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+	READW6692BFIFO(cs, bcs->channel, ptr, count);
+	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
 	restore_flags(flags);
 	if (cs->debug & L1_DEB_HSCX_FIFO) {
 		char *t = bcs->blog;
 
 		t += sprintf(t, "W6692B_empty_fifo %c cnt %d",
-			     bcs->hw.w6692.bchan ? 'B' : 'A', count);
+			     bcs->channel + '1', count);
 		QuickHex(t, ptr, count);
 		debugl1(cs, bcs->blog);
 	}
@@ -307,14 +295,14 @@
 	skb_pull(bcs->tx_skb, count);
 	bcs->tx_cnt -= count;
 	bcs->hw.w6692.count += count;
-	WRITEW6692BFIFO(cs, bcs->hw.w6692.bchan, ptr, count);
-	cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME));
+	WRITEW6692BFIFO(cs, bcs->channel, ptr, count);
+	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME));
 	restore_flags(flags);
 	if (cs->debug & L1_DEB_HSCX_FIFO) {
 		char *t = bcs->blog;
 
 		t += sprintf(t, "W6692B_fill_fifo %c cnt %d",
-			     bcs->hw.w6692.bchan ? 'B' : 'A', count);
+			     bcs->channel + '1', count);
 		QuickHex(t, ptr, count);
 		debugl1(cs, bcs->blog);
 	}
@@ -325,13 +313,11 @@
 {
 	u_char val;
 	u_char r;
-	struct BCState *bcs = cs->bcs;
+	struct BCState *bcs;
 	struct sk_buff *skb;
 	int count;
 
-	if (bcs->channel != bchan)
-	  bcs++; /* hardware bchan must match ! */
-
+	bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1);
 	val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR);
 	debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val);
 
@@ -418,7 +404,7 @@
 				bcs->tx_cnt += bcs->hw.w6692.count;
 				bcs->hw.w6692.count = 0;
 			}
-			cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "W6692 B EXIR %x Lost TX", val);
 		}
@@ -732,18 +718,16 @@
 }
 
 static void
-W6692Bmode(struct BCState *bcs, int mode, int bc)
+W6692Bmode(struct BCState *bcs, int mode, int bchan)
 {
 	struct IsdnCardState *cs = bcs->cs;
-	int bchan = bc;
-
-	bcs->hw.w6692.bchan = bc;
 
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "w6692 %c mode %d ichan %d",
-			'1' + bchan, mode, bc);
+			'1' + bchan, mode, bchan);
 	bcs->mode = mode;
-	bcs->channel = bc;
+	bcs->channel = bchan;
+	bcs->hw.w6692.bchan = bchan;
 
 	switch (mode) {
 		case (L1_MODE_NULL):
@@ -912,8 +896,6 @@
 		cs->bcs[1].BC_SetStack = setstack_w6692;
 		cs->bcs[0].BC_Close = close_w6692state;
 		cs->bcs[1].BC_Close = close_w6692state;
-		cs->bcs[0].hw.w6692.bchan = 0;
-		cs->bcs[1].hw.w6692.bchan = 1;
 		W6692Bmode(cs->bcs, 0, 0);
 		W6692Bmode(cs->bcs + 1, 0, 0);
 	}
@@ -996,6 +978,9 @@
 	u_char pci_irq = 0;
 	u_int pci_ioaddr = 0;
 
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
 	strcpy(tmp, w6692_revision);
 	printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp));
 	if (cs->typ != ISDN_CTYPE_W6692)
@@ -1009,8 +994,11 @@
 		dev_w6692 = pci_find_device(id_list[id_idx].vendor_id,
 					    id_list[id_idx].device_id,
 					    dev_w6692);
-		if (dev_w6692)
+		if (dev_w6692) {
+			if (pci_enable_device(dev_w6692))
+				continue;
 			break;
+		}
 		id_idx++;
 	}
 	if (dev_w6692) {
@@ -1018,7 +1006,7 @@
 		pci_irq = dev_w6692->irq;
 		/* I think address 0 is allways the configuration area */
 		/* and address 1 is the real IO space KKe 03.09.99 */
-		pci_ioaddr = dev_w6692->base_address[ 1];
+		pci_ioaddr = dev_w6692->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
 	}
 	if (!found) {
 		printk(KERN_WARNING "W6692: No PCI card found\n");
@@ -1029,7 +1017,6 @@
 		printk(KERN_WARNING "W6692: No IRQ for PCI card found\n");
 		return (0);
 	}
-	pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
 	if (!pci_ioaddr) {
 		printk(KERN_WARNING "W6692: NO I/O Base Address found\n");
 		return (0);
@@ -1069,6 +1056,7 @@
 	cs->BC_Send_Data = &W6692B_fill_fifo;
 	cs->cardmsg = &w6692_card_msg;
 	cs->irq_func = &W6692_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
 	W6692Version(cs, "W6692:");
 	printk(KERN_INFO "W6692 ISTA=0x%X\n", ReadW6692(cs, W_ISTA));
 	printk(KERN_INFO "W6692 IMASK=0x%X\n", ReadW6692(cs, W_IMASK));
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/w6692.h linux.19p1/drivers/isdn/hisax/w6692.h
--- linux.vanilla/drivers/isdn/hisax/w6692.h	Fri Apr 21 12:46:12 2000
+++ linux.19p1/drivers/isdn/hisax/w6692.h	Wed Dec 13 22:08:26 2000
@@ -1,18 +1,10 @@
-/* $Id: w6692.h,v 1.1 1999/09/04 06:28:58 keil Exp $
-
+/* $Id: w6692.h,v 1.2 2000/06/26 08:59:15 keil Exp $
+ *
  * w6692.h   Winbond W6692 specific defines
  *
  * Author       Petr Novak <petr.novak@i.cz>
  *
- *
- * $Log: w6692.h,v $
- * Revision 1.1  1999/09/04 06:28:58  keil
- * first revision
- *
- *
- * Revision 1.0  1999/08/28 21:58:00 pnovak
- * first version
- *
+ * This file is (c) under GNU PUBLIC LICENSE
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_common.c linux.19p1/drivers/isdn/isdn_common.c
--- linux.vanilla/drivers/isdn/isdn_common.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_common.c	Wed Dec 13 22:10:08 2000
@@ -28,6 +28,7 @@
 #include <linux/poll.h>
 #include <linux/vmalloc.h>
 #include <linux/isdn.h>
+#include <linux/smp_lock.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
 #include "isdn_net.h"
@@ -437,6 +438,7 @@
 	int r;
 	int retval = 0;
 	isdn_ctrl cmd;
+	isdn_net_dev *p;
 
 	di = c->driver;
 	i = isdn_dc2minor(di, c->arg);
@@ -515,9 +517,16 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_ACCEPTD;
-					isdn_command(&cmd);
-					retval = 1;
+					for ( p = dev->netdev; p; p = p->next )
+						if ( p->local->isdn_channel == cmd.arg )
+						{
+							strcpy( cmd.parm.setup.eazmsn, p->local->msn );
+							isdn_command(&cmd);
+							retval = 1;
+							break;
+						}
 					break;
+
 				case 2:	/* For calling back, first reject incoming call ... */
 				case 3:	/* Interface found, but down, reject call actively  */
 					retval = 2;
@@ -966,6 +975,7 @@
 	ulong flags;
 	int drvidx;
 	int chidx;
+	int retval;
 	char *p;
 
 	if (off != &file->f_pos)
@@ -973,47 +983,69 @@
 
 	if (minor == ISDN_MINOR_STATUS) {
 		if (!file->private_data) {
-			if (file->f_flags & O_NONBLOCK)
-				return -EAGAIN;
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				goto out;
+			}
 			interruptible_sleep_on(&(dev->info_waitq));
 		}
 		p = isdn_statstr();
 		file->private_data = 0;
 		if ((len = strlen(p)) <= count) {
-			if (copy_to_user(buf, p, len))
-				return -EFAULT;
+			if (copy_to_user(buf, p, len)) {
+				retval = -EFAULT;
+				goto out;
+			}
 			*off += len;
-			return len;
+			retval = len;
+			goto out;
 		}
-		return 0;
+		retval = 0;
+		goto out;
+	}
+	if (!dev->drivers) {
+		retval = -ENODEV;
+		goto out;
 	}
-	if (!dev->drivers)
-		return -ENODEV;
 	if (minor < ISDN_MINOR_CTRL) {
+		printk(KERN_WARNING "isdn_read minor %d obsolete!\n", minor);
 		drvidx = isdn_minor2drv(minor);
-		if (drvidx < 0)
-			return -ENODEV;
-		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
-			return -ENODEV;
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) {
+			retval = -ENODEV;
+			goto out;
+		}
 		chidx = isdn_minor2chan(minor);
-		if(  ! (p = kmalloc(count,GFP_KERNEL))  ) return -ENOMEM;
+		if (!(p = kmalloc(count, GFP_KERNEL))) {
+			retval = -ENOMEM;
+			goto out;
+		}
 		save_flags(flags);
 		cli();
 		len = isdn_readbchan(drvidx, chidx, p, 0, count,
 				     &dev->drv[drvidx]->rcv_waitq[chidx]);
 		*off += len;
 		restore_flags(flags);
-		if( copy_to_user(buf,p,len) ) len = -EFAULT;
+		if (copy_to_user(buf,p,len)) 
+			len = -EFAULT;
 		kfree(p);
-		return len;
+		retval = len;
+		goto out;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-		if (drvidx < 0)
-			return -ENODEV;
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
 		if (!dev->drv[drvidx]->stavail) {
-			if (file->f_flags & O_NONBLOCK)
-				return -EAGAIN;
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				goto out;
+			}
 			interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
 		}
 		if (dev->drv[drvidx]->interface->readstat)
@@ -1030,13 +1062,18 @@
 			dev->drv[drvidx]->stavail = 0;
 		restore_flags(flags);
 		*off += len;
-		return len;
+		retval = len;
+		goto out;
 	}
 #ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return (isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count));
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		retval = isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count);
+		goto out;
+	}
 #endif
-	return -ENODEV;
+	retval = -ENODEV;
+out:
+	return retval;
 }
 
 static loff_t
@@ -1051,6 +1088,7 @@
 	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	int drvidx;
 	int chidx;
+	int retval;
 
 	if (off != &file->f_pos)
 		return -ESPIPE;
@@ -1059,21 +1097,30 @@
 		return -EPERM;
 	if (!dev->drivers)
 		return -ENODEV;
+
 	if (minor < ISDN_MINOR_CTRL) {
+		printk(KERN_WARNING "isdn_write minor %d obsolete!\n", minor);
 		drvidx = isdn_minor2drv(minor);
-		if (drvidx < 0)
-			return -ENODEV;
-		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
-			return -ENODEV;
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) {
+			retval = -ENODEV;
+			goto out;
+		}
 		chidx = isdn_minor2chan(minor);
 		while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
 			interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
-		return count;
+		retval = count;
+		goto out;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-		if (drvidx < 0)
-			return -ENODEV;
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
 		/*
 		 * We want to use the isdnctrl device to load the firmware
 		 *
@@ -1081,16 +1128,21 @@
 		 return -ENODEV;
 		 */
 		if (dev->drv[drvidx]->interface->writecmd)
-			return (dev->drv[drvidx]->interface->
-				writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor)));
+			retval = dev->drv[drvidx]->interface->
+				writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor));
 		else
-			return count;
+			retval = count;
+		goto out;
 	}
 #ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return (isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count));
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		retval = isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count);
+		goto out;
+	}
 #endif
-	return -ENODEV;
+	retval = -ENODEV;
+ out:
+	return retval;
 }
 
 static unsigned int
@@ -1106,26 +1158,30 @@
 		if (file->private_data) {
 			mask |= POLLIN | POLLRDNORM;
 		}
-		return mask;
+		goto out;
 	}
 	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
 		if (drvidx < 0) {
 			/* driver deregistered while file open */
-		        return POLLHUP;
+			mask = POLLHUP;
+			goto out;
 		}
 		poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);
 		mask = POLLOUT | POLLWRNORM;
 		if (dev->drv[drvidx]->stavail) {
 			mask |= POLLIN | POLLRDNORM;
 		}
-		return mask;
+		goto out;
 	}
 #ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return (isdn_ppp_poll(file, wait));
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		mask = isdn_ppp_poll(file, wait);
+		goto out;
+	}
 #endif
-	printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n");
-	return POLLERR;
+	mask = POLLERR;
+ out:
+	return mask;
 }
 
 
@@ -1603,6 +1659,7 @@
 	if (!dev->channels)
 		return -ENODEV;
 	if (minor < ISDN_MINOR_CTRL) {
+		printk(KERN_WARNING "isdn_open minor %d obsolete!\n", minor);
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
@@ -2293,11 +2350,13 @@
 	}
 	if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
 		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
+		restore_flags(flags);
 	} else {
 		del_timer(&dev->timer);
+		restore_flags(flags);
+		/* call vfree with interrupts enabled, else it will hang */
 		vfree(dev);
 		printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
 	}
-	restore_flags(flags);
 }
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_concap.c linux.19p1/drivers/isdn/isdn_concap.c
--- linux.vanilla/drivers/isdn/isdn_concap.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_concap.c	Wed Dec 13 22:10:15 2000
@@ -52,15 +52,19 @@
 
 int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
 {
-	int tmp;
 	struct device *ndev = concap -> net_dev;
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
+	isdn_net_local *lp = isdn_net_get_locked_lp(nd);
 
 	IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
+	if (!lp) {
+		IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1);
+		return 1;
+	}
 	lp->huptimer = 0;
-	tmp=isdn_net_send_skb(ndev, lp, skb);
-	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, tmp);
-	return tmp;
+	isdn_net_writebuf_skb(lp, skb);
+	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0);
+	return 0;
 }
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_net.c linux.19p1/drivers/isdn/isdn_net.c
--- linux.vanilla/drivers/isdn/isdn_net.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_net.c	Wed Dec 13 22:10:21 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.137 2000/09/12 19:43:47 kai Exp $
+/* $Id: isdn_net.c,v 1.140 2000/11/01 17:54:01 detabc Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -72,7 +72,7 @@
  * Find out if the netdevice has been ifup-ed yet.
  * For slaves, look at the corresponding master.
  */
-static int __inline__ isdn_net_started(isdn_net_dev *n)
+static __inline__ int isdn_net_device_started(isdn_net_dev *n)
 {
 	isdn_net_local *lp = n->local;
 	struct device *dev;
@@ -88,7 +88,7 @@
  * wake up the network -> net_device queue.
  * For slaves, wake the corresponding master interface.
  */
-static void __inline__ isdn_net_lp_xon(isdn_net_local * lp)
+static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp)
 {
 	if (lp->master) 
 		netif_wake_queue(lp->master);
@@ -96,6 +96,75 @@
 		netif_wake_queue(&lp->netdev->dev);
 }
 
+/*
+ * stop the network -> net_device queue.
+ * For slaves, stop the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
+{
+	if (lp->master)
+		netif_stop_queue(lp->master);
+	else
+		netif_stop_queue(&lp->netdev->dev);
+}
+
+/*
+ * find out if the net_device which this lp belongs to (lp can be
+ * master or slave) is busy. It's busy iff all (master and slave) 
+ * queues are busy
+ */
+static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
+{
+	isdn_net_local *nlp;
+	isdn_net_dev *nd;
+	unsigned long flags;
+
+	if (!isdn_net_lp_busy(lp))
+		return 0;
+
+	if (lp->master)
+		nd = ((isdn_net_local *) lp->master->priv)->netdev;
+	else
+		nd = lp->netdev;
+	
+	spin_lock_irqsave(&nd->queue_lock, flags);
+	nlp = lp->next;
+	while (nlp != lp) {
+		if (!isdn_net_lp_busy(nlp)) {
+			spin_unlock_irqrestore(&nd->queue_lock, flags);
+			return 0;
+		}
+		nlp = nlp->next;
+	}
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	return 1;
+}
+
+static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
+{
+	atomic_inc(&lp->frame_cnt);
+	if (isdn_net_device_busy(lp))
+		isdn_net_device_stop_queue(lp);
+}
+
+static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
+{
+	atomic_dec(&lp->frame_cnt);
+
+	if (!(isdn_net_device_busy(lp))) {
+		if (!skb_queue_empty(&lp->super_tx_queue)) {
+			queue_task(&lp->tqueue, &tq_immediate);
+		} else {
+			isdn_net_device_wake_queue(lp);
+		}
+       }                                                                      
+}
+
+static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
+{
+	atomic_set(&lp->frame_cnt, 0);
+}
+
 /* For 2.2.x we leave the transmitter busy timeout at 2 secs, just 
  * to be safe.
  * For 2.3.x we push it up to 20 secs, because call establishment
@@ -111,9 +180,8 @@
 
 int isdn_net_force_dial_lp(isdn_net_local *);
 static int isdn_net_start_xmit(struct sk_buff *, struct device *);
-static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
 
-char *isdn_net_revision = "$Revision: 1.114 $";
+char *isdn_net_revision = "$Revision: 1.140 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -122,7 +190,6 @@
 static void
 isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason)
 {
-
 	if(skb) {
 
 		u_short proto = ntohs(skb->protocol);
@@ -225,16 +292,12 @@
 isdn_net_unbind_channel(isdn_net_local * lp)
 {
 	ulong flags;
+	struct sk_buff *skb;
 
 	save_flags(flags);
 	cli();
-	if (lp->first_skb) {
-		dev_kfree_skb(lp->first_skb);
-		lp->first_skb = NULL;
-	}
-	if (lp->sav_skb) {
-		dev_kfree_skb(lp->sav_skb);
-		lp->sav_skb = NULL;
+	while ((skb = skb_dequeue(&lp->super_tx_queue))) {
+		kfree_skb(skb);
 	}
 	if (!lp->master) {	/* reset only master device */
 		/* Moral equivalent of dev_purge_queues():
@@ -331,6 +394,11 @@
 	isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
 }
 
+static void isdn_net_lp_disconnected(isdn_net_local *lp)
+{
+	isdn_net_rm_from_bundle(lp);
+}
+
 /*
  * Handle status-messages from ISDN-interfacecard.
  * This function is called from within the main-status-dispatcher
@@ -354,28 +422,9 @@
 				/* A packet has successfully been sent out */
 				if ((lp->flags & ISDN_NET_CONNECTED) &&
 				    (!lp->dialstate)) {
+					isdn_net_dec_frame_cnt(lp);
 					lp->stats.tx_packets++;
 					lp->stats.tx_bytes += c->parm.length;
-					/* some HL drivers deliver 
-					   ISDN_STAT_BSENT from hw interrupt.
-					   Output routines in isdn_ppp are now
-					   called with irq disabled such that
-					   dequeueing the sav_skb while another
-					   frame is sent will not occur.
-					*/
-					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
-						struct device *mdev;
-						if (lp->master)
-							mdev = lp->master;
-						else
-							mdev = &lp->netdev->dev;
-						if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) {
-							lp->sav_skb = NULL;
-						} else {
-							return 1;
-						}
-					}
-					isdn_net_lp_xon(lp);
 				}
 				return 1;
 			case ISDN_STAT_DCONN:
@@ -405,8 +454,10 @@
 #endif /* CONFIG_ISDN_X25 */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
 #ifdef CONFIG_ISDN_PPP
-					isdn_ppp_free(lp);
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						isdn_ppp_free(lp);
 #endif
+					isdn_net_lp_disconnected(lp);
 					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 					printk(KERN_INFO "%s: remote hangup\n", lp->name);
 					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
@@ -429,6 +480,7 @@
 #endif /* CONFIG_ISDN_X25 */
 			case ISDN_STAT_BCONN:
 				/* B-Channel is up */
+				isdn_net_zero_frame_cnt(lp);
 				switch (lp->dialstate) {
 					case 5:
 					case 6:
@@ -446,13 +498,17 @@
 						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
 						if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
 							isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, 1);
+						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
+							if (lp->master) { /* is lp a slave? */
+								isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
+								isdn_net_add_to_bundle(nd, lp);
+							}
+						}
 						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
 						/* If first Chargeinfo comes before B-Channel connect,
 						 * we correct the timestamp here.
 						 */
 						lp->chargetime = jiffies;
-						printk(KERN_DEBUG "isdn_net: chargetime of %s now %lu\n",
-						lp->name, lp->chargetime);
 
 						/* reset dial-timeout */
 						lp->dialstarted = 0;
@@ -468,13 +524,9 @@
 							if( pops->connect_ind)
 								pops->connect_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
-						/* Immediately send first skb to speed up arp */
-						if (lp->first_skb) {
-
-							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
-								lp->first_skb = NULL;
-						}
-						if(! lp->first_skb) isdn_net_lp_xon(lp);
+						/* ppp needs to do negotiations first */
+						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+							isdn_net_device_wake_queue(lp);
 						return 1;
 				}
 				break;
@@ -584,7 +636,7 @@
 						s = "dial suppressed: isdn system stopped";
 					else
 						s = "dial suppressed: dialmode `off'";
-					isdn_net_unreachable(&p->dev, lp->first_skb, s);
+					isdn_net_unreachable(&p->dev, 0, s);
 					isdn_net_hangup(&p->dev);
 					break;
 				}
@@ -617,7 +669,7 @@
 							restore_flags(flags);
 							lp->dialwait_timer = jiffies + lp->dialwait;
 							lp->dialstarted = 0;
-							isdn_net_unreachable(&p->dev, lp->first_skb, "dial: timed out");
+							isdn_net_unreachable(&p->dev, 0, "dial: timed out");
 							isdn_net_hangup(&p->dev);
 							break;
 						}
@@ -635,7 +687,7 @@
 							if (lp->dialtimeout == 0) {
 								lp->dialwait_timer = jiffies + lp->dialwait;
 								lp->dialstarted = 0;
-								isdn_net_unreachable(&p->dev, lp->first_skb, "dial: tried all numbers dialmax times");
+								isdn_net_unreachable(&p->dev, 0, "dial: tried all numbers dialmax times");
 							}
 							isdn_net_hangup(&p->dev);
 							break;
@@ -651,6 +703,7 @@
 					i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel);
 					if (i >= 0) {
 						strcpy(dev->num[i], cmd.parm.setup.phone);
+						dev->usage[i] |= ISDN_USAGE_OUTGOING;
 						isdn_info_update();
 					}
 					printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
@@ -795,10 +848,21 @@
 #endif
 
 	if (lp->flags & ISDN_NET_CONNECTED) {
+		if (lp->slave != NULL) {
+			isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
+			if (slp->flags & ISDN_NET_CONNECTED) {
+				printk(KERN_INFO
+					"isdn_net: hang up slave %s before %s\n",
+					slp->name, lp->name);
+				isdn_net_hangup(lp->slave);
+			}
+		}
 		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
 #ifdef CONFIG_ISDN_PPP
-		isdn_ppp_free(lp);
+		if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+			isdn_ppp_free(lp);
 #endif
+		isdn_net_lp_disconnected(lp);
 #ifdef CONFIG_ISDN_X25
 		/* try if there are generic encap protocol
 		   receiver routines and signal the closure of
@@ -911,31 +975,81 @@
 }
 
 /*
- * Generic routine to send out an skbuf.
- * If lowlevel-device does not support support skbufs, use
- * standard send-routine, else send directly.
- *
- * Return: 0 on success, !0 on failure.
+ * this function is used to send supervisory data, i.e. data which was
+ * not received from the network layer, but e.g. frames from ipppd, CCP
+ * reset frames etc.
+ */
+void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
+{
+	if (in_interrupt()) {
+		// we can't grab the lock from irq context, 
+		// so we just queue the packet
+		skb_queue_tail(&lp->super_tx_queue, skb); 
+		queue_task(&lp->tqueue, &tq_immediate);
+		return;
+	}
+
+	if (!isdn_net_lp_busy(lp)) {
+		isdn_net_writebuf_skb(lp, skb);
+	} else {
+		skb_queue_tail(&lp->super_tx_queue, skb);
+	}
+}
+
+/*
+ * called from tq_immediate
  */
-int isdn_net_send_skb
-		(struct device *ndev, isdn_net_local * lp,struct sk_buff *skb)
+static void isdn_net_softint(void *private)
+{
+	isdn_net_local *lp = private;
+	struct sk_buff *skb;
+
+	while (!isdn_net_lp_busy(lp)) {
+		skb = skb_dequeue(&lp->super_tx_queue);
+		if (!skb)
+			break;
+		isdn_net_writebuf_skb(lp, skb);                                
+	}
+}
+
+/* 
+ * all frames sent from the (net) LL to a HL driver should go via this function
+ * it's serialized by the caller holding the lp->xmit_lock spinlock
+ */
+void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
 {
 	int ret;
 	int len = skb->len;     /* save len */
 
-	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
-	if (ret == len) {
-		lp->transcount += len;
-		return 0;
+	/* before obtaining the lock the caller should have checked that
+	   the lp isn't busy */
+	if (isdn_net_lp_busy(lp)) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		goto error;
 	}
-	if (ret < 0) {
-		dev_kfree_skb(skb);
-		lp->stats.tx_errors++;
-		return 0;
+
+	if (!(lp->flags & ISDN_NET_CONNECTED)) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		goto error;
 	}
-	return 1;
+	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
+	if (ret != len) {
+		/* we should never get here */
+		printk(KERN_WARNING "%s: HL driver queue full\n", lp->name);
+		goto error;
+	}
+	
+	lp->transcount += len;
+	isdn_net_inc_frame_cnt(lp);
+	return;
+
+ error:
+	dev_kfree_skb(skb);
+	lp->stats.tx_errors++;
+
 }
 
+
 /*
  *  Helper function for isdn_net_start_xmit.
  *  When called, the connection is already established.
@@ -948,9 +1062,18 @@
  */
 
 static int
-isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb)
+isdn_net_xmit(struct device *ndev, struct sk_buff *skb)
 {
-	int ret;
+	isdn_net_dev *nd;
+	isdn_net_local *slp;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int retv = 0;
+
+	if (((isdn_net_local *) (ndev->priv))->master) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		dev_kfree_skb(skb);
+		return 0;
+	}
 
 	/* For the other encaps the header has already been built */
 #ifdef CONFIG_ISDN_PPP
@@ -958,31 +1081,24 @@
 		return isdn_ppp_xmit(skb, ndev);
 	}
 #endif
+	nd = ((isdn_net_local *) ndev->priv)->netdev;
+	lp = isdn_net_get_locked_lp(nd);
+	if (!lp) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
+		return 1;
+	}
+	/* we have our lp locked from now on */
+
 	/* Reset hangup-timeout */
-	lp->huptimer = 0;
-	if (lp->cps > lp->triggercps) {
-		/* Device overloaded */
+	lp->huptimer = 0; // FIXME?
+	isdn_net_writebuf_skb(lp, skb);
 
-		/*
-		 * Packet-delivery via round-robin over master
-		 * and all connected slaves.
-		 */
-		if (lp->master)
-			/* Slaves always deliver themselves */
-			ret = isdn_net_send_skb(ndev, lp, skb);
-		else {
-			isdn_net_local *slp = (isdn_net_local *) (lp->srobin->priv);
-			/* Master delivers via srobin and maintains srobin */
-			if (lp->srobin == ndev)
-				ret = isdn_net_send_skb(ndev, lp, skb);
-			else
-				ret = isdn_net_start_xmit(skb, lp->srobin);
-			lp->srobin = (slp->slave) ? slp->slave : ndev;
-			slp = (isdn_net_local *) (lp->srobin->priv);
-			if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0)))
-				lp->srobin = ndev;
-		}
-		/* Slave-startup using delay-variable */
+	/* the following stuff is here for backwards compatibility.
+	 * in future, start-up and hangup of slaves (based on current load)
+	 * should move to userspace and get based on an overall cps
+	 * calculation
+	 */
+	if (lp->cps > lp->triggercps) {
 		if (lp->slave) {
 			if (!lp->sqfull) {
 				/* First time overload: set timestamp only */
@@ -990,17 +1106,24 @@
 				lp->sqfull_stamp = jiffies;
 			} else {
 				/* subsequent overload: if slavedelay exceeded, start dialing */
-				if ((jiffies - lp->sqfull_stamp) > lp->slavedelay)
-					isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+				if ((jiffies - lp->sqfull_stamp) > lp->slavedelay) {
+					slp = lp->slave->priv;
+					if (!(slp->flags & ISDN_NET_CONNECTED)) {
+						isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+					}
+				}
 			}
 		}
 	} else {
-		/* Not overloaded, deliver locally */
-		ret = isdn_net_send_skb(ndev, lp, skb);
-		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ))))
+		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ)))) {
 			lp->sqfull = 0;
+		}
+		/* this is a hack to allow auto-hangup for slaves on moderate loads */
+		nd->queue = nd->local;
 	}
-	return ret;
+
+	return retv;
+
 }
 
 static void
@@ -1078,6 +1201,7 @@
 		buf = skb->data;
 		isdn_dumppkt("S:", buf, skb->len, 40);
 #endif
+
 		if (!(lp->flags & ISDN_NET_CONNECTED)) {
 			int chi;
 			/* only do autodial if allowed by config */
@@ -1150,19 +1274,11 @@
 					return 1;	/* let upper layer requeue skb packet */
 				}
 #endif
-				/* remember first skb to speed up arp
-				 * when using encap ETHER
-				 */
-				if (lp->first_skb) {
-					printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
-					dev_kfree_skb(lp->first_skb);
-					lp->first_skb = NULL;
-				}
-				lp->first_skb = skb;
 				/* Initiate dialing */
 				restore_flags(flags);
 				isdn_net_dial();
-				return 0;
+				isdn_net_device_stop_queue(lp);
+				return 1;
 			} else {
 				isdn_net_unreachable(ndev, skb,
 						     "No phone number");
@@ -1175,14 +1291,7 @@
 			if (!lp->dialstate) {
 				/* ISDN connection is established, try sending */
 				int ret;
-				if (lp->first_skb) {
-					if (isdn_net_xmit(ndev, lp, lp->first_skb)){
-						netif_stop_queue(ndev);
-						return 1;
-}
-					lp->first_skb = NULL;
-				}
-				ret = (isdn_net_xmit(ndev, lp, skb));
+				ret = (isdn_net_xmit(ndev, skb));
 				if(ret) netif_stop_queue(ndev);
 				return ret;
 			} else
@@ -1297,7 +1406,6 @@
 	unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
 	struct sk_buff *skb = alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp), GFP_ATOMIC);
 	unsigned long t = (jiffies / HZ * 1000000);
-	int len;
 	cisco_hdr *ch;
 	cisco_slarp *s;
 
@@ -1325,9 +1433,7 @@
 	s->rel = 0xffff;
 	s->t1 = t >> 16;
 	s->t0 = t & 0xffff;
-	len = skb->len;
-	if (isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 0, skb) != len)
-		dev_kfree_skb(skb);
+	isdn_net_write_super(lp, skb);
 }
 
 static void
@@ -1411,7 +1517,6 @@
 		lp->stats.rx_packets++;
 		lp->stats.rx_bytes += skb->len;
 	}
-
 	skb->dev = ndev;
 	skb->pkt_type = PACKET_HOST;
 	skb->mac.raw = skb->data;
@@ -1494,6 +1599,7 @@
 			isdn_ppp_receive(lp->netdev, olp, skb);
 			return;
 #endif
+
 		default:
 #ifdef CONFIG_ISDN_X25
 		  /* try if there are generic sync_device receiver routines */
@@ -1960,7 +2066,7 @@
 				 * Is the interface up?
 				 * If not, reject the call actively.
 				 */
-				if (!isdn_net_started(p)) {
+				if (!isdn_net_device_started(p)) {
 					restore_flags(flags);
 					printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
 					       lp->name);
@@ -2051,6 +2157,7 @@
 						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 							isdn_ppp_free(lp);
 #endif
+						isdn_net_lp_disconnected(lp);
 						isdn_free_channel(lp->isdn_device, lp->isdn_channel,
 							 ISDN_USAGE_NET);
 					}
@@ -2240,10 +2347,17 @@
 	netdev->local->magic = ISDN_NET_MAGIC;
 
 	netdev->queue = netdev->local;
+	spin_lock_init(&netdev->queue_lock);
+
 	netdev->local->last = netdev->local;
 	netdev->local->netdev = netdev;
 	netdev->local->next = netdev->local;
 
+	memset(&netdev->local->tqueue, 0, sizeof(struct tq_struct));
+	netdev->local->tqueue.routine = isdn_net_softint;
+	netdev->local->tqueue.data = netdev->local;
+	spin_lock_init(&netdev->local->xmit_lock);
+
 	netdev->local->isdn_device = -1;
 	netdev->local->isdn_channel = -1;
 	netdev->local->pre_device = -1;
@@ -2251,13 +2365,11 @@
 	netdev->local->exclusive = -1;
 	netdev->local->ppp_slot = -1;
 	netdev->local->pppbind = -1;
-	netdev->local->sav_skb = NULL;
-	netdev->local->first_skb = NULL;
+	skb_queue_head_init(&netdev->local->super_tx_queue);
 	netdev->local->l2_proto = ISDN_PROTO_L2_X75I;
 	netdev->local->l3_proto = ISDN_PROTO_L3_TRANS;
 	netdev->local->triggercps = 6000;
 	netdev->local->slavedelay = 10 * HZ;
-	netdev->local->srobin = &netdev->dev;
 	netdev->local->hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
 	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
 	   of those who forget configuring this */
@@ -2295,7 +2407,7 @@
 		if (n->local->master)
 			return NULL;
 		/* Master must not be started yet */
-		if (isdn_net_started(n)) 
+		if (isdn_net_device_started(n)) 
 			return NULL;
 		return (isdn_net_new(newname, &(n->dev)));
 	}
@@ -2338,7 +2450,7 @@
 #ifdef CONFIG_ISDN_X25
 			struct concap_proto * cprot = p -> cprot;
 #endif
-			if (isdn_net_started(p)) {
+			if (isdn_net_device_started(p)) {
 				printk(KERN_WARNING "%s: cannot change encap when if is up\n",
 				       lp->name);
 				return -EBUSY;
@@ -2771,8 +2883,7 @@
 
 	save_flags(flags);
 	cli();
-
-	if (isdn_net_started(p)) {
+	if (isdn_net_device_started(p)) {
 		restore_flags(flags);
 		return -EBUSY;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_net.h linux.19p1/drivers/isdn/isdn_net.h
--- linux.vanilla/drivers/isdn/isdn_net.h	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_net.h	Wed Dec 13 22:10:21 2000
@@ -82,8 +82,87 @@
 extern int isdn_net_force_hangup(char *);
 extern int isdn_net_force_dial(char *);
 extern isdn_net_dev *isdn_net_findif(char *);
-extern int isdn_net_send_skb(struct device *, isdn_net_local *,
-			     struct sk_buff *);
 extern int isdn_net_rcv_skb(int, struct sk_buff *);
 extern void isdn_net_slarp_out(void);
 extern int isdn_net_dial_req(isdn_net_local *);
+extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
+extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
+
+#define ISDN_NET_MAX_QUEUE_LENGTH 2
+
+/*
+ * is this particular channel busy?
+ */
+static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
+{
+	if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
+		return 0;
+	else 
+		return 1;
+}
+
+/*
+ * For the given net device, this will get a non-busy channel out of the
+ * corresponding bundle. The returned channel is locked.
+ */
+static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
+{
+	unsigned long flags;
+	isdn_net_local *lp;
+
+	spin_lock_irqsave(&nd->queue_lock, flags);
+	lp = nd->queue;         /* get lp on top of queue */
+	while (isdn_net_lp_busy(nd->queue)) {
+		nd->queue = nd->queue->next;
+		if (nd->queue == lp) { /* not found -- should never happen */
+			lp = NULL;
+			goto errout;
+		}
+	}
+	lp = nd->queue;
+	nd->queue = nd->queue->next;
+errout:
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	return lp;
+}
+
+/*
+ * add a channel to a bundle
+ */
+static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
+{
+	isdn_net_local *lp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nd->queue_lock, flags);
+
+	lp = nd->queue;
+	nlp->last = lp->last;
+	lp->last->next = nlp;
+	lp->last = nlp;
+	nlp->next = lp;
+	nd->queue = nlp;
+
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+}
+/*
+ * remove a channel from the bundle it belongs to
+ */
+static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
+{
+	isdn_net_local *master_lp = lp;
+	unsigned long flags;
+
+	if (lp->master)
+		master_lp = (isdn_net_local *) lp->master->priv;
+
+	spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
+	lp->last->next = lp->next;
+	lp->next->last = lp->last;
+	if (master_lp->netdev->queue == lp)
+		master_lp->netdev->queue = lp->next;
+	lp->next = lp->last = lp;	/* (re)set own pointers */
+	spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
+}
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_ppp.c linux.19p1/drivers/isdn/isdn_ppp.c
--- linux.vanilla/drivers/isdn/isdn_ppp.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_ppp.c	Wed Dec 13 22:10:27 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.63 2000/03/16 15:46:37 kai Exp $
+/* $Id: isdn_ppp.c,v 1.77 2000/06/12 16:46:34 keil Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -20,8 +20,6 @@
  *
  */
 
-#define CONFIG_ISDN_CCP 1
-
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
@@ -85,11 +83,9 @@
 static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
 
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
-  
-#define MP_UNLOCK(b)	up(&(b)->lock)
 #endif	/* CONFIG_ISDN_MPP */
   
-char *isdn_ppp_revision = "$Revision: 1.63 $";
+char *isdn_ppp_revision = "$Revision: 1.77 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
@@ -124,55 +120,28 @@
 int
 isdn_ppp_free(isdn_net_local * lp)
 {
-#ifdef CONFIG_ISDN_MPP
-	isdn_net_local *master_lp = NULL;
-#endif
 	unsigned long flags;
 	struct ippp_struct *is;
-	isdn_net_local * nlp;
 
 	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
 		return 0;
 
-	is = ippp_table[lp->ppp_slot];
-
 	save_flags(flags);
 	cli();
-#ifdef CONFIG_ISDN_MPP
-    	spin_lock(&lp->netdev->pb->lock); // irq_save not necessary
 
-	if (lp->master)
-		master_lp = (isdn_net_local *) lp->master->priv;
-
-
-	/* make sure none of the queue pointers will point to the 
-	 * interface being removed */
-	for (nlp=lp->next; nlp != lp; nlp=nlp->next)
-		if (nlp->netdev->queue == lp )
-			nlp->netdev->queue = lp->next;
-
-	lp->last->next = lp->next;
-	lp->next->last = lp->last;
-	
-	if (master_lp && master_lp->netdev->queue == lp)
-		master_lp->netdev->queue = lp->next;
-/*	
-	if (lp->next->netdev->queue == lp)
-		lp->next->netdev->queue = lp->next;
-	if (lp->last->netdev->queue == lp)
-		lp->last->netdev->queue = lp->last;
-*/
+#ifdef CONFIG_ISDN_MPP
+	spin_lock(&lp->netdev->pb->lock);
+#endif
+	isdn_net_rm_from_bundle(lp);
+#ifdef CONFIG_ISDN_MPP
 	if (lp->netdev->pb->ref_ct == 1)	/* last link in queue? */
-			isdn_ppp_mp_cleanup(lp);
-	
-	lp->next = lp->last = lp;	/* (re)set own pointers */
-	lp->netdev->queue = lp;
-	
-	spin_unlock(&lp->netdev->pb->lock);
+		isdn_ppp_mp_cleanup(lp);
+
 	lp->netdev->pb->ref_ct--;
-	
+	spin_unlock(&lp->netdev->pb->lock);
 #endif /* CONFIG_ISDN_MPP */
 
+	is = ippp_table[lp->ppp_slot];
 	if ((is->state & IPPP_CONNECT))
 		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
 	else if (is->state & IPPP_ASSIGNED)
@@ -183,8 +152,8 @@
 
 	is->lp = NULL;          /* link is down .. set lp to NULL */
 	lp->ppp_slot = -1;      /* is this OK ?? */
-	restore_flags(flags);
 
+	restore_flags(flags);
 	return 0;
 }
 
@@ -199,12 +168,8 @@
 	long flags;
 	struct ippp_struct *is;
 
-	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
-		return -1;
-
 	save_flags(flags);
 	cli();
-
 	if (lp->pppbind < 0) {  /* device bounded to ippp device ? */
 		isdn_net_dev *net_dev = dev->netdev;
 		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
@@ -243,7 +208,6 @@
 	}
 	
 	lp->ppp_slot = i;
-
 	is = ippp_table[i];
 	is->lp = lp;
 	is->unit = unit;
@@ -270,7 +234,7 @@
 		return;
 
 	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
-	
+
 	if (ippp_table[lp->ppp_slot]->wq)
 		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
 }
@@ -805,8 +769,6 @@
 			lp->dialstate == 0 &&
 		    (lp->flags & ISDN_NET_CONNECTED)) {
 			unsigned short hl;
-			unsigned long flags;
-			int cnt;
 			struct sk_buff *skb;
 			/*
 			 * we need to reserve enought space in front of
@@ -829,17 +791,7 @@
 
 			isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
 
-			save_flags(flags);
-			cli();
-			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) {
-				if (lp->sav_skb) {
-					dev_kfree_skb(lp->sav_skb);
-					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count);
-				} else
-					printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n", cnt, count);
-				lp->sav_skb = skb;
-			}
-			restore_flags(flags);
+			isdn_net_write_super(lp, skb);
 		}
 	}
 	return count;
@@ -954,7 +906,7 @@
 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
 
 		if(is->compflags & SC_LINK_DECOMP_ON) {	
-			if(proto == PPP_LINK_COMP) {
+			if(proto == PPP_COMPFRAG) {
 				if(is->debug & 0x10)
 					printk(KERN_DEBUG "received single link compressed frame\n");
 				skb = isdn_ppp_decompress(skb,is,NULL,proto);
@@ -1000,7 +952,6 @@
 		return;
 	}
 	is = ippp_table[slot];
-
 	if (is->debug & 0x10) {
 		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
 		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
@@ -1100,7 +1051,7 @@
 #endif
 			break;
 		case PPP_CCP:
-		case PPP_LINK_CCP:
+		case PPP_CCPFRAG:
 			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
 			/* Dont pop up ResetReq/Ack stuff to the daemon any
 			   longer - the job is done already */
@@ -1159,20 +1110,13 @@
 int
 isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev)
 {
-	struct device *mdev = ((isdn_net_local *) (netdev->priv))->master;	/* get master (for redundancy) */
 	isdn_net_local *lp,*mlp;
 	isdn_net_dev *nd;
 	unsigned int proto = PPP_IP;     /* 0x21 */
 	struct ippp_struct *ipt,*ipts;
-	unsigned long flags;
 	int slot;
 
-	if (mdev)
-		mlp = (isdn_net_local *) (mdev->priv);
-	else {
-		mdev = netdev;
-		mlp = (isdn_net_local *) (netdev->priv);
-	}
+	mlp = (isdn_net_local *) (netdev->priv);
 	nd = mlp->netdev;       /* get master lp */
 
 	slot = mlp->ppp_slot;
@@ -1198,21 +1142,18 @@
 			break;
 		default:
 			dev_kfree_skb(skb);
-			printk(KERN_ERR "isdn_ppp: skipped frame with unsupported protocol: %#x.\n", skb->protocol);
+			printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", 
+			       skb->protocol);
 			return 0;
 	}
 
-	lp = nd->queue;         /* get lp on top of queue */
-
-	if (lp->sav_skb) {      /* find a non-busy device */
-		isdn_net_local *nlp = lp->next;
-		while (nlp->sav_skb) {
-			if (lp == nlp)
-				return 1;
-			nlp = nd->queue = nd->queue->next;
-		}
-		lp = nlp;
+	lp = isdn_net_get_locked_lp(nd);
+	if (!lp) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
+		return 1;
 	}
+	/* we have our lp locked from now on */
+
 	slot = lp->ppp_slot;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
 		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", lp->ppp_slot);
@@ -1227,8 +1168,8 @@
 	 */
 
 	/* Pull off the fake header we stuck on earlier to keep
-     * the fragemntation code happy.
-     */
+	 * the fragmentation code happy.
+	 */
 	skb_pull(skb,IPPP_MAX_HEADER);
 
 	if (ipt->debug & 0x4)
@@ -1301,11 +1242,10 @@
 		/* we get mp_seqno from static isdn_net_local */
 		long mp_seqno = ipts->mp_seqno;
 		ipts->mp_seqno++;
-		nd->queue = nd->queue->next;
 		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
 			unsigned char *data = isdn_ppp_skb_push(&skb, 3);
 			if(!data)
-				return 0;
+				goto unlock;
 			mp_seqno &= 0xfff;
 			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf);	/* (B)egin & (E)ndbit .. */
 			data[1] = mp_seqno & 0xff;
@@ -1313,7 +1253,7 @@
 		} else {
 			unsigned char *data = isdn_ppp_skb_push(&skb, 5);
 			if(!data)
-				return 0;
+				goto unlock;
 			data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
 			data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
 			data[2] = (mp_seqno >> 8) & 0xff;
@@ -1333,20 +1273,20 @@
 	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
 		unsigned char *data = isdn_ppp_skb_push(&skb,1);
 		if(!data)
-			return 0;
+			goto unlock;
 		data[0] = proto & 0xff;
 	}
 	else {
 		unsigned char *data = isdn_ppp_skb_push(&skb,2);
 		if(!data)
-			return 0;
+			goto unlock;
 		data[0] = (proto >> 8) & 0xff;
 		data[1] = proto & 0xff;
 	}
 	if(!(ipt->pppcfg & SC_COMP_AC)) {
 		unsigned char *data = isdn_ppp_skb_push(&skb,2);
 		if(!data)
-			return 0;
+			goto unlock;
 		data[0] = 0xff;    /* All Stations */
 		data[1] = 0x03;    /* Unnumbered information */
 	}
@@ -1357,16 +1297,10 @@
 		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
 		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
 	}
-	save_flags(flags);
-	cli();
-	if (isdn_net_send_skb(netdev, lp, skb)) {
-		if (lp->sav_skb) {	/* should never happen as sav_skb are sent with disabled IRQs) */
-			printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", netdev->name);
-			dev_kfree_skb(skb);
-		} else
-			lp->sav_skb = skb;
-	}
-	restore_flags(flags);
+	
+	isdn_net_writebuf_skb(lp, skb);
+
+ unlock:
 	return 0;
 }
 
@@ -1397,8 +1331,8 @@
 
 static int isdn_ppp_mp_bundle_array_init(void)
 {
-   int i;
-   int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
+	int i;
+	int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
 	if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, 
 							GFP_KERNEL)) == NULL )
 		return -ENOMEM;
@@ -1410,7 +1344,7 @@
 
 static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
 {
-    int i;
+	int i;
 	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
 		if (isdn_ppp_bundle_arr[i].ref_ct <= 0)
 			return (isdn_ppp_bundle_arr + i);
@@ -1419,7 +1353,7 @@
 
 static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
 {
-   struct ippp_struct * is = ippp_table[lp->ppp_slot];
+	struct ippp_struct * is = ippp_table[lp->ppp_slot];
    
 	if (add_to) {
 		if( lp->netdev->pb )
@@ -1452,18 +1386,27 @@
 static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
 							struct sk_buff *skb)
 {
-	struct ippp_struct *is = ippp_table[lp->ppp_slot];
+	struct ippp_struct *is;
 	isdn_net_local * lpq;
 	ippp_bundle * mp;
 	isdn_mppp_stats * stats;
 	struct sk_buff * newfrag, * frag, * start, *nextf;
 	u32 newseq, minseq, thisseq;
 	unsigned long flags;
-    
-    	spin_lock_irqsave(&net_dev->pb->lock, flags);
+	int slot;
+
+	spin_lock_irqsave(&net_dev->pb->lock, flags);
     	mp = net_dev->pb;
         stats = &mp->stats;
-	
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_mp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
+		stats->frame_drops++;
+		dev_kfree_skb(skb);
+		spin_unlock_irqrestore(&mp->lock, flags);
+		return;
+	}
+	is = ippp_table[slot];
     	if( ++mp->frames > stats->max_queue_len )
 		stats->max_queue_len = mp->frames;
 	
@@ -1491,9 +1434,14 @@
 	/* find the minimum received sequence number over all links */
 	is->last_link_seqno = minseq = newseq;
 	for (lpq = net_dev->queue;;) {
-	   u32 lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
-		if (MP_LT(lls, minseq))
-			minseq = lls;
+		slot = lpq->ppp_slot;
+		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+			printk(KERN_ERR "isdn_ppp_mp_receive: lpq->ppp_slot %d\n", lpq->ppp_slot);
+		} else {
+			u32 lls = ippp_table[slot]->last_link_seqno;
+			if (MP_LT(lls, minseq))
+				minseq = lls;
+		}
 		if ((lpq = lpq->next) == net_dev->queue)
 			break;
 	}
@@ -1645,20 +1593,19 @@
 	 * queue overflow */
 	if (mp->frames > MP_MAX_QUEUE_LEN) {
 		stats->overflows++;
-		while (mp->frames < MP_MAX_QUEUE_LEN) {
+		while (mp->frames > MP_MAX_QUEUE_LEN) {
 			frag = mp->frags->next;
 			isdn_ppp_mp_free_skb(mp, mp->frags);
 			mp->frags = frag;
 		}
 	}
-	
 	spin_unlock_irqrestore(&mp->lock, flags);
 }
 
 static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
 {
-    struct sk_buff * frag = lp->netdev->pb->frags;
-    struct sk_buff * nextfrag;
+	struct sk_buff * frag = lp->netdev->pb->frags;
+	struct sk_buff * nextfrag;
     	while( frag ) {
 		nextfrag = frag->next;
 		isdn_ppp_mp_free_skb(lp->netdev->pb, frag);
@@ -1670,8 +1617,8 @@
 static u32 isdn_ppp_mp_get_seq( int short_seq, 
 					struct sk_buff * skb, u32 last_seq )
 {
-   u32 seq;
-   int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
+	u32 seq;
+	int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
    
    	if( !short_seq )
 	{
@@ -1716,46 +1663,40 @@
 void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
 				struct sk_buff * from, struct sk_buff * to )
 {
-   ippp_bundle * mp = net_dev->pb;
-   int proto;
-   struct sk_buff * skb;
-   unsigned int tot_len;
-   
-   	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) )
-	{
+	ippp_bundle * mp = net_dev->pb;
+	int proto;
+	struct sk_buff * skb;
+	unsigned int tot_len;
+
+	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
 		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
 			printk(KERN_DEBUG"isdn_mppp: reassembly: frame %d, "
 					"len %d\n", MP_SEQ(from), from->len );
 		skb = from;
 		skb_pull(skb, MP_HEADER_LEN);
 		mp->frames--;	
-	}
-	else
-	{
-	    struct sk_buff * frag;
-	    int n;
-	    
-		for( tot_len=0, frag=from, n = 0; frag != to; 
-							frag=frag->next, n++ )
+	} else {
+		struct sk_buff * frag;
+		int n;
+
+		for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
 			tot_len += frag->len - MP_HEADER_LEN;
-		
+
 		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
 			printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
 				"to %d, len %d\n", MP_SEQ(from), 
 				(MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
-		
-		if( (skb = dev_alloc_skb(tot_len)) == NULL )
-		{
-			printk(KERN_ERR"isdn_mppp: cannot allocate sk buff "
+		if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
+			printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
 					"of size %d\n", tot_len);
 			isdn_ppp_mp_discard(mp, from, to);
 			return;
 		}
-		
-		while( from != to )
-		{
-		   unsigned int len = from->len - MP_HEADER_LEN;
-		   	memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+
+		while( from != to ) {
+			unsigned int len = from->len - MP_HEADER_LEN;
+
+			memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
 			frag = from->next;
 			isdn_ppp_mp_free_skb(mp, from);
 			from = frag; 
@@ -1784,15 +1725,13 @@
 {
 	char ifn[IFNAMSIZ + 1];
 	isdn_net_dev *p;
-	isdn_net_local *lp,
-	*nlp;
+	isdn_net_local *lp, *nlp;
 	int rc;
 	unsigned long flags;
 
 	sprintf(ifn, "ippp%d", unit);
 	p = isdn_net_findif(ifn);
-	if (!p)
-	{
+	if (!p) {
 		printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
 		return -EINVAL;
 	}
@@ -1800,38 +1739,29 @@
     	spin_lock_irqsave(&p->pb->lock, flags);
 
 	nlp = is->lp;
-
 	lp = p->queue;
-	
 	if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||
-		lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS )
-	{
-		spin_unlock_irqrestore(&p->pb->lock, flags);
+		lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) {
 		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
-		   nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? 
-		   				nlp->ppp_slot : lp->ppp_slot );
-		return -EINVAL;
-	}
-	
-	nlp->last = lp->last;
-	lp->last->next = nlp;
-	lp->last = nlp;
-	nlp->next = lp;
-	p->queue = nlp;
+			nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? 
+			nlp->ppp_slot : lp->ppp_slot );
+		rc = -EINVAL;
+		goto out;
+ 	}
+
+	isdn_net_add_to_bundle(p, nlp);
 
 	ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
-/* maybe also SC_CCP stuff */
-	ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
-	    (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
 
+	/* maybe also SC_CCP stuff */
+	ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
+		(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
 	ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
-	    (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
-
- 	rc = isdn_ppp_mp_init(nlp, p->pb);
-  
+		(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+	rc = isdn_ppp_mp_init(nlp, p->pb);
+out:
 	spin_unlock_irqrestore(&p->pb->lock, flags);
-  
- 	return rc;
+	return rc;
 }
   
 #endif /* CONFIG_ISDN_MPP */
@@ -2051,8 +1981,7 @@
 {
 	struct sk_buff *skb;
 	unsigned char *p;
-	int count, hl;
-	unsigned long flags;
+	int hl;
 	int cnt = 0;
 	isdn_net_local *lp = is->lp;
 
@@ -2093,26 +2022,7 @@
 	printk(KERN_DEBUG "Sending CCP Frame:\n");
 	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
 
-	/* Just ripped from isdn_ppp_write. Dunno whether it makes sense,
-	   especially dunno what the sav_skb stuff is good for. */
-
-	count = skb->len;
-	save_flags(flags);
-	cli();
-	if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel,
-					  1, skb)) != count) {
-		if (lp->sav_skb) {
-			dev_kfree_skb(lp->sav_skb);
-			printk(KERN_INFO
-			       "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",
-			       cnt, count);
-		} else
-			printk(KERN_INFO
-			       "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",
-			       cnt, count);
-		lp->sav_skb = skb;
-	}
-	restore_flags(flags);
+	isdn_net_write_super(lp, skb);
 }
 
 /* Allocate the reset state vector */
@@ -2362,14 +2272,6 @@
 static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
 	int proto)
 {
-#ifndef CONFIG_ISDN_CCP
-	if(proto == PPP_COMP || proto == PPP_LINK_COMP) {
-		printk(KERN_ERR "isdn_ppp: Ouch! Compression not included!\n");
-		dev_kfree_skb(skb);
-		return NULL;
-	}
-	return skb;
-#else
 	void *stat = NULL;
 	struct isdn_ppp_compressor *ipc = NULL;
 	struct sk_buff *skb_out;
@@ -2415,7 +2317,7 @@
 	printk(KERN_DEBUG "ippp: Decompress valid!\n");
 	*/
 
-	if((master && proto == PPP_COMP) || (!master && proto == PPP_LINK_COMP) ) {
+	if((master && proto == PPP_COMP) || (!master && proto == PPP_COMPFRAG) ) {
 		/* Set up reset params for the decompressor */
 		memset(&rsparm, 0, sizeof(rsparm));
 		rsparm.data = rsdata;
@@ -2455,7 +2357,6 @@
 		ipc->incomp(stat,skb,proto);
 		return skb;
 	}
-#endif
 }
 
 /*
@@ -2474,13 +2375,9 @@
     void *stat;
     struct sk_buff *skb_out;
 
-#ifdef CONFIG_ISDN_CCP
 	/* we do not compress control protocols */
     if(*proto < 0 || *proto > 0x3fff) {
-#else
-    {
-#endif
-      return skb_in;
+	    return skb_in;
     }
 
 	if(type) { /* type=1 => Link compression */
@@ -2681,7 +2578,7 @@
 	}
 
 	proto = ((int)data[0]<<8)+data[1];
-	if(proto != PPP_CCP && proto != PPP_LINK_CCP)
+	if(proto != PPP_CCP && proto != PPP_CCPFRAG)
 		return;
 
 	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_tty.c linux.19p1/drivers/isdn/isdn_tty.c
--- linux.vanilla/drivers/isdn/isdn_tty.c	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_tty.c	Wed Dec 13 22:10:27 2000
@@ -53,14 +53,20 @@
 #define MODEM_PARANOIA_CHECK
 #define MODEM_DO_RESTART
 
+#ifdef CONFIG_DEVFS_FS
+static char *isdn_ttyname_ttyI = "isdn/ttyI%d";
+static char *isdn_ttyname_cui = "isdn/cui%d";
+#else
 static char *isdn_ttyname_ttyI = "ttyI";
 static char *isdn_ttyname_cui = "cui";
+#endif
+
 static int bit2si[8] =
 {1, 5, 7, 7, 7, 7, 7, 7};
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.84 $";
+char *isdn_tty_revision = "$Revision: 1.93 $";
 
 
 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
@@ -1177,6 +1183,7 @@
 	int c;
 	int total = 0;
 	modem_info *info = (modem_info *) tty->driver_data;
+	atemu *m = &info->emu;
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
 		return 0;
@@ -1197,8 +1204,6 @@
 		    || (info->vonline & 3)
 #endif
 			) {
-			atemu *m = &info->emu;
-
 #ifdef CONFIG_ISDN_AUDIO
 			if (!info->vonline)
 #endif
@@ -1256,7 +1261,9 @@
 					isdn_command(&c);
 				}
 				info->vonline = 0;
-				printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc,c);
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);
+#endif
 				info->xmit_count += cc;
 			} else
 #endif
@@ -1278,9 +1285,14 @@
 		count -= c;
 		total += c;
 	}
-	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
-		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
 	atomic_dec(&info->xmit_lock);
+	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) {
+		if (m->mdmreg[REG_DXMT] & BIT_DXMT) {
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+	}
 	if (from_user)
 		up(&info->write_sem);
 	return total;
@@ -1700,7 +1712,7 @@
 	restore_flags(flags);
 	info->blocked_open++;
 	while (1) {
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 		if (tty_hung_up_p(filp) ||
 		    !(info->flags & ISDN_ASYNC_INITIALIZED)) {
 #ifdef MODEM_DO_RESTART
@@ -1873,7 +1885,7 @@
 		 */
 		timeout = jiffies + HZ;
 		while (!(info->lsr & UART_LSR_TEMT)) {
-			current->state = TASK_INTERRUPTIBLE;
+			set_current_state(TASK_INTERRUPTIBLE);
 			schedule_timeout(20);
 			if (time_after(jiffies,timeout))
 				break;
@@ -1889,7 +1901,7 @@
 	info->ncarrier = 0;
 	tty->closing = 0;
 	if (info->blocked_open) {
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(50);
 		wake_up_interruptible(&info->open_wait);
 	}
@@ -2364,11 +2376,21 @@
 #ifdef ISDN_TTY_STAT_DEBUG
 				printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
 #endif
+				/* Wake up any processes waiting
+				 * for incoming call of this device when
+				 * DCD follow the state of incoming carrier
+				 */
+				if (info->blocked_open &&
+				   (info->emu.mdmreg[REG_DCD] & BIT_DCD)) {
+					wake_up_interruptible(&info->open_wait);
+				}
+
 				/* Schedule CONNECT-Message to any tty
 				 * waiting for it and
 				 * set DCD-bit of its modem-status.
 				 */
-				if (TTY_IS_ACTIVE(info)) {
+				if (TTY_IS_ACTIVE(info) ||
+				    (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) {
 					info->msr |= UART_MSR_DCD;
 					info->emu.charge = 0;
 					if (info->dialing & 0xf)
@@ -2609,7 +2631,7 @@
 				if ((jiffies - *lastplus) < PLUSWAIT2)
 					*pluscount = 0;
 			}
-			if ((*pluscount == 3) && (count = 1))
+			if ((*pluscount == 3) && (count == 1))
 				isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
 			if (*pluscount > 3)
 				*pluscount = 1;
@@ -2822,8 +2844,8 @@
 	int limit = ISDN_MSNLEN - 1;
 
 	while (((*p[0] >= '0' && *p[0] <= '9') ||
-	       /* Why a comma ??? */
-	       (*p[0] == ',')) &&
+		/* Why a comma ??? */
+		(*p[0] == ',') || (*p[0] == ':')) &&
 		(limit--))
 		*n++ = *p[0]++;
 	*n = '\0';
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_tty.h linux.19p1/drivers/isdn/isdn_tty.h
--- linux.vanilla/drivers/isdn/isdn_tty.h	Mon Dec 11 22:12:45 2000
+++ linux.19p1/drivers/isdn/isdn_tty.h	Wed Dec 13 22:11:20 2000
@@ -57,6 +57,8 @@
 #define REG_CPPP     12
 #define BIT_CPPP    128
 
+#define REG_DXMT     13
+#define BIT_DXMT      1
 #define REG_T70      13
 #define BIT_T70       2
 #define BIT_T70_EXT  32
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/sc/debug.c linux.19p1/drivers/isdn/sc/debug.c
--- linux.vanilla/drivers/isdn/sc/debug.c	Fri Apr 21 12:46:13 2000
+++ linux.19p1/drivers/isdn/sc/debug.c	Wed Dec 13 22:10:59 2000
@@ -1,5 +1,5 @@
 /*
- *  $Id: debug.c,v 1.3 1997/10/01 09:22:20 fritz Exp $
+ *  $Id: debug.c,v 1.5 2000/11/12 15:29:37 kai Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -27,11 +27,6 @@
  */
 #include <linux/kernel.h>
 
-#define NULL	0x0
-
-#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
-#define FREE_IRQ(a,b) free_irq(a,b)
-
 inline char *strcpy(char *, const char *);
 
 int dbg_level = 0;
@@ -61,7 +56,7 @@
 		*i = *j;
 		i++; j++;
 	}
-	*(++i) = NULL;
+	*(++i) = 0;
 	return dest;
 }
 
@@ -70,6 +65,6 @@
 	int i = 0;
 
 	while(dn[i] != ',')
-		str[i] = dn[i++];
+		str[i] = dn[i], i++;
 	str[i] = 0x0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/sc/debug.h linux.19p1/drivers/isdn/sc/debug.h
--- linux.vanilla/drivers/isdn/sc/debug.h	Fri Apr 21 12:46:13 2000
+++ linux.19p1/drivers/isdn/sc/debug.h	Wed Dec 13 22:11:07 2000
@@ -1,5 +1,5 @@
 /*
- *  $Id: debug.h,v 1.1 1996/11/07 13:07:42 fritz Exp $
+ *  $Id: debug.h,v 1.2 2000/02/26 01:00:53 keil Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -26,10 +26,5 @@
  *     +1 (416) 297-6433 Facsimile
  */
 
-#if LINUX_VERSION_CODE < 131072
-	#error You cant use this driver on kernels older than 2.0
-#else
-	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
-	#define FREE_IRQ(a,b) free_irq(a,b)
-#endif
-
+#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+#define FREE_IRQ(a,b) free_irq(a,b)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/sc/init.c linux.19p1/drivers/isdn/sc/init.c
--- linux.vanilla/drivers/isdn/sc/init.c	Fri Apr 21 12:46:13 2000
+++ linux.19p1/drivers/isdn/sc/init.c	Wed Dec 13 22:11:07 2000
@@ -164,7 +164,7 @@
 		if(do_reset) {
 			pr_debug("Doing a SAFE probe reset\n");
 			outb(0xFF, io[b] + RESET_OFFSET);
-			current->state = TASK_INTERRUPTIBLE;
+			set_current_state(TASK_INTERRUPTIBLE);
 			schedule_timeout(milliseconds(10000));
 		}
 		pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b],
@@ -302,7 +302,7 @@
 			/*
 			 * No interrupt could be used
 			 */
-			pr_debug("Failed to aquire an IRQ line\n");
+			pr_debug("Failed to acquire an IRQ line\n");
 			continue;
 		}
 
@@ -512,7 +512,7 @@
 	 * Try to identify a PRI card
 	 */
 	outb(PRI_BASEPG_VAL, pgport);
-	current->state = TASK_INTERRUPTIBLE;
+	set_current_state(TASK_INTERRUPTIBLE);
 	schedule_timeout(HZ);
 	sig = readl(rambase + SIG_OFFSET);
 	pr_debug("Looking for a signature, got 0x%x\n", sig);
@@ -523,7 +523,7 @@
 	 * Try to identify a PRI card
 	 */
 	outb(BRI_BASEPG_VAL, pgport);
-	current->state = TASK_INTERRUPTIBLE;
+	set_current_state(TASK_INTERRUPTIBLE);
 	schedule_timeout(HZ);
 	sig = readl(rambase + SIG_OFFSET);
 	pr_debug("Looking for a signature, got 0x%x\n", sig);
@@ -555,7 +555,7 @@
 	 */
 	x = 0;
 	while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(1);
 		x++;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/sc/message.c linux.19p1/drivers/isdn/sc/message.c
--- linux.vanilla/drivers/isdn/sc/message.c	Fri Apr 21 12:46:14 2000
+++ linux.19p1/drivers/isdn/sc/message.c	Wed Dec 13 22:11:13 2000
@@ -266,7 +266,7 @@
 	tries = 0;
 	/* wait for the response */
 	while (tries < timeout) {
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(1);
 		
 		pr_debug("SAR waiting..\n");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/sc/timer.c linux.19p1/drivers/isdn/sc/timer.c
--- linux.vanilla/drivers/isdn/sc/timer.c	Fri Apr 21 12:46:14 2000
+++ linux.19p1/drivers/isdn/sc/timer.c	Wed Dec 13 22:11:13 2000
@@ -1,5 +1,5 @@
 /*
- *  $Id: timer.c,v 1.2 1996/11/20 17:49:57 fritz Exp $
+ *  $Id: timer.c,v 1.3 2000/05/06 00:52:39 kai Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -91,9 +91,7 @@
 	else  {
 		pr_debug("%s: No signature yet, waiting another %d jiffies.\n", 
 			adapter[card]->devicename, CHECKRESET_TIME);
-		del_timer(&adapter[card]->reset_timer);
-		adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
-		add_timer(&adapter[card]->reset_timer);
+		mod_timer(&adapter[card]->reset_timer, jiffies+CHECKRESET_TIME);
 	}
 	restore_flags(flags);
 		
@@ -138,9 +136,7 @@
 	/* Reinitialize the timer */
 	save_flags(flags);
 	cli();
-	del_timer(&adapter[card]->stat_timer);
-	adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
-	add_timer(&adapter[card]->stat_timer);
+	mod_timer(&adapter[card]->stat_timer, jiffies+CHECKSTAT_TIME);
 	restore_flags(flags);
 
 	/* Send a new cePhyStatus message */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c527.c linux.19p1/drivers/net/3c527.c
--- linux.vanilla/drivers/net/3c527.c	Mon Dec 11 22:12:47 2000
+++ linux.19p1/drivers/net/3c527.c	Wed Dec 13 22:42:54 2000
@@ -1,9 +1,7 @@
-
 /* 3c527.c: 3Com Etherlink/MC32 driver for Linux
  *
  *	(c) Copyright 1998 Red Hat Software Inc
  *	Written by Alan Cox.
- *	Further debugging by Carl Drougge.
  *
  *	Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
  *	(for the MCA stuff) written by Wim Dumon.
@@ -17,7 +15,9 @@
  */
 
 static const char *version =
-	"3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n";
+	"3c527.c:v0.06 1999/09/16 Alan Cox (alan@redhat.com)\n";
+
+/* Modified by Richard Procter (rprocter@mcs.vuw.ac.nz, rnp@netlink.co.nz) */
 
 /*
  *	Things you need
@@ -31,6 +31,11 @@
  *	The documentation in places seems to miss things. In actual fact
  *	I've always eventually found everything is documented, it just
  *	requires careful study.
+ *
+ *      The manual contradicts itself when describing the minimum number 
+ *      buffers in the 'configure lists' command. 
+ *      My card accepts a buffer config of 4/4. 
+ *
  */
 
 #include <linux/module.h>
@@ -57,6 +62,8 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+#include <linux/proc_fs.h>
+
 #include "3c527.h"
 
 /*
@@ -69,23 +76,31 @@
 #ifndef NET_DEBUG
 #define NET_DEBUG 2
 #endif
+
+#undef DEBUG_IRQ
+
 static unsigned int mc32_debug = NET_DEBUG;
 
 /* The number of low I/O ports used by the ethercard. */
 #define NETCARD_IO_EXTENT	8
 
+/* As implemented, values must be a power-of-2 -- 4/8/16/32 */ 
 
-struct mc32_mailbox
+#define TX_RING_LEN     16       /* Typically the card supports 37  */
+#define RX_RING_LEN     8        /*     "       "        "          */
+
+#define RX_COPYBREAK    200      /* Value from 3c59x.c */
+
+
+/* Pointers to buffers and their on-card records */
+struct mc32_ring_desc 
 {
-	u16	mbox __attribute((packed));
-	u16	data[1] __attribute((packed));
+  volatile struct skb_header *p;                    
+  struct sk_buff *skb;          
 };
 
-/* Information that need to be kept for each board. */
-
-#define TX_RING_MAX	16	/* Typically the card supports 37 */
-#define RX_RING_MAX	32	/*        "     "       "         */
 
+/* Information that needs to be kept for each board. */
 struct mc32_local 
 {
 	struct net_device_stats net_stats;
@@ -93,25 +108,28 @@
 	volatile struct mc32_mailbox *rx_box;
 	volatile struct mc32_mailbox *tx_box;
 	volatile struct mc32_mailbox *exec_box;
-	volatile u16 *stats;
-	u16 tx_chain;
-	u16 rx_chain;
-	u16 tx_len;
-	u16 rx_len;
+        volatile struct mc32_stats *stats;    /* Start of on-card statistics */
+        u16 tx_chain;           /* Transmit list start offset */
+	u16 rx_chain;           /* Receive list start offset */
+        u16 tx_len;             /* Transmit list count */ 
+        u16 rx_len;             /* Receive list count */
 	u32 base;
 	u16 rx_halted;
 	u16 tx_halted;
-	u16 rx_pending;		/* ring due a service */
 	u16 exec_pending;
 	u16 mc_reload_wait;	/* a multicast load request is pending */
-	atomic_t tx_count;		/* buffers left */
+	atomic_t tx_count;	/* buffers left */
 	struct wait_queue *event;
-	struct sk_buff *tx_skb[TX_RING_MAX];	/* Transmit ring */
-	u16 tx_skb_top;
-	u16 tx_skb_end;
-	struct sk_buff *rx_skb[RX_RING_MAX];	/* Receive ring */
-	void *rx_ptr[RX_RING_MAX];		/* Data pointers */
-	u32 mc_list_valid;			/* True when the mclist is set */
+	
+	struct mc32_ring_desc tx_ring[TX_RING_LEN];	/* Host Transmit ring */
+	struct mc32_ring_desc rx_ring[RX_RING_LEN];	/* Host Receive ring */
+
+	u16 tx_ring_tail;       /* index to tx de-queue end */
+	u16 tx_ring_head;       /* index to tx en-queue end */
+
+	u16 rx_ring_tail;       /* index to rx de-queue end */ 
+
+	u32 mc_list_valid;	/* True when the mclist is set */
 };
 
 /* The station (ethernet) address prefix, used for a sanity check. */
@@ -124,7 +142,7 @@
 	char		*name;
 };
 
-static struct mca_adapters_t mc32_adapters[] __initdata = {
+const struct mca_adapters_t mc32_adapters[] = {
 	{ 0x0041, "3COM EtherLink MC/32" },
 	{ 0x8EF5, "IBM High Performance Lan Adapter" },
 	{ 0x0000, NULL }
@@ -136,6 +154,7 @@
 extern int mc32_probe(struct device *dev);
 
 static int	mc32_probe1(struct device *dev, int ioaddr);
+static int      mc32_command(struct device *dev, u16 cmd, void *data, int len);
 static int	mc32_open(struct device *dev);
 static int	mc32_send_packet(struct sk_buff *skb, struct device *dev);
 static void	mc32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
@@ -143,6 +162,7 @@
 static struct	net_device_stats *mc32_get_stats(struct device *dev);
 static void	mc32_set_multicast_list(struct device *dev);
 static void	mc32_reset_multicast_list(struct device *dev);
+static void     mc32_flush_tx_ring(struct mc32_local *lp);
 
 /*
  * Check for a network adaptor of this type, and return '0' iff one exists.
@@ -224,7 +244,7 @@
 		"82586 initialisation failure",
 		"Adapter list configuration error"
 	};
-	
+
 	/* Time to play MCA games */
 
 	if (mc32_debug  &&  version_printed++ == 0)
@@ -399,11 +419,11 @@
 	
 	lp->exec_box=bus_to_virt(dev->mem_start+base);
 	
-	base=lp->exec_box->data[1]<<16|lp->exec_box->data[0];
+	base=lp->exec_box->data[1]<<16|lp->exec_box->data[0];  
 	
 	lp->base = dev->mem_start+base;
 	
-	lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]);
+	lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]); 
 	lp->tx_box=bus_to_virt(lp->base + lp->exec_box->data[3]);
 	
 	lp->stats = bus_to_virt(lp->base + lp->exec_box->data[5]);
@@ -412,17 +432,14 @@
 	 *	Descriptor chains (card relative)
 	 */
 	 
-	lp->tx_chain 		= lp->exec_box->data[8];
-	lp->rx_chain 		= lp->exec_box->data[10];
-	lp->tx_len 		= lp->exec_box->data[9];
-	lp->rx_len 		= lp->exec_box->data[11];
-	
-	printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
-		dev->name, lp->rx_len, lp->tx_len, lp->base);
+	lp->tx_chain 		= lp->exec_box->data[8];   /* Transmit list start offset */
+	lp->rx_chain 		= lp->exec_box->data[10];  /* Receive list start offset */
+	lp->tx_len 		= lp->exec_box->data[9];   /* Transmit list count */ 
+	lp->rx_len 		= lp->exec_box->data[11];  /* Receive list count */
 	
-	if(lp->tx_len >TX_RING_MAX)
-		lp->tx_len = TX_RING_MAX;
-		
+	printk("%s: Firmware Rev %d. %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
+		dev->name, lp->exec_box->data[12], lp->rx_len, lp->tx_len, lp->base);
+
 	dev->open		= mc32_open;
 	dev->stop		= mc32_close;
 	dev->hard_start_xmit	= mc32_send_packet;
@@ -431,14 +448,13 @@
 	
 	lp->rx_halted		= 1;
 	lp->tx_halted		= 1;
-	lp->rx_pending		= 0;
 
 	/* Fill in the fields of the device structure with ethernet values. */
 	ether_setup(dev);
+	
 	return 0;
 }
 
-
 /*
  *	Polled command stuff 
  */
@@ -531,17 +547,18 @@
 	/* Send the command */
 	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
 	outb(1<<6, ioaddr+HOST_CMD);	
-	
+
 	save_flags(flags);
 	cli();
+
 	while(lp->exec_pending!=2)
-		sleep_on(&lp->event);
+	  sleep_on(&lp->event);
 	lp->exec_pending=0;
 	restore_flags(flags);
 	
-	 
-	if(lp->exec_box->data[0]&(1<<13))
+	if(lp->exec_box->mbox&(1<<13))
 		ret = -1;
+
 	/*
 	 *	A multicast set got blocked - do it now
 	 */
@@ -562,10 +579,12 @@
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	mc32_ring_poll(dev);	
 	
 	lp->rx_box->mbox=0;
-	outb(3<<3, ioaddr+HOST_CMD);	/* Suspend reception */
+	outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD);	/* Suspend reception */
+	
+	mc32_ring_poll(dev);	
 }
 
  
@@ -577,58 +596,32 @@
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	
-	lp->rx_box->mbox=0;
-	outb(1<<3, ioaddr+HOST_CMD);	/* GO */
+
+	/* Tell the card start reception at the first descriptor */ 
+	lp->rx_box->data[0]=lp->rx_chain; 
+ 
 	mc32_ring_poll(dev);	
 	
+	lp->rx_box->mbox=0;
+	outb(HOST_CMD_START_RX, ioaddr+HOST_CMD);	/* GO */
+
+	mc32_ring_poll(dev);
 	lp->rx_halted=0;
-	lp->rx_pending=0;
 }
 
 static void mc32_tx_abort(struct device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+		
+	mc32_ring_poll(dev);
 	
 	lp->tx_box->mbox=0;
-	outb(3, ioaddr+HOST_CMD);	/* Suspend */
-	
-	/* Ring empty */
-	
-	atomic_set(&lp->tx_count, lp->tx_len);
-	
-	/* Flush */
-	if(lp->tx_skb_top!=lp->tx_skb_end)
-	{
-		int i;
-		if(lp->tx_skb_top<=lp->tx_skb_end)
-		{
-			for(i=lp->tx_skb_top;i<lp->tx_skb_end;i++)
-			{
-				dev_kfree_skb(lp->tx_skb[i]);
-				lp->tx_skb[i]=NULL;
-			}
-		}
-		else
-		{
-			for(i=lp->tx_skb_end;i<TX_RING_MAX;i++)
-			{
-				dev_kfree_skb(lp->tx_skb[i]);
-				lp->tx_skb[i]=NULL;
-			}
-			for(i=0;i<lp->tx_skb_top;i++)
-			{
-				dev_kfree_skb(lp->tx_skb[i]);
-				lp->tx_skb[i]=NULL;
-			}
-		}
-	}
-	lp->tx_skb_top=lp->tx_skb_end=0;
+	outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD);	/* Suspend */
+
+	mc32_flush_tx_ring(lp); 
+
+	mc32_ring_poll(dev);
 }
 
 /*
@@ -640,81 +633,132 @@
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	
+	mc32_ring_poll(dev);
+
 	lp->tx_box->mbox=0;
-#if 0	
-	outb(5, ioaddr+HOST_CMD);	/* GO */
-	printk("TX=>5\n");
+	outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD);	/* GO */
+
 	mc32_ring_poll(dev);	
-	if(lp->tx_box->mbox&(1<<13))
-		printk("TX begin error!\n");
-#endif		
 	lp->tx_halted=0;
 }
 
 	
 /*
- *	Load the rx ring
+ *	Load the rx ring.
  */
  
 static int mc32_load_rx_ring(struct device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int i;
-	u16 base;
+	u16 rx_base;
 	volatile struct skb_header *p;
 	
-	base = lp->rx_box->data[0];
-	
-	/* Fix me - should use card size - also fix flush ! */ 
+	rx_base=lp->rx_chain;
 
-	for(i=0;i<RX_RING_MAX;i++)
+	for(i=0;i<RX_RING_LEN;i++)
 	{
-		lp->rx_skb[i]=alloc_skb(1532, GFP_KERNEL);
-		if(lp->rx_skb[i]==NULL)
+		lp->rx_ring[i].skb=alloc_skb(1532, GFP_KERNEL);
+		skb_reserve(lp->rx_ring[i].skb, 18);  
+
+		if(lp->rx_ring[i].skb==NULL)
 		{
 			for(;i>=0;i--)
-				kfree_skb(lp->rx_skb[i]);
+				kfree_skb(lp->rx_ring[i].skb);
 			return -ENOBUFS;
 		}
-		lp->rx_ptr[i]=lp->rx_skb[i]->data+18;
 		
-		p=bus_to_virt(lp->base+base);
+		p=bus_to_virt(lp->base+rx_base);
+				
 		p->control=0;
-		p->data = virt_to_bus(lp->rx_ptr[i]);
+		p->data=virt_to_bus(lp->rx_ring[i].skb->data);
 		p->status=0;
-		p->length = 1532;
-		base = p->next;
+		p->length=1532;
+	
+		lp->rx_ring[i].p=p; 
+		rx_base=p->next; 
 	}
-	p->control = (1<<6);
-	lp->rx_box->mbox = 0;
+
+	lp->rx_ring[i-1].p->control |= CONTROL_EOL;
+
+	lp->rx_ring_tail=0; 
+
+	lp->rx_box->mbox=0;   /* check: needed ? */
 	return 0;
 }	
 
 static void mc32_flush_rx_ring(struct mc32_local *lp)
 {
-	int i;
-	for(i=0;i<RX_RING_MAX;i++)
-		kfree_skb(lp->rx_skb[i]);
+	int i; 
+	for(i=0; i < RX_RING_LEN; i++) { 
+		kfree_skb(lp->rx_ring[i].skb);
+		lp->rx_ring[i].p=NULL; 
+	} 
 }
 
+
+/* Load the addresses of the on-card buffer descriptors into main memory */ 
+static void mc32_load_tx_ring(struct device *dev)
+{ 
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	volatile struct skb_header *p;
+	int i; 
+	u16 tx_base;
+
+	tx_base=lp->tx_box->data[0]; 
+
+	/* Read the 'next' pointers from the on-card list into      */
+	/* our tx_ring array so we can reduce slow shared-mem reads */ 
+
+	for(i=0;i<lp->tx_len;i++) 
+	{
+		p=bus_to_virt(lp->base+tx_base);
+		lp->tx_ring[i].p=p; 
+		lp->tx_ring[i].skb=NULL;
+
+		tx_base=p->next;
+	}
+
+	lp->tx_ring_head=lp->tx_ring_tail=0; 
+} 
+
 static void mc32_flush_tx_ring(struct mc32_local *lp)
 {
-	int i;
 	
-	if(lp->tx_skb_top <= lp->tx_skb_end)
-	{
-		for(i=lp->tx_skb_top;i<lp->tx_skb_end;i++)
-			dev_kfree_skb(lp->tx_skb[i]);
-	}
-	else
+	if(lp->tx_ring_tail!=lp->tx_ring_head)
 	{
-		for(i=0;i<lp->tx_skb_end;i++)
-			dev_kfree_skb(lp->tx_skb[i]);
-		for(i=lp->tx_skb_top;i<TX_RING_MAX;i++)
-			dev_kfree_skb(lp->tx_skb[i]);
+		int i;
+	
+		if(lp->tx_ring_tail < lp->tx_ring_head)
+		{
+			for(i=lp->tx_ring_tail;i<lp->tx_ring_head;i++)
+			{
+				dev_kfree_skb(lp->tx_ring[i].skb);
+				lp->tx_ring[i].skb=NULL;
+				lp->tx_ring[i].p=NULL; 
+			}
+		}
+		else
+		{
+			for(i=lp->tx_ring_tail; i<TX_RING_LEN; i++) 
+			{
+				dev_kfree_skb(lp->tx_ring[i].skb);
+				lp->tx_ring[i].skb=NULL;
+				lp->tx_ring[i].p=NULL; 
+			}
+			for(i=0; i<lp->tx_ring_head; i++) 
+			{
+				dev_kfree_skb(lp->tx_ring[i].skb);
+				lp->tx_ring[i].skb=NULL;
+				lp->tx_ring[i].p=NULL; 
+			}
+		}
 	}
+	
+	/* -1 so that tx_ring_head cannot "lap" tx_ring_tail, */
+	/* which would be bad news for mc32_tx_ring as cur. implemented */ 
+	atomic_set(&lp->tx_count, TX_RING_LEN-1); 
+	lp->tx_ring_tail=lp->tx_ring_head=0;
 }
  	
 /*
@@ -725,9 +769,11 @@
 static int mc32_open(struct device *dev)
 {
 	int ioaddr = dev->base_addr;
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	u16 zero_word=0;
 	u8 one=1;
 	u8 regs;
+	u16 descnumbuffs[2] = {TX_RING_LEN, RX_RING_LEN};
 	
 	dev->tbusy = 0;
 	dev->interrupt = 0;
@@ -748,7 +794,6 @@
 
 	mc32_command(dev, 4, &one, 2);
 
-	 	
 	/*
 	 *	Send the command sequence "abort, resume" for RX and TX.
 	 *	The abort cleans up the buffer chains if needed.
@@ -757,6 +802,22 @@
 	mc32_rx_abort(dev);
 	mc32_tx_abort(dev);
 	
+	/* Ask card to set up on-card descriptors to our spec */ 
+
+	if(mc32_command(dev, 8, descnumbuffs, 4)) { 
+		printk("%s: %s rejected our buffer configuration!\n",
+	 	       dev->name, cardname);
+		return -ENOBUFS; 
+	}
+	
+	/* Report new configuration */ 
+	mc32_command(dev, 6, NULL, 0); 
+
+	lp->tx_chain 		= lp->exec_box->data[8];   /* Transmit list start offset */
+	lp->rx_chain 		= lp->exec_box->data[10];  /* Receive list start offset */
+	lp->tx_len 		= lp->exec_box->data[9];   /* Transmit list count */ 
+	lp->rx_len 		= lp->exec_box->data[11];  /* Receive list count */
+ 
 	/* Set Network Address */
 	mc32_command(dev, 1, dev->dev_addr, 6);
 	
@@ -767,9 +828,11 @@
 	   but basically means for all lans now days - has a performance
 	   cost but best set */
 	   
-	mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */
-	
+	/* mc32_command(dev, 0x0D, &zero_word, 2); */   /* 82586 bug workaround on  */
+
 	/* Load the ring we just initialised */
+
+	mc32_load_tx_ring(dev);
 	
 	if(mc32_load_rx_ring(dev))
 	{
@@ -778,7 +841,7 @@
 	}
 	
 	/* And the resume command goes last */
-	
+
 	mc32_rx_begin(dev);
 	mc32_tx_begin(dev);
 	
@@ -787,10 +850,10 @@
 	return 0;
 }
 
+
 static int mc32_send_packet(struct sk_buff *skb, struct device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
-	int ioaddr = dev->base_addr;
 
 	if (dev->tbusy) {
 		/*
@@ -817,9 +880,7 @@
 	}
 	else 
 	{
-		unsigned long flags;
-		
-		u16 tx_head;
+		unsigned long flags;	       
 		volatile struct skb_header *p, *np;
 
 		save_flags(flags);
@@ -832,130 +893,230 @@
 			return 1;
 		}
 
-		tx_head = lp->tx_box->data[0];
-		atomic_dec(&lp->tx_count);
+		atomic_dec(&lp->tx_count); 
 
-		/* We will need this to flush the buffer out */
-		
-		lp->tx_skb[lp->tx_skb_end] = skb;
-		lp->tx_skb_end++;
-		lp->tx_skb_end&=(TX_RING_MAX-1);
-		
-		/* TX suspend - shouldnt be needed but apparently is.
-		   This is a research item ... */
-		   
-		while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-		lp->tx_box->mbox=0;
-		outb(3, ioaddr+HOST_CMD);
-		
-		/* Transmit now stopped */
 		/* P is the last sending/sent buffer as a pointer */
-		p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
+		p=lp->tx_ring[lp->tx_ring_head].p; 
 		
+		lp->tx_ring_head++;
+		lp->tx_ring_head&=(TX_RING_LEN-1); 
+
 		/* NP is the buffer we will be loading */
-		np=(struct skb_header *)bus_to_virt(lp->base+p->next);
-		
-		np->control	|= (1<<6);	/* EOL */
-		wmb();
-				
-		np->length	= skb->len;
+		np=lp->tx_ring[lp->tx_ring_head].p; 
 
-		if(np->length < 60)
-			np->length = 60;
+	        /* We will need this to flush the buffer out */
+		lp->tx_ring[lp->tx_ring_head].skb=skb;
+   	   
+		np->length = (skb->len < 60) ? 60 : skb->len; 
 			
 		np->data	= virt_to_bus(skb->data);
-		np->status	= 0;
-		np->control	= (1<<7)|(1<<6);	/* EOP EOL */
-		wmb();
+   		np->status	= 0;
+		np->control     = CONTROL_EOP | CONTROL_EOL;     
+	        wmb();
 		
-		p->status	= 0;
-		p->control	&= ~(1<<6);
-		
-		dev->tbusy	= 0;			/* Keep feeding me */		
-		
-		while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-		lp->tx_box->mbox=0;
-		outb(5, ioaddr+HOST_CMD);		/* Restart TX */
-		restore_flags(flags);
+		p->control     &= ~CONTROL_EOL;     /* Clear EOL on p */ 
+	   
+       	        dev->tbusy	= 0;	       /* Keep feeding me */		
+	
+	        restore_flags(flags);
 	}
 	return 0;
 }
 
 static void mc32_update_stats(struct device *dev)
 {
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	volatile struct mc32_stats *st = lp->stats; 
+	
+        u32 rx_errors=0; 
+
+	/* The databook isn't at all clear about whether      */ 
+        /* the host should suspend rx/tx here to avoid races. */
+        /* Assuming that it should, my code here is probably  */
+        /* a bit dodgey - RP                                  */
+
+	/* TO DO: count rx_errors, tx_errors, figure out how to measure colisions */     
+
+	/* RX */ 
+
+    	rx_errors += lp->net_stats.rx_crc_errors += st->rx_crc_errors;
+	st->rx_crc_errors = 0; 
+
+    	rx_errors += lp->net_stats.rx_length_errors += st->rx_tooshort_errors;
+	st->rx_tooshort_errors = 0; 
+
+	rx_errors += lp->net_stats.rx_length_errors += st->rx_toolong_errors;
+	st->rx_toolong_errors = 0; 
+
+	rx_errors += lp->net_stats.rx_fifo_errors += st->rx_overrun_errors; 
+	st->rx_overrun_errors = 0; 
+
+	rx_errors += lp->net_stats.rx_frame_errors += st->rx_alignment_errors;
+	st->rx_alignment_errors = 0; 
+
+	rx_errors += lp->net_stats.rx_missed_errors += st->rx_outofresource_errors;
+	st->rx_outofresource_errors = 0; 
+
+	lp->net_stats.rx_errors = rx_errors;
+	
+        /* TX */ 
+
+	/* How to count collisions when you're only 
+           told frames which had 1 or 2-15? - RP */
+	
+	lp->net_stats.collisions += (st->tx_max_collisions * 16); 
+	st->tx_max_collisions = 0;  
+
+	lp->net_stats.tx_carrier_errors += st->tx_carrier_errors; 
+	st->tx_carrier_errors = 0; 
+
+	lp->net_stats.tx_fifo_errors += st->tx_underrun_errors; 
+	st->tx_underrun_errors = 0; 
+	
 }
 
 
 static void mc32_rx_ring(struct device *dev)
 {
-	struct mc32_local *lp=dev->priv;
-	int ioaddr = dev->base_addr;
-	int x=0;
+	struct mc32_local *lp=dev->priv;		
 	volatile struct skb_header *p;
-	u16 base;
-	u16 top;
+	u16 rx_ring_tail = lp->rx_ring_tail;
+	u16 rx_old_tail = rx_ring_tail; 
+
+	int x=0;
 	
-	top = base = lp->rx_box->data[0];
 	do
-	{
-		p=(struct skb_header *)bus_to_virt(base+lp->base);
-		if(!(p->status & (1<<7)))
+	{ 
+		p=lp->rx_ring[rx_ring_tail].p; 
+
+		if(!(p->status & (1<<7))) { /* Not COMPLETED */ 
 			break;
-		if(p->status & (1<<6))
-		{
-			u16 length = p->length;
-			struct sk_buff *skb=dev_alloc_skb(length+2);
-			if(skb!=NULL)
+		} 
+		if(p->status & (1<<6)) /* COMPLETED_OK */
+		{		        
+
+			u16 length=p->length;
+			struct sk_buff *skb; 
+			struct sk_buff *newskb; 
+
+#ifdef DEBUG_IRQ 
+ 			printk("skb_header %p has frame, x==%d\n", p, x); 
+#endif 
+
+			/* Try to save time by avoiding a copy on big frames */
+
+			if ((length > RX_COPYBREAK) 
+			    && ((newskb=dev_alloc_skb(1532)) != NULL)) 
+			{ 
+				skb=lp->rx_ring[rx_ring_tail].skb;
+				skb_put(skb, length);
+				
+				skb_reserve(newskb,18); 
+				lp->rx_ring[rx_ring_tail].skb=newskb;  
+				p->data=virt_to_bus(newskb->data);  
+			} 
+			else 
 			{
+				skb=dev_alloc_skb(length+2);  
+
+				if(skb==NULL) 
+				{ 
+					lp->net_stats.rx_dropped++; 
+
+					p->length = 1532; 
+					p->status = 0;
+					p->control = 0; 	        
+					
+					rx_ring_tail++;
+					rx_ring_tail&=(RX_RING_LEN-1); 
+
+					continue; /* better to use a goto? */ 
+				}
+
 				skb_reserve(skb,2);
-				/*printk("Frame at %p\n", bus_to_virt(p->data)); */
 				memcpy(skb_put(skb, length),
-					bus_to_virt(p->data), length);
-				skb->protocol=eth_type_trans(skb,dev);
-				skb->dev=dev;
-				lp->net_stats.rx_packets++;
-				lp->net_stats.rx_bytes+=skb->len;
-				netif_rx(skb);
+				       lp->rx_ring[rx_ring_tail].skb->data, length);
 			}
-			else
-				lp->net_stats.rx_dropped++;
+			
+			skb->protocol=eth_type_trans(skb,dev); 
+			skb->dev=dev; 
+ 			lp->net_stats.rx_packets++; 
+ 			lp->net_stats.rx_bytes+=skb->len; 
+			netif_rx(skb); 
 		}
-		else
+		else    /* NOT COMPLETED_OK */
 		{
-			lp->net_stats.rx_errors++;
-			switch(p->status&0x0F)
-			{
-				case 1:
-					lp->net_stats.rx_crc_errors++;break;
-				case 2:
-					lp->net_stats.rx_fifo_errors++;break;
-				case 3:
-					lp->net_stats.rx_frame_errors++;break;
-				case 4:
-					lp->net_stats.rx_missed_errors++;break;
-				case 5:
-					lp->net_stats.rx_length_errors++;break;
-			}
+		        /* There was some sort of reception error.     */
+                        /* This case should never occur unless         */
+			/* the card is asked to upload damaged frames.  */
+  
+			/* do nothing */ 
 		}
-		p->length = 1532;
-		p->control &= ~(1<<6);
+
+		p->length = 1532; 
 		p->status = 0;
-		base = p->next;
+		p->control = 0; 	        
+
+		rx_ring_tail++;
+		rx_ring_tail&=(RX_RING_LEN-1); 
+	}
+        while(x++<48);  
+
+	/* If there was actually a frame to be processed, */ 
+	/* place the EL bit at the descriptor prior to the one to be filled next */ 
+
+	if (rx_ring_tail != rx_old_tail) { 
+		lp->rx_ring[(rx_ring_tail-1)&(RX_RING_LEN-1)].p->control |=  CONTROL_EOL; 
+		lp->rx_ring[(rx_old_tail-1)&(RX_RING_LEN-1)].p->control  &= ~CONTROL_EOL; 
+
+		lp->rx_ring_tail=rx_ring_tail; 
 	}
-	while(x++<48);
-	
-	/*
-	 *	Restart ring processing
-	 */	
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	lp->rx_box->mbox=0;
-	lp->rx_box->data[0] = top;
-	outb(1<<3, ioaddr+HOST_CMD);	
-	lp->rx_halted = 0;
 }
 
 
+static void mc32_tx_ring(struct device *dev) {
+
+  struct mc32_local *lp=(struct mc32_local *)dev->priv;
+  volatile struct skb_header *np;
+
+  /* NB: lp->tx_count=TX_RING_LEN-1 so that tx_ring_head cannot "lap" tail here */
+
+  while (lp->tx_ring_tail != lp->tx_ring_head)  
+  {   
+	  u16 t; 
+
+	  t=(lp->tx_ring_tail+1)&(TX_RING_LEN-1); 
+	  np=lp->tx_ring[t].p; 
+
+	  if(!(np->status & (1<<7))) { /* Not COMPLETED */ 
+		  break; 
+	  } 
+
+	  lp->net_stats.tx_packets++;
+
+	  if(!(np->status & (1<<6))) /* Not COMPLETED_OK */
+	  {
+		  lp->net_stats.tx_errors++;   
+		  
+                  /* Error stats are stored on-card to be picked up by
+	             mc32_update_stats() - RP */
+	  }
+	  
+	  
+	  /* Packets are sent in order - this is
+	     basically a FIFO queue of buffers matching
+	     the card ring */
+	  lp->net_stats.tx_bytes+=lp->tx_ring[t].skb->len;
+	  dev_kfree_skb(lp->tx_ring[t].skb);
+	  lp->tx_ring[t].skb=NULL;
+	  atomic_inc(&lp->tx_count);
+	  dev->tbusy=0;
+	  mark_bh(NET_BH);
+
+	  lp->tx_ring_tail=t; 
+  }
+} 
+
 /*
  * The typical workload of the driver:
  *   Handle the network interface interrupts.
@@ -965,26 +1126,29 @@
 	struct device *dev = dev_id;
 	struct mc32_local *lp;
 	int ioaddr, status, boguscount = 0;
+	int rx_event = 0;
+	int tx_event = 0; 
+	int must_restart = 0;
 	
 	if (dev == NULL) {
 		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
 		return;
 	}
 	dev->interrupt = 1;
-
+ 
 	ioaddr = dev->base_addr;
 	lp = (struct mc32_local *)dev->priv;
 
 	/* See whats cooking */
-	
-	while((inb(ioaddr+2)&(1<<5)) && boguscount++<2000)
+
+	while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000)
 	{
 		status=inb(ioaddr+HOST_CMD);
 
 #ifdef DEBUG_IRQ		
-		printk("Status TX%d RX%d EX%d OV%d\n",
+		printk("Status TX%d RX%d EX%d OV%d BC%d\n",
 			(status&7), (status>>3)&7, (status>>6)&1,
-			(status>>7)&1);
+			(status>>7)&1, boguscount);
 #endif
 			
 		switch(status&7)
@@ -992,20 +1156,8 @@
 			case 0:
 				break;
 			case 6: /* TX fail */
-				lp->net_stats.tx_errors++;
 			case 2:	/* TX ok */
-				lp->net_stats.tx_packets++;
-				/* Packets are sent in order - this is
-				   basically a FIFO queue of buffers matching
-				   the card ring */
-				lp->net_stats.tx_bytes+=lp->tx_skb[lp->tx_skb_top]->len;
-				dev_kfree_skb(lp->tx_skb[lp->tx_skb_top]);
-				lp->tx_skb[lp->tx_skb_top]=NULL;
-				lp->tx_skb_top++;
-				lp->tx_skb_top&=(TX_RING_MAX-1);
-				atomic_inc(&lp->tx_count);
-				dev->tbusy=0;
-				mark_bh(NET_BH);
+				tx_event = 1; 
 				break;
 			case 3: /* Halt */
 			case 4: /* Abort */
@@ -1017,8 +1169,7 @@
 				wake_up(&lp->event);
 				break;
 			default:
-				printk("%s: strange tx ack %d\n", 
-					dev->name, status&7);
+				printk("%s: strange tx ack %d\n", dev->name, status&7);
 		}
 		status>>=3;
 		switch(status&7)
@@ -1026,15 +1177,7 @@
 			case 0:
 				break;
 			case 2:	/* RX */
-				lp->rx_pending=1;
-				if(!lp->rx_halted)
-				{
-					/*
-					 *	Halt ring receive
-					 */
-					while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-					outb(3<<3, ioaddr+HOST_CMD);
-				}
+				rx_event=1; 
 				break;
 			case 3:
 			case 4:
@@ -1047,25 +1190,32 @@
 				break;
 			case 6:
 				/* Out of RX buffers stat */
-				lp->net_stats.rx_dropped++;
-				lp->rx_pending=1;
 				/* Must restart */
-				lp->rx_halted=1;
+				lp->net_stats.rx_dropped++;
+				must_restart = 1; 	/* To restart */
+				rx_event = 1; 
+
 				break;
 			default:
 				printk("%s: strange rx ack %d\n", 
-					dev->name, status&7);
-			
+					dev->name, status&7);			
 		}
 		status>>=3;
 		if(status&1)
 		{
+			
 			/* 0=no 1=yes 2=replied, get cmd, 3 = wait reply & dump it */
-			if(lp->exec_pending!=3)
+			
+			if(lp->exec_pending!=3) 
+			{
 				lp->exec_pending=2;
-			else
+				wake_up(&lp->event);
+			}
+			else 
+			{
 				lp->exec_pending=0;
-			wake_up(&lp->event);
+				wake_up(&lp->event);
+			}
 		}
 		if(status&2)
 		{
@@ -1074,28 +1224,42 @@
 			 *	we have it flagged and can 
 			 *	send an immediate reply (CRR set)
 			 */
-			 
+	
 			if(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)
 			{
-				mc32_update_stats(dev);
-				outb(0, ioaddr+HOST_CMD);
+			 	mc32_update_stats(dev);
+			        outb(0, ioaddr+HOST_CMD);
 			}
+			
 		}
 	}
-	
+
+
+	if(tx_event) { 
+		mc32_tx_ring(dev);
+	}
+
 	/*
-	 *	Process and restart the receive ring. This has some state
-	 *	as we must halt the ring to process it and halting the ring
-	 *	might not occur in the same IRQ handling loop as we issue
-	 *	the halt.
-	 */
+	 *	Process the receive ring and restart if the card 
+	 *      stopped due to a shortage of free buffers.
+         */
 	 
-	if(lp->rx_pending && lp->rx_halted)
-	{
+	if(rx_event) {
 		mc32_rx_ring(dev);
-		lp->rx_pending = 0;
+		if (must_restart) { 
+			u16 rx_prev_tail=(lp->rx_ring_tail-1)&(RX_RING_LEN-1);
+
+			/* Restart at the current rx_ring_tail */ 
+			lp->rx_box->data[0]=lp->rx_ring[rx_prev_tail].p->next; 
+			lp->rx_box->mbox=0; 
+
+			while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); 
+			outb(HOST_CMD_START_RX, ioaddr+HOST_CMD); /* Re-enable receives */
+		}
 	}
-	dev->interrupt = 0;
+
+	dev->interrupt = 0;	
+
 	return;
 }
 
@@ -1152,7 +1316,12 @@
 
 static struct net_device_stats *mc32_get_stats(struct device *dev)
 {
-	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	struct mc32_local *lp;
+	
+	mc32_update_stats(dev); 
+
+	lp = (struct mc32_local *)dev->priv;
+
 	return &lp->net_stats;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c527.h linux.19p1/drivers/net/3c527.h
--- linux.vanilla/drivers/net/3c527.h	Fri Apr 21 12:46:15 2000
+++ linux.19p1/drivers/net/3c527.h	Wed Dec 13 22:42:31 2000
@@ -7,11 +7,19 @@
  */
   
 #define HOST_CMD		0
+#define         HOST_CMD_START_RX   (1<<3)
+#define         HOST_CMD_SUSPND_RX  (3<<3)
+#define         HOST_CMD_RESTRT_RX  (5<<3)
+
+#define         HOST_CMD_SUSPND_TX  3
+#define         HOST_CMD_RESTRT_TX  5
+
 
 #define HOST_STATUS		2
 #define		HOST_STATUS_CRR	(1<<6)
 #define		HOST_STATUS_CWR	(1<<5)
 
+
 #define HOST_CTRL		6
 #define		HOST_CTRL_ATTN	(1<<7)
 #define 	HOST_CTRL_RESET	(1<<6)
@@ -19,6 +27,13 @@
 
 #define HOST_RAMPAGE		8
 
+
+struct mc32_mailbox
+{
+	u16	mbox __attribute((packed));
+	u16	data[1] __attribute((packed));
+};
+
 struct skb_header
 {
 	u8	status __attribute((packed));
@@ -28,13 +43,37 @@
 	u32	data __attribute((packed));
 };
 
-#define STATUS_MASK	0x0F
-#define COMPLETED	0x80
-#define COMPLETED_OK	0x40
-#define BUFFER_BUSY	0x20
+struct mc32_stats
+{
+	/* RX Errors */
+	u32     rx_crc_errors       __attribute((packed)); 	
+	u32     rx_alignment_errors  __attribute((packed)); 	
+	u32     rx_overrun_errors    __attribute((packed));
+	u32     rx_tooshort_errors  __attribute((packed));
+	u32     rx_toolong_errors   __attribute((packed));
+	u32     rx_outofresource_errors  __attribute((packed)); 
+
+	u32     rx_discarded   __attribute((packed));  // via card pattern match filter
+
+	/* TX Errors */
+	u32     tx_max_collisions __attribute((packed)); 
+	u32     tx_carrier_errors __attribute((packed)); 
+	u32     tx_underrun_errors __attribute((packed)); 
+	u32     tx_cts_errors     __attribute((packed)); 
+	u32     tx_timeout_errors __attribute((packed)) ;
+	
+	/* various cruft */
+	u32     dataA[6] __attribute((packed));   
+        u16	dataB[5] __attribute((packed));   
+  	u32     dataC[14] __attribute((packed)); 	
+};
 
-#define CONTROL_EOP	0x80	/* End Of Packet */
-#define CONTROL_EL	0x40	/* End of List */
+#define STATUS_MASK	0x0F
+#define COMPLETED	(1<<7)
+#define COMPLETED_OK	(1<<6)
+#define BUFFER_BUSY	(1<<5)
 
+#define CONTROL_EOP	(1<<7)	/* End Of Packet */
+#define CONTROL_EOL	(1<<6)	/* End of List */
 
-#define MCA_MC32_ID	0x0041	/* Our MCA ident */
\ No newline at end of file
+#define MCA_MC32_ID	0x0041	/* Our MCA ident */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Config.in linux.19p1/drivers/net/Config.in
--- linux.vanilla/drivers/net/Config.in	Mon Dec 11 22:12:47 2000
+++ linux.19p1/drivers/net/Config.in	Wed Dec 13 22:30:41 2000
@@ -135,6 +135,7 @@
       tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
     fi
     tristate 'Apricot Xen-II on board Ethernet' CONFIG_APRICOT
+    tristate 'LP486E on board Ethernet' CONFIG_LP486E
     tristate 'CS89x0 support' CONFIG_CS89x0
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Makefile linux.19p1/drivers/net/Makefile
--- linux.vanilla/drivers/net/Makefile	Mon Dec 11 22:12:47 2000
+++ linux.19p1/drivers/net/Makefile	Wed Dec 13 22:30:41 2000
@@ -776,6 +776,11 @@
   endif
 endif
 
+# This is also a 82596 and should probably be merged
+ifeq ($(CONFIG_LP486E),y)
+L_OBJS += lp486e.o
+endif
+
 ifeq ($(CONFIG_DEC_ELCP),y)
 L_OBJS += tulip.o
 else
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Space.c linux.19p1/drivers/net/Space.c
--- linux.vanilla/drivers/net/Space.c	Mon Dec 11 22:12:47 2000
+++ linux.19p1/drivers/net/Space.c	Wed Dec 13 22:30:41 2000
@@ -64,6 +64,7 @@
 extern int fmv18x_probe(struct device *);
 extern int eth16i_probe(struct device *);
 extern int depca_probe(struct device *);
+extern int lp486e_probe(struct device *);
 extern int i82596_probe(struct device *);
 extern int ewrk3_probe(struct device *);
 extern int de4x5_probe(struct device *);
@@ -419,6 +420,9 @@
 #endif
 #if defined(CONFIG_APRICOT) || defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET)	/* Intel I82596 */
 	{i82596_probe, 0},
+#endif
+#if defined(CONFIG_LP486E)
+	{lp486e_probe, 0},
 #endif
 #ifdef CONFIG_EL1		/* 3c501 */
 	{el1_probe, 0},
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lance.c linux.19p1/drivers/net/lance.c
--- linux.vanilla/drivers/net/lance.c	Fri Apr 21 12:46:17 2000
+++ linux.19p1/drivers/net/lance.c	Wed Dec 13 21:57:10 2000
@@ -926,6 +926,8 @@
 
 	lp->tx_ring[entry].misc = 0x0000;
 
+	lp->stats.tx_bytes += skb->len;
+
 	/* If any part of this buffer is >16M we must copy it to a low-memory
 	   buffer. */
 	if ((u32)virt_to_bus(skb->data) + skb->len > 0x01000000) {
@@ -941,7 +943,6 @@
 		lp->tx_ring[entry].base = ((u32)virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
 	}
 	lp->cur_tx++;
-	lp->stats.tx_bytes += skb->len;
 
 	/* Trigger an immediate send poll. */
 	outw(0x0000, ioaddr+LANCE_ADDR);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lp486e.c linux.19p1/drivers/net/lp486e.c
--- linux.vanilla/drivers/net/lp486e.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/net/lp486e.c	Wed Dec 13 22:30:41 2000
@@ -0,0 +1,1401 @@
+/* Intel Professional Workstation/panther ethernet driver */
+/* lp486e.c: A panther 82596 ethernet driver for linux. */
+/*
+    History and copyrights:
+
+    Driver skeleton
+        Written 1993 by Donald Becker.
+        Copyright 1993 United States Government as represented by the Director,
+        National Security Agency.  This software may only be used and
+	distributed according to the terms of the GNU Public License
+	as modified by SRC, incorporated herein by reference.
+
+        The author may be reached as becker@super.org or
+        C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+    Apricot
+        Written 1994 by Mark Evans.
+        This driver is for the Apricot 82596 bus-master interface
+
+        Modularised 12/94 Mark Evans
+
+    Professional Workstation
+	Derived from apricot.c by Ard van Breemen
+	<ard@murphy.nl>|<ard@cstmel.hobby.nl>|<ard@cstmel.nl.eu.org>
+
+	Credits:
+	Thanks to Murphy Software BV for letting me write this in their time.
+	Well, actually, I get payed doing this...
+	(Also: see http://www.murphy.nl for murphy, and my homepage ~ard for
+	more information on the Professional Workstation)
+
+    Present version
+	aeb@cwi.nl
+*/
+/*
+    There are currently two motherboards that I know of in the
+    professional workstation. The only one that I know is the
+    intel panther motherboard. -- ard
+*/
+/*
+The pws is equipped with an intel 82596. This is a very intelligent controller
+which runs its own micro-code. Communication with the hostprocessor is done
+through linked lists of commands and buffers in the hostprocessors memory.
+A complete description of the 82596 is available from intel. Search for
+a file called "29021806.pdf". It is a complete description of the chip itself.
+To use it for the pws some additions are needed regarding generation of
+the PORT and CA signal, and the interrupt glue needed for a pc.
+I/O map:
+PORT  SIZE ACTION MEANING
+0xCB0    2 WRITE  Lower 16 bits for PORT command
+0xCB2    2 WRITE  Upper 16 bits for PORT command, and issue of PORT command
+0xCB4    1 WRITE  Generation of CA signal
+0xCB8    1 WRITE  Clear interrupt glue
+All other communication is through memory!
+*/
+
+#define SLOW_DOWN_IO udelay(5);
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define kkmalloc(typ, prio)	(typ *)(kmalloc(sizeof(typ), prio))
+
+#ifdef REALLY_OLD
+#ifndef HAVE_PORTRESERVE
+#define check_region(addr, size)	0
+#define request_region(addr, size,name)	do ; while(0)
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(buff, size) kfree_s(buff,size)
+#endif
+#endif /* REALLY_OLD */
+
+#define LOG_SRCDST    0x80000000
+#define LOG_STATINT   0x40000000
+#define LOG_STARTINT  0x20000000
+
+#define i596_debug debug
+
+static int i596_debug = 0;
+
+static const char * const medianame[] = {
+	"10baseT", "AUI",
+	"10baseT-FD", "AUI-FD",
+};
+
+#define LP486E_TOTAL_SIZE 16
+
+#define I596_NULL (0xffffffff)
+
+#define CMD_EOL		0x8000	/* The last command of the list, stop. */
+#define CMD_SUSP	0x4000	/* Suspend after doing cmd. */
+#define CMD_INTR	0x2000	/* Interrupt after doing cmd. */
+
+#define CMD_FLEX	0x0008	/* Enable flexible memory model */
+
+enum commands {
+	CmdNOP = 0,
+	CmdIASetup = 1,
+	CmdConfigure = 2,
+	CmdMulticastList = 3,
+	CmdTx = 4,
+	CmdTDR = 5,
+	CmdDump = 6,
+	CmdDiagnose = 7
+};
+
+char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList",
+			"Tx", "TDR", "Dump", "Diagnose" };
+
+/* Status word bits */
+#define	STAT_CX		0x8000	/* The CU finished executing a command
+				   with the Interrupt bit set */
+#define	STAT_FR		0x4000	/* The RU finished receiving a frame */
+#define	STAT_CNA	0x2000	/* The CU left the active state */
+#define	STAT_RNR	0x1000	/* The RU left the active state */
+#define STAT_ACK	(STAT_CX | STAT_FR | STAT_CNA | STAT_RNR)
+#define	STAT_CUS	0x0700	/* Status of CU: 0: idle, 1: suspended,
+				   2: active, 3-7: unused */
+#define STAT_RUS	0x00f0	/* Status of RU: 0: idle, 1: suspended,
+				   2: no resources, 4: ready,
+				   10: no resources due to no more RBDs,
+				   12: no more RBDs, other: unused */
+#define	STAT_T		0x0008	/* Bus throttle timers loaded */
+#define	STAT_ZERO	0x0807	/* Always zero */
+
+char *CUstates[8] = { "idle", "suspended", "active", 0, 0, 0, 0, 0 };
+char *RUstates[16] = { "idle", "suspended", "no resources", 0, "ready", 0,
+		       0, 0, 0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0 };
+
+#if 0
+static void
+i596_out_status(int status) {
+	int bad = 0;
+	char *s;
+
+	printk("status %4.4x:", status);
+	if (status == 0xffff)
+		printk(" strange..\n");
+	else {
+		if (status & STAT_CX)
+			printk("  CU done");
+		if (status & STAT_CNA)
+			printk("  CU stopped");
+		if (status & STAT_FR)
+			printk("  got a frame");
+		if (status & STAT_RNR)
+			printk("  RU stopped");
+		if (status & STAT_T)
+			printk("  throttled");
+		if (status & STAT_ZERO)
+			bad = 1;
+		s = CUstates[(status & STAT_CUS) >> 8];
+		if (!s)
+			bad = 1;
+		else
+			printk("  CU(%s)", s);
+		s = RUstates[(status & STAT_RUS) >> 4];
+		if (!s)
+			bad = 1;
+		else
+			printk("  RU(%s)", s);
+		if (bad)
+			printk("  bad status");
+		printk("\n");
+	}
+}
+#endif
+
+/* Command word bits */
+#define ACK_CX		0x8000
+#define ACK_FR		0x4000
+#define ACK_CNA		0x2000
+#define ACK_RNR		0x1000
+
+#define CUC_START	0x0100
+#define CUC_RESUME	0x0200
+#define CUC_SUSPEND	0x0300
+#define CUC_ABORT	0x0400
+
+#define RX_START	0x0010
+#define RX_RESUME	0x0020
+#define RX_SUSPEND	0x0030
+#define RX_ABORT	0x0040
+
+typedef u32 phys_addr;
+
+static inline phys_addr
+va_to_pa(volatile void *x) {
+	return x ? virt_to_bus(x) : I596_NULL;
+}
+
+static inline void *
+pa_to_va(phys_addr x) {
+	return (x == I596_NULL) ? NULL : bus_to_virt(x);
+}
+
+/* status bits for cmd */
+#define CMD_STAT_C	0x8000	/* CU command complete */
+#define CMD_STAT_B	0x4000	/* CU command in progress */
+#define CMD_STAT_OK	0x2000	/* CU command completed without errors */
+#define CMD_STAT_A	0x1000	/* CU command abnormally terminated */
+
+struct i596_cmd {		/* 8 bytes */
+	unsigned short status;
+	unsigned short command;
+	phys_addr pa_next;	/* va_to_pa(struct i596_cmd *next) */
+};
+
+#define EOF		0x8000
+#define SIZE_MASK	0x3fff
+
+struct i596_tbd {
+	unsigned short size;
+	unsigned short pad;
+	phys_addr pa_next;	/* va_to_pa(struct i596_tbd *next) */
+	phys_addr pa_data;	/* va_to_pa(char *data) */
+	struct sk_buff *skb;
+};
+
+struct tx_cmd {
+	struct i596_cmd cmd;
+	phys_addr pa_tbd;	/* va_to_pa(struct i596_tbd *tbd) */
+	unsigned short size;
+	unsigned short pad;
+};
+
+/* status bits for rfd */
+#define RFD_STAT_C	0x8000	/* Frame reception complete */
+#define RFD_STAT_B	0x4000	/* Frame reception in progress */
+#define RFD_STAT_OK	0x2000	/* Frame received without errors */
+#define RFD_STATUS	0x1fff
+#define RFD_LENGTH_ERR	0x1000
+#define RFD_CRC_ERR	0x0800
+#define RFD_ALIGN_ERR	0x0400
+#define RFD_NOBUFS_ERR	0x0200
+#define RFD_DMA_ERR	0x0100	/* DMA overrun failure to acquire system bus */
+#define RFD_SHORT_FRAME_ERR	0x0080
+#define RFD_NOEOP_ERR	0x0040
+#define RFD_TRUNC_ERR	0x0020
+#define RFD_MULTICAST  0x0002	/* 0: destination had our address
+				   1: destination was broadcast/multicast */
+#define RFD_COLLISION  0x0001
+
+/* receive frame descriptor */
+struct i596_rfd {
+	unsigned short stat;
+	unsigned short cmd;
+	phys_addr pa_next;	/* va_to_pa(struct i596_rfd *next) */
+	phys_addr pa_rbd;	/* va_to_pa(struct i596_rbd *rbd) */
+	unsigned short count;
+	unsigned short size;
+	char data[1532];
+};
+
+#define RBD_EL		0x8000
+#define RBD_P		0x4000
+#define RBD_SIZEMASK	0x3fff
+#define RBD_EOF		0x8000
+#define RBD_F		0x4000
+
+/* receive buffer descriptor */
+struct i596_rbd {
+	unsigned short size;
+	unsigned short pad;
+	phys_addr pa_next;	/* va_to_pa(struct i596_tbd *next) */
+	phys_addr pa_data;	/* va_to_pa(char *data) */
+	phys_addr pa_prev;	/* va_to_pa(struct i596_tbd *prev) */
+	
+	/* Driver private part */
+	struct sk_buff *skb;
+};
+
+#define RX_RING_SIZE 64
+#define RX_SKBSIZE (ETH_FRAME_LEN+10)
+#define RX_RBD_SIZE 32
+
+/* System Control Block - 40 bytes */
+struct i596_scb {
+	u16 status;		/* 0 */
+	u16 command;		/* 2 */
+	phys_addr pa_cmd;	/* 4 - va_to_pa(struct i596_cmd *cmd) */
+	phys_addr pa_rfd;	/* 8 - va_to_pa(struct i596_rfd *rfd) */
+	u32 crc_err;		/* 12 */
+	u32 align_err;		/* 16 */
+	u32 resource_err;	/* 20 */
+	u32 over_err;		/* 24 */
+	u32 rcvdt_err;		/* 28 */
+	u32 short_err;		/* 32 */
+	u16 t_on;		/* 36 */
+	u16 t_off;		/* 38 */
+};
+
+/* Intermediate System Configuration Pointer - 8 bytes */
+struct i596_iscp {
+	u32 busy;		/* 0 */
+	phys_addr pa_scb;	/* 4 - va_to_pa(struct i596_scb *scb) */
+};
+
+/* System Configuration Pointer - 12 bytes */
+struct i596_scp {
+	u32 sysbus;		/* 0 */
+	u32 pad;		/* 4 */
+	phys_addr pa_iscp;	/* 8 - va_to_pa(struct i596_iscp *iscp) */
+};
+
+/* Selftest and dump results - needs 16-byte alignment */
+/*
+ * The size of the dump area is 304 bytes. When the dump is executed
+ * by the Port command an extra word will be appended to the dump area.
+ * The extra word is a copy of the Dump status word (containing the
+ * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump]
+ */
+struct i596_dump {
+	u16 dump[153];		/* (304 = 130h) + 2 bytes */
+};
+
+struct i596_private {		/* aligned to a 16-byte boundary */
+	struct i596_scp scp;	/* 0 - needs 16-byte alignment */
+	struct i596_iscp iscp;	/* 12 */
+	struct i596_scb scb;	/* 20 */
+	u32 dummy;		/* 60 */
+	struct i596_dump dump;	/* 64 - needs 16-byte alignment */
+
+	struct i596_cmd set_add;
+	char eth_addr[8];	/* directly follows set_add */
+
+	struct i596_cmd set_conf;
+	char i596_config[16];	/* directly follows set_conf */
+
+	struct i596_cmd tdr;
+	unsigned long tdr_stat;	/* directly follows tdr */
+
+	int last_restart;
+	volatile struct i596_rbd *rbd_list;
+	volatile struct i596_rbd *rbd_tail;
+	volatile struct i596_rfd *rx_tail;
+	volatile struct i596_cmd *cmd_tail;
+	volatile struct i596_cmd *cmd_head;
+	int cmd_backlog;
+	unsigned long last_cmd;
+	struct enet_statistics stats;
+};
+
+static char init_setup[14] = {
+	0x8E,	/* length 14 bytes, prefetch on */
+	0xC8,	/* default: fifo to 8, monitor off */
+	0x40,	/* default: don't save bad frames (apricot.c had 0x80) */
+	0x2E,	/* (default is 0x26)
+		   No source address insertion, 8 byte preamble */
+	0x00,	/* default priority and backoff */
+	0x60,	/* default interframe spacing */
+	0x00,	/* default slot time LSB */
+	0xf2,	/* default slot time and nr of retries */
+	0x00,	/* default various bits
+		   (0: promiscuous mode, 1: broadcast disable,
+		    2: encoding mode, 3: transmit on no CRS,
+		    4: no CRC insertion, 5: CRC type,
+		    6: bit stuffing, 7: padding) */
+	0x00,	/* default carrier sense and collision detect */
+	0x40,	/* default minimum frame length */
+	0xff,	/* (default is 0xff, and that is what apricot.c has;
+		   elp486.c has 0xfb: Enable crc append in memory.) */
+	0x00,	/* default: not full duplex */
+	0x7f	/* (default is 0x3f) multi IA */
+};
+
+static int i596_open(struct device *dev);
+static int i596_start_xmit(struct sk_buff *skb, struct device *dev);
+static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int i596_close(struct device *dev);
+static struct enet_statistics *i596_get_stats(struct device *dev);
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd);
+static void print_eth(char *);
+static void set_multicast_list(struct device *dev);
+
+static int
+i596_timeout(struct device *dev, char *msg, int ct) {
+	volatile struct i596_private *lp;
+	int boguscnt = ct;
+
+	lp = (struct i596_private *) dev->priv;
+	while (lp->scb.command) {
+		if (--boguscnt == 0) {
+			printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n",
+			       dev->name, msg,
+			       lp->scb.status, lp->scb.command);
+			return 1;
+		}
+		udelay(5);
+	}
+	return 0;
+}
+
+static inline int
+init_rx_bufs(struct device *dev, int num) {
+	volatile struct i596_private *lp;
+	struct i596_rfd *rfd;
+	int i;
+	// struct i596_rbd *rbd;
+
+	lp = (struct i596_private *) dev->priv;
+	lp->scb.pa_rfd = I596_NULL;
+
+	for (i = 0; i < num; i++) {
+		rfd = kkmalloc(struct i596_rfd, GFP_KERNEL);
+		if (rfd == NULL)
+			break;
+
+		rfd->stat = 0;
+		rfd->pa_rbd = I596_NULL;
+		rfd->count = 0;
+		rfd->size = 1532;
+		if (i == 0) {
+			rfd->cmd = CMD_EOL;
+			lp->rx_tail = rfd;
+		} else {
+			rfd->cmd = 0;
+		}
+		rfd->pa_next = lp->scb.pa_rfd;
+		lp->scb.pa_rfd = va_to_pa(rfd);
+		lp->rx_tail->pa_next = lp->scb.pa_rfd;
+	}
+
+#if 0
+	for (i = 0; i<RX_RBD_SIZE; i++) {
+		rbd = kkmalloc(struct i596_rbd,GFP_KERNEL);
+		if (rbd) {
+			rbd->pad = 0;
+			rbd->count = 0;
+			rbd->skb = dev_alloc_skb(RX_SKB_SIZE);
+			if (!rbd->skb) {
+				printk("dev_alloc_skb failed");
+			}
+			rbd->next = rfd->rbd;
+			if (i) {
+				rfd->rbd->prev = rbd;
+				rbd->size = RX_SKB_SIZE;
+			} else {
+				rbd->size = (RX_SKB_SIZE | RBD_EL);
+				lp->rbd_tail = rbd;
+			}
+
+			rfd->rbd = rbd;
+		} else {
+			printk("Could not kmalloc rbd\n");
+		}
+	}
+	lp->rbd_tail->next = rfd->rbd;
+#endif
+	return (i);
+}
+
+static inline void
+remove_rx_bufs(struct device *dev) {
+	struct i596_private *lp;
+	struct i596_rfd *rfd;
+
+	lp = (struct i596_private *) dev->priv;
+	lp->rx_tail->pa_next = I596_NULL;
+
+	do {
+		rfd = pa_to_va(lp->scb.pa_rfd);
+		lp->scb.pa_rfd = rfd->pa_next;
+		kfree(rfd);
+	} while (rfd != lp->rx_tail);
+
+	lp->rx_tail = 0;
+
+#if 0
+	for (lp->rbd_list) {
+	}
+#endif
+}
+
+#define PORT_RESET              0x00    /* reset 82596 */
+#define PORT_SELFTEST           0x01    /* selftest */
+#define PORT_ALTSCP             0x02    /* alternate SCB address */
+#define PORT_DUMP               0x03    /* dump */
+
+#define IOADDR	0xcb0
+#define IRQ	10		/* default IRQ - can be changed by ECU */
+
+/* The 82596 requires two 16-bit write cycles for a port command */
+static inline void
+PORT(phys_addr a, unsigned int cmd) {
+	if (a & 0xf)
+		printk("lp486e.c: PORT: address not aligned\n");
+	outw(((a & 0xffff) | cmd), IOADDR);
+	outw(((a>>16) & 0xffff), IOADDR+2);
+}
+
+static inline void
+CA(void) {
+	outb(0, IOADDR+4);
+	udelay(8);
+}
+
+static inline void
+CLEAR_INT(void) {
+	outb(0, IOADDR+8);
+}
+
+#define SIZE(x)	(sizeof(x)/sizeof((x)[0]))
+
+#if 0
+/* selftest or dump */
+static void
+i596_port_do(struct device *dev, int portcmd, char *cmdname) {
+	volatile struct i596_private *lp = (struct i596_private *)dev->priv;
+	volatile u16 *outp;
+	int i, m;
+
+	memset((void *)&(lp->dump), 0, sizeof(struct i596_dump));
+	outp = &(lp->dump.dump[0]);
+
+	PORT(va_to_pa(outp), portcmd);
+	mdelay(30);             /* random, unmotivated */
+
+	printk("lp486e i82596 %s result:\n", cmdname);
+	for (m = SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--)
+		;
+	for (i = 0; i < m; i++) {
+		printk(" %04x", lp->dump.dump[i]);
+		if (i%8 == 7)
+			printk("\n");
+	}
+	printk("\n");
+}
+#endif
+
+static int
+i596_scp_setup(struct device *dev) {
+	volatile struct i596_private *lp = (struct i596_private *)dev->priv;
+	int boguscnt;
+
+	/* Setup SCP, ISCP, SCB */
+	/*
+	 * sysbus bits:
+	 *  only a single byte is significant - here 0x44
+	 *  0x80: big endian mode (details depend on stepping)
+	 *  0x40: 1
+	 *  0x20: interrupt pin is active low
+	 *  0x10: lock function disabled
+	 *  0x08: external triggering of bus throttle timers
+	 *  0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode
+	 *  0x01: unused
+	 */
+	lp->scp.sysbus = 0x00440000; 		/* linear mode */
+	lp->scp.pad = 0;			/* must be zero */
+	lp->scp.pa_iscp = va_to_pa(&(lp->iscp));
+
+	/*
+	 * The CPU sets the ISCP to 1 before it gives the first CA()
+	 */
+	lp->iscp.busy = 0x0001;
+	lp->iscp.pa_scb = va_to_pa(&(lp->scb));
+
+	lp->scb.command = 0;
+	lp->scb.status = 0;
+	lp->scb.pa_cmd = I596_NULL;
+	/* lp->scb.pa_rfd has been initialised already */
+
+	lp->last_cmd = jiffies;
+	lp->cmd_backlog = 0;
+	lp->cmd_head = NULL;
+
+	/*
+	 * Reset the 82596.
+	 * We need to wait 10 systemclock cycles, and
+	 * 5 serial clock cycles.
+	 */
+	PORT(0, PORT_RESET);	/* address part ignored */
+	udelay(100);
+
+	/*
+	 * Before the CA signal is asserted, the default SCP address
+	 * (0x00fffff4) can be changed to a 16-byte aligned value
+	 */
+	PORT(va_to_pa(&lp->scp), PORT_ALTSCP);	/* change the scp address */
+
+	/*
+	 * The initialization procedure begins when a
+	 * Channel Attention signal is asserted after a reset.
+	 */
+	CA();
+
+	CLEAR_INT();
+
+	/*
+	 * The ISCP busy is cleared by the 82596 after the SCB base
+	 * and offset are read.
+	 */
+	boguscnt = 100;
+	while (lp->iscp.busy) {
+		if (--boguscnt == 0) {
+			/* No i82596 present? */
+			printk("%s: i82596 initialization timed out\n",
+			       dev->name);
+			return 1;
+		}
+		udelay(5);
+	}
+	/* I find here boguscnt==100, so no delay was required. */
+
+	return 0;
+}
+
+static int
+init_i596(struct device *dev) {
+	volatile struct i596_private *lp;
+
+	if (i596_scp_setup(dev))
+		return 1;
+
+	lp = (struct i596_private *) dev->priv;
+	lp->scb.command = 0;
+
+	memcpy ((void *)lp->i596_config, init_setup, 14);
+	lp->set_conf.command = CmdConfigure;
+	i596_add_cmd(dev, (void *)&lp->set_conf);
+
+	memcpy ((void *)lp->eth_addr, dev->dev_addr, 6);
+	lp->set_add.command = CmdIASetup;
+	i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add);
+
+	lp->tdr.command = CmdTDR;
+	i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr);
+
+	if (lp->scb.command && i596_timeout(dev, "i82596 init", 200))
+		return 1;
+
+	lp->scb.command = RX_START;
+	CA();
+
+	if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100))
+		return 1;
+
+	return 0;
+}
+
+/* Receive a single frame */
+static inline int
+i596_rx_one(struct device *dev, volatile struct i596_private *lp,
+	    struct i596_rfd *rfd, int *frames) {
+
+	if (rfd->stat & RFD_STAT_OK) {
+		/* a good frame */
+		int pkt_len = (rfd->count & 0x3fff);
+		struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+		(*frames)++;
+
+		if (rfd->cmd & CMD_EOL)
+			printk("Received on EOL\n");
+
+		if (skb == NULL) {
+			printk ("%s: i596_rx Memory squeeze, "
+				"dropping packet.\n", dev->name);
+			lp->stats.rx_dropped++;
+			return 1;
+		}
+
+		skb->dev = dev;		
+		memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len);
+
+		skb->protocol = eth_type_trans(skb,dev);
+		netif_rx(skb);
+		lp->stats.rx_packets++;
+	} else {
+#if 0
+		printk("Frame reception error status %04x\n",
+		       rfd->stat);
+#endif
+		lp->stats.rx_errors++;
+		if (rfd->stat & RFD_COLLISION)
+			lp->stats.collisions++;
+		if (rfd->stat & RFD_SHORT_FRAME_ERR)
+			lp->stats.rx_length_errors++;
+		if (rfd->stat & RFD_DMA_ERR)
+			lp->stats.rx_over_errors++;
+		if (rfd->stat & RFD_NOBUFS_ERR)
+			lp->stats.rx_fifo_errors++;
+		if (rfd->stat & RFD_ALIGN_ERR)
+			lp->stats.rx_frame_errors++;
+		if (rfd->stat & RFD_CRC_ERR)
+			lp->stats.rx_crc_errors++;
+		if (rfd->stat & RFD_LENGTH_ERR)
+			lp->stats.rx_length_errors++;
+	}
+	rfd->stat = rfd->count = 0;
+	return 0;
+}
+
+static int
+i596_rx(struct device *dev) {
+	volatile struct i596_private *lp = (struct i596_private *) dev->priv;
+	struct i596_rfd *rfd;
+	int frames = 0;
+
+	while (1) {
+		rfd = pa_to_va(lp->scb.pa_rfd);
+		if (!rfd) {
+			printk("i596_rx: NULL rfd?\n");
+			return 0;
+		}
+#if 1
+		if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B)))
+			printk("SF:%p-%04x\n", rfd, rfd->stat);
+#endif
+		if (!(rfd->stat & RFD_STAT_C))
+			break;		/* next one not ready */
+		if (i596_rx_one(dev, lp, rfd, &frames))
+			break;		/* out of memory */
+		rfd->cmd = CMD_EOL;
+		lp->rx_tail->cmd = 0;
+		lp->rx_tail = rfd;
+		lp->scb.pa_rfd = rfd->pa_next;
+	}
+
+	return frames;
+}
+
+static void
+i596_cleanup_cmd(struct device *dev) {
+	volatile struct i596_private *lp;
+	struct i596_cmd *cmd;
+
+	lp = (struct i596_private *) dev->priv;
+	while (lp->cmd_head) {
+		cmd = (struct i596_cmd *)lp->cmd_head;
+
+		lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
+		lp->cmd_backlog--;
+
+		switch ((cmd->command) & 0x7) {
+			case CmdTx: {
+				struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd;
+				struct i596_tbd * tx_cmd_tbd;
+				tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
+
+				dev_kfree_skb(tx_cmd_tbd->skb);
+
+				lp->stats.tx_errors++;
+				lp->stats.tx_aborted_errors++;
+
+				cmd->pa_next = I596_NULL;
+				kfree((unsigned char *)tx_cmd);
+				break;
+			}
+			case CmdMulticastList: {
+				// unsigned short count = *((unsigned short *) (ptr + 1));
+
+				cmd->pa_next = I596_NULL;
+				kfree((unsigned char *)cmd);
+				break;
+			}
+			default: {
+				cmd->pa_next = I596_NULL;
+				break;
+			}
+		}
+	}
+
+	if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100))
+		;
+
+	lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
+}
+
+static inline void
+i596_reset(struct device *dev, volatile struct i596_private *lp, int ioaddr) {
+
+	if (lp->scb.command && i596_timeout(dev, "i596_reset", 100))
+		;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	lp->scb.command = CUC_ABORT | RX_ABORT;
+	CA();
+
+	/* wait for shutdown */
+	if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400))
+		;
+
+	i596_cleanup_cmd(dev);
+	i596_rx(dev);
+
+	dev->start = 1;
+	dev->tbusy = 0;
+	/*dev_kfree_skb(skb, FREE_WRITE);*/
+	dev->interrupt = 0;
+	init_i596(dev);
+}
+
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd) {
+	volatile struct i596_private *lp = (struct i596_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	unsigned long flags;
+
+	cmd->status = 0;
+	cmd->command |= (CMD_EOL | CMD_INTR);
+	cmd->pa_next = I596_NULL;
+
+	save_flags(flags);
+	cli();
+	if (lp->cmd_head) {
+		lp->cmd_tail->pa_next = va_to_pa(cmd);
+	} else {
+		lp->cmd_head = cmd;
+		if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100))
+			;
+		lp->scb.pa_cmd = va_to_pa(cmd);
+		lp->scb.command = CUC_START;
+		CA();
+	}
+	lp->cmd_tail = cmd;
+	lp->cmd_backlog++;
+
+	lp->cmd_head = pa_to_va(lp->scb.pa_cmd);
+	restore_flags(flags);
+
+	if (lp->cmd_backlog > 16) {
+		int tickssofar = jiffies - lp->last_cmd;
+		if (tickssofar < 25) return;
+
+		printk("%s: command unit timed out, status resetting.\n",
+		       dev->name);
+		i596_reset(dev, lp, ioaddr);
+	}
+}
+
+static int
+i596_open(struct device *dev) {
+	int i;
+
+	if (request_irq(dev->irq, &i596_interrupt, SA_SHIRQ, dev->name, dev)) {
+		printk("%s: IRQ %d not free\n", dev->name, dev->irq);
+		return -EAGAIN;
+	}
+
+//	irq2dev_map[dev->irq] = dev;
+
+	if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+		printk("%s: only able to allocate %d receive buffers\n",
+		       dev->name, i);
+
+	if (i < 4) {
+// release buffers
+		free_irq(dev->irq, dev);
+//		irq2dev_map[dev->irq] = 0;
+		return -EAGAIN;
+	}
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	MOD_INC_USE_COUNT;
+
+	init_i596(dev);
+
+	return 0;			/* Always succeed */
+}
+
+static int
+i596_start_xmit(struct sk_buff *skb, struct device *dev) {
+	volatile struct i596_private *lp = (struct i596_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	struct tx_cmd *tx_cmd;
+
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5) {
+			return 1;
+		}
+		printk("%s: transmit timed out, status resetting.\n",
+		       dev->name);
+		lp->stats.tx_errors++;
+		/* Try to restart the adaptor */
+		if (lp->last_restart == lp->stats.tx_packets) {
+			printk ("Resetting board.\n");
+
+			/* Shutdown and restart */
+			i596_reset(dev,lp, ioaddr);
+		} else {
+			/* Issue a channel attention signal */
+			printk ("Kicking board.\n");
+
+			lp->scb.command = (CUC_START | RX_START);
+			CA(); /*outw(0, ioaddr+4);*/
+
+			lp->last_restart = lp->stats.tx_packets;
+		}
+		dev->tbusy = 0;
+		dev->trans_start = jiffies;
+	}
+
+	/* If some higher level thinks we've missed a tx-done interrupt
+	we are passed NULL. n.b. dev_tint handles the cli()/sti()
+	itself. */
+	if (skb == NULL) {
+		printk("What about dev_tint\n");
+		/*dev_tint(dev);*/
+		return 0;
+	}
+
+	/* shouldn't happen */
+	if (skb->len <= 0) return 0;
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	} else {
+		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+		dev->trans_start = jiffies;
+
+		tx_cmd = (struct tx_cmd *)
+			kmalloc ((sizeof (struct tx_cmd)
+				  + sizeof (struct i596_tbd)), GFP_ATOMIC);
+		if (tx_cmd == NULL) {
+			printk ("%s: i596_xmit Memory squeeze, dropping packet.\n",
+				dev->name);
+			lp->stats.tx_dropped++;
+
+			dev_kfree_skb(skb);
+		} else {
+			struct i596_tbd * tx_cmd_tbd;
+			tx_cmd_tbd = (struct i596_tbd *) (tx_cmd+1);
+			tx_cmd->pa_tbd = va_to_pa(tx_cmd_tbd);
+			tx_cmd_tbd->pa_next = I596_NULL;
+
+			tx_cmd->cmd.command = (CMD_FLEX | CmdTx);
+
+			tx_cmd->pad = 0;
+			tx_cmd->size = 0;
+			tx_cmd_tbd->pad = 0;
+			tx_cmd_tbd->size = (EOF | length);
+
+			tx_cmd_tbd->pa_data = va_to_pa(skb->data);
+			tx_cmd_tbd->skb = skb;
+
+			if (i596_debug & LOG_SRCDST)
+				print_eth(skb->data);
+
+			i596_add_cmd(dev, (struct i596_cmd *)tx_cmd);
+
+			lp->stats.tx_packets++;
+		}
+	}
+
+	dev->tbusy = 0;
+
+	return 0;
+}
+
+static void
+print_eth(char *add) {
+	int i;
+
+	printk ("Dest  ");
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", (unsigned char) add[i]);
+	printk ("\n");
+
+	printk ("Source");
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", (unsigned char) add[i+6]);
+	printk ("\n");
+
+	printk ("type %2.2X%2.2X\n",
+		(unsigned char) add[12], (unsigned char) add[13]);
+}
+
+int __init
+lp486e_probe(struct device *dev) {
+	volatile struct i596_private *lp;
+	unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 };
+	unsigned char *bios;
+	int i,j;
+	static int probed = 0;
+
+	if (probed)
+		return -ENODEV;
+	probed++;
+
+	if (check_region(IOADDR, LP486E_TOTAL_SIZE)) {
+		printk("lp486e: IO address 0x%x in use\n", IOADDR);
+		return -ENODEV;
+	}
+
+	/*
+	 * Allocate working memory, 16-byte aligned
+	 */
+	dev->mem_start = (unsigned long)
+		kmalloc(sizeof(struct i596_private) + 0x0f, GFP_KERNEL);
+	if (!dev->mem_start)
+		return -ENOMEM;
+	dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0);
+	lp = (struct i596_private *) dev->priv;
+	memset((void *)lp, 0, sizeof(struct i596_private));
+
+	/*
+	 * Do we really have this thing?
+	 */
+	if (i596_scp_setup(dev)) {
+		kfree ((void *) dev->mem_start);
+		return -ENODEV;
+	}
+
+	request_region(IOADDR, LP486E_TOTAL_SIZE, "lp486e");
+
+	dev->base_addr = IOADDR;
+	dev->irq = IRQ;
+
+	ether_setup(dev);
+
+	/*
+	 * How do we find the ethernet address? I don't know.
+	 * One possibility is to look at the EISA configuration area
+	 * [0xe8000-0xe9fff]. This contains the ethernet address
+	 * but not at a fixed address - things depend on setup options.
+	 *
+	 * If we find no address, or the wrong address, use
+	 *   ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6
+	 * with the value found in the BIOS setup.
+	 */
+	bios = bus_to_virt(0xe8000);
+	for (j = 0; j < 0x2000; j++) {
+		if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) {
+			printk("%s: maybe address at BIOS 0x%x:",
+			       dev->name, 0xe8000+j);
+			for (i = 0; i < 6; i++) {
+				eth_addr[i] = bios[i+j];
+				printk(" %2.2X", eth_addr[i]);
+			}
+			printk("\n");
+		}
+	}
+
+	printk("%s: lp486e 82596 at %#3x, IRQ %d,", dev->name, IOADDR, IRQ);
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
+	printk("\n");
+
+	/* The LP486E-specific entries in the device structure. */
+	dev->open = &i596_open;
+	dev->stop = &i596_close;
+	dev->hard_start_xmit = &i596_start_xmit;
+	dev->get_stats = &i596_get_stats;
+	dev->set_multicast_list = &set_multicast_list;
+
+#if 0
+	/* selftest reports 0x320925ae - don't know what that means */
+	i596_port_do(dev, PORT_SELFTEST, "selftest");
+	i596_port_do(dev, PORT_DUMP, "dump");
+#endif
+	return 0;
+}
+
+static void inline
+i596_handle_CU_completion(struct device *dev,
+			  volatile struct i596_private *lp,
+			  unsigned short status,
+			  unsigned short *ack_cmdp) {
+	volatile struct i596_cmd *cmd;
+	int frames_out = 0;
+	int commands_done = 0;
+	int cmd_val;
+
+	cmd = lp->cmd_head;
+
+	while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) {
+		cmd = lp->cmd_head;
+
+		lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
+		lp->cmd_backlog--;
+
+		commands_done++;
+		cmd_val = cmd->command & 0x7;
+#if 0
+		printk("finished CU %s command (%d)\n",
+		       CUcmdnames[cmd_val], cmd_val);
+#endif
+		switch (cmd_val) {
+		case CmdTx:
+		{
+			struct tx_cmd *tx_cmd;
+			struct i596_tbd *tx_cmd_tbd;
+
+			tx_cmd = (struct tx_cmd *) cmd;
+			tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
+
+			frames_out++;
+			if (cmd->status & CMD_STAT_OK) {
+				if (i596_debug)
+					print_eth(pa_to_va(tx_cmd_tbd->pa_data));
+			} else {
+				lp->stats.tx_errors++;
+				if (i596_debug)
+					printk("transmission failure:%04x\n",
+					       cmd->status);
+				if (cmd->status & 0x0020)
+					lp->stats.collisions++;
+				if (!(cmd->status & 0x0040))
+					lp->stats.tx_heartbeat_errors++;
+				if (cmd->status & 0x0400)
+					lp->stats.tx_carrier_errors++;
+				if (cmd->status & 0x0800)
+					lp->stats.collisions++;
+				if (cmd->status & 0x1000)
+					lp->stats.tx_aborted_errors++;
+			}
+			dev_kfree_skb(tx_cmd_tbd->skb);
+
+			cmd->pa_next = I596_NULL;
+			kfree((unsigned char *)tx_cmd);
+			break;
+		}
+
+		case CmdMulticastList:
+			cmd->pa_next = I596_NULL;
+			kfree((unsigned char *)cmd);
+			break;
+
+		case CmdTDR:
+		{
+			unsigned long status = *((unsigned long *) (cmd + 1));
+			if (status & 0x8000) {
+				if (i596_debug)
+					printk("%s: link ok.\n", dev->name);
+			} else {
+				if (status & 0x4000)
+					printk("%s: Transceiver problem.\n",
+					       dev->name);
+				if (status & 0x2000)
+					printk("%s: Termination problem.\n",
+					       dev->name);
+				if (status & 0x1000)
+					printk("%s: Short circuit.\n",
+					       dev->name);
+				printk("%s: Time %ld.\n",
+				       dev->name, status & 0x07ff);
+			}
+		}
+		default:
+			cmd->pa_next = I596_NULL;
+			lp->last_cmd = jiffies;
+			
+		}
+	}
+
+	cmd = lp->cmd_head;
+	while (cmd && (cmd != lp->cmd_tail)) {
+		cmd->command &= 0x1fff;
+		cmd = pa_to_va(cmd->pa_next);
+	}
+
+	if (lp->cmd_head && dev->start)
+		*ack_cmdp |= CUC_START;
+	lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
+}
+
+static void
+i596_interrupt (int irq, void *dev_instance, struct pt_regs *regs) {
+	struct device *dev = (struct device *) dev_instance;
+	volatile struct i596_private *lp;
+	unsigned short status, ack_cmd = 0;
+	int frames_in = 0;
+
+	if (dev == NULL) {
+		printk ("i596_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+	dev->interrupt = 1;
+
+	lp = (struct i596_private *) dev->priv;
+
+	/*
+	 * The 82596 examines the command, performs the required action,
+	 * and then clears the SCB command word.
+	 */
+	if (lp->scb.command && i596_timeout(dev, "interrupt", 40))
+		;
+
+	/*
+	 * The status word indicates the status of the 82596.
+	 * It is modified only by the 82596.
+	 *
+	 * [So, we must not clear it. I find often status 0xffff,
+	 *  which is not one of the values allowed by the docs.]
+	 */
+	status = lp->scb.status;
+#if 0
+	if (i596_debug) {
+		printk("%s: i596 interrupt, ", dev->name);
+		i596_out_status(status);
+	}
+#endif
+	/* Impossible, but it happens - perhaps when we get
+	   a receive interrupt but scb.pa_rfd is I596_NULL. */
+	if (status == 0xffff) {
+		printk("%s: i596_interrupt: got status 0xffff\n", dev->name);
+		goto out;
+	}
+
+	ack_cmd = (status & STAT_ACK);
+
+	if (status & (STAT_CX | STAT_CNA))
+		i596_handle_CU_completion(dev, lp, status, &ack_cmd);
+
+	if (status & (STAT_FR | STAT_RNR)) {
+		/* Restart the receive unit when it got inactive somehow */
+		if ((status & STAT_RNR) && dev->start)
+			ack_cmd |= RX_START;
+
+		if (status & STAT_FR) {
+			frames_in = i596_rx(dev);
+			if (!frames_in)
+				printk("receive frame reported, but no frames\n");
+		}
+	}
+
+	/* acknowledge the interrupt */
+	/*
+	if ((lp->scb.pa_cmd != I596_NULL) && dev->start)
+		ack_cmd |= CUC_START;
+	*/
+
+	if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100))
+		;
+
+	lp->scb.command = ack_cmd;
+
+	CLEAR_INT();
+	CA();
+
+ out:
+	dev->interrupt = 0;
+	return;
+}
+
+static int i596_close(struct device *dev) {
+	volatile struct i596_private *lp = (struct i596_private *)dev->priv;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (i596_debug)
+		printk("%s: Shutting down ethercard, status was %4.4x.\n",
+		       dev->name, lp->scb.status);
+
+	lp->scb.command = (CUC_ABORT | RX_ABORT);
+	CA();
+
+	i596_cleanup_cmd(dev);
+
+	if (lp->scb.command && i596_timeout(dev, "i596_close", 200))
+		;
+
+	free_irq(dev->irq, dev);
+	remove_rx_bufs(dev);
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static struct enet_statistics * i596_get_stats(struct device *dev) {
+	struct i596_private *lp = (struct i596_private *)dev->priv;
+
+	return &lp->stats;
+}
+
+/*
+*	Set or clear the multicast filter for this adaptor.
+*/
+
+static void set_multicast_list(struct device *dev) {
+	volatile struct i596_private *lp = (struct i596_private *)dev->priv;
+	struct i596_cmd *cmd;
+
+	if (i596_debug > 1)
+		printk ("%s: set multicast list %d\n",
+			dev->name, dev->mc_count);
+
+	if (dev->mc_count > 0) {
+		struct dev_mc_list *dmi;
+		char *cp;
+		cmd = (struct i596_cmd *)
+			kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6,
+				GFP_ATOMIC);
+		if (cmd == NULL) {
+			printk ("%s: set_multicast Memory squeeze.\n",
+				dev->name);
+			return;
+		}
+		cmd->command = CmdMulticastList;
+		*((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
+		cp = ((char *)(cmd + 1))+2;
+		for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) {
+			memcpy(cp, dmi,6);
+			cp += 6;
+		}
+		if (i596_debug & LOG_SRCDST)
+			print_eth (((char *)(cmd + 1)) + 2);
+		i596_add_cmd(dev, cmd);
+	} else {
+		if (lp->set_conf.pa_next != I596_NULL) {
+			return;
+		}
+		if (dev->mc_count == 0 &&
+		    !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+			if (dev->flags & IFF_ALLMULTI)
+				dev->flags |= IFF_PROMISC;
+			lp->i596_config[8] &= ~0x01;
+		} else {
+			lp->i596_config[8] |= 0x01;
+		}
+
+		i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf);
+	}
+}
+
+#ifdef HAVE_DEVLIST
+static unsigned int lp486e_portlist[] = {0xcb0, 0};
+struct netdev_entry lp486e_drv = {
+	"lp486e",
+	lp486e_probe,
+	LP486E_TOTAL_SIZE,
+	lp486e_portlist
+};
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR("Ard van Breemen <ard@cstmel.nl.eu.org>");
+MODULE_DESCRIPTION("Intel Panther onboard i82596 driver");
+MODULE_PARM(debug, "i");
+//MODULE_PARM(max_interrupt_work, "i");
+//MODULE_PARM(reverse_probe, "i");
+//MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+static char devicename[9] = { 0, };
+static struct device dev_lp486e = {
+	devicename, /* device name inserted by /linux/drivers/net/net_init.c */
+	0, 0, 0, 0,
+	IOADDR, IRQ,
+	0, 0, 0, NULL, NULL
+};
+
+static int full_duplex;
+static int options;
+static int io = IOADDR;
+static int irq = IRQ;
+
+int init_module(void) {
+	struct device *dev = &dev_lp486e;
+	dev->name = devicename;
+	dev->irq = irq;
+	dev->base_addr = io;
+	dev->init = lp486e_probe;
+	if (register_netdev(dev) != 0) {
+		return -EIO;
+	}
+	full_duplex = 0;
+	options = 0;
+	return 0;
+}
+
+void cleanup_module(void) {
+	unregister_netdev(&dev_lp486e);
+	kfree((void *)dev_lp486e.mem_start);
+	dev_lp486e.priv = NULL;
+	/* If we don't do this, we can't re-insmod it later. */
+	release_region(dev_lp486e.base_addr, LP486E_TOTAL_SIZE);
+}
+#endif /* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/strip.c linux.19p1/drivers/net/strip.c
--- linux.vanilla/drivers/net/strip.c	Fri Apr 21 12:46:20 2000
+++ linux.19p1/drivers/net/strip.c	Wed Dec 13 22:28:39 2000
@@ -14,7 +14,7 @@
  *		for kernel-based devices like TTY.  It interfaces between a
  *		raw TTY, and the kernel's INET protocol layers (via DDI).
  *
- * Version:	@(#)strip.c	1.3	July 1997
+ * Version:	@(#)strip.c	1.4	September 2000
  *
  * Author:	Stuart Cheshire <cheshire@cs.stanford.edu>
  *
@@ -66,12 +66,15 @@
  *              It is no longer necessarily to manually set the radio's
  *              rate permanently to 115200 -- the driver handles setting
  *              the rate automatically.
+ *
+ *              v1.4 September 2000 (AB)
+ *              Added support for long serial numbers.
  */
 
 #ifdef MODULE
-static const char StripVersion[] = "1.3-STUART.CHESHIRE-MODULAR";
+static const char StripVersion[] = "1.4-STUART.CHESHIRE-MODULAR";
 #else
-static const char StripVersion[] = "1.3-STUART.CHESHIRE";
+static const char StripVersion[] = "1.4-STUART.CHESHIRE";
 #endif
 
 #define TICKLE_TIMERS 0
@@ -897,20 +900,37 @@
  * Convert a string to a Metricom Address.
  */
 
-#define IS_RADIO_ADDRESS(p) (                                                 \
+#define IS_RADIO_ADDRESS_1(p) (                                               \
   isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \
   (p)[4] == '-' &&                                                            \
   isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8])    )
 
+#define IS_RADIO_ADDRESS_2(p) (                                               \
+  isdigit((p)[0]) && isdigit((p)[1]) &&                                       \
+  (p)[2] == '-' &&                                                            \
+  isdigit((p)[3]) && isdigit((p)[4]) && isdigit((p)[5]) && isdigit((p)[6]) && \
+  (p)[7] == '-' &&                                                            \
+  isdigit((p)[8]) && isdigit((p)[9]) && isdigit((p)[10]) && isdigit((p)[11])  )
+
 static int string_to_radio_address(MetricomAddress *addr, __u8 *p)
 {
-    if (!IS_RADIO_ADDRESS(p)) return(1);
+    if (IS_RADIO_ADDRESS_2(p))
+    {
+        addr->c[0] = 0;
+        addr->c[1] = (READHEX(p[0]) << 4 | READHEX(p[1])) ^ 0xFF;
+        addr->c[2] = READHEX(p[3]) << 4 | READHEX(p[4]);
+        addr->c[3] = READHEX(p[5]) << 4 | READHEX(p[6]);
+        addr->c[4] = READHEX(p[8]) << 4 | READHEX(p[9]);
+        addr->c[5] = READHEX(p[10]) << 4 | READHEX(p[11]);
+    }else{
+        if(!IS_RADIO_ADDRESS_1(p)) return(1);
     addr->c[0] = 0;
     addr->c[1] = 0;
     addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]);
     addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]);
     addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]);
     addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]);
+    }
     return(0);
 }
 
@@ -920,6 +940,9 @@
 
 static __u8 *radio_address_to_string(const MetricomAddress *addr, MetricomAddressString *p)
 {
+    if(addr->c[1])
+        sprintf(p->c, "%02X-%02X%02X-%02X%02X", addr->c[1] ^ 0xFF, addr->c[2], addr->c[3], addr->c[4], addr->c[5]);
+    else
     sprintf(p->c, "%02X%02X-%02X%02X", addr->c[2], addr->c[3], addr->c[4], addr->c[5]);
     return(p->c);
 }
@@ -1481,6 +1504,12 @@
 
     *ptr++ = 0x0D;
     *ptr++ = '*';
+    if(haddr.c[1])
+    {
+        *ptr++ = hextable[(haddr.c[1] >> 4) ^ 0xF];
+        *ptr++ = hextable[(haddr.c[1] & 0xF) ^ 0xF];
+        *ptr++ = '-';
+    }
     *ptr++ = hextable[haddr.c[2] >> 4];
     *ptr++ = hextable[haddr.c[2] & 0xF];
     *ptr++ = hextable[haddr.c[3] >> 4];
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/3w-xxxx.c linux.19p1/drivers/scsi/3w-xxxx.c
--- linux.vanilla/drivers/scsi/3w-xxxx.c	Sat Sep  9 18:42:40 2000
+++ linux.19p1/drivers/scsi/3w-xxxx.c	Wed Dec 13 21:34:49 2000
@@ -3,6 +3,8 @@
 
    Written By: Adam Radford <linux@3ware.com>
    Modifications By: Joel Jacobson <linux@3ware.com>
+		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+
 
    Copyright (C) 1999-2000 3ware Inc.
 
@@ -64,6 +66,8 @@
                  Bug fix so hot spare drives don't show up.
    1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some
                  systems.
+   12/09/2000  - release previously allocated resources on failure at
+		 tw_allocate_memory (acme)
 */
 
 #include <linux/module.h>
@@ -416,37 +420,29 @@
 /* This function will allocate memory and check if it is 16 d-word aligned */
 int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which)
 {
-	u32 *virt_addr;
+	u32 *virt_addr = kmalloc(size, GFP_ATOMIC);
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
 
+	if (!virt_addr) {
+		printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
+		return 1;
+	}
+
+	if ((u32)virt_addr % TW_ALIGNMENT) {
+		kfree(virt_addr);
+		printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
+		return 1;
+	}
+
 	if (which == 0) {
-		/* Allocate command packet memory */
-		virt_addr = kmalloc(size, GFP_ATOMIC);
-		if (virt_addr == NULL) {
-			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
-			return 1;
-		}
-		if ((u32)virt_addr % TW_ALIGNMENT) {
-			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
-			return 1;
-		}
 		tw_dev->command_packet_virtual_address[request_id] = virt_addr;
 		tw_dev->command_packet_physical_address[request_id] = 
-		virt_to_bus(virt_addr);
+			virt_to_bus(virt_addr);
 	} else {
-		/* Allocate generic buffer */
-		virt_addr = kmalloc(size, GFP_ATOMIC);
-		if (virt_addr == NULL) {
-			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
-			return 1;
-		}
-		if ((u32)virt_addr % TW_ALIGNMENT) {
-			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
-			return 1;
-		}
 		tw_dev->alignment_virtual_address[request_id] = virt_addr;
-		tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr);
+		tw_dev->alignment_physical_address[request_id] =
+			virt_to_bus(virt_addr);
 	}
 	return 0;
 } /* End tw_allocate_memory() */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Config.in linux.19p1/drivers/scsi/Config.in
--- linux.vanilla/drivers/scsi/Config.in	Mon Dec 11 22:12:53 2000
+++ linux.19p1/drivers/scsi/Config.in	Wed Dec 13 21:31:47 2000
@@ -2,6 +2,7 @@
 
 dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI
 dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
+dep_tristate 'OnStream SC-x0 SCSI tape support' CONFIG_CHR_DEV_OSST $CONFIG_SCSI
 dep_tristate 'SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
 if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
   bool '  Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Makefile linux.19p1/drivers/scsi/Makefile
--- linux.vanilla/drivers/scsi/Makefile	Mon Dec 11 22:12:53 2000
+++ linux.19p1/drivers/scsi/Makefile	Wed Dec 13 21:31:47 2000
@@ -61,6 +61,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_CHR_DEV_OSST),y)
+L_OBJS += osst.o
+else
+  ifeq ($(CONFIG_CHR_DEV_OSST),m)
+  M_OBJS += osst.o
+  endif
+endif
+
 ifeq ($(CONFIG_BLK_DEV_SD),y)
 L_OBJS += sd.o sd_ioctl.o
 else
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.osst linux.19p1/drivers/scsi/README.osst
--- linux.vanilla/drivers/scsi/README.osst	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/scsi/README.osst	Wed Dec 13 21:31:47 2000
@@ -0,0 +1,259 @@
+README file for the osst driver
+===============================
+(w) Kurt Garloff <garloff@suse.de> 10/2000
+
+This file describes the osst driver as of version 0.8.x/0.9.x, the released
+version of the osst driver.
+It is intended to help advanced users to understand the role of osst and to
+get them started using (and maybe debugging) it.
+It won't address issues like "How do I compile a kernel?" or "How do I load
+a module?", as these are too basic.
+Once the OnStream got merged into the official kernel, the distro makers
+will provide the OnStream support for those who are not familiar with
+hacking their kernels.
+
+
+Purpose
+-------
+The osst driver was developed, because the standard SCSI tape driver in
+Linux, st, does not support the OnStream SC-x0 SCSI tape. The st is not to
+blame for that, as the OnStream tape drives do not support the standard SCSI
+command set for Serial Access Storage Devices (SASDs), which basically
+corresponds to the QIC-157 spec.
+Nevertheless, the OnStream tapes are nice pieces of hardware and therefore
+the osst driver has been written to make these tape devs supported by Linux.
+The driver is free software. It's released under the GNU GPL and planned to
+be integrated into the mainstream kernel.
+
+
+Implementation
+--------------
+The osst is a new high-level SCSI driver, just like st, sr, sd and sg. It
+can be compiled into the kernel or loaded as a module.
+As it represents a new device, it got assigned a new device node: /dev/osstX
+are character devices with major no 206 and minor numbers like the /dev/stX
+devices. If those are not present, you may create them by calling
+Makedevs.sh as root (see below).
+The driver started beig a copy of st and as such, the osst devices'
+behaviour looks very much the same as st to the userspace applications.
+
+
+History
+-------
+In the first place, osst shared it's identity very much with st. That meant
+that it used the same kernel structures and the same device node as st.
+So you could only have either of them being present in the kernel. This has
+been fixed by registering an own device, now.
+st and osst can coexist, each only accessing the devices it can support by
+their own device.
+
+
+Installation
+------------
+There is an easy and a correct approach to install the osst driver
+(a) Without patching your kernel
+(b) Full integration into your kernel
+(c) The mainstream kernel does already include the osst driver
+
+While it is still possible to use possibility (a), i.e. to compile the osst
+driver as a module without patching your kernel, this is not recommended, as
+you then have only very limited possibility to take advantage of the possible
+coexistence of st and osst. This possibility will be removed later.
+Anyway here is a description of how installation works.
+
+Make sure you have a configured 2.2.1x Linux kernel in /usr/src/linux by
+installing the approriate packages from your distro or by fetching the 
+source tarball from ftp.kernel.org or mirror and unpacking it in /usr/src/
+If your kernel is not yet configure, get a reasonable config file by doing
+cd /usr/src/linux
+make oldconfig			# You may use make menuconfig if starting
+				# from scratch
+make dep
+
+(a) Without patching your kernel
+
+cd /path/to/onstream/driver/	# REPLACE THIS WITH THE REAL PATH
+make
+make install
+
+That's all. You will get a couple of warnings on compilation, which you may
+ignore. The osst.o module is now in your module directory ready for loading
+and the device nodes have been created.
+
+(b) Full integration into the kernel. You need to have a reasonable kernel
+    config, otherwise you may be unable to use your system, as the produced
+    kernel may not support your hardware! IF YOU NEVER COMPILED YOUR OWN
+    KERNEL BEFORE, DON'T PROCEED, UNLESS YOU SUCEEDED USING YOUR SELF-
+    COMPILED KERNELS.
+
+cd /usr/src/linux
+patch -p1 < /path/to/onstream/driver/osst-2X.diff	# USE REAL PATH! X = 2,3,4
+cp -p /path/to/onstream/driver/osst.? drivers/scsi/	# Install drv srcs
+cp -p /path/to/onstream/driver/osst_options.h drivers/scsi/
+
+(b) and (c)
+
+make oldconfig			# or menuconfig / xconfig if you prefer
+				# enable the OSST driver!
+make dep
+make bzlilo			# to recopmile your kernel and have it
+				# installed
+make modules			# to compile the kernel modules
+make modules_install		# to install your modules
+depmod -a			# append kernel version if necessary
+
+Now, your osst driver is inside the kernel or available as a module,
+depending on your choice during kernel config. You may still need to create
+the device nodes by calling the Makedevs.sh script (see below) manually,
+unless you use a devfs kernel, where this won't be needed.
+
+To load your module, you may use the command 
+modprobe osst
+as root. dmesg should show you, whether your OnStream tapes have been
+recognized.
+
+If you want to have the module autoloaded on access to /dev/osst, you may
+add somethind like
+alias char-major-206 osst
+to your /etc/modules.conf (old name: conf.modules).
+
+You may find it convenient to create a symbolic link 
+ln -s nosst0 /dev/tape
+to make programs assuming a default name of /dev/tape more convenient to
+use.
+
+
+Using it
+--------
+You may use the OnStream tape driver with your standard backup software,
+which may be tar, cpio, amanda, arkeia, BRU, Lone Tar, ...
+by specifying /dev/(n)osst0 as the tape device to use or using the above
+symlink trick. The IOCTLs to control tape operation are also mostly
+supported and you may try the mt (or mt_st) program to jump between
+filemarks, eject the tape, ...
+There's one limitation: You need to use a blocksize of 32kB.
+
+If you just want to get started with standard software, here is an example
+for creating and restoring a full backup:
+# Backup
+tar cvf - / --exclude /proc | buffer -s 32k -m 24M -B -t -o /dev/nosst0
+# Restore
+buffer -s 32k -m 8M -B -t -i /dev/osst0 | tar xvf - -C /
+
+The buffer command has been used to buffer the data before it goes to the
+tape (or the filesystem) in order to smooth out the data stream and prevent
+the tape from needing to stop and rewind. The OnStream does have an internal
+buffer and a variable speed which help this, but especially on writing, the
+buffering still proves useful in most cases. It also pads the data to
+guarantees the blocksize of 32k. (Otherwise you may pass the -b64 option to
+tar.)
+Expect something like 1.8MB/s for the SC-x0 drives and 0.9MB/s for the DI-30.
+The USB drive will give you about 0.7MB/s.
+On a fast machine, you may profit from software data compression (z flag for
+tar).
+
+
+USB and IDE
+-----------
+Via the SCSI emulation layers usb-storage and ide-scsi, you can also use the
+osst driver to drive the USB-30 and the DI-30 drives. (Unfortunately, there
+is no such layer for the parallel port, otherwise the DP-30 would work as
+well.) For the USB support, you need the latest 2.4.0-test kernels and the 
+latest usb-storage driver from 
+http://www.linux-usb.org/
+http://sourceforge.net/cvs/?group_id=3581
+
+Note that the ide-tape driver as of 1.16f uses a slightly outdated on-tape
+format and therefore is not compeletely interoperable with osst tapes.
+
+The ADR-x0 line is fully SCSI-2 compliant and is spported by st, not osst.
+The on-tape format is supposed to be compatible with the one used by osst.
+
+
+Feedback and updates
+--------------------
+The driver development is coordinated through a mailing list
+<osst@linux1.onstream.nl>
+a CVS repository and some web pages. 
+The tester's pages which contain recent news and updated drivers to download
+can be found on
+http://linux1.onstream.nl/test/
+
+If you find any problems, please have a look at the tester's page in order
+to see whether the problem is already known and solved. Otherwise, please
+report it to the mailing list. Your feedback is welcome. (This holds also
+for reports of successful usage, of course.) 
+In case of trouble, please do always provide the following infos:
+* driver and kernel version used (see syslog)
+* driver messages (syslog)
+* SCSI config and OnStream Firmware (/proc/scsi/scsi)
+* description of error. Is it reproducible?
+* software and commands used
+
+You may subscribe to the mailing list, BTW, it's a majordomo list.
+
+
+Status
+------
+0.8.0 was the first widespread BETA release. Since then a lot of reports
+have been sent, but mostly reported success or only minor trouble.
+All the issues have been addressed.
+Check the web pages for more info about the current developments.
+
+
+Acknowledgements
+----------------
+The driver has been started by making a copy of Kai Makisara's st driver.
+Most of the development has been done by Willem Riede. The presence of the
+userspace program osg (onstreamsg) from Terry Hardie has been rather
+helpful. The same holds for Gadi Oxman's ide-tape support for the DI-30.
+I did add some patches to those drivers as well and coordinated things a
+little bit. 
+Note that most of them did mostly spend their spare time for the creation of
+this driver.
+The people from OnStream, especially Jack Bombeeck did support this project
+and always tried to answer HW or FW related questions. Furthermore, he
+pushed the FW developers to do the right things.
+SuSE did support this project by allowing me to work on it during my working
+time for them and by integrating the driver into their distro.
+
+More people did help by sending useful comments. Sorry to those who have
+been forgotten. Thanks to all the GNU/FSF and Linux developers who made this
+platform such an interesting, nice and stable platform.
+Thanks go to those who tested the drivers and did send useful reports. Your
+help is needed!
+
+
+Makedevs.sh
+-----------
+#!/bin/sh
+# Script to create OnStream SC-x0 device nodes (major 206)
+# Usage: Makedevs.sh [nos [path to dev]]
+# $Id: README.osst,v 1.5.6.1 2000/10/10 13:45:37 garloff Exp $
+major=206
+nrs=4
+dir=/dev
+test -z "$1" || nrs=$1
+test -z "$2" || dir=$2
+declare -i nr
+nr=0
+test -d $dir || mkdir -p $dir
+while test $nr -lt $nrs; do
+  mknod $dir/osst$nr c $major $nr
+  chown 0.disk $dir/osst$nr; chmod 660 $dir/osst$nr;
+  mknod $dir/nosst$nr c $major $[nr+128]
+  chown 0.disk $dir/nosst$nr; chmod 660 $dir/nosst$nr;
+  mknod $dir/osst${nr}l c $major $[nr+32]
+  chown 0.disk $dir/osst${nr}l; chmod 660 $dir/osst${nr}l;
+  mknod $dir/nosst${nr}l c $major $[nr+160]
+  chown 0.disk $dir/nosst${nr}l; chmod 660 $dir/nosst${nr}l;
+  mknod $dir/osst${nr}m c $major $[nr+64]
+  chown 0.disk $dir/osst${nr}m; chmod 660 $dir/osst${nr}m;
+  mknod $dir/nosst${nr}m c $major $[nr+192]
+  chown 0.disk $dir/nosst${nr}m; chmod 660 $dir/nosst${nr}m;
+  mknod $dir/osst${nr}a c $major $[nr+96]
+  chown 0.disk $dir/osst${nr}a; chmod 660 $dir/osst${nr}a;
+  mknod $dir/nosst${nr}a c $major $[nr+224]
+  chown 0.disk $dir/nosst${nr}a; chmod 660 $dir/nosst${nr}a;
+  let nr+=1
+done
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/hosts.c linux.19p1/drivers/scsi/hosts.c
--- linux.vanilla/drivers/scsi/hosts.c	Mon Dec 11 22:12:54 2000
+++ linux.19p1/drivers/scsi/hosts.c	Wed Dec 13 21:31:48 2000
@@ -898,6 +898,9 @@
 #ifdef CONFIG_CHR_DEV_ST
     scsi_register_device(&st_template);
 #endif
+#ifdef CONFIG_CHR_DEV_OSST
+    scsi_register_device(&osst_template);
+#endif
 #ifdef CONFIG_CHR_DEV_SG
     scsi_register_device(&sg_template);
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/hosts.h linux.19p1/drivers/scsi/hosts.h
--- linux.vanilla/drivers/scsi/hosts.h	Sun Jun 11 21:44:15 2000
+++ linux.19p1/drivers/scsi/hosts.h	Wed Dec 13 22:58:10 2000
@@ -458,6 +458,7 @@
 
 extern struct Scsi_Device_Template sd_template;
 extern struct Scsi_Device_Template st_template;
+extern struct Scsi_Device_Template osst_template;
 extern struct Scsi_Device_Template sr_template;
 extern struct Scsi_Device_Template sg_template;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/i60uscsi.c linux.19p1/drivers/scsi/i60uscsi.c
--- linux.vanilla/drivers/scsi/i60uscsi.c	Fri Apr 21 12:46:28 2000
+++ linux.19p1/drivers/scsi/i60uscsi.c	Wed Dec 13 21:25:35 2000
@@ -765,10 +765,10 @@
 		if (inia100_adpt[i].ADPT_BIOS < wBIOS)
 			continue;
 		if (inia100_adpt[i].ADPT_BIOS == wBIOS) {
-			if (inia100_adpt[i].ADPT_BASE == wBASE)
+			if (inia100_adpt[i].ADPT_BASE == wBASE) {
 				if (inia100_adpt[i].ADPT_Bus != 0xFF)
 					return (FAILURE);
-				else if (inia100_adpt[i].ADPT_BASE < wBASE)
+			} else if (inia100_adpt[i].ADPT_BASE < wBASE)
 					continue;
 		}
 		for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/osst.c linux.19p1/drivers/scsi/osst.c
--- linux.vanilla/drivers/scsi/osst.c	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/scsi/osst.c	Wed Dec 13 21:31:48 2000
@@ -0,0 +1,5112 @@
+/*
+  SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+  file README.st for more information.
+
+  History:
+
+  OnStream SCSI Tape support (osst) cloned from st.c by
+  Willem Riede (osst@riede.org) Feb 2000
+  Fixes ... Kurt Garloff <garloff@suse.de> Mar 2000
+
+  Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
+  Contribution and ideas from several people including (in alphabetical
+  order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
+  Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+
+  Copyright 1992 - 2000 Kai Makisara
+		 email Kai.Makisara@metla.fi
+
+  $Header: /home/cvsroot/Driver/osst.c,v 1.28.2.7 2000/10/05 00:45:52 riede Exp $
+
+  Last modified: Wed Feb  2 22:04:05 2000 by makisara@kai.makisara.local
+  Some small formal changes - aeb, 950809
+*/
+
+static const char * cvsid = "$Id: osst.c,v 1.28.2.7 2000/10/05 00:45:52 riede Exp $";
+const char * osst_version = "0.8.5";
+
+/* The "failure to reconnect" firmware bug */
+#define OSST_FW_NEED_POLL_MIN 10602 /*(107A)*/
+#define OSST_FW_NEED_POLL_MAX 10708 /*(108D)*/
+#define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7)
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/spinlock.h>
+
+/* The driver prints some debugging information on the console if DEBUG
+   is defined and non-zero. */
+#define DEBUG 0
+
+/* The message level for the debug messages is currently set to KERN_NOTICE
+   so that people can easily see the messages. Later when the debugging messages
+   in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
+#define OSST_DEB_MSG  KERN_NOTICE
+
+#define MAJOR_NR OSST_MAJOR
+#include <linux/blk.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include <scsi/scsi_ioctl.h>
+
+#define ST_KILOBYTE 1024
+
+#include "st.h"
+#include "osst.h"
+#include "osst_options.h"
+#include "osst_detect.h"
+
+#include "constants.h"
+
+#ifdef MODULE
+MODULE_PARM(buffer_kbs, "i");
+MODULE_PARM(write_threshold_kbs, "i");
+MODULE_PARM(max_buffers, "i");
+MODULE_PARM(max_sg_segs, "i");
+static int buffer_kbs = 0;
+static int write_threshold_kbs = 0;
+static int max_buffers = 0;
+static int max_sg_segs = 0;
+#endif
+
+/* Some default definitions have been moved to osst_options.h */
+#define OSST_BUFFER_SIZE (OSST_BUFFER_BLOCKS * ST_KILOBYTE)
+#define OSST_WRITE_THRESHOLD (OSST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE)
+
+/* The buffer size should fit into the 24 bits for length in the
+   6-byte SCSI read and write commands. */
+#if OSST_BUFFER_SIZE >= (2 << 24 - 1)
+#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
+#endif
+
+#if DEBUG
+static int debugging = 1;
+#endif
+
+#define MAX_RETRIES 0
+#define MAX_WRITE_RETRIES 0
+#define MAX_READY_RETRIES 5
+#define NO_TAPE  NOT_READY
+
+#define OSST_TIMEOUT (200 * HZ)
+#define OSST_LONG_TIMEOUT (1800 * HZ)
+
+#define TAPE_NR(x) (MINOR(x) & ~(128 | ST_MODE_MASK))
+#define TAPE_MODE(x) ((MINOR(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
+
+/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
+   24 bits) */
+#define SET_DENS_AND_BLK 0x10001
+
+static int osst_nbr_buffers;
+static OSST_buffer **osst_buffers;
+static int osst_buffer_size     = OSST_BUFFER_SIZE;
+static int osst_write_threshold = OSST_WRITE_THRESHOLD;
+static int osst_max_buffers     = OSST_MAX_BUFFERS;
+static int osst_max_sg_segs     = OSST_MAX_SG;
+
+static OS_Scsi_Tape ** os_scsi_tapes = NULL;
+
+static int modes_defined = FALSE;
+
+static OSST_buffer *new_tape_buffer(int, int);
+static int enlarge_buffer(OSST_buffer *, int, int);
+static void normalize_buffer(OSST_buffer *);
+static int append_to_buffer(const char *, OSST_buffer *, int);
+static int from_buffer(OSST_buffer *, char *, int);
+static int osst_zero_buffer_tail(OSST_buffer *);
+static int osst_copy_to_buffer(OSST_buffer *, unsigned char *);
+static int osst_copy_from_buffer(OSST_buffer *, unsigned char *);
+
+static int osst_init(void);
+static int osst_attach(Scsi_Device *);
+static int osst_detect(Scsi_Device *);
+static void osst_detach(Scsi_Device *);
+
+struct Scsi_Device_Template osst_template = {NULL, "OnStream tape", "osst", NULL, TYPE_TAPE,
+					     OSST_MAJOR, 0, 0, 0, 0,
+					     osst_detect, osst_init,
+					     NULL, osst_attach, osst_detach};
+
+static int osst_int_ioctl(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, unsigned int cmd_in,unsigned long arg);
+
+static int osst_set_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int frame, int skip);
+
+static int osst_get_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt);
+
+static int osst_flush_write_buffer(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int file_blk);
+
+static int osst_write_error_recovery(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int pending);
+
+
+/* Routines that handle the interaction with mid-layer SCSI routines */
+
+/* Convert the result to success code */
+static int osst_chk_result(Scsi_Cmnd * SCpnt)
+{
+  int dev = TAPE_NR(SCpnt->request.rq_dev);
+  int result = SCpnt->result;
+  unsigned char * sense = SCpnt->sense_buffer, scode;
+#if DEBUG
+  const char *stp;
+#endif
+
+  if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
+    return 0;
+
+  if (driver_byte(result) & DRIVER_SENSE)
+      scode = sense[2] & 0x0f;
+  else {
+      sense[0] = 0;    /* We don't have sense data if this byte is zero */
+      scode = 0;
+      if ((sense[2] & 0x0f) == 1)
+	return 0;			/* FIXME -- is this safe? */
+  }
+
+#if DEBUG
+  if (debugging) {
+    printk(OSST_DEB_MSG "osst%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
+	   dev, result,
+	   SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2],
+	   SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5],
+	   SCpnt->request_bufflen);
+    if (driver_byte(result) & DRIVER_SENSE)
+      print_sense("osst", SCpnt);
+  }
+  else
+#endif
+  if (!(driver_byte(result) & DRIVER_SENSE) ||
+      ((sense[0] & 0x70) == 0x70 &&
+       scode != NO_SENSE &&
+       scode != RECOVERED_ERROR &&
+/*       scode != UNIT_ATTENTION && */
+       scode != BLANK_CHECK &&
+       scode != VOLUME_OVERFLOW &&
+       SCpnt->data_cmnd[0] != MODE_SENSE &&
+       SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
+    if (driver_byte(result) & DRIVER_SENSE) {
+      printk(KERN_WARNING "osst%d: Error with sense data: ", dev);
+      print_sense("osst", SCpnt);
+    }
+    else
+      printk(KERN_WARNING
+	     "osst%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
+	     dev, result, suggestion(result), driver_byte(result),
+	     host_byte(result));
+  }
+
+  if ((sense[0] & 0x70) == 0x70 &&
+      scode == RECOVERED_ERROR
+      ) {
+    os_scsi_tapes[dev]->recover_count++;
+    os_scsi_tapes[dev]->recover_erreg++;
+#if DEBUG
+    if (debugging) {
+      if (SCpnt->data_cmnd[0] == READ_6)
+	stp = "read";
+      else if (SCpnt->data_cmnd[0] == WRITE_6)
+	stp = "write";
+      else
+	stp = "ioctl";
+      printk(OSST_DEB_MSG "osst%d: Recovered %s error (%d).\n", dev, stp,
+	     os_scsi_tapes[dev]->recover_count);
+    }
+#endif
+    if ((sense[2] & 0xe0) == 0)
+      return 0;
+  }
+  return (-EIO);
+}
+
+
+/* Wakeup from interrupt */
+static void osst_sleep_done (Scsi_Cmnd * SCpnt)
+{
+  unsigned int st_nbr;
+  int remainder;
+  OS_Scsi_Tape * STp;
+
+  if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < osst_template.nr_dev) {
+    STp = os_scsi_tapes[st_nbr];
+    if ((STp->buffer)->writing &&
+	(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	(SCpnt->sense_buffer[2] & 0x40)) {
+      /* EOM at write-behind, has all been written? */
+      if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+	remainder = (SCpnt->sense_buffer[3] << 24) |
+	      (SCpnt->sense_buffer[4] << 16) |
+		(SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+      else
+	remainder = 0;
+      if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
+	  remainder > 0)
+	(STp->buffer)->last_result = SCpnt->result; /* Error */
+      else
+	(STp->buffer)->last_result = INT_MAX; /* OK */
+    }
+    else
+      (STp->buffer)->last_result = SCpnt->result;
+    SCpnt->request.rq_status = RQ_SCSI_DONE;
+    (STp->buffer)->last_SCpnt = SCpnt;
+
+#if DEBUG
+    STp->write_pending = 0;
+#endif
+    up(SCpnt->request.sem);
+  }
+#if DEBUG
+  else if (debugging)
+    printk(KERN_ERR "osst?: Illegal interrupt device %x\n", st_nbr);
+#endif
+}
+
+
+/* Do the scsi command. Waits until command performed if do_wait is true.
+   Otherwise osst_write_behind_check() is used to check that the command
+   has finished. */
+static Scsi_Cmnd * osst_do_scsi(Scsi_Cmnd *SCpnt, OS_Scsi_Tape *STp, 
+	 unsigned char *cmd, int bytes, int timeout, int retries, int do_wait)
+{
+  unsigned long flags;
+  unsigned char *bp;
+
+  spin_lock_irqsave(&io_request_lock, flags);
+  if (SCpnt == NULL)
+    if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) {
+      printk(KERN_ERR "osst%d: Can't get SCSI request.\n", TAPE_NR(STp->devt));
+      spin_unlock_irqrestore(&io_request_lock, flags);
+      return NULL;
+    }
+
+  cmd[1] |= (SCpnt->lun << 5) & 0xe0;
+  STp->sem = MUTEX_LOCKED;
+  SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+      (STp->buffer)->use_sg : 0;
+  if (SCpnt->use_sg) {
+      bp = (char *)&((STp->buffer)->sg[0]);
+      if ((STp->buffer)->sg_segs < SCpnt->use_sg)
+	  SCpnt->use_sg = (STp->buffer)->sg_segs;
+  }
+  else
+      bp = (STp->buffer)->b_data;
+  SCpnt->cmd_len = 0;
+  SCpnt->request.sem = &(STp->sem);
+  SCpnt->request.rq_status = RQ_SCSI_BUSY;
+  SCpnt->request.rq_dev = STp->devt;
+
+  scsi_do_cmd(SCpnt, (void *)cmd, bp, bytes,
+	      osst_sleep_done, timeout, retries);
+  spin_unlock_irqrestore(&io_request_lock, flags);
+
+  if (do_wait) {
+      down(SCpnt->request.sem);
+
+      (STp->buffer)->last_result_fatal = osst_chk_result(SCpnt);
+  }
+
+  return SCpnt;
+}
+
+
+/* Handle the write-behind checking (downs the semaphore) */
+static void osst_write_behind_check(OS_Scsi_Tape *STp)
+{
+  OSST_buffer * STbuffer;
+  ST_partstat * STps;
+
+  STbuffer = STp->buffer;
+
+#if DEBUG
+  if (STp->write_pending)
+    STp->nbr_waits++;
+  else
+    STp->nbr_finished++;
+#endif
+
+  down(&(STp->sem));
+
+  (STp->buffer)->last_result_fatal = osst_chk_result((STp->buffer)->last_SCpnt);
+
+  if ((STp->buffer)->last_result_fatal)
+    (STp->buffer)->last_result_fatal =
+        osst_write_error_recovery(STp, &((STp->buffer)->last_SCpnt), 1);
+  else
+    STp->first_frame_position++;
+
+  scsi_release_command((STp->buffer)->last_SCpnt);
+
+  if (STbuffer->writing < STbuffer->buffer_bytes)
+#if 0
+    memcpy(STbuffer->b_data,
+	   STbuffer->b_data + STbuffer->writing,
+	   STbuffer->buffer_bytes - STbuffer->writing);
+#else
+  printk(KERN_WARNING "osst: write_behind_check: something left in buffer!\n");
+#endif
+  STbuffer->buffer_bytes -= STbuffer->writing;
+  STps = &(STp->ps[STp->partition]);
+  if (STps->drv_block >= 0) {
+    if (STp->block_size == 0)
+      STps->drv_block++;
+    else
+      STps->drv_block += STbuffer->writing / STp->block_size;
+  }
+  STbuffer->writing = 0;
+
+  return;
+}
+
+
+
+/* Onstream specific Routines */
+/*
+ * Initialize the OnStream AUX
+ */
+static void osst_init_aux(OS_Scsi_Tape * STp, int frame_type, int logical_blk_num)
+{
+	os_aux_t       *aux = STp->buffer->aux;
+	os_partition_t *par = &aux->partition;
+	os_dat_t       *dat = &aux->dat;
+
+	if (STp->raw) return;
+
+	memset(aux, 0, sizeof(*aux));
+	aux->format_id = htonl(0);
+	memcpy(aux->application_sig, "LIN4", 4);
+	aux->hdwr = htonl(0);
+	aux->frame_type = frame_type;
+
+	switch (frame_type) {
+	  case	OS_FRAME_TYPE_HEADER:
+		aux->update_frame_cntr    = htonl(STp->update_frame_cntr);
+		par->partition_num        = OS_CONFIG_PARTITION;
+		par->par_desc_ver         = OS_PARTITION_VERSION;
+		par->wrt_pass_cntr        = htons(0xffff);
+		/* 0-4 = reserved, 5-9 = header, 2990-2994 = header, 2995-2999 = reserved */
+		par->first_frame_ppos     = htonl(0);
+		par->last_frame_ppos      = htonl(0xbb7);
+		aux->frame_seq_num        = htonl(0);
+		aux->logical_blk_num_high = htonl(0);
+		aux->logical_blk_num      = htonl(0);
+		aux->next_mark_ppos       = htonl(STp->first_mark_ppos);
+		break;
+	  case	OS_FRAME_TYPE_DATA:
+	  case	OS_FRAME_TYPE_MARKER:
+		dat->dat_sz = 8;
+		dat->reserved1 = 0;
+		dat->entry_cnt = 1;
+		dat->reserved3 = 0;
+		dat->dat_list[0].blk_sz   = htonl(frame_type==OS_FRAME_TYPE_DATA?STp->block_size:0);
+		dat->dat_list[0].blk_cnt  = htons(1);
+		dat->dat_list[0].flags    = frame_type==OS_FRAME_TYPE_MARKER?OS_DAT_FLAGS_MARK:OS_DAT_FLAGS_DATA;
+		dat->dat_list[0].reserved = 0;
+	  case	OS_FRAME_TYPE_EOD:
+		aux->update_frame_cntr    = htonl(0);
+		par->partition_num        = OS_DATA_PARTITION;
+		par->par_desc_ver         = OS_PARTITION_VERSION;
+		par->wrt_pass_cntr        = htons(STp->wrt_pass_cntr);
+		par->first_frame_ppos     = htonl(STp->first_data_ppos);
+		par->last_frame_ppos      = htonl(STp->capacity);
+		aux->frame_seq_num        = htonl(logical_blk_num);
+		aux->logical_blk_num_high = htonl(0);
+		aux->logical_blk_num      = htonl(logical_blk_num);
+		break;
+	  default: ; /* probably FILL */
+	}
+	aux->filemark_cnt = ntohl(STp->filemark_cnt);        /* FIXME -- violates ADR spec */
+	aux->phys_fm = ntohl(0xffffffff);
+	aux->last_mark_ppos = ntohl(STp->last_mark_ppos);
+}
+
+/*
+ * Verify that we have the correct tape frame
+ */
+static int osst_verify_frame(OS_Scsi_Tape * STp, int logical_blk_num, int quiet)
+{
+	os_aux_t       * aux  = STp->buffer->aux;
+        os_partition_t * par  = &(aux->partition);
+	ST_partstat    * STps = &(STp->ps[STp->partition]);
+        int              i;
+	int		 dev  = TAPE_NR(STp->devt);
+
+        if (STp->raw) {
+                if (STp->buffer->last_result_fatal) {
+                        for (i=0; i < STp->buffer->sg_segs; i++)
+                                memset(STp->buffer->sg[i].address, 0, STp->buffer->sg[i].length);
+                        strcpy(STp->buffer->b_data, "READ ERROR ON FRAME");
+                }
+                return 1;
+        }
+        if (STp->buffer->last_result_fatal) {
+                printk(KERN_INFO "osst%d: Skipping frame, read error\n", dev);
+                return 0;
+        }
+        if (ntohl(aux->format_id) != 0) {
+                printk(KERN_INFO "osst%d: Skipping frame, format_id %u\n", dev, ntohl(aux->format_id));
+                return 0;
+        }
+        if (memcmp(aux->application_sig, STp->application_sig, 4) != 0 &&
+	    (memcmp(aux->application_sig, "LIN3", 4) != 0 || STp->linux_media_version != 4)) {
+                printk(KERN_INFO "osst%d: Skipping frame, incorrect application signature\n", dev);
+                return 0;
+        }
+        if (par->partition_num != OS_DATA_PARTITION) {
+                if (!STp->linux_media || STp->linux_media_version != 2) {
+                        printk(KERN_INFO "osst%d: Skipping frame, partition num %d\n", dev, par->partition_num);              		    return 0;
+                }
+        }
+        if (par->par_desc_ver != OS_PARTITION_VERSION) {
+                printk(KERN_INFO "osst%d: Skipping frame, partition version %d\n", dev, par->par_desc_ver);
+                return 0;
+        }
+        if (ntohs(par->wrt_pass_cntr) != STp->wrt_pass_cntr) {
+                printk(KERN_INFO "osst%d: Skipping frame, wrt_pass_cntr %d (expected %d)\n", 
+				 dev, ntohs(par->wrt_pass_cntr), STp->wrt_pass_cntr);
+                return 0;
+        }
+        if (aux->frame_seq_num != aux->logical_blk_num) {
+                printk(KERN_INFO "osst%d: Skipping frame, seq != logical\n", dev);
+                return 0;
+        }
+        if (aux->frame_type != OS_FRAME_TYPE_DATA &&
+            aux->frame_type != OS_FRAME_TYPE_EOD &&
+            aux->frame_type != OS_FRAME_TYPE_MARKER) {
+                if (!quiet)
+			printk(KERN_INFO "osst%d: Skipping frame, frame type %x\n", dev, aux->frame_type);
+                return 0;
+        }
+        if (aux->frame_type == OS_FRAME_TYPE_EOD &&
+	    STp->first_frame_position < STp->eod_frame_ppos) {
+                printk(KERN_INFO "osst%d: skipping premature EOD frame %d\n", dev, STp->first_frame_position);
+                return 0;
+	}
+	STp->logical_blk_in_buffer = 1;
+
+        if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) {
+                if (!quiet)
+                        printk(KERN_INFO "osst%d: Skipping frame, logical_blk_num %u (expected %d)\n", 
+					 dev, ntohl(aux->logical_blk_num), logical_blk_num);
+                return 0;
+        }
+        if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
+                STps->eof = ST_FM_HIT;
+
+		i = ntohl(aux->filemark_cnt);
+		if (STp->header_cache != NULL && i < OS_FM_TAB_MAX &&
+		    STp->first_frame_position - 1 != ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) {
+#if 1 //DEBUG
+			printk(OSST_DEB_MSG "osst%i: %s filemark %d at frame %d\n", dev,
+				  STp->header_cache->dat_fm_tab.fm_tab_ent[i] == 0?"Learned":"Corrected",
+				  i, STp->first_frame_position - 1);
+#endif
+			STp->header_cache->dat_fm_tab.fm_tab_ent[i] = htonl(STp->first_frame_position - 1);
+		}
+        }
+        if (aux->frame_type == OS_FRAME_TYPE_EOD) {
+                STps->eof = ST_EOD_1;
+        }
+        if (aux->frame_type == OS_FRAME_TYPE_DATA) {
+                STps->eof = ST_NOEOF;
+        }
+        return 1;
+}
+
+/*
+ * Wait for the unit to become Ready
+ */
+static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, unsigned timeout)
+{
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+        Scsi_Cmnd     * SCpnt;
+	long		startwait = jiffies;
+#if DEBUG
+	int		dbg = debugging;
+	int		dev  = TAPE_NR(STp->devt);
+
+	printk(OSST_DEB_MSG "osst%d: reached onstream wait ready\n", dev);
+#endif
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = TEST_UNIT_READY;
+
+	SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE);
+        *aSCpnt = SCpnt;
+	if (!SCpnt) return (-EBUSY);
+
+	while ( SCpnt->sense_buffer[2] == 2 && SCpnt->sense_buffer[12] == 4 &&
+		( SCpnt->sense_buffer[13] == 1 || SCpnt->sense_buffer[13] == 8 ) && 
+		time_before(jiffies, startwait + timeout*HZ) ) {
+#if DEBUG
+	    if (debugging) {
+		printk(OSST_DEB_MSG "osst%d: Sleeping in onstream wait ready\n", dev);
+		printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev);
+		debugging = 0;
+	    }
+#endif
+	    current->state = TASK_INTERRUPTIBLE;
+	    schedule_timeout(HZ / 10);
+
+	    memset(cmd, 0, MAX_COMMAND_SIZE);
+	    cmd[0] = TEST_UNIT_READY;
+
+	    SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE);
+	}
+        *aSCpnt = SCpnt;
+#if DEBUG
+	debugging = dbg;
+#endif
+	if ( SCpnt->sense_buffer[2] &&
+	     osst_write_error_recovery(STp, aSCpnt, 0) ) {
+#if DEBUG
+	    printk(OSST_DEB_MSG "osst%d: Abnormal exit from onstream wait ready\n", dev);
+#endif
+	    return (-EIO);
+	}
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Normal exit from onstream wait ready\n", dev);
+#endif
+	return 0;
+}
+
+static int osst_position_tape_and_confirm(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int frame)
+{
+	int           retval;
+
+	osst_wait_ready(STp, aSCpnt, 15 * 60);			/* TODO - can this catch a write error? */
+	retval = osst_set_frame_position(STp, aSCpnt, frame, 0);
+	if (retval) return (retval);
+	osst_wait_ready(STp, aSCpnt, 15 * 60);
+	return (osst_get_frame_position(STp, aSCpnt));
+}
+
+/*
+ * Wait for write(s) to complete
+ */
+static int osst_flush_drive_buffer(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+        Scsi_Cmnd     * SCpnt;
+
+	int             result = 0;
+#if DEBUG
+	int		dev  = TAPE_NR(STp->devt);
+
+	printk(OSST_DEB_MSG "osst%d: Reached onstream flush drive buffer (write filemark)\n", dev);
+#endif
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = WRITE_FILEMARKS;
+	cmd[1] = 1;
+
+	SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES, TRUE);
+        *aSCpnt = SCpnt;
+	if (!SCpnt) return (-EBUSY);
+
+	if ((STp->buffer)->last_result_fatal)
+		result = osst_write_error_recovery(STp, aSCpnt, 0);
+
+	result |= osst_wait_ready(STp, aSCpnt, 5 * 60);
+	STp->ps[STp->partition].rw = ST_IDLE;
+	return (result);
+}
+
+#define OSST_POLL_PER_SEC 10
+static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int curr, int minlast, int to)
+{
+	long        startwait = jiffies;
+	int         dev       = TAPE_NR(STp->devt);
+#if DEBUG
+	char    notyetprinted = 1;
+#endif
+	if (STp->ps[STp->partition].rw != ST_READING)
+		printk(KERN_ERR "osst%i: waiting for frame without having initialized read!\n", dev);
+
+	while (time_before (jiffies, startwait + to*HZ))
+	{ 
+		int result;
+		result = osst_get_frame_position (STp, aSCpnt);
+		if (result == -EIO)
+			result = osst_write_error_recovery(STp, aSCpnt, 0);
+		if (result < 0) break;
+		if (STp->first_frame_position == curr &&
+		    ((minlast < 0 &&
+		      (signed)STp->last_frame_position > (signed)curr + minlast) ||
+		     (minlast >= 0 && STp->cur_frames > minlast)
+		    ) && result >= 0)
+		{
+#if DEBUG			
+			if (jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC)
+				printk ("osst%i: Succ wait f fr %i (>%i): %i-%i %i (%i): %3li.%li s\n",
+					dev, curr, curr+minlast, STp->first_frame_position,
+					STp->last_frame_position, STp->cur_frames,
+					result, (jiffies-startwait)/HZ, 
+					(((jiffies-startwait)%HZ)*10)/HZ);
+#endif
+			return 0;
+		}
+#if DEBUG
+		if (jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC && notyetprinted)
+		{
+			printk ("osst%i: Wait for frame %i (>%i): %i-%i %i (%i)\n",
+				dev, curr, curr+minlast, STp->first_frame_position,
+				STp->last_frame_position, STp->cur_frames, result);
+			notyetprinted--;
+		}
+#endif
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout (HZ / OSST_POLL_PER_SEC);
+	}
+#if DEBUG
+	printk ("osst%i: Fail wait f fr %i (>%i): %i-%i %i: %3li.%li s\n",
+		dev, curr, curr+minlast, STp->first_frame_position,
+		STp->last_frame_position, STp->cur_frames,
+		(jiffies-startwait)/HZ, (((jiffies-startwait)%HZ)*10)/HZ);
+#endif	
+	return -EBUSY;
+}
+
+/*
+ * Read the next OnStream tape block at the current location
+ */
+static int osst_read_block(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int timeout)
+{
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+	Scsi_Cmnd     * SCpnt;
+	int		retval = 0;
+#if DEBUG
+	os_aux_t      * aux    = STp->buffer->aux;
+	int		dev    = TAPE_NR(STp->devt);
+#endif
+
+	/* TODO: Error handling */
+	if (STp->poll)
+		retval = osst_wait_frame (STp, aSCpnt, STp->first_frame_position, 0, timeout);
+#if 0// DEBUG
+	printk ("osst_read: wait for frame returned %i\n", retval);
+#endif
+	
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = READ_6;
+	cmd[1] = 1;
+	cmd[4] = 1;
+
+#if DEBUG
+	if (debugging)
+	    printk(OSST_DEB_MSG "osst%i: Reading block from OnStream tape\n", dev);
+#endif
+	SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, MAX_RETRIES, TRUE);
+	*aSCpnt = SCpnt;
+        if (!SCpnt)
+	    return (-EBUSY);
+
+	if ((STp->buffer)->last_result_fatal) {
+	    retval = 1;
+#if DEBUG
+	    if (debugging)
+	        printk(OSST_DEB_MSG "osst%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+		   dev,
+		   SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
+		   SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
+		   SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
+		   SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
+#endif
+	}
+	else
+	    STp->first_frame_position++;
+#if DEBUG
+	if (debugging) {
+	   printk(OSST_DEB_MSG "osst%i: AUX: %c%c%c%c UpdFrCt#%d %s FrSeq#%d LogBlk#%d\n", dev,
+			aux->application_sig[0], aux->application_sig[1],
+			aux->application_sig[2], aux->application_sig[3], ntohl(aux->update_frame_cntr),
+			aux->frame_type==1?"EOD":aux->frame_type==2?"MARK":
+			aux->frame_type==8?"HEADR":aux->frame_type==0x80?"DATA":"FILL", 
+			ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num) );
+	   if (aux->frame_type==2)
+		printk(OSST_DEB_MSG "osst%i: mark_cnt=%d, last_mark=%d, next_mark=%d\n", dev,
+			ntohl(aux->filemark_cnt), ntohl(aux->last_mark_ppos), ntohl(aux->next_mark_ppos));
+	   printk(OSST_DEB_MSG "osst%i: Exit read block from OnStream tape with code %d\n", dev, retval);
+	}
+#endif
+	return (retval);
+}
+
+static int osst_initiate_read(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+        ST_partstat   * STps   = &(STp->ps[STp->partition]);
+	Scsi_Cmnd     * SCpnt  ;
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+	int		retval = 0;
+#if DEBUG
+	int		dev    = TAPE_NR(STp->devt);
+#endif
+
+        if (STps->rw != ST_READING) {         /* Initialize read operation */
+                if (STps->rw == ST_WRITING) {
+                        osst_flush_write_buffer(STp, aSCpnt, 1);
+			osst_flush_drive_buffer(STp, aSCpnt);
+                }
+                STps->rw = ST_READING;
+		STp->logical_blk_in_buffer = 0;
+
+                /*
+                 *      Issue a read 0 command to get the OnStream drive
+                 *      read blocks into its buffer.
+                 */
+		memset(cmd, 0, MAX_COMMAND_SIZE);
+		cmd[0] = READ_6;
+		cmd[1] = 1;
+
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Start Read Ahead on OnStream tape\n", dev);
+#endif
+		SCpnt   = osst_do_scsi(*aSCpnt, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE);
+		*aSCpnt = SCpnt;
+		retval  = STp->buffer->last_result_fatal;
+        }
+
+        return retval;
+}
+
+static int osst_get_logical_blk(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int logical_blk_num, int quiet)
+{
+	ST_partstat * STps  = &(STp->ps[STp->partition]);
+	int           dev   = TAPE_NR(STp->devt);
+        int           cnt   = 0,
+		      x,
+		      position;
+
+        /*
+         * Search and wait for the next logical tape block
+         */
+        while (1) {
+                if (cnt++ > 200) {
+                        printk(KERN_WARNING "osst%d: Couldn't find logical block %d, aborting\n",
+					    dev, logical_blk_num);
+                        return (-EIO);
+                }
+#if DEBUG
+		if (debugging)
+			printk(OSST_DEB_MSG "osst%d: Looking for block %d, attempt %d\n",
+					  dev, logical_blk_num, cnt);
+#endif
+                if ( osst_initiate_read(STp, aSCpnt)
+                || ( (!STp->logical_blk_in_buffer) && osst_read_block(STp, aSCpnt, 30) ) ) {
+                        position = osst_get_frame_position(STp, aSCpnt);
+			if (position >= 0xbae && position < 0xbb8)
+				position = 0xbb8;
+			else {
+				position += 39;
+                        	cnt += 30;
+			}
+#if DEBUG
+                        printk(OSST_DEB_MSG "osst%d: Blank block detected, positioning tape to block %d\n",
+					 dev, position);
+#endif
+                        osst_set_frame_position(STp, aSCpnt, position, 0);
+                        continue;
+                }
+                if (osst_verify_frame(STp, logical_blk_num, quiet))
+                        break;
+                if (osst_verify_frame(STp, -1, quiet)) {
+                        x = ntohl(STp->buffer->aux->logical_blk_num);
+			if (STp->fast_open) {
+#if 1 //DEBUG
+				printk(OSST_DEB_MSG "osst%d: Found logical block %d instead of %d after fast open\n",
+					  dev, x, logical_blk_num);
+#endif
+				STp->header_ok = 0;
+				return (-EIO);
+			}
+                        if (x > logical_blk_num) {
+#if DEBUG
+                                printk(OSST_DEB_MSG
+				       "osst%d: Found logical block %d while looking for %d: back up %d\n",
+						dev, x, logical_blk_num, x - logical_blk_num + 1);
+#endif
+			        position = osst_get_frame_position(STp, aSCpnt);
+                        	osst_set_frame_position(STp, aSCpnt, position + logical_blk_num - x - 1, 0);
+                        	cnt += 10;
+                        }
+                }
+                if (osst_get_frame_position(STp, aSCpnt) == 0xbaf) {
+#if DEBUG
+                        printk(OSST_DEB_MSG "osst%d: Skipping config partition\n", dev);
+#endif
+			osst_set_frame_position(STp, aSCpnt, 0xbb8, 0);
+			cnt--;
+		}
+		STp->logical_blk_in_buffer = 0;
+        }
+        if (cnt > 1) {
+		STp->recover_count++;
+		STp->recover_erreg++;
+ 	}
+        STp->logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num);
+
+#if DEBUG
+	if (debugging || STps->eof)
+		printk(OSST_DEB_MSG "osst%i: Exit get logical block (%d=>%d) from OnStream tape with code %d\n",							 dev, logical_blk_num, STp->logical_blk_num, STps->eof);
+#endif
+	STp->fast_open = FALSE;
+        return (STps->eof);
+}
+
+static int osst_seek_logical_blk(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int logical_blk_num)
+{
+	int  estimate;
+	int  retries = 0;
+	int  dev     = TAPE_NR(STp->devt);
+
+	if (logical_blk_num < 0) logical_blk_num = 0;
+	/* FIXME -- this may not be valid for foreign formats */
+	if (logical_blk_num < 2980) estimate  = logical_blk_num + 10;
+	else			    estimate  = logical_blk_num + 20;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Seeking logical block %d (now at %d)\n",
+			  dev, logical_blk_num, STp->logical_blk_num);
+#endif
+	while (++retries < 10) {
+	   osst_set_frame_position(STp, aSCpnt, estimate, 0);
+	   if (osst_get_logical_blk(STp, aSCpnt, logical_blk_num, 1) >= 0)
+	      return 0;
+	   if (osst_get_logical_blk(STp, aSCpnt, -1, 1) < 0)
+	      goto error;
+	   if (STp->logical_blk_num != logical_blk_num)
+	      estimate += logical_blk_num - STp->logical_blk_num;
+	   else
+	      break;
+	}
+error:
+	printk(KERN_WARNING "osst%d: Couldn't seek to logical block %d (at %d), %d retries\n", 
+			    dev, logical_blk_num, STp->logical_blk_num, retries);
+	return (-EIO);
+}
+
+static int osst_seek_frame(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int frame)
+{
+        ST_partstat   * STps = &(STp->ps[STp->partition]);
+	int		r;
+
+	if (frame < 0 || frame >= STp->capacity) return (-ENXIO);
+
+	if (frame <= STp->first_data_ppos) {
+		STp->logical_blk_num = STps->drv_file = STps->drv_block = 0;
+		return (osst_set_frame_position(STp, aSCpnt, frame, 0));
+	}
+	r = osst_set_frame_position(STp, aSCpnt, frame-1, 0);
+	if (r < 0) return r;
+
+	r = osst_get_logical_blk(STp, aSCpnt, -1, 1);
+	if (r < 0) return r;
+
+	if (osst_get_frame_position(STp, aSCpnt) != frame) return (-EIO);
+
+	STp->logical_blk_num++;
+	STp->logical_blk_in_buffer = 0;
+	STps->drv_file  = htonl(STp->buffer->aux->filemark_cnt);
+	STps->drv_block = -1;
+	STps->eof       = ST_NOEOF;
+	return 0;
+}
+
+/*
+ * Read back the drive's internal buffer contents, as a part
+ * of the write error recovery mechanism for old OnStream
+ * firmware revisions.
+ */
+static int osst_read_back_buffer_and_rewrite(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt,
+					unsigned int block, unsigned int skip, int pending)
+{
+	Scsi_Cmnd     * SCpnt = * aSCpnt;
+	unsigned char * buffer, * p;
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+	int             frames, flag, new_block, i, logical_blk_num;
+	int		dev  = TAPE_NR(STp->devt);
+	long		startwait = jiffies;
+#if DEBUG
+	int		dbg = debugging;
+#endif
+
+	frames = STp->cur_frames;
+	if ((buffer = (unsigned char *)vmalloc((frames + pending) * OS_DATA_SIZE)) == NULL)
+		return (-EIO);
+
+	logical_blk_num = STp->logical_blk_num - frames - pending;
+	printk(KERN_INFO "osst%d: Reading back %d frames from drive buffer%s\n",
+			 dev, frames, pending?" and one that was pending":"");
+
+	if (pending) {
+		osst_copy_from_buffer(STp->buffer, (p = &buffer[frames * OS_DATA_SIZE]));
+//		memcpy((p = &buffer[frames * OS_DATA_SIZE]), STp->buffer->b_data, OS_DATA_SIZE);
+#if DEBUG
+		if (debugging)
+			printk(OSST_DEB_MSG "osst%d: Pending logical block %d, data %x %x %x %x\n",
+					  dev, logical_blk_num + frames, p[0], p[1], p[2], p[3]);
+#endif
+	}
+	for (i = 0, p = buffer; i < frames; i++, p += OS_DATA_SIZE) {
+
+		memset(cmd, 0, MAX_COMMAND_SIZE);
+		cmd[0] = 0x3C;		/* Buffer Read           */
+		cmd[1] = 6;		/* Retrieve Faulty Block */
+		cmd[7] = 32768 >> 8;
+		cmd[8] = 32768 & 0xff;
+
+		SCpnt = osst_do_scsi(SCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, MAX_RETRIES, TRUE);
+	
+		if ((STp->buffer)->last_result_fatal) {
+			printk(KERN_ERR "osst%d: Failed to read block back from OnStream buffer\n", dev);
+			vfree((void *)buffer);
+			*aSCpnt = SCpnt;
+			return (-EIO);
+		}
+		osst_copy_from_buffer(STp->buffer, p);
+//		memcpy(p, STp->buffer->b_data, OS_DATA_SIZE);
+#if DEBUG
+		if (debugging)
+			printk(OSST_DEB_MSG "osst%d: Read back logical block %d, data %x %x %x %x\n",
+					  dev, logical_blk_num + i, p[0], p[1], p[2], p[3]);
+#endif
+	}
+        *aSCpnt = SCpnt;
+	osst_get_frame_position(STp, aSCpnt);
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Frames left in buffer: %d\n", dev, STp->cur_frames);
+#endif
+	/* Write synchronously so we can be sure we're OK again and don't have to recover recursively */
+	/* In the header we don't actually re-write the blocks that fail, just the ones after them */
+
+	for (flag=1, new_block=block, p=buffer, i=0; i < frames + pending; ) {
+
+		if (flag) {
+			if (STp->write_type == OS_WRITE_HEADER) {
+				i += skip;
+				p += skip * OS_DATA_SIZE;
+			}
+			else if (new_block < 2990 && new_block+skip+frames+pending >= 2990)
+				new_block = 3000-i;
+			else
+				new_block += skip;
+#if DEBUG
+			printk(OSST_DEB_MSG "osst%d: Position to frame %d, write lblk %d\n",
+						dev, new_block+i, logical_blk_num+i); /* FIXME var blk sz */
+#endif
+			osst_set_frame_position(STp, aSCpnt, new_block + i, 0);
+			osst_wait_ready(STp, aSCpnt, 60);
+			osst_get_frame_position(STp, aSCpnt);
+			SCpnt = * aSCpnt;
+
+			if (new_block > block + 1000) {
+				printk(KERN_ERR "osst%d: Failed to find valid tape media\n", dev);
+				vfree((void *)buffer);
+				return (-EIO);
+			}
+			flag = 0;
+			if ( i >= frames + pending ) break;
+		}
+		osst_copy_to_buffer(STp->buffer, p);
+//		memcpy(STp->buffer->b_data, p, OS_DATA_SIZE);
+		/*
+		 * IMPORTANT: for error recovery to work, _never_ queue frames with mixed frame type!
+		 */
+		osst_init_aux(STp, STp->buffer->aux->frame_type, logical_blk_num+i );
+		memset(cmd, 0, MAX_COMMAND_SIZE);
+		cmd[0] = WRITE_6;
+		cmd[1] = 1;
+		cmd[4] = 1;
+
+#if DEBUG
+		if (debugging)
+			printk(OSST_DEB_MSG "osst%d: About to attempt to write to frame %d\n", dev, new_block+i);
+#endif
+		SCpnt = osst_do_scsi(SCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, MAX_WRITE_RETRIES, TRUE);
+
+		if (STp->buffer->last_result_fatal)
+			flag = 1;
+		else {
+			p += OS_DATA_SIZE; i++;
+
+			/* if we just sent the last frame, wait till all successfully written */
+			if ( i == frames + pending ) {
+#if DEBUG
+				printk(OSST_DEB_MSG "osst%d: Check re-write successful\n", dev);
+#endif
+				memset(cmd, 0, MAX_COMMAND_SIZE);
+				cmd[0] = WRITE_FILEMARKS;
+				cmd[1] = 1;
+				SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES, TRUE);
+
+#if DEBUG
+				if (debugging) {
+					printk(OSST_DEB_MSG "osst%d: Sleeping in re-write wait ready\n", dev);
+					printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev);
+					debugging = 0;
+				}
+#endif
+				flag = STp->buffer->last_result_fatal;
+				while ( !flag && time_before(jiffies, startwait + 60*HZ) ) {
+
+					memset(cmd, 0, MAX_COMMAND_SIZE);
+					cmd[0] = TEST_UNIT_READY;
+
+					SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout,
+									 MAX_READY_RETRIES, TRUE);
+
+					if (SCpnt->sense_buffer[2] == 2 && SCpnt->sense_buffer[12] == 4 &&
+					    (SCpnt->sense_buffer[13] == 1 || SCpnt->sense_buffer[13] == 8)) {
+						/* in the process of becoming ready */
+						schedule_timeout(HZ / 10);
+						continue;
+					}
+					if (STp->buffer->last_result_fatal)
+						flag = 1;
+					break;
+				}
+#if DEBUG
+				debugging = dbg;
+				printk(OSST_DEB_MSG "osst%d: Wait re-write finished\n", dev);
+#endif
+			}
+		}
+		if (flag) {
+			if ((SCpnt->sense_buffer[ 2] & 0x0f) == 13 &&
+			     SCpnt->sense_buffer[12]         ==  0 &&
+			     SCpnt->sense_buffer[13]         ==  2) {
+				printk(KERN_ERR "osst%d: Volume overflow in write error recovery\n", dev);
+				vfree((void *)buffer);
+				return (-EIO);			/* hit end of tape = fail */
+			}
+			i = ((SCpnt->sense_buffer[3] << 24) |
+	        	     (SCpnt->sense_buffer[4] << 16) |
+			     (SCpnt->sense_buffer[5] <<  8) |
+			      SCpnt->sense_buffer[6]        ) - new_block;
+			p = &buffer[i * OS_DATA_SIZE];
+#if 1 //DEBUG
+			printk(OSST_DEB_MSG "osst%d: Additional write error at %d\n", dev, new_block+i);
+#endif
+			osst_get_frame_position(STp, aSCpnt);
+#if DEBUG
+			printk(OSST_DEB_MSG "osst%d: reported frame positions: host = %d, tape = %d\n",
+					  dev, STp->first_frame_position, STp->last_frame_position);
+#endif
+		}
+		*aSCpnt = SCpnt;
+	}    
+	vfree((void *)buffer);
+	return 0;
+}
+
+static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt,
+					unsigned int block, unsigned int skip, int pending)
+{
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+	Scsi_Cmnd     * SCpnt     = * aSCpnt;
+	int		dev       = TAPE_NR(STp->devt);
+	int		attempts  = 1000 / skip;
+	int		flag      = 1;
+	long		startwait = jiffies;
+#if DEBUG
+	int		dbg       = debugging;
+#endif
+
+	while (attempts && time_before(jiffies, startwait + 60*HZ)) {
+		if (flag) {
+#if DEBUG
+			debugging = dbg;
+#endif
+			if (block < 2990 && block+skip+STp->cur_frames+pending >= 2990)
+				block = 3000-skip;
+#if 1 //DEBUG
+			printk(OSST_DEB_MSG "osst%d: Position to frame %d, re-write from lblk %d\n",
+					  dev, block+skip, STp->logical_blk_num-STp->cur_frames-pending);
+#endif
+			osst_set_frame_position(STp, aSCpnt, block + skip, 1);
+			flag = 0;
+			attempts--;
+		}
+		if (osst_get_frame_position(STp, aSCpnt) < 0) {		/* additional write error */
+#if 1 //DEBUG
+			printk(OSST_DEB_MSG "osst%d: Addl error, host %d, tape %d, buffer %d\n",
+					  dev, STp->first_frame_position,
+					  STp->last_frame_position, STp->cur_frames);
+#endif
+			block = STp->last_frame_position;
+			flag = 1;
+			continue;
+		}
+		if (pending && STp->cur_frames < 50) {
+
+			memset(cmd, 0, MAX_COMMAND_SIZE);
+			cmd[0] = WRITE_6;
+			cmd[1] = 1;
+			cmd[4] = 1;
+#if 1 //DEBUG
+			printk(OSST_DEB_MSG "osst%d: About to write pending lblk %d at frame %d\n",
+					  dev, STp->logical_blk_num-1, STp->first_frame_position);
+#endif
+			SCpnt = osst_do_scsi(SCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout,
+									MAX_WRITE_RETRIES, TRUE);
+			*aSCpnt = SCpnt;
+
+			if (STp->buffer->last_result_fatal) {		/* additional write error */
+				if ((SCpnt->sense_buffer[ 2] & 0x0f) == 13 &&
+				     SCpnt->sense_buffer[12]         ==  0 &&
+				     SCpnt->sense_buffer[13]         ==  2) {
+					printk(OSST_DEB_MSG
+					       "osst%d: Volume overflow in write error recovery\n",
+					       dev);
+					break;				/* hit end of tape = fail */
+				}
+				flag = 1;
+			}
+			else
+				pending = 0;
+
+			continue;
+		}
+		if (STp->cur_frames == 0) {
+#if DEBUG
+			debugging = dbg;
+			printk(OSST_DEB_MSG "osst%d: Wait re-write finished\n", dev);
+#endif
+			return 0;
+		}
+#if DEBUG
+		if (debugging) {
+			printk(OSST_DEB_MSG "osst%d: Sleeping in re-write wait ready\n", dev);
+			printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev);
+			debugging = 0;
+		}
+#endif
+		schedule_timeout(HZ / 10);
+	}
+	printk(KERN_ERR "osst%d: Failed to find valid tape media\n", dev);
+#if DEBUG
+	debugging = dbg;
+#endif
+	return (-EIO);
+}
+
+/*
+ * Error recovery algorithm for the OnStream tape.
+ */
+
+static int osst_write_error_recovery(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int pending)
+{
+	Scsi_Cmnd   * SCpnt  = * aSCpnt;
+	ST_partstat * STps   = & STp->ps[STp->partition];
+	int           dev    = TAPE_NR(STp->devt);
+	int           retval = 0;
+	int           rw_state;
+	unsigned int  block, skip;
+
+	rw_state = STps->rw;
+
+	if ((SCpnt->sense_buffer[ 2] & 0x0f) != 3
+	  || SCpnt->sense_buffer[12]         != 12
+	  || SCpnt->sense_buffer[13]         != 0) {
+#if 1 //DEBUG
+		printk(OSST_DEB_MSG "osst%d: Write error recovery cannot handle %02x:%02x:%02x\n",
+			dev, SCpnt->sense_buffer[ 2], SCpnt->sense_buffer[12], SCpnt->sense_buffer[13]);
+#endif
+		return (-EIO);
+	}
+	block =	(SCpnt->sense_buffer[3] << 24) |
+	        (SCpnt->sense_buffer[4] << 16) |
+		(SCpnt->sense_buffer[5] <<  8) |
+		 SCpnt->sense_buffer[6];
+	skip  =  SCpnt->sense_buffer[9];
+ 
+#if 1 //DEBUG
+	printk(OSST_DEB_MSG "osst%d: Detected physical bad block at %u, advised to skip %d\n", dev, block, skip);
+#endif
+	osst_get_frame_position(STp, aSCpnt);
+#if 1 //DEBUG
+	printk(OSST_DEB_MSG "osst%d: reported frame positions: host = %d, tape = %d\n",
+			dev, STp->first_frame_position, STp->last_frame_position);
+#endif
+	switch (STp->write_type) {
+	   case OS_WRITE_DATA:
+	   case OS_WRITE_EOD:
+	   case OS_WRITE_NEW_MARK:
+		printk(KERN_WARNING "osst%d: Relocating %d buffered logical blocks to physical block %u\n",
+			dev, STp->cur_frames, block + skip);
+		if (STp->os_fw_rev >= 10600)
+			retval = osst_reposition_and_retry(STp, aSCpnt, block, skip, pending);
+		else
+			retval = osst_read_back_buffer_and_rewrite(STp, aSCpnt, block, skip, pending);
+		break;
+	   case OS_WRITE_LAST_MARK:
+		printk(KERN_ERR "osst%d: Bad block in update last marker, fatal\n", dev);
+		osst_set_frame_position(STp, aSCpnt, block + STp->cur_frames + pending, 0);
+		retval = -EIO;
+		break;
+	   case OS_WRITE_HEADER:
+		printk(KERN_WARNING "osst%d: Bad block in header partition, skipped\n", dev);
+		retval = osst_read_back_buffer_and_rewrite(STp, aSCpnt, block, 1, pending);
+		break;
+	   default:
+		printk(KERN_WARNING "osst%d: Bad block in filler, ignored\n", dev);
+		osst_set_frame_position(STp, aSCpnt, block + STp->cur_frames + pending, 0);
+	}
+	osst_get_frame_position(STp, aSCpnt);
+#if 1 //DEBUG
+	printk(KERN_ERR "osst%d: Positioning complete, cur_frames %d, pos %d, tape pos %d\n", 
+			dev, STp->cur_frames, STp->first_frame_position, STp->last_frame_position);
+	printk(OSST_DEB_MSG "osst%d: next logical block to write: %d\n", dev, STp->logical_blk_num);
+#endif
+	if (retval == 0) {
+		STp->recover_count++;
+		STp->recover_erreg++;
+	}
+	STps->rw = rw_state;
+	return retval;
+}
+
+static int osst_space_over_filemarks_backward(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int mt_op, int mt_count)
+{
+	int     dev = TAPE_NR(STp->devt);
+	int     cnt;
+	int     last_mark_ppos = -1;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_backwards %d %d\n", dev, mt_op, mt_count);
+#endif
+	if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+		printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_bwd\n", dev);
+		return -EIO;
+	}
+	if (STp->linux_media_version >= 4) {
+		/*
+		 * direct lookup in header filemark list
+		 */
+		cnt = ntohl(STp->buffer->aux->filemark_cnt);
+		if (STp->header_ok                         && 
+		    STp->header_cache != NULL              &&
+		    (cnt - mt_count)  >= 0                 &&
+		    (cnt - mt_count)   < OS_FM_TAB_MAX     &&
+		    (cnt - mt_count)   < STp->filemark_cnt &&
+		    STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] == STp->buffer->aux->last_mark_ppos)
+
+			last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt - mt_count]);
+#if 1 //DEBUG
+		if (STp->header_cache == NULL || (cnt - mt_count) < 0 || (cnt - mt_count) >= OS_FM_TAB_MAX)
+			printk(OSST_DEB_MSG "osst%i: Filemark lookup fail due to %s\n", dev,
+			       STp->header_cache == NULL?"lack of header cache":"count out of range");
+		else
+			printk(OSST_DEB_MSG "osst%i: Filemark lookup: prev mark %d (%s), skip %d to %d\n", dev, cnt,
+			       ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+			        (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] ==
+					 STp->buffer->aux->last_mark_ppos))?"match":"error",
+			       mt_count, last_mark_ppos);
+#endif
+		if (last_mark_ppos > 10 && last_mark_ppos < STp->eod_frame_ppos) {
+			osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0);
+			if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+				printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+				return (-EIO);
+			}
+			if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+				printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n",
+						 dev, last_mark_ppos);
+				return (-EIO);
+			}
+			if (mt_op == MTBSFM) {
+				STp->logical_blk_num++;
+				STp->logical_blk_in_buffer = 0;
+			}
+			return 0;
+		}
+		printk(KERN_INFO "osst%i: Reverting to scan filemark backwards\n", dev);
+	}
+	cnt = 0;
+	while (cnt != mt_count) {
+		last_mark_ppos = ntohl(STp->buffer->aux->last_mark_ppos);
+		if (last_mark_ppos == -1)
+			return (-EIO);
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Positioning to last mark at %d\n", dev, last_mark_ppos);
+#endif
+		osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0);
+		cnt++;
+		if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+			printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+			return (-EIO);
+		}
+		if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+			printk(KERN_INFO "osst%i: expected to find marker at block %d, not found\n", dev, last_mark_ppos);
+			return (-EIO);
+		}
+	}
+	if (mt_op == MTBSFM) {
+		STp->logical_blk_num++;
+		STp->logical_blk_in_buffer = 0;
+	}
+	return 0;
+}
+
+/*
+ * ADRL 1.1 compatible "slow" space filemarks fwd version
+ *
+ * Just scans for the filemark sequentially.
+ */
+static int osst_space_over_filemarks_forward_slow(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int mt_op, int mt_count)
+{
+	int           dev = TAPE_NR(STp->devt);
+	int           cnt = 0;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_forward_slow %d %d\n", dev, mt_op, mt_count);
+#endif
+	if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+		printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_fwd\n", dev);
+		return (-EIO);
+	}
+	while (1) {
+		if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+			printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+			return (-EIO);
+		}
+		if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
+			cnt++;
+		if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if DEBUG
+			printk(OSST_DEB_MSG "osst%i: space_fwd: EOD reached\n", dev);
+#endif
+			return (-EIO);
+		}
+		if (cnt == mt_count)
+			break;
+		STp->logical_blk_in_buffer = 0;
+	}
+	if (mt_op == MTFSF) {
+		STp->logical_blk_num++;
+		STp->logical_blk_in_buffer = 0;
+	}
+	return 0;
+}
+
+/*
+ * Fast linux specific version of OnStream FSF
+ */
+static int osst_space_over_filemarks_forward_fast(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int mt_op, int mt_count)
+{
+	int           dev = TAPE_NR(STp->devt);
+	int           cnt = 0,
+		      next_mark_ppos = -1;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_forward_fast %d %d\n", dev, mt_op, mt_count);
+#endif
+	if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+		printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_fwd\n", dev);
+		return (-EIO);
+	}
+
+	if (STp->linux_media_version >= 4) {
+		/*
+		 * direct lookup in header filemark list
+		 */
+		cnt = ntohl(STp->buffer->aux->filemark_cnt) - 1;
+		if (STp->header_ok                         && 
+		    STp->header_cache != NULL              &&
+		    (cnt + mt_count)   < OS_FM_TAB_MAX     &&
+		    (cnt + mt_count)   < STp->filemark_cnt &&
+		    ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+		     (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] == STp->buffer->aux->last_mark_ppos)))
+
+			next_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt + mt_count]);
+#if 1 //DEBUG
+		if (STp->header_cache == NULL || (cnt + mt_count) >= OS_FM_TAB_MAX)
+			printk(OSST_DEB_MSG "osst%i: Filemark lookup fail due to %s\n", dev,
+			       STp->header_cache == NULL?"lack of header cache":"count out of range");
+		else
+			printk(OSST_DEB_MSG "osst%i: Filemark lookup: prev mark %d (%s), skip %d to %d\n", dev, cnt,
+			       ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+			        (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] ==
+					 STp->buffer->aux->last_mark_ppos))?"match":"error",
+			       mt_count, next_mark_ppos);
+#endif
+		if (next_mark_ppos <= 10 || next_mark_ppos > STp->eod_frame_ppos) {
+			printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev);
+			return osst_space_over_filemarks_forward_slow(STp, aSCpnt, mt_op, mt_count);
+		} else {
+			osst_set_frame_position(STp, aSCpnt, next_mark_ppos, 0);
+			if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+				printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+				return (-EIO);
+			}
+			if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+				printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n",
+						 dev, next_mark_ppos);
+				return (-EIO);
+			}
+		}
+	} else {
+		/*
+		 * Find nearest (usually previous) marker, then jump from marker to marker
+		 */
+		while (1) {
+			if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
+				break;
+			if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if DEBUG
+				printk(OSST_DEB_MSG "osst%i: space_fwd: EOD reached\n", dev);
+#endif
+				return (-EIO);
+			}
+			if (ntohl(STp->buffer->aux->filemark_cnt) == 0) {
+				if (STp->first_mark_ppos == -1) {
+					printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev);
+					return osst_space_over_filemarks_forward_slow(STp, aSCpnt, mt_op, mt_count);
+				}
+				osst_set_frame_position(STp, aSCpnt, STp->first_mark_ppos, 0);
+				if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+					printk(KERN_INFO
+					       "osst%i: Couldn't get logical blk num in space_filemarks_fwd_fast\n",
+					       dev);
+					return (-EIO);
+				}
+				if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+					printk(KERN_INFO "osst%i: Expected to find filemark at %d\n",
+							 dev, STp->first_mark_ppos);
+					return (-EIO);
+				}
+			} else {
+				if (osst_space_over_filemarks_backward(STp, aSCpnt, MTBSF, 1) < 0)
+					return (-EIO);
+				mt_count++;
+			}
+		}
+		cnt++;
+		while (cnt != mt_count) {
+			next_mark_ppos = ntohl(STp->buffer->aux->next_mark_ppos);
+			if (!next_mark_ppos || next_mark_ppos > STp->eod_frame_ppos) {
+				printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev);
+				return osst_space_over_filemarks_forward_slow(STp, aSCpnt, mt_op, mt_count - cnt);
+			}
+#if DEBUG
+			else printk(OSST_DEB_MSG "osst%i: Positioning to next mark at %d\n", dev, next_mark_ppos);
+#endif
+			osst_set_frame_position(STp, aSCpnt, next_mark_ppos, 0);
+			cnt++;
+			if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+				printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+				return (-EIO);
+			}
+			if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+				printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n",
+						 dev, next_mark_ppos);
+				return (-EIO);
+			}
+		}
+	}
+	if (mt_op == MTFSF) 
+		STp->logical_blk_num++;
+		STp->logical_blk_in_buffer = 0;
+	return 0;
+}
+
+/*
+ * In debug mode, we want to see as many errors as possible
+ * to test the error recovery mechanism.
+ */
+#if DEBUG
+static void osst_set_retries(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int retries)
+{
+	unsigned char	cmd[MAX_COMMAND_SIZE];
+	Scsi_Cmnd     * SCpnt  = * aSCpnt;
+	int		dev  = TAPE_NR(STp->devt);
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SELECT;
+	cmd[1] = 0x10;
+	cmd[4] = NUMBER_RETRIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+	(STp->buffer)->b_data[0] = cmd[4] - 1;
+	(STp->buffer)->b_data[1] = 0;			/* Medium Type - ignoring */
+	(STp->buffer)->b_data[2] = 0;			/* Reserved */
+	(STp->buffer)->b_data[3] = 0;			/* Block Descriptor Length */
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = NUMBER_RETRIES_PAGE | (1 << 7);
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 2;
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 4;
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = retries;
+
+	if (debugging)
+	    printk(OSST_DEB_MSG "osst%i: Setting number of retries on OnStream tape to %d\n", dev, retries);
+
+	SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+	*aSCpnt = SCpnt;
+
+	if ((STp->buffer)->last_result_fatal)
+	    printk (KERN_ERR "osst%d: Couldn't set retries to %d\n", dev, retries);
+}
+#endif
+
+#if 0
+static void osst_update_markers(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int last_mark_ppos, int this_mark_ppos)
+{
+	int           dev = TAPE_NR(STp->devt);
+	int	      frame,
+		      reslt;
+
+	if (STp->raw) return;
+
+	STp->last_mark_ppos = this_mark_ppos;
+	if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX) 
+		STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos);
+	if (STp->filemark_cnt++ == 0)
+		STp->first_mark_ppos = this_mark_ppos;
+
+	if (STp->linux_media_version >= 4) return;
+	if (last_mark_ppos == -1)          return;
+
+	STp->write_type = OS_WRITE_LAST_MARK;
+	frame = osst_get_frame_position(STp, aSCpnt);
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Update last_marker at frame %d\n", dev, last_mark_ppos);
+	printk(OSST_DEB_MSG "osst%i: current position %d, lblk %d, tape blk %d\n",
+			  dev, frame, STp->logical_blk_num, STp->last_frame_position);
+#endif
+	osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0);
+	osst_initiate_read (STp, aSCpnt);
+	reslt = osst_read_block(STp, aSCpnt, 180);
+
+	if (reslt) {
+		printk(KERN_WARNING "osst%i: couldn't read last marker\n", dev);
+		osst_set_frame_position(STp, aSCpnt, frame, 0);
+		return;
+	}
+	if (STp->buffer->aux->frame_type  != OS_FRAME_TYPE_MARKER) {
+		printk(KERN_WARNING "osst%i: expected marker at addr %d\n", dev, last_mark_ppos);
+		osst_set_frame_position(STp, aSCpnt, frame, 0);
+		return;
+	}
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: writing back marker\n", dev);
+#endif
+	STp->buffer->aux->next_mark_ppos = htonl(this_mark_ppos);
+	osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0);
+	STp->dirty = 1;
+	if (osst_flush_write_buffer(STp, aSCpnt, 0) ||
+	    osst_flush_drive_buffer(STp, aSCpnt)     ) {
+		printk(KERN_WARNING "osst%i: couldn't write marker back at addr %d\n", dev, last_mark_ppos);
+	}
+	osst_set_frame_position(STp, aSCpnt, frame, 0);	
+
+	return; /* FIXME -- errors should go back to user space */
+}
+#endif
+
+static int osst_write_filemark(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	int	result;
+	int	this_mark_ppos;
+#if DEBUG
+	int	dev = TAPE_NR(STp->devt);
+#endif
+
+	if (STp->raw) return 0;
+
+	STp->write_type = OS_WRITE_NEW_MARK;
+	this_mark_ppos = osst_get_frame_position(STp, aSCpnt);
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Writing Filemark %i at frame %d (lblk %d)\n", 
+	       dev, STp->filemark_cnt, this_mark_ppos, STp->logical_blk_num);
+#endif
+	osst_init_aux(STp, OS_FRAME_TYPE_MARKER, STp->logical_blk_num++);
+	STp->dirty = 1;
+	result  = osst_flush_write_buffer(STp, aSCpnt, 0);
+	result |= osst_flush_drive_buffer(STp, aSCpnt);
+	STp->last_mark_ppos = this_mark_ppos;
+	if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX) 
+		STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos);
+	if (STp->filemark_cnt++ == 0)
+		STp->first_mark_ppos = this_mark_ppos;
+//	osst_update_markers(STp, aSCpnt, STp->last_mark_ppos, this_mark_ppos);
+	return result;
+}
+
+static int osst_write_eod(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	int	result;
+#if DEBUG
+	int	dev = TAPE_NR(STp->devt);
+#endif
+
+	if (STp->raw) return 0;
+
+	STp->write_type = OS_WRITE_EOD;
+	STp->eod_frame_ppos = osst_get_frame_position(STp, aSCpnt);
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Writing EOD at %d=>%d\n", dev, STp->logical_blk_num, STp->eod_frame_ppos);
+#endif
+	osst_init_aux(STp, OS_FRAME_TYPE_EOD, STp->logical_blk_num++);
+	STp->dirty = 1;
+
+	result  = osst_flush_write_buffer(STp, aSCpnt, 0);	
+	result |= osst_flush_drive_buffer(STp, aSCpnt);
+	STp->eod_frame_lfa = --(STp->logical_blk_num);
+	return result;
+}
+
+static int osst_write_filler(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int block, int count)
+{
+	int	      dev = TAPE_NR(STp->devt);
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Reached onstream write filler group %d\n", dev, block);
+#endif
+	osst_wait_ready(STp, aSCpnt, 60 * 5);
+	osst_set_frame_position(STp, aSCpnt, block, 0);
+	STp->write_type = OS_WRITE_FILLER;
+	osst_init_aux(STp, OS_FRAME_TYPE_FILL, 0);
+	while (count--) {
+	    memcpy(STp->buffer->b_data, "Filler", 6);
+	    STp->buffer->buffer_bytes = 6;
+	    STp->dirty = 1;
+	    if (osst_flush_write_buffer(STp, aSCpnt, 0)) {
+		printk(KERN_INFO "osst%i: Couldn't write filler frame\n", dev);
+		return (-EIO);
+	    }
+	}
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Exiting onstream write filler group\n", dev);
+#endif
+	return osst_flush_drive_buffer(STp, aSCpnt);
+}
+
+static int __osst_write_header(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int block, int count)
+{
+	int	dev   = TAPE_NR(STp->devt);
+	int     result;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Reached onstream write header group %d\n", dev, block);
+#endif
+	osst_wait_ready(STp, aSCpnt, 60 * 5);
+	osst_set_frame_position(STp, aSCpnt, block, 0);
+	STp->write_type = OS_WRITE_HEADER;
+	osst_init_aux(STp, OS_FRAME_TYPE_HEADER, STp->logical_blk_num);
+	while (count--) {
+	    osst_copy_to_buffer(STp->buffer, (unsigned char *)STp->header_cache);
+//	    memcpy(STp->buffer->b_data, STp->header_cache, sizeof(os_header_t));
+	    STp->buffer->buffer_bytes = sizeof(os_header_t);
+	    STp->dirty = 1;
+	    if (osst_flush_write_buffer(STp, aSCpnt, 0)) {
+		printk(KERN_INFO "osst%i: Couldn't write header frame\n", dev);
+		return (-EIO);
+	    }
+	}
+	result = osst_flush_drive_buffer(STp, aSCpnt);
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Write onstream header group %s\n", dev, result?"failed":"done");
+#endif
+	return result;
+}
+
+static int osst_write_header(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int locate_eod)
+{
+	os_header_t * header;
+	int	      reslt ;
+	int	      dev   = TAPE_NR(STp->devt);
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Writing tape header\n", dev);
+#endif
+	if (STp->raw) return 0;
+
+	if (STp->header_cache == NULL) {
+		if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) {
+			printk(KERN_ERR "osst%i: Failed to allocate header cache\n", dev);
+			return (-ENOMEM);
+		}
+		memset(STp->header_cache, 0, sizeof(os_header_t));
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%d: Allocated and cleared memory for header cache\n", dev);
+#endif
+	}
+	if (STp->header_ok) STp->update_frame_cntr++;
+	else                STp->update_frame_cntr = 0;
+
+	header = STp->header_cache;
+	strcpy(header->ident_str, "ADR_SEQ");
+	header->major_rev      = 1;
+	header->minor_rev      = 4;
+	header->ext_trk_tb_off = htons(17192);
+	header->pt_par_num     = 1;
+	header->partition[0].partition_num              = OS_DATA_PARTITION;
+	header->partition[0].par_desc_ver               = OS_PARTITION_VERSION;
+	header->partition[0].wrt_pass_cntr              = htons(STp->wrt_pass_cntr);
+	header->partition[0].first_frame_ppos           = htonl(STp->first_data_ppos);
+	header->partition[0].last_frame_ppos            = htonl(STp->capacity);
+	header->partition[0].eod_frame_ppos             = htonl(STp->eod_frame_ppos);
+	header->cfg_col_width                           = htonl(20);
+	header->dat_col_width                           = htonl(1500);
+	header->qfa_col_width                           = htonl(0);
+	header->ext_track_tb.nr_stream_part             = 1;
+	header->ext_track_tb.et_ent_sz                  = 32;
+	header->ext_track_tb.dat_ext_trk_ey.et_part_num = 0;
+	header->ext_track_tb.dat_ext_trk_ey.fmt         = 1;
+	header->ext_track_tb.dat_ext_trk_ey.fm_tab_off  = htons(17736);
+	header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi = 0;
+	header->ext_track_tb.dat_ext_trk_ey.last_hlb    = htonl(STp->eod_frame_lfa);
+	header->ext_track_tb.dat_ext_trk_ey.last_pp	= htonl(STp->eod_frame_ppos);
+	header->dat_fm_tab.fm_part_num                  = 0;
+	header->dat_fm_tab.fm_tab_ent_sz                = 4;
+	header->dat_fm_tab.fm_tab_ent_cnt               = htons(STp->filemark_cnt<OS_FM_TAB_MAX?
+								STp->filemark_cnt:OS_FM_TAB_MAX);
+
+	reslt  = __osst_write_header(STp, aSCpnt, 0xbae, 5);
+	if (STp->update_frame_cntr == 0)
+		   osst_write_filler(STp, aSCpnt, 0xbb3, 5);
+	reslt &= __osst_write_header(STp, aSCpnt,     5, 5);
+
+	if (locate_eod) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: locating back to eod frame addr %d\n", dev, STp->eod_frame_ppos);
+#endif
+		osst_set_frame_position(STp, aSCpnt, STp->eod_frame_ppos, 0);
+	}
+	if (reslt)
+		printk(KERN_WARNING "osst%i: write header failed\n", dev);
+	else {
+		memcpy(STp->application_sig, "LIN4", 4);
+		STp->linux_media         = 1;
+		STp->linux_media_version = 4;
+		STp->header_ok           = 1;
+	}
+	return reslt;
+}
+
+static int osst_reset_header(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	if (STp->header_cache != NULL)
+		memset(STp->header_cache, 0, sizeof(os_header_t));
+
+	STp->logical_blk_num = 0;
+	STp->logical_blk_in_buffer = 0;
+	STp->eod_frame_ppos = STp->first_data_ppos = 0x0000000A;
+	STp->filemark_cnt = 0;
+	STp->first_mark_ppos = STp->last_mark_ppos = -1;
+	return osst_write_header(STp, aSCpnt, 1);
+}
+
+static int __osst_analyze_headers(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int block)
+{
+	int           dev = TAPE_NR(STp->devt);
+	os_header_t * header;
+	os_aux_t    * aux;
+	char          id_string[8];
+	int	      linux_media_version,
+		      update_frame_cntr;
+
+	if (STp->raw)
+		return 1;
+
+	if (block == 5 || block == 0xbae || STp->buffer->last_result_fatal) {
+		if (osst_set_frame_position(STp, aSCpnt, block, 0))
+			printk(KERN_WARNING "osst%i: Couldn't position tape\n", dev);
+		if (osst_initiate_read (STp, aSCpnt)) {
+			printk(KERN_WARNING "osst%i: Couldn't initiate read\n", dev);
+			return 0;
+		}
+	}
+	if (osst_read_block(STp, aSCpnt, 180)) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Couldn't read header frame\n", dev);
+#endif
+		return 0;
+	}
+	header = (os_header_t *) STp->buffer->b_data;	/* warning: only first segment addressable */
+	aux = STp->buffer->aux;
+	if (aux->frame_type != OS_FRAME_TYPE_HEADER) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Skipping non-header frame (%d)\n", dev, block);
+#endif
+		return 0;
+	}
+	if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0 &&
+	    strncmp(header->ident_str, "ADR-SEQ", 7) != 0) {
+                strncpy(id_string, header->ident_str, 7);
+		id_string[7] = 0;
+		printk(KERN_INFO "osst%i: Invalid header identification string %s\n", dev, id_string);
+		return 0;
+	}
+	update_frame_cntr = ntohl(aux->update_frame_cntr);
+	if (update_frame_cntr < STp->update_frame_cntr) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Skipping frame %d with update_frame_counter %d<%d\n",
+				   dev, block, update_frame_cntr, STp->update_frame_cntr);
+#endif
+		return 0;
+	}
+	if (header->major_rev != 1 || header->minor_rev != 4 ) {
+		printk(KERN_INFO "osst%i: %s revision %d.%d detected (1.4 supported)\n", 
+				 dev, (header->major_rev != 1 || header->minor_rev < 2 || 
+				       header->minor_rev  > 4 )? "Invalid" : "Warning:",
+				 header->major_rev, header->minor_rev);
+		if (header->major_rev != 1 || header->minor_rev < 2 || header->minor_rev > 4)
+			return 0;
+	}
+	if (header->pt_par_num != 1)
+		printk(KERN_INFO "osst%i: Warning: %d partitions defined, only one supported\n", 
+				 dev, header->pt_par_num);
+	memcpy(id_string, aux->application_sig, 4);
+	id_string[4] = 0;
+	if (memcmp(id_string, "LIN", 3) == 0) {
+		STp->linux_media = 1;
+		linux_media_version = id_string[3] - '0';
+		if (linux_media_version != 4)
+			printk(KERN_INFO "osst%i: Linux media version %d detected (current 4)\n",
+					 dev, linux_media_version);
+	} else {
+		printk(KERN_WARNING "osst%i: non Linux media detected (%s)\n", dev, id_string);
+		return 0;
+	}
+	if (linux_media_version < STp->linux_media_version) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Skipping frame %d with linux_media_version %d\n",
+				  dev, block, linux_media_version);
+#endif
+		return 0;
+	}
+	if (linux_media_version > STp->linux_media_version) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Frame %d sets linux_media_version to %d\n",
+				   dev, block, linux_media_version);
+#endif
+		memcpy(STp->application_sig, id_string, 5);
+		STp->linux_media_version = linux_media_version;
+		STp->update_frame_cntr = -1;
+	}
+	if (update_frame_cntr > STp->update_frame_cntr) {
+#if DEBUG
+		printk(OSST_DEB_MSG "osst%i: Frame %d sets update_frame_counter to %d\n",
+				   dev, block, update_frame_cntr);
+#endif
+		if (STp->header_cache == NULL) {
+			if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) {
+				printk(KERN_ERR "osst%i: Failed to allocate header cache\n", dev);
+				return 0;
+			}
+#if DEBUG
+			printk(OSST_DEB_MSG "osst%d: Allocated memory for header cache\n", dev);
+#endif
+		}
+		osst_copy_from_buffer(STp->buffer, (unsigned char *)STp->header_cache);
+		header = STp->header_cache;	/* further accesses from cached (full) copy */
+
+		STp->wrt_pass_cntr     = ntohs(header->partition[0].wrt_pass_cntr);
+		STp->first_data_ppos   = ntohl(header->partition[0].first_frame_ppos);
+		STp->eod_frame_ppos    = ntohl(header->partition[0].eod_frame_ppos);
+		STp->eod_frame_lfa     = ntohl(header->ext_track_tb.dat_ext_trk_ey.last_hlb);
+		STp->filemark_cnt      = ntohl(aux->filemark_cnt);
+		STp->first_mark_ppos   = ntohl(aux->next_mark_ppos);
+		STp->last_mark_ppos    = ntohl(aux->last_mark_ppos);
+		STp->update_frame_cntr = update_frame_cntr;
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: detected write pass %d, update frame counter %d, filemark counter %d\n",
+			  dev, STp->wrt_pass_cntr, STp->update_frame_cntr, STp->filemark_cnt);
+	printk(OSST_DEB_MSG "osst%i: first data frame on tape = %d, last = %d, eod frame = %d\n", dev,
+			  STp->first_data_ppos,
+			  ntohl(header->partition[0].last_frame_ppos),
+			  ntohl(header->partition[0].eod_frame_ppos));
+	printk(OSST_DEB_MSG "osst%i: first mark on tape = %d, last = %d, eod frame = %d\n", 
+			  dev, STp->first_mark_ppos, STp->last_mark_ppos, STp->eod_frame_ppos);
+#endif
+		if (header->minor_rev < 4 && STp->linux_media_version == 4) {
+			printk(OSST_DEB_MSG "osst%i: Moving filemark list to ADR 1.4 location\n", dev);
+			memcpy((void *)header->dat_fm_tab.fm_tab_ent, 
+			       (void *)header->old_filemark_list, sizeof(header->dat_fm_tab.fm_tab_ent));
+			memset((void *)header->old_filemark_list, 0, sizeof(header->old_filemark_list));
+		}
+		if (header->minor_rev == 4   &&
+		    (header->ext_trk_tb_off                          != htons(17192)               ||
+		     header->partition[0].partition_num              != OS_DATA_PARTITION          ||
+		     header->partition[0].par_desc_ver               != OS_PARTITION_VERSION       ||
+		     header->partition[0].last_frame_ppos            != htonl(STp->capacity)       ||
+		     header->cfg_col_width                           != htonl(20)                  ||
+		     header->dat_col_width                           != htonl(1500)                ||
+		     header->qfa_col_width                           != htonl(0)                   ||
+		     header->ext_track_tb.nr_stream_part             != 1                          ||
+		     header->ext_track_tb.et_ent_sz                  != 32                         ||
+		     header->ext_track_tb.dat_ext_trk_ey.et_part_num != OS_DATA_PARTITION          ||
+		     header->ext_track_tb.dat_ext_trk_ey.fmt         != 1                          ||
+		     header->ext_track_tb.dat_ext_trk_ey.fm_tab_off  != htons(17736)               ||
+		     header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi != 0                          ||
+		     header->ext_track_tb.dat_ext_trk_ey.last_pp     != htonl(STp->eod_frame_ppos) ||
+		     header->dat_fm_tab.fm_part_num                  != OS_DATA_PARTITION          ||
+		     header->dat_fm_tab.fm_tab_ent_sz                != 4                          ||
+		     header->dat_fm_tab.fm_tab_ent_cnt               !=
+			     htons(STp->filemark_cnt<OS_FM_TAB_MAX?STp->filemark_cnt:OS_FM_TAB_MAX)))
+			printk(KERN_WARNING "osst%i: Failed consistency check ADR 1.4 format\n", dev);
+
+//		memcpy(STp->header_cache, header, sizeof(os_header_t));
+	}
+
+	return 1;
+}
+
+static int osst_analyze_headers(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	int position, block;
+	int first, last;
+	int valid = 0;
+	int dev = TAPE_NR(STp->devt);
+
+        position = osst_get_frame_position(STp, aSCpnt);
+
+	if (STp->raw) {
+		STp->header_ok = STp->linux_media = 1;
+		STp->linux_media_version = 0;
+		return 1;
+	}
+	STp->header_ok = STp->linux_media = STp->linux_media_version = 0;
+	STp->wrt_pass_cntr = STp->update_frame_cntr = -1;
+	STp->eod_frame_ppos = STp->first_data_ppos = -1;
+	STp->first_mark_ppos = STp->last_mark_ppos = -1;
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Reading header\n", dev);
+#endif
+
+	/* optimization for speed - if we are positioned at block 10, read second group first */	
+	/* TODO try the ADR 1.1 locations for the second group if we have no valid one yet... */
+
+	first = position==10?0xbae: 5;
+	last  = position==10?0xbb3:10;
+
+	for (block = first; block < last; block++)
+		if (__osst_analyze_headers(STp, aSCpnt, block))
+			valid = 1;
+
+	first = position==10? 5:0xbae;
+	last  = position==10?10:0xbb3;
+
+	for (block = first; block < last; block++)
+		if (__osst_analyze_headers(STp, aSCpnt, block))
+			valid = 1;
+
+	if (!valid) {
+		printk(KERN_ERR "osst%i: Failed to find valid ADRL header, new media?\n", dev);
+		STp->eod_frame_ppos = STp->first_data_ppos = 0;
+		osst_set_frame_position(STp, aSCpnt, 10, 0);
+		return 0;
+	}
+	if (position <= STp->first_data_ppos) {
+		position = STp->first_data_ppos;
+		STp->ps[0].drv_file = STp->ps[0].drv_block = STp->logical_blk_num = 0;
+	}
+	osst_set_frame_position(STp, aSCpnt, position, 0);
+	STp->header_ok = 1;
+
+	return 1;
+}
+
+static int osst_verify_position(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	int frame_position  = STp->first_frame_position;
+	int logical_blk_num = STp->logical_blk_num;
+	int prev_mark_ppos  = -1;
+	int actual_mark_ppos, i, n;
+#if 1 //DEBUG
+	int dev = TAPE_NR(STp->devt);
+
+	printk(OSST_DEB_MSG "osst%i: Verify that the tape is really the one we think before writing\n", dev);
+#endif
+	osst_set_frame_position(STp, aSCpnt, frame_position - 1, 0);
+	if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) {
+#if 1 //DEBUG
+		printk(OSST_DEB_MSG "osst%i: Couldn't get logical blk num in verify_position\n", dev);
+#endif
+		return (-EIO);
+	}
+	if (STp->linux_media_version >= 4) {
+		for (i=0; i<STp->filemark_cnt; i++)
+			if ((n=ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) < frame_position)
+				prev_mark_ppos = n;
+	} else
+		prev_mark_ppos = frame_position - 1;  /* usually - we don't really know */
+	actual_mark_ppos = STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER ?
+				frame_position - 1 : ntohl(STp->buffer->aux->last_mark_ppos);
+	if (frame_position  != STp->first_frame_position ||
+	    logical_blk_num != STp->logical_blk_num + 1  ||
+	    prev_mark_ppos  != actual_mark_ppos           ) {
+#if 1 //DEBUG
+		printk(OSST_DEB_MSG "osst%i: Block mismatch: frame %d-%d, lblk %d-%d, mark %d-%d\n", dev,
+				  STp->first_frame_position, frame_position, STp->logical_blk_num + 1,
+				  logical_blk_num, actual_mark_ppos, prev_mark_ppos);
+#endif
+		return (-EIO);
+	}
+	STp->logical_blk_in_buffer = 0;
+	STp->logical_blk_num = logical_blk_num;
+	return 0;
+}
+
+/* Acc. to OnStream, the vers. numbering is the following:
+ * X.XX for released versions (X=digit), 
+ * XXXY for unreleased versions (Y=letter)
+ * Ordering 1.05 < 106A < 106a < 106B < ... < 1.06
+ * This fn makes monoton numbers out of this scheme ...
+ */
+static unsigned int osst_parse_firmware_rev (const char * str)
+{
+	unsigned int rev;
+	if (str[1] == '.') {
+		rev = (str[0]-0x30)*10000
+			+(str[2]-0x30)*1000
+			+(str[3]-0x30)*100;
+	} else {
+		rev = (str[0]-0x30)*10000
+			+(str[1]-0x30)*1000
+			+(str[2]-0x30)*100 - 100;
+		rev += 2*(str[3] & 0x1f)
+			+(str[3] >= 0x60? 1: 0);
+	}
+	return rev;
+}
+
+/*
+ * Configure the OnStream SCII tape drive for default operation
+ */
+static int osst_configure_onstream(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt)
+{
+	int                            dev   = TAPE_NR(STp->devt);
+	unsigned char                  cmd[MAX_COMMAND_SIZE];
+        Scsi_Cmnd                    * SCpnt = * aSCpnt;
+        osst_mode_parameter_header_t * header;
+        osst_block_size_page_t       * bs;
+	osst_capabilities_page_t     * cp;
+	osst_tape_paramtr_page_t     * prm;
+	int                            drive_buffer_size;
+
+	if (STp->ready != ST_READY) {
+#if DEBUG
+	    printk(OSST_DEB_MSG "osst%i: Not Ready\n", dev);
+#endif
+	    return (-EIO);
+	}
+	
+	if (STp->os_fw_rev < 10600) {
+            printk("osst%i: Old OnStream firmware revision detected (%s)\n", 
+                       dev, STp->device->rev);
+            printk("osst%i: An upgrade to version 1.06 or above is recommended\n",
+                       dev);
+        }
+
+        /*
+         * Configure 32.5KB (data+aux) frame size.
+         * Get the current block size from the block size mode page
+         */
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SENSE;
+	cmd[1] = 8;
+	cmd[2] = BLOCK_SIZE_PAGE;
+	cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+	SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+	if (SCpnt == NULL) {
+#if DEBUG
+ 	    printk(OSST_DEB_MSG "osst: Busy\n");
+#endif
+	    return (-EBUSY);
+	}
+	*aSCpnt = SCpnt;
+	if ((STp->buffer)->last_result_fatal != 0) {
+	    printk (KERN_ERR "osst%i: Can't get tape block size mode page\n", dev);
+	    return (-EIO);
+	}
+
+        header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+        bs = (osst_block_size_page_t *) ((STp->buffer)->b_data + sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+#if DEBUG
+        printk(KERN_INFO "osst%i: 32KB play back: %s\n",   dev, bs->play32     ? "Yes" : "No");
+        printk(KERN_INFO "osst%i: 32.5KB play back: %s\n", dev, bs->play32_5   ? "Yes" : "No");
+        printk(KERN_INFO "osst%i: 32KB record: %s\n",      dev, bs->record32   ? "Yes" : "No");
+        printk(KERN_INFO "osst%i: 32.5KB record: %s\n",    dev, bs->record32_5 ? "Yes" : "No");
+#endif
+
+        /*
+         * Configure default auto columns mode, 32.5KB transfer mode
+         */ 
+        bs->one = 1;
+        bs->play32 = 0;
+        bs->play32_5 = 1;
+        bs->record32 = 0;
+        bs->record32_5 = 1;
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SELECT;
+	cmd[1] = 0x10;
+	cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+	SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+	*aSCpnt = SCpnt;
+        if ((STp->buffer)->last_result_fatal != 0) {
+            printk (KERN_ERR "osst%i: Couldn't set tape block size mode page\n", dev);
+	    return (-EIO);
+	}
+
+	STp->block_size = (STp->raw) ? OS_FRAME_SIZE : OS_DATA_SIZE;
+	STp->min_block  = OS_FRAME_SIZE; /* FIXME */
+	STp->max_block  = STp->block_size;
+
+#if DEBUG
+        printk(KERN_INFO "osst%i: Block Size changed to 32.5K\n", dev);
+         /*
+         * In debug mode, we want to see as many errors as possible
+         * to test the error recovery mechanism.
+         */
+        osst_set_retries(STp, aSCpnt, 0);
+	SCpnt = * aSCpnt;
+#endif
+
+        /*
+         * Set vendor name to 'LIN4' for "Linux support version 4".
+         */
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SELECT;
+	cmd[1] = 0x10;
+	cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+	header->mode_data_length = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH - 1;
+	header->medium_type      = 0;	/* Medium Type - ignoring */
+	header->dsp              = 0;	/* Reserved */
+	header->bdl              = 0;	/* Block Descriptor Length */
+	
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = VENDOR_IDENT_PAGE | (1 << 7);
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 6;
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 'L';
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 'I';
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 4] = 'N';
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 5] = '4';
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 6] = 0;
+	(STp->buffer)->b_data[MODE_HEADER_LENGTH + 7] = 0;
+
+	SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+	*aSCpnt = SCpnt;
+
+        if ((STp->buffer)->last_result_fatal != 0) {
+            printk (KERN_ERR "osst%i: Couldn't set vendor name to %s\n", dev, 
+			(char *) ((STp->buffer)->b_data + MODE_HEADER_LENGTH + 2));
+	    return (-EIO);
+	}
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SENSE;
+	cmd[1] = 8;
+	cmd[2] = CAPABILITIES_PAGE;
+	cmd[4] = CAPABILITIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+	SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+	*aSCpnt = SCpnt;
+
+	if ((STp->buffer)->last_result_fatal != 0) {
+	    printk (KERN_ERR "osst%i: can't get capabilities page\n", dev);
+	    return (-EIO);
+	}
+
+        header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+	cp     = (osst_capabilities_page_t    *) ((STp->buffer)->b_data +
+		 sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+	drive_buffer_size = ntohs(cp->buffer_size) / 2;
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SENSE;
+	cmd[1] = 8;
+	cmd[2] = TAPE_PARAMTR_PAGE;
+	cmd[4] = TAPE_PARAMTR_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+	SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+	*aSCpnt = SCpnt;
+
+	if ((STp->buffer)->last_result_fatal != 0) {
+	    printk (KERN_ERR "osst%i: can't get tape parameter page\n", dev);
+	    return (-EIO);
+	}
+
+        header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+	prm    = (osst_tape_paramtr_page_t    *) ((STp->buffer)->b_data +
+		 sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+	STp->density  = prm->density;
+	STp->capacity = ntohs(prm->segtrk) * ntohs(prm->trks);
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Density %d, tape length: %dMB, drive buffer size: %dKB\n",
+			  dev, STp->density, STp->capacity / 32, drive_buffer_size);
+#endif
+
+	return 0;
+	
+}
+
+
+/* Step over EOF if it has been inadvertently crossed (ioctl not used because
+   it messes up the block number). */
+static int cross_eof(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int forward)
+{
+	int         result;
+	int         dev   = TAPE_NR(STp->devt);
+
+#if DEBUG
+	if (debugging)
+		printk(OSST_DEB_MSG "osst%d: Stepping over filemark %s.\n",
+	   			  dev, forward ? "forward" : "backward");
+#endif
+
+	if (forward) {
+	   /* assumes that the filemark is already read by the drive, so this is low cost */
+	   result = osst_space_over_filemarks_forward_slow(STp, aSCpnt, MTFSF, 1);
+	}
+	else
+	   /* assumes this is only called if we just read the filemark! */
+	   result = osst_seek_logical_blk(STp, aSCpnt, STp->logical_blk_num - 1);
+
+	if (result < 0)
+	   printk(KERN_ERR "osst%d: Stepping over filemark %s failed.\n",
+				dev, forward ? "forward" : "backward");
+
+	return result;
+}
+
+
+/* Get the tape position. */
+
+static int osst_get_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt)
+{
+    int result = 0;
+    unsigned char scmd[MAX_COMMAND_SIZE];
+    Scsi_Cmnd   * SCpnt;
+
+    /* KG: We want to be able to use it for checking Write Buffer availability
+     *  and thus don't want to risk to overwrite anything. Exchange buffers ... */
+    char mybuf[24];
+    char * olddata = STp->buffer->b_data;
+    int oldsize = STp->buffer->buffer_size;
+    int dev = TAPE_NR(STp->devt);
+
+    if (STp->ready != ST_READY)
+      return (-EIO);
+
+    memset (scmd, 0, MAX_COMMAND_SIZE);
+    scmd[0] = READ_POSITION;
+
+    STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+    SCpnt = osst_do_scsi(*aSCpnt, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE);
+    if (!SCpnt) {
+	    STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+	    return (-EBUSY);
+    }
+    *aSCpnt = SCpnt;
+
+    if (STp->buffer->last_result_fatal)
+      result = ((SCpnt->sense_buffer[2] & 0x0f) == 3) ? -EIO : -EINVAL;
+
+    if (result == -EINVAL)
+      printk(KERN_ERR "osst%d: Can't read tape position.\n", dev);
+    else {
+
+      if (result == -EIO) {	/* re-read position */
+	memset (scmd, 0, MAX_COMMAND_SIZE);
+	scmd[0] = READ_POSITION;
+	STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+	SCpnt = osst_do_scsi(SCpnt, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE);
+      }
+      STp->first_frame_position = ((STp->buffer)->b_data[4] << 24)
+				+ ((STp->buffer)->b_data[5] << 16)
+				+ ((STp->buffer)->b_data[6] << 8)
+				+  (STp->buffer)->b_data[7];
+      STp->last_frame_position  = ((STp->buffer)->b_data[ 8] << 24)
+				+ ((STp->buffer)->b_data[ 9] << 16)
+				+ ((STp->buffer)->b_data[10] <<  8)
+				+  (STp->buffer)->b_data[11];
+      STp->cur_frames           =  (STp->buffer)->b_data[15];
+#if DEBUG
+      if (debugging) {
+	printk(OSST_DEB_MSG "osst%d: Drive Positions: host %d, tape %d%s, buffer %d\n", dev,
+			   STp->first_frame_position, STp->last_frame_position,
+			   ((STp->buffer)->b_data[0]&0x80)?" (BOP)":
+			   ((STp->buffer)->b_data[0]&0x40)?" (EOP)":"",
+			   STp->cur_frames);
+      }
+#endif
+      if (STp->cur_frames == 0 && STp->first_frame_position != STp->last_frame_position) {
+#if DEBUG
+	  printk(KERN_WARNING "osst%d: Correcting read position %d, %d, %d\n", dev,
+				STp->first_frame_position, STp->last_frame_position, STp->cur_frames);
+#endif
+	  STp->first_frame_position = STp->last_frame_position;
+      }
+    }
+    STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+
+    return (result == 0 ? STp->first_frame_position : result);
+}
+
+
+/* Set the tape block */
+static int osst_set_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int block, int skip)
+{
+    ST_partstat *STps;
+    int result = 0;
+    int timeout;
+    unsigned char scmd[MAX_COMMAND_SIZE];
+    Scsi_Cmnd   * SCpnt;
+    int dev = TAPE_NR(STp->devt);
+
+    if (STp->ready != ST_READY)
+      return (-EIO);
+    timeout = STp->long_timeout;
+    STps = &(STp->ps[STp->partition]);
+
+    if (block < 0 || block > STp->capacity) {
+      printk(KERN_ERR "osst%d: Reposition request %d out of range\n", dev, block);
+      block = block < 0 ? 0 : (STp->capacity - 1);
+      result = (-EINVAL);
+    }
+#if DEBUG
+    if (debugging)
+      printk(OSST_DEB_MSG "osst%d: Setting block to %d.\n", dev, block);
+#endif
+    memset (scmd, 0, MAX_COMMAND_SIZE);
+    scmd[0] = SEEK_10;
+    scmd[1] = 1;
+    scmd[3] = (block >> 24);
+    scmd[4] = (block >> 16);
+    scmd[5] = (block >> 8);
+    scmd[6] = block;
+    if (skip)
+      scmd[9] = 0x80;
+
+    SCpnt = osst_do_scsi(*aSCpnt, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE);
+    if (!SCpnt) {
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: Busy\n", dev);
+#endif
+      return (-EBUSY);
+    }
+    *aSCpnt  = SCpnt;
+
+    STp->first_frame_position = STp->last_frame_position = block;
+    STps->eof = ST_NOEOF;
+    if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: SEEK command failed.\n", dev);
+#endif
+      result = (-EIO);
+    }
+    STps->at_sm = 0;
+    STps->rw = ST_IDLE;
+    STp->logical_blk_in_buffer = 0;
+    return result;
+}
+
+
+
+/* osst versions of st functions - augmented and stripped to suit OnStream only */
+
+/* Flush the write buffer (never need to write if variable blocksize). */
+static int osst_flush_write_buffer(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int file_blk)
+{
+  int offset, transfer, blks = 0;
+  int result = 0;
+  unsigned char cmd[MAX_COMMAND_SIZE];
+  Scsi_Cmnd * SCpnt = *aSCpnt;
+  ST_partstat * STps;
+  int dev = TAPE_NR(STp->devt);
+
+  if ((STp->buffer)->writing) {
+    if (SCpnt == (STp->buffer)->last_SCpnt)
+#if DEBUG
+    { printk(OSST_DEB_MSG
+	 "osst%d: aSCpnt points to Scsi_Cmnd that write_behind_check will release -- cleared\n", dev);
+#endif
+      *aSCpnt = SCpnt = NULL;
+#if DEBUG
+    } else if (SCpnt)
+      printk(OSST_DEB_MSG
+	 "osst%d: aSCpnt does not point to Scsi_Cmnd that write_behind_check will release -- strange\n", dev);
+#endif	
+    osst_write_behind_check(STp);
+    if ((STp->buffer)->last_result_fatal) {
+#if DEBUG
+      if (debugging)
+	printk(OSST_DEB_MSG "osst%d: Async write error (flush) %x.\n",
+	       dev, (STp->buffer)->last_result);
+#endif
+      if ((STp->buffer)->last_result == INT_MAX)
+	return (-ENOSPC);
+      return (-EIO);
+    }
+  }
+
+  result = 0;
+  if (STp->dirty == 1) {
+
+    offset   = STp->buffer->buffer_bytes;
+    transfer = OS_FRAME_SIZE;
+    blks     = 1;
+    
+#if DEBUG
+    if (debugging)
+      printk(OSST_DEB_MSG "osst%d: Flushing %d bytes, Tranfering %d bytes in %d blocks.\n",
+			 dev, offset, transfer, blks);
+#endif
+    if (offset < OS_DATA_SIZE)
+      osst_zero_buffer_tail(STp->buffer);
+
+    /* TODO: Error handling! */
+    if (STp->poll)
+	result = osst_wait_frame (STp, aSCpnt, STp->first_frame_position, -50, 120);
+
+    memset(cmd, 0, MAX_COMMAND_SIZE);
+    cmd[0] = WRITE_6;
+    cmd[1] = 1;
+    cmd[4] = blks;
+
+    SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, TRUE);
+    *aSCpnt = SCpnt;
+    if (!SCpnt)
+      return (-EBUSY);
+
+    STps = &(STp->ps[STp->partition]);
+    if ((STp->buffer)->last_result_fatal != 0) {
+	printk(OSST_DEB_MSG "osst%d: write sense [0]=0x%02x [2]=%02x [12]=%02x [13]=%02x\n", TAPE_NR(STp->devt),
+		SCpnt->sense_buffer[0], SCpnt->sense_buffer[2], SCpnt->sense_buffer[12], SCpnt->sense_buffer[13]);
+      if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	  (SCpnt->sense_buffer[2] & 0x40) &&			/* FIXME - SC-30 drive doesn't assert EOM bit */
+	  (SCpnt->sense_buffer[2] & 0x0f) == NO_SENSE) {
+	STp->dirty = 0;
+	(STp->buffer)->buffer_bytes = 0;
+	result = (-ENOSPC);
+      }
+      else {
+	if (osst_write_error_recovery(STp, aSCpnt, 1)) {
+	  printk(KERN_ERR "osst%d: Error on flush.\n", dev);
+	  result = (-EIO);
+        }
+      }
+      STps->drv_block = (-1);
+    }
+    else {
+      STp->first_frame_position++;
+      if (file_blk && STps->drv_block >= 0)
+	STps->drv_block += blks;
+      STp->first_frame_position += blks;
+      STp->dirty = 0;
+      (STp->buffer)->buffer_bytes = 0;
+    }
+  }
+#if DEBUG
+  printk(OSST_DEB_MSG "osst%d: Exit flush write buffer with code %d\n", dev, result);
+#endif
+  return result;
+}
+
+
+/* Flush the tape buffer. The tape will be positioned correctly unless
+   seek_next is true. */
+static int osst_flush_buffer(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int seek_next)
+{
+  int backspace, result;
+  OSST_buffer    * STbuffer;
+  ST_partstat  * STps;
+#if DEBUG
+  int dev = TAPE_NR(STp->devt);
+#endif
+
+  STbuffer = STp->buffer;
+
+  /*
+   * If there was a bus reset, block further access
+   * to this device.
+   */
+  if( STp->device->was_reset )
+    return (-EIO);
+
+  if (STp->ready != ST_READY)
+    return 0;
+
+  STps = &(STp->ps[STp->partition]);
+  if (STps->rw == ST_WRITING)  /* Writing */
+    return osst_flush_write_buffer(STp, aSCpnt, 1);
+
+  if (STp->block_size == 0)
+    return 0;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%i: Reached flush (read) buffer\n", dev);
+#endif
+  backspace = ((STp->buffer)->buffer_bytes +
+    (STp->buffer)->read_pointer) / STp->block_size -
+      ((STp->buffer)->read_pointer + STp->block_size - 1) /
+	STp->block_size;
+  (STp->buffer)->buffer_bytes = 0;
+  (STp->buffer)->read_pointer = 0;
+  result = 0;
+  if (!seek_next) {
+    if (STps->eof == ST_FM_HIT) {
+      result = cross_eof(STp, aSCpnt, FALSE); /* Back over the EOF hit */
+      if (!result)
+	  STps->eof = ST_NOEOF;
+      else {
+	  if (STps->drv_file >= 0)
+	      STps->drv_file++;
+	  STps->drv_block = 0;
+      }
+    }
+    if (!result && backspace > 0)	/* TODO -- design and run a test case for this */
+      result = osst_seek_logical_blk(STp, aSCpnt, STp->logical_blk_num - backspace);
+  }
+  else if (STps->eof == ST_FM_HIT) {
+    if (STps->drv_file >= 0)
+	STps->drv_file++;
+    STps->drv_block = 0;
+    STps->eof = ST_NOEOF;
+  }
+
+  return result;
+
+}
+
+
+/* Entry points to osst */
+
+/* Write command */
+static ssize_t osst_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
+{
+    struct inode *inode = filp->f_dentry->d_inode;
+    ssize_t total;
+    ssize_t i, do_count, blks, retval, transfer;
+    int write_threshold;
+    int doing_write = 0;
+    unsigned char cmd[MAX_COMMAND_SIZE];
+    const char *b_point;
+    Scsi_Cmnd * SCpnt = NULL;
+    OS_Scsi_Tape * STp;
+    ST_mode * STm;
+    ST_partstat * STps;
+    int dev = TAPE_NR(inode->i_rdev);
+
+    STp = os_scsi_tapes[dev];
+
+    /*
+     * If we are in the middle of error recovery, don't let anyone
+     * else try and use this device.  Also, if error recovery fails, it
+     * may try and take the device offline, in which case all further
+     * access to the device is prohibited.
+     */
+    if( !scsi_block_when_processing_errors(STp->device) ) {
+        return -ENXIO;
+    }
+    
+    if (ppos != &filp->f_pos) {
+      /* "A request was outside the capabilities of the device." */
+      return -ENXIO;
+    }
+
+    if (STp->ready != ST_READY) {
+      if (STp->ready == ST_NO_TAPE)
+	return (-ENOMEDIUM);
+      else
+        return (-EIO);
+    }
+    STm = &(STp->modes[STp->current_mode]);
+    if (!STm->defined)
+      return (-ENXIO);
+    if (count == 0)
+      return 0;
+
+    /*
+     * If there was a bus reset, block further access
+     * to this device.
+     */
+    if( STp->device->was_reset )
+      return (-EIO);
+
+#if DEBUG
+    if (!STp->in_use) {
+      printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev);
+      return (-EIO);
+    }
+#endif
+
+    if (STp->write_prot)
+      return (-EACCES);
+
+    /* Write must be integral number of blocks */
+    if (STp->block_size != 0 && (count % STp->block_size) != 0) {
+	printk(KERN_WARNING "osst%d: Write (%d bytes) not multiple of tape block size (32k).\n",
+	       dev, count);
+	return (-EIO);
+    }
+
+    if (STp->first_frame_position >= STp->capacity - 164) {
+	printk(KERN_WARNING "osst%d: Write truncated at EOM early warning (frame %d).\n",
+	       dev, STp->first_frame_position);
+	return (-ENOSPC);
+    }
+
+    STps = &(STp->ps[STp->partition]);
+
+    if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+       !osst_int_ioctl(STp, &SCpnt, MTLOCK, 0))
+      STp->door_locked = ST_LOCKED_AUTO;
+
+    if (STps->rw == ST_READING) {
+      retval = osst_flush_buffer(STp, &SCpnt, 0);
+      if (retval) {
+	if (SCpnt) scsi_release_command(SCpnt);
+	return retval;
+      }
+      STps->rw = ST_IDLE;
+    }
+    else if (STps->rw != ST_WRITING) {
+      if (!STp->header_ok || (STps->drv_file == 0 && STps->drv_block == 0)) {
+	STp->wrt_pass_cntr++;
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Allocating next write pass counter: %d\n",
+				  dev, STp->wrt_pass_cntr);
+#endif
+	osst_reset_header(STp, &SCpnt);
+	STps->drv_file = STps->drv_block = STp->logical_blk_num = 0;
+      } else if (STp->fast_open && osst_verify_position(STp, &SCpnt)) {
+	if (SCpnt) scsi_release_command(SCpnt);
+	return (-EIO);
+      }
+      STp->fast_open = FALSE;
+    }
+    if (!STp->header_ok) {
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: Write cannot proceed without valid headers\n", dev);
+#endif
+      if (SCpnt) scsi_release_command(SCpnt);
+      return (-EIO);
+    }
+    if ((STp->buffer)->writing) {
+if (SCpnt) printk(KERN_ERR "osst%d: Not supposed to have SCpnt at line %d\n", dev, __LINE__);
+      osst_write_behind_check(STp);
+      if ((STp->buffer)->last_result_fatal) {
+#if DEBUG
+	if (debugging)
+	  printk(OSST_DEB_MSG "osst%d: Async write error (write) %x.\n", dev,
+		 (STp->buffer)->last_result);
+#endif
+	if ((STp->buffer)->last_result == INT_MAX)
+	  STps->eof = ST_EOM_OK;
+	else
+	  STps->eof = ST_EOM_ERROR;
+      }
+    }
+    if (SCpnt) scsi_release_command(SCpnt);
+    SCpnt = NULL;
+    if (STps->eof == ST_EOM_OK)
+      return (-ENOSPC);
+    else if (STps->eof == ST_EOM_ERROR)
+      return (-EIO);
+
+    /* Check the buffer readability in cases where copy_user might catch
+       the problems after some tape movement. */
+    if ((copy_from_user(&i, buf, 1) != 0 ||
+	 copy_from_user(&i, buf + count - 1, 1) != 0))
+	return (-EFAULT);
+
+    if (!STm->do_buffer_writes) {
+#if 0
+      if (STp->block_size != 0 && (count % STp->block_size) != 0)
+	return (-EIO);   /* Write must be integral number of blocks */
+#endif
+      write_threshold = 1;
+    }
+    else
+      write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
+    if (!STm->do_async_writes)
+      write_threshold--;
+
+    total = count;
+
+    if ((!STp-> raw) && (STp->first_frame_position == 0xbae)) {
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Skipping over config partition.\n", dev);
+#endif
+	if (osst_flush_drive_buffer(STp, &SCpnt) < 0) {
+	    if (SCpnt) scsi_release_command(SCpnt);
+	    return (-EIO);
+	}
+	/* error recovery may have bumped us past the header partition */
+	if (osst_get_frame_position(STp, &SCpnt) < 0xbb8)
+	    osst_position_tape_and_confirm(STp, &SCpnt, 0xbb8);
+    }
+	
+    if (STp->poll)
+	retval = osst_wait_frame (STp, &SCpnt, STp->first_frame_position, -50, 60);
+    /* TODO: Check for an error ! */
+	
+    memset(cmd, 0, MAX_COMMAND_SIZE);
+    cmd[0] = WRITE_6;
+    cmd[1] = 1;
+
+    STps->rw = ST_WRITING;
+    STp->write_type = OS_WRITE_DATA;
+
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: Writing %d bytes to file %d block %d lblk %d frame %d\n",
+			  dev, count, STps->drv_file, STps->drv_block,
+			  STp->logical_blk_num, STp->first_frame_position);
+#endif
+
+    b_point = buf;
+    while ((STp->buffer)->buffer_bytes + count > write_threshold)
+    {
+      doing_write = 1;
+      do_count = (STp->buffer)->buffer_blocks * STp->block_size -
+	  (STp->buffer)->buffer_bytes;
+      if (do_count > count)
+	  do_count = count;
+
+      i = append_to_buffer(b_point, STp->buffer, do_count);
+      if (i) {
+	  if (SCpnt != NULL) {
+	      scsi_release_command(SCpnt);
+	      SCpnt = NULL;
+	  }
+	  return i;
+      }
+
+      transfer = OS_FRAME_SIZE;
+      blks     = 1;
+
+      osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->logical_blk_num++ );
+
+      cmd[2] = blks >> 16;
+      cmd[3] = blks >> 8;
+      cmd[4] = blks;
+
+      SCpnt = osst_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, TRUE);
+      if (!SCpnt)
+	return (-EBUSY);
+
+      if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+	if (debugging)
+	  printk(OSST_DEB_MSG "osst%d: Error on write:\n", dev);
+#endif
+	if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	    (SCpnt->sense_buffer[2] & 0x40)) {
+	  if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+	    transfer = (SCpnt->sense_buffer[3] << 24) |
+		       (SCpnt->sense_buffer[4] << 16) |
+		       (SCpnt->sense_buffer[5] <<  8) | SCpnt->sense_buffer[6];
+	  else
+	    transfer = 0;
+	  transfer *= STp->block_size;
+	  if (transfer <= do_count) {
+	    filp->f_pos += do_count - transfer;
+	    count -= do_count - transfer;
+	    if (STps->drv_block >= 0) {
+		STps->drv_block += (do_count - transfer) / STp->block_size;
+	    }
+	    STps->eof = ST_EOM_OK;
+	    retval = (-ENOSPC); /* EOM within current request */
+#if DEBUG
+	    if (debugging)
+	      printk(OSST_DEB_MSG "osst%d: EOM with %d bytes unwritten.\n",
+		     dev, transfer);
+#endif
+	  }
+	  else {
+	    STps->eof = ST_EOM_ERROR;
+	    STps->drv_block = (-1);    /* Too cautious? */
+	    retval = (-EIO); /* EOM for old data */
+#if DEBUG
+	    if (debugging)
+	      printk(OSST_DEB_MSG "osst%d: EOM with lost data.\n", dev);
+#endif
+	  }
+	}
+	else {
+	  if (osst_write_error_recovery(STp, &SCpnt, 1) == 0) goto ok;
+	  STps->drv_block = (-1);    /* Too cautious? */
+	  retval = (-EIO);
+	}
+
+	scsi_release_command(SCpnt);
+	SCpnt = NULL;
+	(STp->buffer)->buffer_bytes = 0;
+	STp->dirty = 0;
+	if (count < total)
+	  return total - count;
+	else
+	  return retval;
+      }
+      STp->first_frame_position++;
+ok:
+      filp->f_pos += do_count;
+      b_point += do_count;
+      count -= do_count;
+      if (STps->drv_block >= 0) {
+	  STps->drv_block += blks;
+      }
+      STp->first_frame_position += blks;
+      (STp->buffer)->buffer_bytes = 0;
+      STp->dirty = 0;
+    }
+    if (count != 0) {
+      STp->dirty = 1;
+      i = append_to_buffer(b_point, STp->buffer, count);
+      if (i) {
+	  if (SCpnt != NULL) {
+	      scsi_release_command(SCpnt);
+	      SCpnt = NULL;
+	  }
+	  return i;
+      }
+      filp->f_pos += count;
+      count = 0;
+    }
+
+    if (doing_write && (STp->buffer)->last_result_fatal != 0) {
+      scsi_release_command(SCpnt);
+      SCpnt = NULL;
+      return (STp->buffer)->last_result_fatal;
+    }
+
+    if (STm->do_async_writes &&
+	((STp->buffer)->buffer_bytes >= STp->write_threshold &&
+	 (STp->buffer)->buffer_bytes >= OS_DATA_SIZE)      ) { 
+      /* Schedule an asynchronous write */
+      (STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
+	  STp->block_size) * STp->block_size;
+      STp->dirty = !((STp->buffer)->writing ==
+		     (STp->buffer)->buffer_bytes);
+
+      transfer = OS_FRAME_SIZE;
+      blks     = 1;
+
+      osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->logical_blk_num++ );
+
+      cmd[2] = blks >> 16;
+      cmd[3] = blks >> 8;
+      cmd[4] = blks;
+#if DEBUG
+      STp->write_pending = 1;
+#endif
+
+      SCpnt = osst_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, FALSE);
+      if (SCpnt == NULL)
+	  return (-EIO);
+    }
+    else if (SCpnt != NULL) {
+	scsi_release_command(SCpnt);
+	SCpnt = NULL;
+    }
+    STps->at_sm &= (total == 0);
+    if (total > 0)
+	STps->eof = ST_NOEOF;
+
+    return( total);
+}
+
+
+/* Read command */
+static ssize_t osst_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+{
+    struct inode * inode = filp->f_dentry->d_inode;
+    ssize_t total;
+    ssize_t i, transfer;
+    int special;
+    OS_Scsi_Tape * STp;
+    ST_mode * STm;
+    ST_partstat * STps;
+    Scsi_Cmnd *SCpnt = NULL;
+    int dev = TAPE_NR(inode->i_rdev);
+
+    STp = os_scsi_tapes[dev];
+
+    /*
+     * If we are in the middle of error recovery, don't let anyone
+     * else try and use this device.  Also, if error recovery fails, it
+     * may try and take the device offline, in which case all further
+     * access to the device is prohibited.
+     */
+    if( !scsi_block_when_processing_errors(STp->device) ) {
+        return -ENXIO;
+    }
+    
+    if (ppos != &filp->f_pos) {
+      /* "A request was outside the capabilities of the device." */
+      return -ENXIO;
+    }
+
+    if (STp->ready != ST_READY) {
+      if (STp->ready == ST_NO_TAPE)
+	return (-ENOMEDIUM);
+      else
+        return (-EIO);
+    }
+    STm = &(STp->modes[STp->current_mode]);
+    if (!STm->defined)
+      return (-ENXIO);
+#if DEBUG
+    if (!STp->in_use) {
+      printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev);
+      return (-EIO);
+    }
+#endif
+    /* Must have initialized medium */
+    if (!STp->header_ok) return (-EIO);
+
+    if ((count % STp->block_size) != 0) {
+      printk(KERN_WARNING "osst%d: Use multiple of %d bytes as block size (%d requested)\n",
+			  dev, STp->block_size, count);
+      return (-EIO);	/* Read must be integral number of blocks */
+    }
+
+    if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+	!osst_int_ioctl(STp, &SCpnt, MTLOCK, 0))
+      STp->door_locked = ST_LOCKED_AUTO;
+
+    STps = &(STp->ps[STp->partition]);
+    if (STps->rw == ST_WRITING) {
+      transfer = osst_flush_buffer(STp, &SCpnt, 0);
+      if (SCpnt) scsi_release_command(SCpnt);
+      SCpnt = NULL;
+      if (transfer)
+	return transfer;
+      STps->rw = ST_IDLE;
+    }
+
+#if DEBUG
+    if (debugging && STps->eof != ST_NOEOF)
+      printk(OSST_DEB_MSG "osst%d: EOF/EOM flag up (%d). Bytes %d\n", dev,
+	     STps->eof, (STp->buffer)->buffer_bytes);
+#endif
+    if ((STp->buffer)->buffer_bytes == 0 &&
+	STps->eof >= ST_EOD_1) {
+	if (SCpnt) scsi_release_command(SCpnt);
+	if (STps->eof < ST_EOD) {
+	    STps->eof += 1;
+	    return 0;
+	}
+	return (-EIO);  /* EOM or Blank Check */
+    }
+
+    /* Check the buffer writability before any tape movement. Don't alter
+       buffer data. */
+    if (copy_from_user(&i, buf, 1) != 0 ||
+	copy_to_user(buf, &i, 1) != 0 ||
+	copy_from_user(&i, buf + count - 1, 1) != 0 ||
+	copy_to_user(buf + count - 1, &i, 1) != 0) {
+	if (SCpnt) scsi_release_command(SCpnt);
+	return (-EFAULT);
+    }
+
+    /* Loop until enough data in buffer or a special condition found */
+    for (total = 0, special = 0; total < count && !special; ) {
+
+      /* Get new data if the buffer is empty */
+      if ((STp->buffer)->buffer_bytes == 0) {
+	  special = osst_get_logical_blk(STp, &SCpnt, STp->logical_blk_num, 0);
+	  STp->buffer->buffer_bytes = special ? 0 : OS_DATA_SIZE; 
+	  STp->buffer->read_pointer = 0;
+	  STp->logical_blk_num++;		/* block to look for next time */
+	  STp->logical_blk_in_buffer = 0;
+	  if (special < 0) { 			/* No need to continue read */
+	      if (SCpnt) scsi_release_command(SCpnt);
+	      return special;
+          }
+	  STps->drv_block++;
+      }
+
+      /* Move the data from driver buffer to user buffer */
+      if ((STp->buffer)->buffer_bytes > 0) {
+#if DEBUG
+	if (debugging && STps->eof != ST_NOEOF)
+	  printk(OSST_DEB_MSG "osst%d: EOF up (%d). Left %d, needed %d.\n", dev,
+		 STps->eof, (STp->buffer)->buffer_bytes, count - total);
+#endif
+	transfer = (STp->buffer)->buffer_bytes < count - total ?
+	  (STp->buffer)->buffer_bytes : count - total;
+	i = from_buffer(STp->buffer, buf, transfer);
+	if (i) {
+	    if (SCpnt) scsi_release_command(SCpnt);
+	    return i;
+	}
+	filp->f_pos += transfer;
+	buf += transfer;
+	total += transfer;
+      }
+
+    } /* for (total = 0, special = 0; total < count && !special; ) */
+    if (SCpnt) scsi_release_command(SCpnt);
+
+    /* Change the eof state if no data from tape or buffer */
+    if (total == 0) {
+	if (STps->eof == ST_FM_HIT) {
+	    STps->eof = ST_FM;
+	    STps->drv_block = 0;
+	    if (STps->drv_file >= 0)
+		STps->drv_file++;
+	}
+	else if (STps->eof == ST_EOD_1) {
+	    STps->eof = ST_EOD_2;
+	    STps->drv_block = 0;
+	    if (STps->drv_file >= 0)
+		STps->drv_file++;
+	}
+	else if (STps->eof == ST_EOD_2)
+	    STps->eof = ST_EOD;
+    }
+    else if (STps->eof == ST_FM)
+	STps->eof = ST_NOEOF;
+
+    return total;
+}
+
+
+/* Set the driver options */
+static void osst_log_options(OS_Scsi_Tape *STp, ST_mode *STm, int dev)
+{
+  printk(KERN_INFO
+"osst%d: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+	 dev, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+	 STm->do_read_ahead);
+  printk(KERN_INFO
+"osst%d:    can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
+	 dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
+  printk(KERN_INFO
+"osst%d:    defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+	 dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+	 STp->scsi2_logical);
+  printk(KERN_INFO
+"osst%d:    sysv: %d\n", dev, STm->sysv);
+#if DEBUG
+  printk(KERN_INFO
+	 "osst%d:    debugging: %d\n",
+	 dev, debugging);
+#endif
+}
+
+
+static int osst_set_options(OS_Scsi_Tape *STp, long options)
+{
+  int value;
+  long code;
+  ST_mode *STm;
+  int dev = TAPE_NR(STp->devt);
+
+  STm = &(STp->modes[STp->current_mode]);
+  if (!STm->defined) {
+    memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
+    modes_defined = TRUE;
+#if DEBUG
+    if (debugging)
+      printk(OSST_DEB_MSG "osst%d: Initialized mode %d definition from mode 0\n",
+	     dev, STp->current_mode);
+#endif
+  }
+
+  code = options & MT_ST_OPTIONS;
+  if (code == MT_ST_BOOLEANS) {
+    STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+    STm->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
+    STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
+    STm->do_read_ahead    = (options & MT_ST_READ_AHEAD) != 0;
+    STp->two_fm		  = (options & MT_ST_TWO_FM) != 0;
+    STp->fast_mteom	  = (options & MT_ST_FAST_MTEOM) != 0;
+    STp->do_auto_lock     = (options & MT_ST_AUTO_LOCK) != 0;
+    STp->can_bsr          = (options & MT_ST_CAN_BSR) != 0;
+    STp->omit_blklims	  = (options & MT_ST_NO_BLKLIMS) != 0;
+    if ((STp->device)->scsi_level >= SCSI_2)
+      STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
+    STp->scsi2_logical    = (options & MT_ST_SCSI2LOGICAL) != 0;
+    STm->sysv		  = (options & MT_ST_SYSV) != 0;
+#if DEBUG
+    debugging = (options & MT_ST_DEBUGGING) != 0;
+#endif
+    osst_log_options(STp, STm, dev);
+  }
+  else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
+    value = (code == MT_ST_SETBOOLEANS);
+    if ((options & MT_ST_BUFFER_WRITES) != 0)
+      STm->do_buffer_writes = value;
+    if ((options & MT_ST_ASYNC_WRITES) != 0)
+      STm->do_async_writes = value;
+    if ((options & MT_ST_DEF_WRITES) != 0)
+      STm->defaults_for_writes = value;
+    if ((options & MT_ST_READ_AHEAD) != 0)
+      STm->do_read_ahead = value;
+    if ((options & MT_ST_TWO_FM) != 0)
+      STp->two_fm = value;
+    if ((options & MT_ST_FAST_MTEOM) != 0)
+      STp->fast_mteom = value;
+    if ((options & MT_ST_AUTO_LOCK) != 0)
+      STp->do_auto_lock = value;
+    if ((options & MT_ST_CAN_BSR) != 0)
+      STp->can_bsr = value;
+    if ((options & MT_ST_NO_BLKLIMS) != 0)
+      STp->omit_blklims = value;
+    if ((STp->device)->scsi_level >= SCSI_2 &&
+	(options & MT_ST_CAN_PARTITIONS) != 0)
+      STp->can_partitions = value;
+    if ((options & MT_ST_SCSI2LOGICAL) != 0)
+      STp->scsi2_logical = value;
+    if ((options & MT_ST_SYSV) != 0)
+      STm->sysv = value;
+#if DEBUG
+    if ((options & MT_ST_DEBUGGING) != 0)
+      debugging = value;
+#endif
+    osst_log_options(STp, STm, dev);
+  }
+  else if (code == MT_ST_WRITE_THRESHOLD) {
+    value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
+    if (value < 1 || value > osst_buffer_size) {
+      printk(KERN_WARNING "osst%d: Write threshold %d too small or too large.\n",
+	     dev, value);
+      return (-EIO);
+    }
+    STp->write_threshold = value;
+    printk(KERN_INFO "osst%d: Write threshold set to %d bytes.\n",
+	   dev, value);
+  }
+  else if (code == MT_ST_DEF_BLKSIZE) {
+    value = (options & ~MT_ST_OPTIONS);
+    if (value == ~MT_ST_OPTIONS) {
+      STm->default_blksize = (-1);
+      printk(KERN_INFO "osst%d: Default block size disabled.\n", dev);
+    }
+    else {
+      STm->default_blksize = value;
+      printk(KERN_INFO "osst%d: Default block size set to %d bytes.\n",
+	     dev, STm->default_blksize);
+    }
+  }
+  else if (code == MT_ST_TIMEOUTS) {
+    value = (options & ~MT_ST_OPTIONS);
+    if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
+      STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
+      printk(KERN_INFO "osst%d: Long timeout set to %d seconds.\n", dev,
+	     (value & ~MT_ST_SET_LONG_TIMEOUT));
+    }
+    else {
+      STp->timeout = value * HZ;
+      printk(KERN_INFO "osst%d: Normal timeout set to %d seconds.\n", dev,
+	     value);
+    }
+  }
+  else if (code == MT_ST_DEF_OPTIONS) {
+    code = (options & ~MT_ST_CLEAR_DEFAULT);
+    value = (options & MT_ST_CLEAR_DEFAULT);
+    if (code == MT_ST_DEF_DENSITY) {
+      if (value == MT_ST_CLEAR_DEFAULT) {
+	STm->default_density = (-1);
+	printk(KERN_INFO "osst%d: Density default disabled.\n", dev);
+      }
+      else {
+	STm->default_density = value & 0xff;
+	printk(KERN_INFO "osst%d: Density default set to %x\n",
+	       dev, STm->default_density);
+      }
+    }
+    else if (code == MT_ST_DEF_DRVBUFFER) {
+      if (value == MT_ST_CLEAR_DEFAULT) {
+	STp->default_drvbuffer = 0xff;
+	printk(KERN_INFO "osst%d: Drive buffer default disabled.\n", dev);
+      }
+      else {
+	STp->default_drvbuffer = value & 7;
+	printk(KERN_INFO "osst%d: Drive buffer default set to %x\n",
+	       dev, STp->default_drvbuffer);
+      }
+    }
+    else if (code == MT_ST_DEF_COMPRESSION) {
+      if (value == MT_ST_CLEAR_DEFAULT) {
+	STm->default_compression = ST_DONT_TOUCH;
+	printk(KERN_INFO "osst%d: Compression default disabled.\n", dev);
+      }
+      else {
+	STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+	printk(KERN_INFO "osst%d: Compression default set to %x\n",
+	       dev, (value & 1));
+      }
+    }
+  }
+  else
+    return (-EIO);
+
+  return 0;
+}
+
+
+/* Internal ioctl function */
+static int osst_int_ioctl(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, unsigned int cmd_in, unsigned long arg)
+{
+   int timeout;
+   long ltmp;
+   int i, ioctl_result;
+   int chg_eof = TRUE;
+   unsigned char cmd[MAX_COMMAND_SIZE];
+   Scsi_Cmnd * SCpnt = *aSCpnt;
+   ST_partstat * STps;
+   int fileno, blkno, at_sm, undone, datalen;
+   int logical_blk_num;
+   int dev = TAPE_NR(STp->devt);
+
+   if (STp->ready != ST_READY && cmd_in != MTLOAD) {
+     if (STp->ready == ST_NO_TAPE)
+       return (-ENOMEDIUM);
+     else
+       return (-EIO);
+   }
+   timeout = STp->long_timeout;
+   STps = &(STp->ps[STp->partition]);
+   fileno = STps->drv_file;
+   blkno = STps->drv_block;
+   at_sm = STps->at_sm;
+   logical_blk_num = STp->logical_blk_num;
+
+   memset(cmd, 0, MAX_COMMAND_SIZE);
+   datalen = 0;
+   switch (cmd_in) {
+     case MTFSFM:
+	chg_eof = FALSE; /* Changed from the FSF after this */
+     case MTFSF:
+	if (STp->raw)
+	   return (-EIO);
+	if (STp->linux_media)
+	   ioctl_result = osst_space_over_filemarks_forward_fast(STp, &SCpnt, cmd_in, arg);
+	else
+	   ioctl_result = osst_space_over_filemarks_forward_slow(STp, &SCpnt, cmd_in, arg);
+	logical_blk_num = STp->logical_blk_num;
+	if (fileno >= 0)
+	   fileno += arg;
+	blkno = 0;
+	at_sm &= (arg == 0);
+	goto os_bypass;
+
+     case MTBSF:
+	chg_eof = FALSE; /* Changed from the FSF after this */
+     case MTBSFM:
+	if (STp->raw)
+	   return (-EIO);
+	ioctl_result = osst_space_over_filemarks_backward(STp, &SCpnt, cmd_in, arg);
+	logical_blk_num = STp->logical_blk_num;
+	if (fileno >= 0)
+	   fileno -= arg;
+	blkno = (-1);  /* We can't know the block number */
+	at_sm &= (arg == 0);
+	goto os_bypass;
+
+     case MTFSR:
+     case MTBSR:
+#if DEBUG
+	if (debugging)
+	   printk(OSST_DEB_MSG "osst%i: Skipping %lu blocks %s from logical block %d\n",
+				dev, arg, cmd_in==MTFSR?"forward":"backward", logical_blk_num);
+#endif
+	if (cmd_in == MTFSR) {
+	   logical_blk_num += arg;
+	   if (blkno >= 0) blkno += arg;
+	}
+	else {
+	   logical_blk_num -= arg;
+	   if (blkno >= 0) blkno -= arg;
+	}
+	ioctl_result = osst_seek_logical_blk(STp, &SCpnt, logical_blk_num-1);
+	STp->logical_blk_in_buffer = 0;
+	at_sm &= (arg == 0);
+	goto os_bypass;
+
+     case MTFSS:
+       cmd[0] = SPACE;
+       cmd[1] = 0x04; /* Space Setmarks */   /* FIXME -- OS can't do this? */
+       cmd[2] = (arg >> 16);
+       cmd[3] = (arg >> 8);
+       cmd[4] = arg;
+#if DEBUG
+       if (debugging)
+	 printk(OSST_DEB_MSG "osst%d: Spacing tape forward %d setmarks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+       if (arg != 0) {
+	 blkno = fileno = (-1);
+	 at_sm = 1;
+       }
+       break;
+     case MTBSS:
+       cmd[0] = SPACE;
+       cmd[1] = 0x04; /* Space Setmarks */   /* FIXME -- OS can't do this? */
+       ltmp = (-arg);
+       cmd[2] = (ltmp >> 16);
+       cmd[3] = (ltmp >> 8);
+       cmd[4] = ltmp;
+#if DEBUG
+       if (debugging) {
+	 if (cmd[2] & 0x80)
+	   ltmp = 0xff000000;
+	 ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+	 printk(OSST_DEB_MSG "osst%d: Spacing tape backward %ld setmarks.\n",
+		dev, (-ltmp));
+       }
+#endif
+       if (arg != 0) {
+	 blkno = fileno = (-1);
+	 at_sm = 1;
+       }
+       break;
+     case MTWEOF:
+       if ( STps->rw == ST_WRITING && !(STp->device)->was_reset)
+         ioctl_result = osst_flush_write_buffer(STp, &SCpnt, 1);
+       else
+	 ioctl_result = 0;
+       for (i=0; i<arg; i++)
+         ioctl_result |= osst_write_filemark(STp, &SCpnt);
+       logical_blk_num = STp->logical_blk_num;
+       if (fileno >= 0) fileno += arg;
+       if (blkno  >= 0) blkno   = 0;
+       goto os_bypass;
+
+     case MTWSM:
+       if (STp->write_prot)
+	 return (-EACCES);
+       if (!STp->raw)
+	 return 0;
+       cmd[0] = WRITE_FILEMARKS;   /* FIXME -- need OS version */
+       if (cmd_in == MTWSM)
+	 cmd[1] = 2;
+       cmd[2] = (arg >> 16);
+       cmd[3] = (arg >> 8);
+       cmd[4] = arg;
+       timeout = STp->timeout;
+#if DEBUG
+       if (debugging) {
+	 if (cmd_in == MTWEOF)
+	   printk(OSST_DEB_MSG "osst%d: Writing %d filemarks.\n", dev,
+		  cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+	 else
+	   printk(OSST_DEB_MSG "osst%d: Writing %d setmarks.\n", dev,
+		  cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+       }
+#endif
+       if (fileno >= 0)
+	 fileno += arg;
+       blkno = 0;
+       at_sm = (cmd_in == MTWSM);
+       break;
+     case MTOFFL:
+     case MTLOAD:
+     case MTUNLOAD:
+     case MTRETEN:
+       cmd[0] = START_STOP;
+	 cmd[1] = 1;		/* Don't wait for completion */
+       if (cmd_in == MTLOAD)
+	 cmd[4] = 1;		/* load */
+       if (cmd_in == MTRETEN)
+	 cmd[4] = 3;		/* retension then mount */
+       if (cmd_in == MTOFFL)
+	 cmd[4] = 4;		/* rewind then eject */
+       timeout = STp->timeout;
+#if DEBUG
+       if (debugging) {
+	 switch (cmd_in) {
+	     case MTUNLOAD:
+		 printk(OSST_DEB_MSG "osst%d: Unloading tape.\n", dev);
+		 break;
+	     case MTLOAD:
+		 printk(OSST_DEB_MSG "osst%d: Loading tape.\n", dev);
+		 break;
+	     case MTRETEN:
+		 printk(OSST_DEB_MSG "osst%d: Retensioning tape.\n", dev);
+		 break;
+	     case MTOFFL:
+		 printk(OSST_DEB_MSG "osst%d: Ejecting tape.\n", dev);
+		 break;
+	 }
+       }
+#endif
+       fileno = blkno = at_sm = logical_blk_num = 0 ;
+       break;
+     case MTNOP:
+#if DEBUG
+       if (debugging)
+	 printk(OSST_DEB_MSG "osst%d: No op on tape.\n", dev);
+#endif
+       return 0;  /* Should do something ? */
+       break;
+     case MTEOM:
+#if DEBUG
+	if (debugging)
+	   printk(OSST_DEB_MSG "osst%d: Spacing to end of recorded medium.\n", dev);
+#endif
+	osst_set_frame_position(STp, &SCpnt, STp->eod_frame_ppos, 0);
+	if (osst_get_logical_blk(STp, &SCpnt, -1, 0) < 0)
+	   return (-EIO);
+	if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_EOD)
+	   return (-EIO);
+	ioctl_result = osst_set_frame_position(STp, &SCpnt, STp->eod_frame_ppos, 0);
+	logical_blk_num = STp->logical_blk_num;
+	fileno          = STp->filemark_cnt;
+	blkno  = at_sm  = 0;
+	goto os_bypass;
+
+     case MTERASE:
+	if (STp->write_prot)
+	   return (-EACCES);
+	ioctl_result = osst_reset_header(STp, &SCpnt);
+	i = osst_write_eod(STp, &SCpnt);
+	if (i < ioctl_result) ioctl_result = i;
+	i = osst_position_tape_and_confirm(STp, &SCpnt, STp->eod_frame_ppos);
+	if (i < ioctl_result) ioctl_result = i;
+	fileno = blkno = at_sm = logical_blk_num = 0 ;
+	goto os_bypass;
+
+     case MTREW:
+	cmd[0] = REZERO_UNIT; /* rewind */
+	cmd[1] = 1;
+#if DEBUG
+	if (debugging)
+	   printk(OSST_DEB_MSG "osst%d: Rewinding tape, Immed=%d.\n", dev, cmd[1]);
+#endif
+	fileno = blkno = at_sm = logical_blk_num = 0 ;
+	break;
+
+     case MTLOCK:
+	chg_eof = FALSE;
+	cmd[0] = ALLOW_MEDIUM_REMOVAL;
+	cmd[4] = SCSI_REMOVAL_PREVENT;
+#if DEBUG
+	if (debugging)
+	    printk(OSST_DEB_MSG "osst%d: Locking drive door.\n", dev);
+#endif;
+	break;
+
+     case MTUNLOCK:
+	chg_eof = FALSE;
+	cmd[0] = ALLOW_MEDIUM_REMOVAL;
+	cmd[4] = SCSI_REMOVAL_ALLOW;
+#if DEBUG
+	if (debugging)
+	   printk(OSST_DEB_MSG "osst%d: Unlocking drive door.\n", dev);
+#endif;
+	break;
+
+     case MTSETBLK:           /* Set block length */
+     case MTSETDENSITY:       /* Set tape density */
+     case MTSETDRVBUFFER:     /* Set drive buffering */
+     case SET_DENS_AND_BLK:   /* Set density and block size */
+       chg_eof = FALSE;
+       if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
+	 return (-EIO);       /* Not allowed if data in buffer */
+       if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
+	   (arg & MT_ST_BLKSIZE_MASK) != 0 &&
+	   ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
+	    (arg & MT_ST_BLKSIZE_MASK) > STp->max_block ||
+	    (arg & MT_ST_BLKSIZE_MASK) > osst_buffer_size)) {
+	 printk(KERN_WARNING "osst%d: Illegal block size.\n", dev);
+	 return (-EINVAL);
+       }
+       return 0;  /* silently ignore if block size didn't change */
+
+     default:
+	return (-ENOSYS);
+   }
+
+   SCpnt = osst_do_scsi(SCpnt, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE);
+   if (!SCpnt) {
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: Couldn't exec scsi cmd for IOCTL\n", dev);
+#endif
+      return (-EBUSY);
+   }
+
+   ioctl_result = (STp->buffer)->last_result_fatal;
+
+os_bypass:
+#if DEBUG
+   if (debugging)
+      printk(OSST_DEB_MSG "osst%d: IOCTL (%d) Result=%d\n", dev, cmd_in, ioctl_result);
+#endif
+
+   if (!ioctl_result) {  /* SCSI command successful */
+
+      if (cmd_in == MTFSFM) {
+	 fileno--;
+	 blkno--;
+      }
+      if (cmd_in == MTBSFM) {
+	 fileno++;
+	 blkno++;
+      }
+      STps->drv_block = blkno;
+      STps->drv_file = fileno;
+      STps->at_sm = at_sm;
+      STp->logical_blk_num = logical_blk_num;
+
+      if (cmd_in == MTLOCK)
+         STp->door_locked = ST_LOCKED_EXPLICIT;
+      else if (cmd_in == MTUNLOCK)
+         STp->door_locked = ST_UNLOCKED;
+
+      if (cmd_in == MTEOM)
+         STps->eof = ST_EOD;
+      else if (cmd_in == MTFSF)
+         STps->eof = ST_FM;
+      else if (chg_eof)
+         STps->eof = ST_NOEOF;
+
+      if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
+         STp->rew_at_close = 0;
+      else if (cmd_in == MTLOAD) {
+/*       STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;  FIXME */
+	 for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	    STp->ps[i].rw = ST_IDLE;
+	    STp->ps[i].last_block_valid = FALSE;
+	 }
+	 STp->partition = 0;
+      }
+
+      if (cmd_in == MTREW) {
+	ioctl_result = osst_position_tape_and_confirm(STp, &SCpnt, STp->first_data_ppos); 
+        if (ioctl_result > 0)
+            ioctl_result = 0;
+      }
+
+   } else if (SCpnt) {  /* SCSI command was not completely successful. */
+      if (SCpnt->sense_buffer[2] & 0x40) {
+         if (cmd_in != MTBSF && cmd_in != MTBSFM &&
+	    cmd_in != MTBSR && cmd_in != MTBSS)
+	    STps->eof = ST_EOM_OK;
+	 STps->drv_block = 0;
+      }
+
+      undone = (
+	  (SCpnt->sense_buffer[3] << 24) +
+	  (SCpnt->sense_buffer[4] << 16) +
+	  (SCpnt->sense_buffer[5] << 8) +
+	  SCpnt->sense_buffer[6] );
+      if (cmd_in == MTWEOF &&
+	 (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	 (SCpnt->sense_buffer[2] & 0x4f) == 0x40 &&
+	 ((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) {
+	    ioctl_result = 0;  /* EOF written succesfully at EOM */
+	    if (fileno >= 0)
+		fileno++;
+	    STps->drv_file = fileno;
+	    STps->eof = ST_NOEOF;
+      }
+      else if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
+         if (fileno >= 0)
+	    STps->drv_file = fileno - undone ;
+	 else
+	    STps->drv_file = fileno;
+       STps->drv_block = 0;
+       STps->eof = ST_NOEOF;
+     }
+     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
+       if (fileno >= 0)
+         STps->drv_file = fileno + undone ;
+       else
+	 STps->drv_file = fileno;
+       STps->drv_block = 0;
+       STps->eof = ST_NOEOF;
+     }
+     else if (cmd_in == MTFSR) {
+       if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+	 if (STps->drv_file >= 0)
+	   STps->drv_file++;
+	 STps->drv_block = 0;
+	 STps->eof = ST_FM;
+       }
+       else {
+	 if (blkno >= undone)
+	   STps->drv_block = blkno - undone;
+	 else
+	   STps->drv_block = (-1);
+	 STps->eof = ST_NOEOF;
+       }
+     }
+     else if (cmd_in == MTBSR) {
+       if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+	 STps->drv_file--;
+	 STps->drv_block = (-1);
+       }
+       else {
+	 if (blkno >= 0)
+	   STps->drv_block = blkno + undone;
+	 else
+	   STps->drv_block = (-1);
+       }
+       STps->eof = ST_NOEOF;
+     }
+     else if (cmd_in == MTEOM) {
+       STps->drv_file = (-1);
+       STps->drv_block = (-1);
+       STps->eof = ST_EOD;
+     }
+     else if (chg_eof)
+       STps->eof = ST_NOEOF;
+
+      if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+	 STps->eof = ST_EOD;
+
+      if (cmd_in == MTLOCK)
+	 STp->door_locked = ST_LOCK_FAILS;
+
+   }
+   *aSCpnt = SCpnt;
+
+   return ioctl_result;
+}
+
+
+/* Open the device */
+static int os_scsi_tape_open(struct inode * inode, struct file * filp)
+{
+    unsigned short flags;
+    int i, b_size, need_dma_buffer, new_session = FALSE;
+    unsigned char cmd[MAX_COMMAND_SIZE];
+    Scsi_Cmnd * SCpnt;
+    OS_Scsi_Tape * STp;
+    ST_mode * STm;
+    ST_partstat * STps;
+    int dev = TAPE_NR(inode->i_rdev);
+    int mode = TAPE_MODE(inode->i_rdev);
+
+    if (dev >= osst_template.dev_max || (STp = os_scsi_tapes[dev]) == NULL || !STp->device)
+      return (-ENXIO);
+
+    if( !scsi_block_when_processing_errors(STp->device) ) {
+        return -ENXIO;
+    }
+
+    if (STp->in_use) {
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: Device already in use.\n", dev);
+#endif
+      return (-EBUSY);
+    }
+    STp->in_use       = 1;
+    STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+
+    if (STp->device->host->hostt->module)
+       __MOD_INC_USE_COUNT(STp->device->host->hostt->module);
+    if (osst_template.module)
+       __MOD_INC_USE_COUNT(osst_template.module);
+
+    if (mode != STp->current_mode) {
+#if DEBUG
+      if (debugging)
+	printk(OSST_DEB_MSG "osst%d: Mode change from %d to %d.\n",
+	       dev, STp->current_mode, mode);
+#endif
+      new_session = TRUE;
+      STp->current_mode = mode;
+    }
+    STm = &(STp->modes[STp->current_mode]);
+
+    flags = filp->f_flags;
+    STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
+
+    STp->raw = (MINOR(inode->i_rdev) & 0x40) != 0;
+
+    /* Allocate a buffer for this user */
+    need_dma_buffer = STp->restr_dma;
+    for (i=0; i < osst_nbr_buffers; i++)
+      if (!osst_buffers[i]->in_use &&
+	  (!need_dma_buffer || osst_buffers[i]->dma))
+	break;
+    if (i >= osst_nbr_buffers) {
+      STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
+      if (STp->buffer == NULL) {
+	printk(KERN_WARNING "osst%d: Can't allocate tape buffer.\n", dev);
+	STp->in_use = 0;
+	if (STp->device->host->hostt->module)
+	    __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+	if(osst_template.module)
+	    __MOD_DEC_USE_COUNT(osst_template.module);
+	return (-EBUSY);
+      }
+    }
+    else
+      STp->buffer = osst_buffers[i];
+    (STp->buffer)->in_use = 1;
+    (STp->buffer)->writing = 0;
+    (STp->buffer)->last_result_fatal = 0;
+    (STp->buffer)->use_sg = STp->device->host->sg_tablesize;
+
+    /* Compute the usable buffer size for this SCSI adapter */
+    if (!(STp->buffer)->use_sg)
+	(STp->buffer)->buffer_size = (STp->buffer)->sg[0].length;
+    else {
+	for (i=0, (STp->buffer)->buffer_size = 0; i < (STp->buffer)->use_sg &&
+		 i < (STp->buffer)->sg_segs; i++)
+	    (STp->buffer)->buffer_size += (STp->buffer)->sg[i].length;
+    }
+
+    STp->dirty = 0;
+    for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	STps = &(STp->ps[i]);
+	STps->rw = ST_IDLE;
+    }
+    STp->ready = ST_READY;
+    STp->recover_count = 0;
+#if DEBUG
+    STp->nbr_waits = STp->nbr_finished = 0;
+#endif
+    memset (cmd, 0, MAX_COMMAND_SIZE);
+    cmd[0] = TEST_UNIT_READY;
+
+    SCpnt = osst_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE);
+    if (!SCpnt) {
+        STp->buffer->in_use = 0;
+        STp->buffer = NULL;
+        STp->in_use = 0;
+
+	if (STp->device->host->hostt->module)
+	    __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+	if (osst_template.module)
+	    __MOD_DEC_USE_COUNT(osst_template.module);
+	return (-EBUSY);
+    }
+    if ((SCpnt->sense_buffer[0] & 0x70) == 0x70      &&
+	(SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
+	 SCpnt->sense_buffer[12]        == 4         ) {
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: Unit not ready, cause %x\n", dev, SCpnt->sense_buffer[13]);
+#endif
+      if (SCpnt->sense_buffer[13] == 2) {	/* initialize command required (LOAD) */
+	memset (cmd, 0, MAX_COMMAND_SIZE);
+        cmd[0] = START_STOP;
+	cmd[1] = 1;
+	cmd[4] = 1;
+        SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE);
+      }
+      osst_wait_ready(STp, &SCpnt, (SCpnt->sense_buffer[13]==1?15:3) * 60);
+    }
+    if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: Unit wants attention\n", dev);
+#endif
+      STp->header_ok = 0;
+
+      for (i=0; i < 10; i++) {
+
+        memset (cmd, 0, MAX_COMMAND_SIZE);
+        cmd[0] = TEST_UNIT_READY;
+
+        SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE);
+        if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
+            (SCpnt->sense_buffer[2] & 0x0f) != UNIT_ATTENTION)
+            break;
+      }
+
+      STp->device->was_reset = 0;
+      STp->partition = STp->new_partition = 0;
+      if (STp->can_partitions)
+	STp->nbr_partitions = 1;  /* This guess will be updated later if necessary */
+      for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	  STps = &(STp->ps[i]);
+	  STps->rw = ST_IDLE;
+	  STps->eof = ST_NOEOF;
+	  STps->at_sm = 0;
+	  STps->last_block_valid = FALSE;
+	  STps->drv_block = 0;
+	  STps->drv_file = 0 ;
+      }
+      new_session = TRUE;
+    }
+    /*
+     * if we have valid headers from before, and the drive/tape seem untouched,
+     * open without reconfiguring and re-reading the headers
+     */
+    if (!STp->buffer->last_result_fatal && STp->header_ok &&
+        !SCpnt->result && SCpnt->sense_buffer[0] == 0       ) {
+
+      memset(cmd, 0, MAX_COMMAND_SIZE);
+      cmd[0] = MODE_SENSE;
+      cmd[1] = 8;
+      cmd[2] = VENDOR_IDENT_PAGE;
+      cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+      SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+
+      if (STp->buffer->last_result_fatal                     ||
+	  STp->buffer->b_data[MODE_HEADER_LENGTH + 2] != 'L' ||
+	  STp->buffer->b_data[MODE_HEADER_LENGTH + 3] != 'I' ||
+	  STp->buffer->b_data[MODE_HEADER_LENGTH + 4] != 'N' ||
+	  STp->buffer->b_data[MODE_HEADER_LENGTH + 5] != '4'  ) {
+#if DEBUG
+	printk(OSST_DEB_MSG "osst%d: signature was changed to %c%c%c%c\n", dev,
+			  STp->buffer->b_data[MODE_HEADER_LENGTH + 2],
+			  STp->buffer->b_data[MODE_HEADER_LENGTH + 3],
+			  STp->buffer->b_data[MODE_HEADER_LENGTH + 4],
+			  STp->buffer->b_data[MODE_HEADER_LENGTH + 5]);
+#endif
+	STp->header_ok = 0;
+      }
+      i = STp->first_frame_position;
+      if (STp->header_ok && i == osst_get_frame_position(STp, &SCpnt)) {
+	if (STp->door_locked == ST_UNLOCKED) {
+	  if (osst_int_ioctl(STp, &SCpnt, MTLOCK, 0))
+	    printk(KERN_WARNING "osst%d: Can't lock drive door\n", dev);
+	  else
+	    STp->door_locked = ST_LOCKED_AUTO;
+	}
+	STp->fast_open = TRUE;
+	scsi_release_command(SCpnt);
+	return 0;
+      }
+#if DEBUG
+      if (i != STp->first_frame_position)
+	printk(OSST_DEB_MSG "osst%d: tape position changed from %d to %d\n", dev, i, STp->first_frame_position);
+#endif
+      STp->header_ok = 0;
+    }
+    STp->fast_open = FALSE;
+
+    if ((STp->buffer)->last_result_fatal != 0 &&   /* in all error conditions except no medium */
+	(SCpnt->sense_buffer[2] != 2 || SCpnt->sense_buffer[12] != 0x3A) ) {
+
+      memset(cmd, 0, MAX_COMMAND_SIZE);
+      cmd[0] = MODE_SELECT;
+      cmd[1] = 0x10;
+      cmd[4] = 4 + MODE_HEADER_LENGTH;
+
+      (STp->buffer)->b_data[0] = cmd[4] - 1;
+      (STp->buffer)->b_data[1] = 0;			/* Medium Type - ignoring */
+      (STp->buffer)->b_data[2] = 0;			/* Reserved */
+      (STp->buffer)->b_data[3] = 0;			/* Block Descriptor Length */
+      (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = 0x3f;
+      (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 1;
+      (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 2;
+      (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 3;
+
+#if 1 //DEBUG
+      printk(OSST_DEB_MSG "osst%i: Applying soft reset\n", dev);
+#endif
+      SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+
+      STp->header_ok = 0;
+
+      for (i=0; i < 10; i++) {
+
+        memset (cmd, 0, MAX_COMMAND_SIZE);
+        cmd[0] = TEST_UNIT_READY;
+
+        SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE);
+        if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
+            (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY)
+            break;
+
+	if ((SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) {
+	  STp->device->was_reset = 0;
+	  STp->partition = STp->new_partition = 0;
+	  if (STp->can_partitions)
+	    STp->nbr_partitions = 1;  /* This guess will be updated later if necessary */
+	  for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	    STps = &(STp->ps[i]);
+	    STps->rw = ST_IDLE;
+	    STps->eof = ST_NOEOF;
+	    STps->at_sm = 0;
+	    STps->last_block_valid = FALSE;
+	    STps->drv_block = 0;
+	    STps->drv_file = 0 ;
+	  }
+	  new_session = TRUE;
+        }
+      }
+    }
+
+    if (osst_wait_ready(STp, &SCpnt,  3 * 60))		/* FIXME - not allowed with NOBLOCK */
+	printk(KERN_WARNING "osst%i: Device did not become Ready in open\n",dev);
+
+    if ((STp->buffer)->last_result_fatal != 0) {
+      if ((STp->device)->scsi_level >= SCSI_2 &&
+	  (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	  (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
+	  SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
+	STp->ready = ST_NO_TAPE;
+      } else
+	STp->ready = ST_NOT_READY;
+      scsi_release_command(SCpnt);
+      SCpnt = NULL;
+      STp->density = 0;   	/* Clear the erroneous "residue" */
+      STp->write_prot = 0;
+      STp->block_size = 0;
+      STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
+      STp->partition = STp->new_partition = 0;
+      STp->door_locked = ST_UNLOCKED;
+      return 0;
+    }
+
+    STp->min_block = STp->max_block = (-1);
+
+    osst_configure_onstream(STp, &SCpnt);
+
+/*	STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0; FIXME */
+
+    if (OS_FRAME_SIZE > (STp->buffer)->buffer_size &&
+	!enlarge_buffer(STp->buffer, OS_FRAME_SIZE, STp->restr_dma)) {
+      printk(KERN_NOTICE "osst%d: Framesize %d too large for buffer.\n", dev,
+	     OS_FRAME_SIZE);
+      scsi_release_command(SCpnt);
+      SCpnt = NULL;
+      STp->buffer->in_use = 0;
+      STp->buffer = NULL;
+      if (STp->device->host->hostt->module)
+	 __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+      if (osst_template.module)
+	 __MOD_DEC_USE_COUNT(osst_template.module);
+      return (-EIO);
+    }
+
+    if ((STp->buffer)->buffer_size >= OS_FRAME_SIZE)
+    {
+      for (i = 0, b_size = 0; 
+           i < STp->buffer->sg_segs && (b_size + STp->buffer->sg[i].length) <= OS_DATA_SIZE; 
+           b_size += STp->buffer->sg[i++].length);
+      STp->buffer->aux = (os_aux_t *) (STp->buffer->sg[i].address + OS_DATA_SIZE - b_size);
+#if DEBUG
+      printk(OSST_DEB_MSG "osst%d: b_data points to %p in segment 0 at %p\n", dev,
+			STp->buffer->b_data, STp->buffer->sg[0].address);
+      printk(OSST_DEB_MSG "osst%d: AUX points to %p in segment %d at %p\n", dev,
+                         STp->buffer->aux, i, STp->buffer->sg[i].address);
+#endif
+    } else
+      STp->buffer->aux = NULL; /* this had better never happen! */
+
+    (STp->buffer)->buffer_blocks = 1;
+    (STp->buffer)->buffer_bytes  =
+    (STp->buffer)->read_pointer  =
+    STp->logical_blk_in_buffer   = 0;
+
+#if DEBUG
+    if (debugging)
+      printk(OSST_DEB_MSG "osst%d: Block size: %d, frame size: %d, buffer size: %d (%d blocks).\n",
+	     dev, STp->block_size, OS_FRAME_SIZE, (STp->buffer)->buffer_size,
+	     (STp->buffer)->buffer_blocks);
+#endif
+
+    if (STp->drv_write_prot) {
+      STp->write_prot = 1;
+#if DEBUG
+      if (debugging)
+	printk(OSST_DEB_MSG "osst%d: Write protected\n", dev);
+#endif
+      if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
+	(STp->buffer)->in_use = 0;
+	STp->buffer = NULL;
+	STp->in_use = 0;
+	STp->in_use = 0;
+	if (STp->device->host->hostt->module)
+	    __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+	if(osst_template.module)
+	    __MOD_DEC_USE_COUNT(osst_template.module);
+        scsi_release_command(SCpnt);
+        SCpnt = NULL;
+	return (-EROFS);
+      }
+    }
+
+    if (new_session) {  /* Change the drive parameters for the new mode */
+#if DEBUG
+      if (debugging)
+	printk(OSST_DEB_MSG "osst%d: New Session\n", dev);
+#endif
+      STp->density_changed = STp->blksize_changed = FALSE;
+      STp->compression_changed = FALSE;
+    }
+
+    /*
+     * properly position the tape and check the ADR headers
+     */
+    if (STp->door_locked == ST_UNLOCKED) {
+       if (osst_int_ioctl(STp, &SCpnt, MTLOCK, 0))
+          printk(KERN_WARNING "osst%d: Can't lock drive door\n", dev);
+       else
+	  STp->door_locked = ST_LOCKED_AUTO;
+    }
+
+    osst_analyze_headers(STp, &SCpnt);
+
+    scsi_release_command(SCpnt);
+    SCpnt = NULL;
+
+    return 0;
+}
+
+
+/* Flush the tape buffer before close */
+static int os_scsi_tape_flush(struct file * filp)
+{
+    int result = 0, result2;
+    OS_Scsi_Tape * STp;
+    ST_mode * STm;
+    ST_partstat * STps;
+    Scsi_Cmnd *SCpnt = NULL;
+
+    struct inode *inode = filp->f_dentry->d_inode;
+    kdev_t devt = inode->i_rdev;
+    int dev;
+
+    if (filp->f_count > 1)
+	return 0;
+
+    dev = TAPE_NR(devt);
+    STp = os_scsi_tapes[dev];
+    STm = &(STp->modes[STp->current_mode]);
+    STps = &(STp->ps[STp->partition]);
+
+    if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
+      result = osst_flush_write_buffer(STp, &SCpnt, 1);
+      if (result != 0 && result != (-ENOSPC))
+	  goto out;
+    }
+
+    if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
+
+#if DEBUG
+      if (debugging) {
+	printk(OSST_DEB_MSG "osst%d: File length %ld bytes.\n",
+	       dev, (long)(filp->f_pos));
+	printk(OSST_DEB_MSG "osst%d: Async write waits %d, finished %d.\n",
+	       dev, STp->nbr_waits, STp->nbr_finished);
+      }
+#endif
+
+      result = osst_flush_drive_buffer(STp, &SCpnt);
+      if (result < 0) goto out;
+      result = osst_write_filemark(STp, &SCpnt);
+      if (result < 0) goto out;
+
+      if (STps->drv_file >= 0)
+	STps->drv_file++ ;
+      STps->drv_block = 0;
+
+      result = osst_write_eod(STp, &SCpnt);
+      osst_write_header(STp, &SCpnt, !(STp->rew_at_close));
+
+      STps->eof = ST_FM;
+
+#if DEBUG
+      if (debugging)
+	printk(OSST_DEB_MSG "osst%d: Buffer flushed, %d EOF(s) written\n",
+	       dev, 1+STp->two_fm);
+#endif
+    }
+    else if (!STp->rew_at_close) {
+	STps = &(STp->ps[STp->partition]);
+	if (!STm->sysv || STps->rw != ST_READING) {
+	    if (STp->can_bsr)
+		result = osst_flush_buffer(STp, &SCpnt, 0);
+	    else if (STps->eof == ST_FM_HIT) {
+		result = cross_eof(STp, &SCpnt, FALSE);
+		if (result) {
+		    if (STps->drv_file >= 0)
+			STps->drv_file++;
+		    STps->drv_block = 0;
+		    STps->eof = ST_FM;
+		}
+		else
+		    STps->eof = ST_NOEOF;
+	    }
+	}
+	else if ((STps->eof == ST_NOEOF &&
+		  !(result = cross_eof(STp, &SCpnt, TRUE))) ||
+		 STps->eof == ST_FM_HIT) {
+	    if (STps->drv_file >= 0)
+		STps->drv_file++;
+	    STps->drv_block = 0;
+	    STps->eof = ST_FM;
+	}
+    }
+
+out:
+    if (STp->rew_at_close) {
+      result2 = osst_position_tape_and_confirm(STp, &SCpnt, STp->first_data_ppos);
+      STps->drv_file = STps->drv_block = STp->logical_blk_num = 0;
+      if (result == 0)
+	  result = result2;
+    }
+    if (SCpnt) scsi_release_command(SCpnt);
+
+    return result;
+}
+
+
+/* Close the device and release it */
+static int os_scsi_tape_close(struct inode * inode, struct file * filp)
+{
+    int result = 0;
+    OS_Scsi_Tape * STp;
+    Scsi_Cmnd *SCpnt = NULL;
+
+    kdev_t devt = inode->i_rdev;
+    int dev;
+
+    dev = TAPE_NR(devt);
+    STp = os_scsi_tapes[dev];
+
+    if (STp->door_locked == ST_LOCKED_AUTO)
+      osst_int_ioctl(STp, &SCpnt, MTUNLOCK, 0);
+    if (SCpnt) scsi_release_command(SCpnt);
+
+    if (STp->buffer != 0)
+      STp->buffer->in_use = 0;
+
+    STp->in_use = 0;
+    if (STp->device->host->hostt->module)
+	__MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+    if(osst_template.module)
+	__MOD_DEC_USE_COUNT(osst_template.module);
+
+    return result;
+}
+
+
+/* The ioctl command */
+static int osst_ioctl(struct inode * inode,struct file * file,
+	 unsigned int cmd_in, unsigned long arg)
+{
+   int i, cmd_nr, cmd_type;
+   unsigned int blk;
+   struct mtop mtc;
+   struct mtpos mt_pos;
+   OS_Scsi_Tape *STp;
+   ST_mode *STm;
+   ST_partstat *STps;
+   Scsi_Cmnd *SCpnt = NULL;
+   int dev = TAPE_NR(inode->i_rdev);
+
+   STp = os_scsi_tapes[dev];
+#if DEBUG
+   if (debugging && !STp->in_use) {
+     printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev);
+     return (-EIO);
+   }
+#endif
+   STm = &(STp->modes[STp->current_mode]);
+   STps = &(STp->ps[STp->partition]);
+
+    /*
+     * If we are in the middle of error recovery, don't let anyone
+     * else try and use this device.  Also, if error recovery fails, it
+     * may try and take the device offline, in which case all further
+     * access to the device is prohibited.
+     */
+    if( !scsi_block_when_processing_errors(STp->device) ) {
+        return -ENXIO;
+    }
+
+   cmd_type = _IOC_TYPE(cmd_in);
+   cmd_nr   = _IOC_NR(cmd_in);
+
+   if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+     if (_IOC_SIZE(cmd_in) != sizeof(mtc))
+       return (-EINVAL);
+
+     i = copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop));
+     if (i)
+	 return (-EFAULT);
+
+     if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
+       printk(KERN_WARNING "osst%d: MTSETDRVBUFFER only allowed for root.\n", dev);
+       return (-EPERM);
+     }
+     if (!STm->defined &&
+	 (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0))
+       return (-ENXIO);
+
+     if (!(STp->device)->was_reset) {
+
+       if (STps->eof == ST_FM_HIT) {
+	 if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) {
+	   mtc.mt_count -= 1;
+	   if (STps->drv_file >= 0)
+	     STps->drv_file += 1;
+	 }
+	 else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
+	   mtc.mt_count += 1;
+	   if (STps->drv_file >= 0)
+	     STps->drv_file += 1;
+	 }
+       }
+
+       if (mtc.mt_op == MTSEEK) {
+	   /* Old position must be restored if partition will be changed */
+	   i = !STp->can_partitions ||
+	       (STp->new_partition != STp->partition);
+       }
+       else {
+	   i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+	       mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
+	       mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+	       mtc.mt_op == MTCOMPRESSION;
+       }
+       i = osst_flush_buffer(STp, &SCpnt, i);
+       if (SCpnt) scsi_release_command(SCpnt);
+       if (i < 0)
+	 return i;
+     }
+     else {
+       /*
+	* If there was a bus reset, block further access
+	* to this device.  If the user wants to rewind the tape,
+	* then reset the flag and allow access again.
+	*/
+       if(mtc.mt_op != MTREW &&
+	  mtc.mt_op != MTOFFL &&
+	  mtc.mt_op != MTRETEN &&
+	  mtc.mt_op != MTERASE &&
+	  mtc.mt_op != MTSEEK &&
+	  mtc.mt_op != MTEOM)
+	 return (-EIO);
+       STp->device->was_reset = 0;
+       if (STp->door_locked != ST_UNLOCKED &&
+	   STp->door_locked != ST_LOCK_FAILS) {
+	 if (osst_int_ioctl(STp, &SCpnt, MTLOCK, 0)) {
+	   printk(KERN_NOTICE "osst%d: Could not relock door after bus reset.\n",
+		  dev);
+	   STp->door_locked = ST_UNLOCKED;
+	 }
+       }
+     }
+
+     if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+	 mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+	 mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
+       STps->rw = ST_IDLE;  /* Prevent automatic WEOF and fsf */
+
+     if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
+       osst_int_ioctl(STp, &SCpnt, MTUNLOCK, 0);  /* Ignore result! */
+     if (SCpnt) scsi_release_command(SCpnt);
+
+     if (mtc.mt_op == MTSETDRVBUFFER &&
+	 (mtc.mt_count & MT_ST_OPTIONS) != 0)
+       return osst_set_options(STp, mtc.mt_count);
+     if (mtc.mt_op == MTSETPART) {
+/*     if (!STp->can_partitions ||
+	   mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
+	 return (-EINVAL);
+       if (mtc.mt_count >= STp->nbr_partitions &&
+	   (STp->nbr_partitions = nbr_partitions(inode)) < 0)
+	 return (-EIO);*/
+       if (mtc.mt_count >= STp->nbr_partitions)
+	 return (-EINVAL);
+       STp->new_partition = mtc.mt_count;
+       return 0;
+     }
+     if (mtc.mt_op == MTMKPART) {
+       if (!STp->can_partitions)
+	 return (-EINVAL);
+       if ((i = osst_int_ioctl(STp, &SCpnt, MTREW, 0)) < 0 /*||
+	   (i = partition_tape(inode, mtc.mt_count)) < 0*/) {
+         if (SCpnt) scsi_release_command(SCpnt);
+	 return i;
+       }
+       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	 STp->ps[i].rw = ST_IDLE;
+	 STp->ps[i].at_sm = 0;
+	 STp->ps[i].last_block_valid = FALSE;
+       }
+       STp->partition = STp->new_partition = 0;
+       STp->nbr_partitions = 1;  /* Bad guess ?-) */
+       STps->drv_block = STps->drv_file = 0;
+       return 0;
+     }
+     if (mtc.mt_op == MTSEEK) {
+       i = osst_seek_frame(STp, &SCpnt, mtc.mt_count);
+       if (SCpnt) scsi_release_command(SCpnt);
+       if (!STp->can_partitions)
+	   STp->ps[0].rw = ST_IDLE;
+       return i;
+     }
+/*   if (STp->can_partitions && STp->ready == ST_READY &&
+	 (i = update_partition(inode)) < 0)
+       return i;*/
+     if (mtc.mt_op == MTCOMPRESSION)
+       return (-EINVAL)/*osst_compression(STp, (mtc.mt_count & 1))*/;
+     else {
+       i = osst_int_ioctl(STp, &SCpnt, mtc.mt_op, mtc.mt_count);
+       if (SCpnt) scsi_release_command(SCpnt);
+       return i;
+     }
+   }
+
+   if (!STm->defined)
+     return (-ENXIO);
+
+   i = osst_flush_buffer(STp, &SCpnt, FALSE);
+   if (SCpnt) scsi_release_command(SCpnt);
+   if (i < 0)
+     return i;
+/* if (STp->can_partitions &&
+       (i = update_partition(inode)) < 0)
+     return i;*/
+
+   if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+     struct mtget mt_status;
+
+     if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
+       return (-EINVAL);
+
+     mt_status.mt_type = MT_ISONSTREAM_SC;
+     mt_status.mt_erreg = STp->recover_erreg << MT_ST_SOFTERR_SHIFT;
+     mt_status.mt_dsreg =
+       ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+       ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+     mt_status.mt_blkno = STps->drv_block;
+     mt_status.mt_fileno = STps->drv_file;
+     if (STp->block_size != 0) {
+       if (STps->rw == ST_WRITING)
+	 mt_status.mt_blkno +=
+	   (STp->buffer)->buffer_bytes / STp->block_size;
+       else if (STps->rw == ST_READING)
+	 mt_status.mt_blkno -= ((STp->buffer)->buffer_bytes +
+	   STp->block_size - 1) / STp->block_size;
+     }
+
+     mt_status.mt_gstat = 0;
+     if (STp->drv_write_prot)
+       mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+     if (mt_status.mt_blkno == 0) {
+       if (mt_status.mt_fileno == 0)
+	 mt_status.mt_gstat |= GMT_BOT(0xffffffff);
+       else
+	 mt_status.mt_gstat |= GMT_EOF(0xffffffff);
+     }
+     mt_status.mt_resid = STp->partition;
+     if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
+       mt_status.mt_gstat |= GMT_EOT(0xffffffff);
+     else if (STps->eof >= ST_EOM_OK)
+       mt_status.mt_gstat |= GMT_EOD(0xffffffff);
+     if (STp->density == 1)
+       mt_status.mt_gstat |= GMT_D_800(0xffffffff);
+     else if (STp->density == 2)
+       mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
+     else if (STp->density == 3)
+       mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
+     if (STp->ready == ST_READY)
+       mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
+     if (STp->ready == ST_NO_TAPE)
+       mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
+     if (STps->at_sm)
+       mt_status.mt_gstat |= GMT_SM(0xffffffff);
+     if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) ||
+	 STp->drv_buffer != 0)
+       mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+
+     i = copy_to_user((char *)arg, (char *)&mt_status,
+		      sizeof(struct mtget));
+     if (i)
+	 return (-EFAULT);
+
+     STp->recover_erreg = 0;  /* Clear after read */
+     return 0;
+   } /* End of MTIOCGET */
+
+   if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+     if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
+       return (-EINVAL);
+     blk = osst_get_frame_position(STp, &SCpnt);
+     if (SCpnt) scsi_release_command(SCpnt);
+     if (blk < 0)
+       return blk;
+     mt_pos.mt_blkno = blk;
+     i = copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
+     if (i)
+	 return (-EFAULT);
+     return 0;
+   }
+
+   return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+}
+
+
+/* Memory handling routines */
+
+/* Try to allocate a new tape buffer */
+static OSST_buffer * new_tape_buffer( int from_initialization, int need_dma )
+{
+  int i, priority, b_size, order, got = 0, segs = 0;
+  OSST_buffer *tb;
+
+  if (osst_nbr_buffers >= osst_template.dev_max)
+    return NULL;  /* Should never happen */
+
+  if (from_initialization)
+    priority = GFP_ATOMIC;
+  else
+    priority = GFP_KERNEL;
+
+  i = sizeof(OSST_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist);
+  tb = (OSST_buffer *)kmalloc(i, priority);
+  if (tb) {
+//    tb->this_size = i;
+    if (need_dma)
+      priority |= GFP_DMA;
+
+    /* Try to allocate the first segment up to OSST_FIRST_ORDER and the
+       others big enough to reach the goal */
+    for (b_size = PAGE_SIZE,          order = 0;
+	 b_size < osst_buffer_size && order < OSST_FIRST_ORDER;
+	 b_size *= 2,                 order++ );
+
+    for ( ; b_size >= PAGE_SIZE; order--, b_size /= 2) {
+	tb->sg[0].address =
+	    (unsigned char *)__get_free_pages(priority, order);
+	if (tb->sg[0].address != NULL) {
+	    tb->sg[0].alt_address = NULL;
+	    tb->sg[0].length = b_size;
+	    break;
+	}
+    }
+    if (tb->sg[segs].address == NULL) {
+	kfree(tb);
+	tb = NULL;
+    }
+    else {  /* Got something, continue */
+
+	for (b_size = PAGE_SIZE, order = 0;
+	     osst_buffer_size > tb->sg[0].length + (OSST_FIRST_SG - 1) * b_size;
+	     b_size *= 2, order++ );
+
+	for (segs=1, got=tb->sg[0].length;
+	     got < osst_buffer_size && segs < OSST_FIRST_SG; ) {
+	    tb->sg[segs].address =
+		(unsigned char *)__get_free_pages(priority, order);
+	    if (tb->sg[segs].address == NULL) {
+		if (osst_buffer_size - got <=
+		    (OSST_FIRST_SG - segs) * b_size / 2) {
+		    b_size /= 2; /* Large enough for the rest of the buffers */
+		    order--;
+		    continue;
+		}
+		tb->sg_segs = segs;
+		tb->orig_sg_segs = 0;
+#if DEBUG
+		tb->buffer_size = got;
+#endif
+		normalize_buffer(tb);
+		kfree(tb);
+		tb = NULL;
+		break;
+	    }
+	    tb->sg[segs].alt_address = NULL;
+	    tb->sg[segs].length = b_size;
+	    got += b_size;
+	    segs++;
+	}
+    }
+  }
+  if (!tb) {
+    printk(KERN_NOTICE "osst: Can't allocate new tape buffer (nbr %d).\n",
+	   osst_nbr_buffers);
+    return NULL;
+  }
+  tb->sg_segs = tb->orig_sg_segs = segs;
+  tb->b_data = tb->sg[0].address;
+
+#if DEBUG
+  if (debugging) {
+    printk(OSST_DEB_MSG
+    "osst: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n",
+	   osst_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data);
+    printk(OSST_DEB_MSG
+	   "osst: segment sizes: first %d, last %d bytes.\n",
+	   tb->sg[0].length, tb->sg[segs-1].length);
+  }
+#endif
+  tb->in_use = 0;
+  tb->dma = need_dma;
+  tb->buffer_size = got;
+  tb->writing = 0;
+  osst_buffers[osst_nbr_buffers++] = tb;
+
+  return tb;
+}
+
+
+/* Try to allocate a temporary enlarged tape buffer */
+static int enlarge_buffer(OSST_buffer *STbuffer, int new_size, int need_dma)
+{
+  int segs, nbr, max_segs, b_size, priority, order, got;
+
+  normalize_buffer(STbuffer);
+
+  max_segs = STbuffer->use_sg;
+  if (max_segs > osst_max_sg_segs)
+      max_segs = osst_max_sg_segs;
+  nbr = max_segs - STbuffer->sg_segs;
+  if (nbr <= 0)
+      return FALSE;
+
+  priority = GFP_KERNEL;
+  if (need_dma)
+    priority |= GFP_DMA;
+  for (b_size = PAGE_SIZE, order = 0;
+       b_size * nbr < new_size - STbuffer->buffer_size;
+       b_size *= 2, order++);
+
+  for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size;
+       segs < max_segs && got < new_size; ) {
+      STbuffer->sg[segs].address =
+	  (unsigned char *)__get_free_pages(priority, order);
+      if (STbuffer->sg[segs].address == NULL) {
+	  if (new_size - got <= (max_segs - segs) * b_size / 2) {
+	      b_size /= 2;  /* Large enough for the rest of the buffers */
+	      order--;
+	      continue;
+	  }
+	  printk(KERN_NOTICE "osst: Failed to enlarge buffer to %d bytes.\n",
+		 new_size);
+#if DEBUG
+	  STbuffer->buffer_size = got;
+#endif
+	  normalize_buffer(STbuffer);
+	  return FALSE;
+      }
+      STbuffer->sg[segs].alt_address = NULL;
+      STbuffer->sg[segs].length = b_size;
+      STbuffer->sg_segs += 1;
+      got += b_size;
+      STbuffer->buffer_size = got;
+      segs++;
+  }
+#if DEBUG
+  if (debugging) {
+    for (nbr=0; osst_buffers[nbr] != STbuffer && nbr < osst_nbr_buffers; nbr++);
+    printk(OSST_DEB_MSG
+    "osst: Expanded tape buffer %d (%d bytes, %d->%d segments, dma: %d, a: %p).\n",
+	   nbr, got, STbuffer->orig_sg_segs, STbuffer->sg_segs, need_dma, STbuffer->b_data);
+    printk(OSST_DEB_MSG
+	   "osst: segment sizes: first %d, last %d bytes.\n",
+	   STbuffer->sg[0].length, STbuffer->sg[segs-1].length);
+  }
+#endif
+
+  return TRUE;
+}
+
+
+/* Release the extra buffer */
+static void normalize_buffer(OSST_buffer *STbuffer)
+{
+  int i, order, b_size;
+
+  for (i=STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
+
+      for (b_size = PAGE_SIZE, order = 0;
+	   b_size < STbuffer->sg[i].length;
+	   b_size *= 2, order++);
+
+      free_pages((unsigned long)STbuffer->sg[i].address, order);
+      STbuffer->buffer_size -= STbuffer->sg[i].length;
+  }
+#if DEBUG
+  if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
+      printk(OSST_DEB_MSG "osst: Buffer at %p normalized to %d bytes (segs %d).\n",
+	     STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
+#endif
+  STbuffer->sg_segs = STbuffer->orig_sg_segs;
+}
+
+
+/* Move data from the user buffer to the tape buffer. Returns zero (success) or
+   negative error code. */
+static int append_to_buffer(const char *ubp, OSST_buffer *st_bp, int do_count)
+{
+    int i, cnt, res, offset;
+
+    for (i=0, offset=st_bp->buffer_bytes;
+	 i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+	offset -= st_bp->sg[i].length;
+    if (i == st_bp->sg_segs) {  /* Should never happen */
+	printk(KERN_WARNING "osst: Append_to_buffer offset overflow.\n");
+	return (-EIO);
+    }
+    for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+	cnt = st_bp->sg[i].length - offset < do_count ?
+	    st_bp->sg[i].length - offset : do_count;
+	res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt);
+	if (res)
+	    return (-EFAULT);
+	do_count -= cnt;
+	st_bp->buffer_bytes += cnt;
+	ubp += cnt;
+	offset = 0;
+    }
+    if (do_count) {  /* Should never happen */
+	printk(KERN_WARNING "osst: Append_to_buffer overflow (left %d).\n",
+	       do_count);
+	return (-EIO);
+    }
+    return 0;
+}
+
+
+/* Move data from the tape buffer to the user buffer. Returns zero (success) or
+   negative error code. */
+static int from_buffer(OSST_buffer *st_bp, char *ubp, int do_count)
+{
+    int i, cnt, res, offset;
+
+    for (i=0, offset=st_bp->read_pointer;
+	 i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+	offset -= st_bp->sg[i].length;
+    if (i == st_bp->sg_segs) {  /* Should never happen */
+	printk(KERN_WARNING "osst: From_buffer offset overflow.\n");
+	return (-EIO);
+    }
+    for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+	cnt = st_bp->sg[i].length - offset < do_count ?
+	    st_bp->sg[i].length - offset : do_count;
+	res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt);
+	if (res)
+	    return (-EFAULT);
+	do_count -= cnt;
+	st_bp->buffer_bytes -= cnt;
+	st_bp->read_pointer += cnt;
+	ubp += cnt;
+	offset = 0;
+    }
+    if (do_count) {  /* Should never happen */
+	printk(KERN_WARNING "osst: From_buffer overflow (left %d).\n",
+	       do_count);
+	return (-EIO);
+    }
+    return 0;
+}
+
+/* Sets the tail of the buffer after fill point to zero.
+   Returns zero (success) or negative error code.        */
+static int osst_zero_buffer_tail(OSST_buffer *st_bp)
+{
+	int	i, offset, do_count, cnt;
+
+	for (i = 0, offset = st_bp->buffer_bytes;
+	     i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+		offset -= st_bp->sg[i].length;
+	if (i == st_bp->sg_segs) {  /* Should never happen */
+		printk(KERN_WARNING "osst: Zero_buffer offset overflow.\n");
+		return (-EIO);
+	}
+	for (do_count = OS_DATA_SIZE - st_bp->read_pointer;
+	     i < st_bp->sg_segs && do_count > 0; i++) {
+		cnt = st_bp->sg[i].length - offset < do_count ?
+		      st_bp->sg[i].length - offset : do_count ;
+		memset(st_bp->sg[i].address + offset, 0, cnt);
+		do_count -= cnt;
+		offset = 0;
+	}
+	if (do_count) {  /* Should never happen */
+		printk(KERN_WARNING "osst: Zero_buffer overflow (left %d).\n", do_count);
+		return (-EIO);
+	}
+	return 0;
+}
+
+/* Copy a osst 32K block of memory into the buffer.
+   Returns zero (success) or negative error code.  */
+static int osst_copy_to_buffer(OSST_buffer *st_bp, unsigned char *ptr)
+{
+	int	i, cnt, do_count = OS_DATA_SIZE;
+
+	for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
+		cnt = st_bp->sg[i].length < do_count ?
+		      st_bp->sg[i].length : do_count ;
+		memcpy(st_bp->sg[i].address, ptr, cnt);
+		do_count -= cnt;
+		ptr      += cnt;
+	}
+	if (do_count || i != st_bp->sg_segs-1) {  /* Should never happen */
+		printk(KERN_WARNING "osst: Copy_to_buffer overflow (left %d at sg %d).\n",
+					 do_count, i);
+		return (-EIO);
+	}
+	return 0;
+}
+
+/* Copy a osst 32K block of memory from the buffer.
+   Returns zero (success) or negative error code.  */
+static int osst_copy_from_buffer(OSST_buffer *st_bp, unsigned char *ptr)
+{
+	int	i, cnt, do_count = OS_DATA_SIZE;
+
+	for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
+		cnt = st_bp->sg[i].length < do_count ?
+		      st_bp->sg[i].length : do_count ;
+		memcpy(ptr, st_bp->sg[i].address, cnt);
+		do_count -= cnt;
+		ptr      += cnt;
+	}
+	if (do_count || i != st_bp->sg_segs-1) {  /* Should never happen */
+		printk(KERN_WARNING "osst: Copy_from_buffer overflow (left %d at sg %d).\n",
+					 do_count, i);
+		return (-EIO);
+	}
+	return 0;
+}
+
+
+/* Module housekeeping */
+
+#ifndef MODULE
+/* Set the boot options. Syntax: st=xxx,yyy
+   where xxx is buffer size in 1024 byte blocks and yyy is write threshold
+   in 1024 byte blocks. */
+__initfunc( void osst_setup(char *str, int *ints))
+{
+  if (ints[0] > 0 && ints[1] > 0)
+    osst_buffer_size = ints[1] * ST_KILOBYTE;
+  if (ints[0] > 1 && ints[2] > 0) {
+    osst_write_threshold = ints[2] * ST_KILOBYTE;
+    if (osst_write_threshold > osst_buffer_size)
+      osst_write_threshold = osst_buffer_size;
+  }
+  if (ints[0] > 2 && ints[3] > 0)
+    osst_max_buffers = ints[3];
+}
+#endif
+
+
+static struct file_operations osst_fops = {
+   NULL,            /* lseek - default */
+   osst_read,         /* read - general block-dev read */
+   osst_write,        /* write - general block-dev write */
+   NULL,            /* readdir - bad */
+   NULL,            /* select */
+   osst_ioctl,        /* ioctl */
+   NULL,            /* mmap */
+   os_scsi_tape_open,  /* open */
+   os_scsi_tape_flush, /* flush */
+   os_scsi_tape_close, /* release */
+   NULL		    /* fsync */
+};
+
+
+static int osst_attach(Scsi_Device * SDp){
+   OS_Scsi_Tape * tpnt;
+   ST_mode * STm;
+   ST_partstat * STps;
+   int i;
+#ifdef CONFIG_DEVFS_FS
+   int mode;
+#endif
+
+   if (SDp->type != TYPE_TAPE || !OSST_SUPPORTS(SDp))
+       return 1;
+
+   if (osst_template.nr_dev >= osst_template.dev_max) {
+       SDp->attached--;
+       return 1;
+   }
+	
+   /* find a free minor number */
+   for (i=0; os_scsi_tapes[i] && i<osst_template.dev_max; i++);
+   if(i >= osst_template.dev_max) panic ("Scsi_devices corrupt (osst)");
+
+   /* allocate a OS_Scsi_Tape for this device */
+   tpnt = (OS_Scsi_Tape *)kmalloc(sizeof(OS_Scsi_Tape), GFP_ATOMIC);
+   if (tpnt == NULL) {
+       SDp->attached--;
+       printk(KERN_ERR "osst: Can't allocate device descriptor.\n");
+       return 1;
+   }
+   memset(tpnt, 0, sizeof(OS_Scsi_Tape));
+   os_scsi_tapes[i] = tpnt;
+   tpnt->capacity = 0xfffff;
+
+   /* allocate a buffer for this device */
+   if (!new_tape_buffer(TRUE, TRUE)) 
+       printk(KERN_ERR "osst: Unable to allocate a tape buffer.\n");
+
+#ifdef CONFIG_DEVFS_FS
+   for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+       char name[8];
+       static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"};
+
+       /*  Rewind entry  */
+       sprintf (name, "mt%s", formats[mode]);
+       tpnt->de_r[mode] =
+	   devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
+			   MAJOR_NR, i + (mode << 5),
+			   S_IFCHR | S_IRUGO | S_IWUGO,
+			   0, 0, &osst_fops, NULL);
+       /*  No-rewind entry  */
+       sprintf (name, "mt%sn", formats[mode]);
+       tpnt->de_n[mode] =
+	   devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
+			   MAJOR_NR, i + (mode << 5) + 128,
+			   S_IFCHR | S_IRUGO | S_IWUGO,
+			   0, 0, &osst_fops, NULL);
+   }
+   devfs_register_tape (tpnt->de_r[0]);
+#endif
+
+   tpnt->device = SDp;
+   tpnt->devt = MKDEV(MAJOR_NR, i);
+   tpnt->dirty = 0;
+   tpnt->in_use = 0;
+   tpnt->drv_buffer = 1;  /* Try buffering if no mode sense */
+   tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
+   tpnt->density = 0;
+   tpnt->do_auto_lock = OSST_AUTO_LOCK;
+   tpnt->can_bsr = OSST_IN_FILE_POS;
+   tpnt->can_partitions = 0;
+   tpnt->two_fm = OSST_TWO_FM;
+   tpnt->fast_mteom = OSST_FAST_MTEOM;
+   tpnt->scsi2_logical = OSST_SCSI2LOGICAL; /* FIXME */
+   tpnt->write_threshold = osst_write_threshold;
+   tpnt->default_drvbuffer = 0xff; /* No forced buffering */
+   tpnt->partition = 0;
+   tpnt->new_partition = 0;
+   tpnt->nbr_partitions = 0;
+   tpnt->timeout = OSST_TIMEOUT;
+   tpnt->long_timeout = OSST_LONG_TIMEOUT;
+
+   /* Recognize OnStream tapes */
+   printk ("osst%i: Tape driver with OnStream support osst %s\nosst%i: %s\n",
+	       i, osst_version, i, cvsid);
+   /* We don't need to test for OnStream, as this has been done in detect () */
+   tpnt->os_fw_rev = osst_parse_firmware_rev (SDp->rev);
+#if DEBUG
+   printk ("osst%i: OnStream tape drive recognized, Model %s\n", i, SDp->model);
+#endif
+   tpnt->omit_blklims = 1;
+
+   tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp);
+   tpnt->logical_blk_in_buffer = 0;
+   tpnt->header_ok = 0;
+   tpnt->linux_media = 0;
+   tpnt->header_cache = NULL;
+
+   for (i=0; i < ST_NBR_MODES; i++) {
+     STm = &(tpnt->modes[i]);
+     STm->defined = FALSE;
+     STm->sysv = OSST_SYSV;
+     STm->defaults_for_writes = 0;
+     STm->do_async_writes = OSST_ASYNC_WRITES;
+     STm->do_buffer_writes = OSST_BUFFER_WRITES;
+     STm->do_read_ahead = OSST_READ_AHEAD;
+     STm->default_compression = ST_DONT_TOUCH;
+     STm->default_blksize = 32 * ST_KILOBYTE;  /* No forced size */
+     STm->default_density = (-1);  /* No forced density */
+   }
+
+   for (i=0; i < ST_NBR_PARTITIONS; i++) {
+     STps = &(tpnt->ps[i]);
+     STps->rw = ST_IDLE;
+     STps->eof = ST_NOEOF;
+     STps->at_sm = 0;
+     STps->last_block_valid = FALSE;
+     STps->drv_block = (-1);
+     STps->drv_file = (-1);
+   }
+
+   tpnt->current_mode = 0;
+   tpnt->modes[0].defined = TRUE;
+
+   tpnt->density_changed = tpnt->compression_changed =
+     tpnt->blksize_changed = FALSE;
+
+   osst_template.nr_dev++;
+   return 0;
+};
+
+static int osst_detect(Scsi_Device * SDp)
+{
+  if (SDp->type != TYPE_TAPE) return 0;
+  /* We are willing to drive OnStream SC-x0 as well as the
+   * IDE, ParPort, USB variants, if accessible by emulation
+   * layer (ide-scsi, usb-storage, ...) */
+  if (!OSST_SUPPORTS(SDp)) return 0;
+
+  printk(KERN_WARNING
+	 "Detected OnStream scsi tape osst%d at scsi%d, channel %d, id %d, lun %d\n",
+	 osst_template.dev_noticed++,
+	 SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+  return 1;
+}
+
+static int osst_registered = 0;
+
+/* Driver initialization (not __initfunc because may be called later) */
+static int osst_init()
+{
+  int i;
+
+  if (osst_template.dev_noticed == 0) return 0;
+
+  if(!osst_registered) {
+#ifdef CONFIG_DEVFS_FS
+    if (devfs_register_chrdev(MAJOR_NR,"osst",&osst_fops)) {
+#else
+    if (register_chrdev(MAJOR_NR,"osst",&osst_fops)) {
+#endif
+      printk(KERN_ERR "osst: Unable to get major %d for OnStream tapes\n",MAJOR_NR);
+      return 1;
+    }
+    osst_registered++;
+  }
+
+  if (os_scsi_tapes) return 0;
+  osst_template.dev_max = OSST_MAX_TAPES;
+  if (osst_template.dev_max > 128 / ST_NBR_MODES)
+    printk(KERN_INFO "osst: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
+  os_scsi_tapes =
+    (OS_Scsi_Tape **)kmalloc(osst_template.dev_max * sizeof(OS_Scsi_Tape *),
+				   GFP_ATOMIC);
+  if (os_scsi_tapes == NULL) {
+    printk(KERN_ERR "osst: Unable to allocate array for OnStream SCSI tapes.\n");
+#ifdef CONFIG_DEVFS_FS
+    devfs_unregister_chrdev(MAJOR_NR, "osst");
+#else
+    unregister_chrdev(MAJOR_NR, "osst");
+#endif
+    return 1;
+  }
+
+  for (i=0; i < osst_template.dev_max; ++i) os_scsi_tapes[i] = NULL;
+
+  /* Allocate the buffer pointers */
+  osst_buffers =
+    (OSST_buffer **)kmalloc(osst_template.dev_max * sizeof(OSST_buffer *),
+				    GFP_ATOMIC);
+  if (osst_buffers == NULL) {
+    printk(KERN_ERR "osst: Unable to allocate tape buffer pointers.\n");
+#ifdef CONFIG_DEVFS_FS
+    devfs_unregister_chrdev(MAJOR_NR, "osst");
+#else
+    unregister_chrdev(MAJOR_NR, "osst");
+#endif
+    kfree(os_scsi_tapes);
+    return 1;
+  }
+  osst_nbr_buffers = 0;
+
+#if DEBUG
+  printk(OSST_DEB_MSG "osst: Buffer size %d bytes, write threshold %d bytes.\n",
+	 osst_buffer_size, osst_write_threshold);
+#endif
+  return 0;
+}
+
+
+static void osst_detach(Scsi_Device * SDp)
+{
+  OS_Scsi_Tape * tpnt;
+  int i;
+#ifdef CONFIG_DEVFS_FS
+  int mode;
+#endif
+
+  for(i=0; i<osst_template.dev_max; i++) {
+    tpnt = os_scsi_tapes[i];
+    if(tpnt != NULL && tpnt->device == SDp) {
+      tpnt->device = NULL;
+#ifdef CONFIG_DEVFS_FS
+      for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+	  devfs_unregister (tpnt->de_r[mode]);
+	  tpnt->de_r[mode] = NULL;
+	  devfs_unregister (tpnt->de_n[mode]);
+	  tpnt->de_n[mode] = NULL;
+      }
+#endif
+      kfree(tpnt);
+      os_scsi_tapes[i] = NULL;
+      SDp->attached--;
+      osst_template.nr_dev--;
+      osst_template.dev_noticed--;
+      return;
+    }
+  }
+  return;
+}
+
+
+#ifdef MODULE
+
+int init_module(void) {
+  int result;
+
+  if (buffer_kbs > 0)
+      osst_buffer_size = buffer_kbs * ST_KILOBYTE;
+  if (write_threshold_kbs > 0)
+      osst_write_threshold = write_threshold_kbs * ST_KILOBYTE;
+  if (osst_write_threshold > osst_buffer_size)
+      osst_write_threshold = osst_buffer_size;
+  if (max_buffers > 0)
+      osst_max_buffers = max_buffers;
+  if (max_sg_segs >= OSST_FIRST_SG)
+      osst_max_sg_segs = max_sg_segs;
+  printk(KERN_INFO "osst: bufsize %d, wrt %d, max buffers %d, s/g segs %d.\n",
+	 osst_buffer_size, osst_write_threshold, osst_max_buffers, osst_max_sg_segs);
+//printk(OSST_DEB_MSG "osst: sizeof(header) = %d (%s)\n",sizeof(os_header_t),sizeof(os_header_t)==OS_DATA_SIZE?"ok":"error");
+  osst_template.module = &__this_module;
+  result = scsi_register_module(MODULE_SCSI_DEV, &osst_template);
+  if (result)
+      return result;
+
+  return 0;
+}
+
+void cleanup_module( void)
+{
+  int i;
+  OS_Scsi_Tape * STp;
+
+  scsi_unregister_module(MODULE_SCSI_DEV, &osst_template);
+#ifdef CONFIG_DEVFS_FS
+  devfs_unregister_chrdev(MAJOR_NR, "osst");
+#else
+  unregister_chrdev(MAJOR_NR, "osst");
+#endif
+  osst_registered--;
+  if(os_scsi_tapes != NULL) {
+    for (i=0; i < osst_template.dev_max; ++i) {
+      if ((STp = os_scsi_tapes[i])) {
+	if (STp->header_cache != NULL) vfree(STp->header_cache);
+	kfree(STp);
+      }
+    }
+    kfree(os_scsi_tapes);
+
+    if (osst_buffers != NULL) {
+      for (i=0; i < osst_nbr_buffers; i++)
+	if (osst_buffers[i] != NULL) {
+	  osst_buffers[i]->orig_sg_segs = 0;
+	  normalize_buffer(osst_buffers[i]);
+	  kfree(osst_buffers[i]);
+	}
+
+      kfree(osst_buffers);
+    }
+  }
+  osst_template.dev_max = 0;
+  printk(KERN_INFO "osst: Unloaded.\n");
+}
+#endif /* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/osst.h linux.19p1/drivers/scsi/osst.h
--- linux.vanilla/drivers/scsi/osst.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/scsi/osst.h	Wed Dec 13 23:15:39 2000
@@ -0,0 +1,513 @@
+/*
+ *	$Header: /home/cvsroot/Driver/osst.h,v 1.5.2.1 2000/09/24 14:26:02 riede Exp $
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+
+/*	FIXME - rename and use the following two types or delete them!
+ *              and the types really should go to st.h anyway...
+ *	INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	response_format :4;	/* Response Data Format */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+	u8		additional_length;	/* Additional Length (total_length-4) */
+	u8		rsv5, rsv6, rsv7;	/* Reserved */
+	u8		vendor_id[8];		/* Vendor Identification */
+	u8		product_id[16];		/* Product Identification */
+	u8		revision_level[4];	/* Revision Level */
+	u8		vendor_specific[20];	/* Vendor Specific - Optional */
+	u8		reserved56t95[40];	/* Reserved - Optional */
+						/* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ *	READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+	unsigned	reserved0_10	:2;	/* Reserved */
+	unsigned	bpu		:1;	/* Block Position Unknown */	
+	unsigned	reserved0_543	:3;	/* Reserved */
+	unsigned	eop		:1;	/* End Of Partition */
+	unsigned	bop		:1;	/* Beginning Of Partition */
+	u8		partition;		/* Partition Number */
+	u8		reserved2, reserved3;	/* Reserved */
+	u32		first_block;		/* First Block Location */
+	u32		last_block;		/* Last Block Location (Optional) */
+	u8		reserved12;		/* Reserved */
+	u8		blocks_in_buffer[3];	/* Blocks In Buffer - (Optional) */
+	u32		bytes_in_buffer;	/* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ *      Follows structures which are related to the SELECT SENSE / MODE SENSE
+ *      packet commands. 
+ */
+#define COMPRESSION_PAGE           0x0f
+#define COMPRESSION_PAGE_LENGTH    16
+
+#define CAPABILITIES_PAGE          0x2a
+#define CAPABILITIES_PAGE_LENGTH   20
+
+#define TAPE_PARAMTR_PAGE          0x2b
+#define TAPE_PARAMTR_PAGE_LENGTH   16
+
+#define NUMBER_RETRIES_PAGE        0x2f
+#define NUMBER_RETRIES_PAGE_LENGTH 4
+
+#define BLOCK_SIZE_PAGE            0x30
+#define BLOCK_SIZE_PAGE_LENGTH     4
+
+#define BUFFER_FILLING_PAGE        0x33
+#define BUFFER_FILLING_PAGE_LENGTH 
+
+#define VENDOR_IDENT_PAGE          0x36
+#define VENDOR_IDENT_PAGE_LENGTH   8
+
+#define LOCATE_STATUS_PAGE         0x37
+#define LOCATE_STATUS_PAGE_LENGTH  0
+
+#define MODE_HEADER_LENGTH         4
+
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+	unsigned	error_code	:7;	/* Current of deferred errors */
+	unsigned	valid		:1;	/* The information field conforms to QIC-157C */
+	u8		reserved1	:8;	/* Segment Number - Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	eom		:1;	/* End Of Medium */
+	unsigned	filemark 	:1;	/* Filemark */
+	u32		information __attribute__ ((packed));
+	u8		asl;			/* Additional sense length (n-7) */
+	u32		command_specific;	/* Additional command specific information */
+	u8		asc;			/* Additional Sense Code */
+	u8		ascq;			/* Additional Sense Code Qualifier */
+	u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
+	unsigned	sk_specific1 	:7;	/* Sense Key Specific */
+	unsigned	sksv		:1;	/* Sense Key Specific information is valid */
+	u8		sk_specific2;		/* Sense Key Specific */
+	u8		sk_specific3;		/* Sense Key Specific */
+	u8		pad[2];			/* Padding to 20 bytes */
+} idetape_request_sense_result_t;
+
+/*
+ *      Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+        u8              mode_data_length;       /* Length of the following data transfer */
+        u8              medium_type;            /* Medium Type */
+        u8              dsp;                    /* Device Specific Parameter */
+        u8              bdl;                    /* Block Descriptor Length */
+} osst_mode_parameter_header_t;
+
+/*
+ *      Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ *      Support for block descriptors is optional.
+ */
+typedef struct {
+        u8              density_code;           /* Medium density code */
+        u8              blocks[3];              /* Number of blocks */
+        u8              reserved4;              /* Reserved */
+        u8              length[3];              /* Block Length */
+} osst_parameter_block_descriptor_t;
+
+/*
+ *      The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+        unsigned        page_code       :6;     /* Page Code - Should be 0xf */
+        unsigned        reserved0       :1;     /* Reserved */
+        unsigned        ps              :1;
+        u8              page_length;            /* Page Length - Should be 14 */
+        unsigned        reserved2       :6;     /* Reserved */
+        unsigned        dcc             :1;     /* Data Compression Capable */
+        unsigned        dce             :1;     /* Data Compression Enable */
+        unsigned        reserved3       :5;     /* Reserved */
+        unsigned        red             :2;     /* Report Exception on Decompression */
+        unsigned        dde             :1;     /* Data Decompression Enable */
+        u32             ca;                     /* Compression Algorithm */
+        u32             da;                     /* Decompression Algorithm */
+        u8              reserved[4];            /* Reserved */
+} osst_data_compression_page_t;
+
+/*
+ *      The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+        unsigned        page_code       :6;     /* Page Code - Should be 0x11 */
+        unsigned        reserved1_6     :1;     /* Reserved */
+        unsigned        ps              :1;
+        u8              page_length;            /* Page Length - Should be 6 */
+        u8              map;                    /* Maximum Additional Partitions - Should be 0 */
+        u8              apd;                    /* Additional Partitions Defined - Should be 0 */
+        unsigned        reserved4_012   :3;     /* Reserved */
+        unsigned        psum            :2;     /* Should be 0 */
+        unsigned        idp             :1;     /* Should be 0 */
+        unsigned        sdp             :1;     /* Should be 0 */
+        unsigned        fdp             :1;     /* Fixed Data Partitions */
+        u8              mfr;                    /* Medium Format Recognition */
+        u8              reserved[2];            /* Reserved */
+} osst_medium_partition_page_t;
+
+/*
+ *      Capabilities and Mechanical Status Page
+ */
+typedef struct {
+        unsigned        page_code       :6;     /* Page code - Should be 0x2a */
+        unsigned        reserved1_67    :2;
+        u8              page_length;            /* Page Length - Should be 0x12 */
+        u8              reserved2, reserved3;
+        unsigned        ro              :1;     /* Read Only Mode */
+        unsigned        reserved4_1234  :4;
+        unsigned        sprev           :1;     /* Supports SPACE in the reverse direction */
+        unsigned        reserved4_67    :2;
+        unsigned        reserved5_012   :3;
+        unsigned        efmt            :1;     /* Supports ERASE command initiated formatting */
+        unsigned        reserved5_4     :1;
+        unsigned        qfa             :1;     /* Supports the QFA two partition formats */
+        unsigned        reserved5_67    :2;
+        unsigned        lock            :1;     /* Supports locking the volume */
+        unsigned        locked          :1;     /* The volume is locked */
+        unsigned        prevent         :1;     /* The device defaults in the prevent state after power up */
+        unsigned        eject           :1;     /* The device can eject the volume */
+	unsigned        reserved6_45    :2;     /* Reserved */  
+        unsigned        ecc             :1;     /* Supports error correction */
+        unsigned        cmprs           :1;     /* Supports data compression */
+        unsigned        reserved7_0     :1;
+        unsigned        blk512          :1;     /* Supports 512 bytes block size */
+        unsigned        blk1024         :1;     /* Supports 1024 bytes block size */
+        unsigned        reserved7_3_6   :4;
+        unsigned        blk32768        :1;     /* slowb - the device restricts the byte count for PIO */
+                                                /* transfers for slow buffer memory ??? */
+                                                /* Also 32768 block size in some cases */
+        u16             max_speed;              /* Maximum speed supported in KBps */
+        u8              reserved10, reserved11;
+        u16             ctl;                    /* Continuous Transfer Limit in blocks */
+        u16             speed;                  /* Current Speed, in KBps */
+        u16             buffer_size;            /* Buffer Size, in 512 bytes */
+        u8              reserved18, reserved19;
+} osst_capabilities_page_t;
+
+/*
+ *      Block Size Page
+ */
+typedef struct {
+        unsigned        page_code       :6;     /* Page code - Should be 0x30 */
+        unsigned        reserved1_6     :1;
+        unsigned        ps              :1;
+        u8              page_length;            /* Page Length - Should be 2 */
+        u8              reserved2;
+        unsigned        play32          :1;
+        unsigned        play32_5        :1;
+        unsigned        reserved2_23    :2;
+        unsigned        record32        :1;
+        unsigned        record32_5      :1;
+        unsigned        reserved2_6     :1;
+        unsigned        one             :1;
+} osst_block_size_page_t;
+
+/*
+ *	Tape Parameters Page
+ */
+typedef struct {
+        unsigned        page_code       :6;     /* Page code - Should be 0x2b */
+        unsigned        reserved1_6     :1;
+        unsigned        ps              :1;
+	u8		reserved2;
+	u8		density;
+	u8		reserved3,reserved4;
+	u16		segtrk;
+	u16		trks;
+	u8		reserved5,reserved6,reserved7,reserved8,reserved9,reserved10;
+} osst_tape_paramtr_page_t;
+
+/* OnStream definitions */
+
+#define OS_CONFIG_PARTITION     (0xff)
+#define OS_DATA_PARTITION       (0)
+#define OS_PARTITION_VERSION    (1)
+
+/*
+ * partition
+ */
+typedef struct os_partition_s {
+        __u8    partition_num;
+        __u8    par_desc_ver;
+        __u16   wrt_pass_cntr;
+        __u32   first_frame_ppos;
+        __u32   last_frame_ppos;
+        __u32   eod_frame_ppos;
+} os_partition_t;
+
+/*
+ * DAT entry
+ */
+typedef struct os_dat_entry_s {
+        __u32   blk_sz;
+        __u16   blk_cnt;
+        __u8    flags;
+        __u8    reserved;
+} os_dat_entry_t;
+
+/*
+ * DAT
+ */
+#define OS_DAT_FLAGS_DATA       (0xc)
+#define OS_DAT_FLAGS_MARK       (0x1)
+
+typedef struct os_dat_s {
+        __u8            dat_sz;
+        __u8            reserved1;
+        __u8            entry_cnt;
+        __u8            reserved3;
+        os_dat_entry_t  dat_list[16];
+} os_dat_t;
+
+/*
+ * Frame types
+ */
+#define OS_FRAME_TYPE_FILL      (0)
+#define OS_FRAME_TYPE_EOD       (1 << 0)
+#define OS_FRAME_TYPE_MARKER    (1 << 1)
+#define OS_FRAME_TYPE_HEADER    (1 << 3)
+#define OS_FRAME_TYPE_DATA      (1 << 7)
+
+/*
+ * AUX
+ */
+typedef struct os_aux_s {
+        __u32           format_id;              /* hardware compability AUX is based on */
+        char            application_sig[4];     /* driver used to write this media */
+        __u32           hdwr;                   /* reserved */
+        __u32           update_frame_cntr;      /* for configuration frame */
+        __u8            frame_type;
+        __u8            frame_type_reserved;
+        __u8            reserved_18_19[2];
+        os_partition_t  partition;
+        __u8            reserved_36_43[8];
+        __u32           frame_seq_num;
+        __u32           logical_blk_num_high;
+        __u32           logical_blk_num;
+        os_dat_t        dat;
+        __u8            reserved188_191[4];
+        __u32           filemark_cnt;
+        __u32           phys_fm;
+        __u32           last_mark_ppos;
+        __u8            reserved204_223[20];
+
+        /*
+         * __u8         app_specific[32];
+         *
+         * Linux specific fields:
+         */
+         __u32          next_mark_ppos;         /* when known, points to next marker */
+         __u8           linux_specific[28];
+
+        __u8            reserved_256_511[256];
+} os_aux_t;
+
+#define OS_FM_TAB_MAX 1024
+
+typedef struct os_fm_tab_s {
+	__u8		fm_part_num;
+	__u8		reserved_1;
+	__u8		fm_tab_ent_sz;
+	__u8		reserved_3;
+	__u16		fm_tab_ent_cnt;
+	__u8		reserved6_15[10];
+	__u32		fm_tab_ent[OS_FM_TAB_MAX];
+} os_fm_tab_t;
+
+typedef struct os_ext_trk_ey_s {
+	__u8		et_part_num;
+	__u8		fmt;
+	__u16		fm_tab_off;
+	__u8		reserved4_7[4];
+	__u32		last_hlb_hi;
+	__u32		last_hlb;
+	__u32		last_pp;
+	__u8		reserved20_31[12];
+} os_ext_trk_ey_t;
+
+typedef struct os_ext_trk_tb_s {
+	__u8		nr_stream_part;
+	__u8		reserved_1;
+	__u8		et_ent_sz;
+	__u8		reserved3_15[13];
+	os_ext_trk_ey_t	dat_ext_trk_ey;
+	os_ext_trk_ey_t	qfa_ext_trk_ey;
+} os_ext_trk_tb_t;
+
+typedef struct os_header_s {
+        char            ident_str[8];
+        __u8            major_rev;
+        __u8            minor_rev;
+	__u16		ext_trk_tb_off;
+        __u8            reserved12_15[4];
+        __u8            pt_par_num;
+        __u8            pt_reserved1_3[3];
+        os_partition_t  partition[16];
+	__u32		cfg_col_width;
+	__u32		dat_col_width;
+	__u32		qfa_col_width;
+	__u8		cartridge[16];
+	__u8		reserved304_511[208];
+	__u32		old_filemark_list[16680/4];		/* in ADR 1.4 __u8 track_table[16680] */
+	os_ext_trk_tb_t	ext_track_tb;
+	__u8		reserved17272_17735[464];
+	os_fm_tab_t	dat_fm_tab;
+	os_fm_tab_t	qfa_fm_tab;
+	__u8		reserved25960_32767[6808];
+} os_header_t;
+
+
+/*
+ * OnStream ADRL frame
+ */
+#define OS_FRAME_SIZE   (32 * 1024 + 512)
+#define OS_DATA_SIZE    (32 * 1024)
+#define OS_AUX_SIZE     (512)
+//#define OSST_MAX_SG      2
+
+/* The tape buffer descriptor. */
+typedef struct {
+  unsigned char in_use;
+  unsigned char dma;	/* DMA-able buffer */
+  int this_size;        /* allocated size of the structure */
+  int buffer_size;
+  int buffer_blocks;
+  int buffer_bytes;
+  int read_pointer;
+  int writing;
+  int last_result;
+  int last_result_fatal;
+  Scsi_Cmnd *last_SCpnt;
+  unsigned char *b_data;
+  os_aux_t *aux;               /* onstream AUX structure at end of each block */
+  unsigned short use_sg;       /* zero or number of segments for this adapter */
+  unsigned short sg_segs;      /* total number of allocated segments */
+  unsigned short orig_sg_segs; /* number of segments allocated at first try */
+  struct scatterlist sg[1];    /* MUST BE last item */
+} OSST_buffer;
+
+/* The tape drive descriptor */
+typedef struct {
+  kdev_t devt;
+  unsigned capacity;
+  Scsi_Device* device;
+  struct semaphore sem;
+  OSST_buffer * buffer;
+
+  /* Drive characteristics */
+  unsigned char omit_blklims;
+  unsigned char do_auto_lock;
+  unsigned char can_bsr;
+  unsigned char can_partitions;
+  unsigned char two_fm;
+  unsigned char fast_mteom;
+  unsigned char restr_dma;
+  unsigned char scsi2_logical;
+  unsigned char default_drvbuffer;  /* 0xff = don't touch, value 3 bits */
+  int write_threshold;
+  int timeout;			/* timeout for normal commands */
+  int long_timeout;		/* timeout for commands known to take long time*/
+
+  /* Mode characteristics */
+  ST_mode modes[ST_NBR_MODES];
+  int current_mode;
+#ifdef CONFIG_DEVFS_FS
+  devfs_handle_t de_r[ST_NBR_MODES];  /*  Rewind entries     */
+  devfs_handle_t de_n[ST_NBR_MODES];  /*  No-rewind entries  */
+#endif
+
+  /* Status variables */
+  int partition;
+  int new_partition;
+  int nbr_partitions;    /* zero until partition support enabled */
+  ST_partstat ps[ST_NBR_PARTITIONS];
+  unsigned char dirty;
+  unsigned char ready;
+  unsigned char write_prot;
+  unsigned char drv_write_prot;
+  unsigned char in_use;
+  unsigned char blksize_changed;
+  unsigned char density_changed;
+  unsigned char compression_changed;
+  unsigned char drv_buffer;
+  unsigned char density;
+  unsigned char door_locked;
+  unsigned char rew_at_close;
+  int block_size;
+  int min_block;
+  int max_block;
+  int recover_count;            /* from tape opening */
+  int recover_erreg;            /* from last status call */
+  /*
+   * OnStream specific data
+   */
+  int	   os_fw_rev;			       /* the firmware revision * 10000 */
+  unsigned char  raw;                          /* flag OnStream raw access (32.5KB block size) */
+  unsigned char  poll;                         /* flag that this drive needs polling (IDE|firmware) */
+  unsigned char  logical_blk_in_buffer;	       /* flag that the block as per logical_blk_num
+						* has been read into STp->buffer and is valid */
+  int      logical_blk_num;                    /* logical block number */
+  unsigned first_frame_position;               /* physical frame to be transfered to/from host */
+  unsigned last_frame_position;                /* physical frame to be transferd to/from tape */
+  int      cur_frames;                         /* current number of frames in internal buffer */
+  int      max_frames;                         /* max number of frames in internal buffer */
+  char     application_sig[5];                 /* application signature */
+  unsigned char  fast_open;                    /* flag that reminds us we didn't check headers at open */
+  unsigned short wrt_pass_cntr;                /* write pass counter */
+  int      update_frame_cntr;                  /* update frame counter */
+  int      onstream_write_error;               /* write error recovery active */
+  int      header_ok;                          /* header frame verified ok */
+  int      linux_media;                        /* reading linux-specifc media */
+  int      linux_media_version;
+  os_header_t * header_cache;		       /* cache is kept for filemark positions */
+  int      filemark_cnt;
+  int      first_mark_ppos;
+  int      last_mark_ppos;
+  int      first_data_ppos;
+  int      eod_frame_ppos;
+  int      eod_frame_lfa;
+  int      write_type;				/* used in write error recovery */
+  unsigned long cmd_start_time;
+  unsigned long max_cmd_time;
+
+#if DEBUG
+  unsigned char write_pending;
+  int nbr_finished;
+  int nbr_waits;
+  unsigned char last_cmnd[6];
+  unsigned char last_sense[16];
+#endif
+} OS_Scsi_Tape;
+
+extern Scsi_Tape * scsi_tapes;
+
+/* Values of write_type */
+#define OS_WRITE_DATA      0
+#define OS_WRITE_EOD       1
+#define OS_WRITE_NEW_MARK  2
+#define OS_WRITE_LAST_MARK 3
+#define OS_WRITE_HEADER    4
+#define OS_WRITE_FILLER    5
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/osst_detect.h linux.19p1/drivers/scsi/osst_detect.h
--- linux.vanilla/drivers/scsi/osst_detect.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/scsi/osst_detect.h	Wed Dec 13 21:31:48 2000
@@ -0,0 +1,5 @@
+#define OSST_SUPPORTS(SDp) (! ( memcmp (SDp->vendor, "OnStream", 8) || \
+			     ( memcmp (SDp->model, "SC-", 3) && \
+			       memcmp (SDp->model, "DI-", 3) && \
+			       memcmp (SDp->model, "DP-", 3) && \
+			       memcmp (SDp->model, "USB", 3) ) ) )
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/osst_options.h linux.19p1/drivers/scsi/osst_options.h
--- linux.vanilla/drivers/scsi/osst_options.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/drivers/scsi/osst_options.h	Wed Dec 13 21:31:48 2000
@@ -0,0 +1,100 @@
+/*
+   The compile-time configurable defaults for the Linux SCSI tape driver.
+
+   Copyright 1995 Kai Makisara.
+   
+   Last modified: Wed Sep  2 21:24:07 1998 by root@home
+   
+   Changed (and renamed) for OnStream SCSI drives garloff@suse.de
+   2000-06-21
+
+   $Header: /home/cvsroot/Driver/osst_options.h,v 1.4 2000/06/26 01:44:01 riede Exp $
+*/
+
+#ifndef _OSST_OPTIONS_H
+#define _OSST_OPTIONS_H
+
+/* The minimum limit for the number of SCSI tape devices is determined by
+   OSST_MAX_TAPES. If the number of tape devices and the "slack" defined by
+   OSST_EXTRA_DEVS exceeds OSST_MAX_TAPES, the large number is used. */
+#define OSST_MAX_TAPES 4
+
+/* If OSST_IN_FILE_POS is nonzero, the driver positions the tape after the
+   record been read by the user program even if the tape has moved further
+   because of buffered reads. Should be set to zero to support also drives
+   that can't space backwards over records. NOTE: The tape will be
+   spaced backwards over an "accidentally" crossed filemark in any case. */
+#define OSST_IN_FILE_POS 0
+
+/* The tape driver buffer size in kilobytes. */
+/* Don't change, as this is the HW blocksize */
+#define OSST_BUFFER_BLOCKS 32
+
+/* The number of kilobytes of data in the buffer that triggers an
+   asynchronous write in fixed block mode. See also OSST_ASYNC_WRITES
+   below. */
+#define OSST_WRITE_THRESHOLD_BLOCKS 30
+
+/* The maximum number of tape buffers the driver allocates. The number
+   is also constrained by the number of drives detected. Determines the
+   maximum number of concurrently active tape drives. */
+#define OSST_MAX_BUFFERS OSST_MAX_TAPES 
+
+/* Maximum number of scatter/gather segments */
+/* Fit one buffer in pages and add one for the AUX header */
+#define OSST_MAX_SG      (((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + 1)
+
+/* The number of scatter/gather segments to allocate at first try (must be
+   smaller or equal to the maximum). */
+#define OSST_FIRST_SG    ((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE)
+
+/* The size of the first scatter/gather segments (determines the maximum block
+   size for SCSI adapters not supporting scatter/gather). The default is set
+   to try to allocate the buffer as one chunk. */
+#define OSST_FIRST_ORDER  5
+
+
+/* The following lines define defaults for properties that can be set
+   separately for each drive using the MTSTOPTIONS ioctl. */
+
+/* If OSST_TWO_FM is non-zero, the driver writes two filemarks after a
+   file being written. Some drives can't handle two filemarks at the
+   end of data. */
+#define OSST_TWO_FM 0
+
+/* If OSST_BUFFER_WRITES is non-zero, writes in fixed block mode are
+   buffered until the driver buffer is full or asynchronous write is
+   triggered. May make detection of End-Of-Medium early enough fail. */
+#define OSST_BUFFER_WRITES 1
+
+/* If OSST_ASYNC_WRITES is non-zero, the SCSI write command may be started
+   without waiting for it to finish. May cause problems in multiple
+   tape backups. */
+#define OSST_ASYNC_WRITES 1
+
+/* If OSST_READ_AHEAD is non-zero, blocks are read ahead in fixed block
+   mode. */
+#define OSST_READ_AHEAD 1
+
+/* If OSST_AUTO_LOCK is non-zero, the drive door is locked at the first
+   read or write command after the device is opened. The door is opened
+   when the device is closed. */
+#define OSST_AUTO_LOCK 0
+
+/* If OSST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the
+   direct SCSI command. The file number status is lost but this method
+   is fast with some drives. Otherwise MTEOM is done by spacing over
+   files and the file number status is retained. */
+#define OSST_FAST_MTEOM 0
+
+/* If OSST_SCSI2LOGICAL is nonzero, the logical block addresses are used for
+   MTIOCPOS and MTSEEK by default. Vendor addresses are used if OSST_SCSI2LOGICAL
+   is zero. */
+#define OSST_SCSI2LOGICAL 0
+
+/* If OSST_SYSV is non-zero, the tape behaves according to the SYS V semantics.
+   The default is BSD semantics. */
+#define OSST_SYSV 0
+
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.h linux.19p1/drivers/scsi/scsi.h
--- linux.vanilla/drivers/scsi/scsi.h	Fri Apr 21 23:20:05 2000
+++ linux.19p1/drivers/scsi/scsi.h	Wed Dec 13 22:58:10 2000
@@ -634,7 +634,7 @@
 extern int scsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device);
 
 
-#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
+#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR)
 #include "hosts.h"
 
 static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr.c linux.19p1/drivers/scsi/sr.c
--- linux.vanilla/drivers/scsi/sr.c	Mon Dec 11 22:12:55 2000
+++ linux.19p1/drivers/scsi/sr.c	Wed Dec 13 21:35:56 2000
@@ -26,6 +26,8 @@
  *	Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
  *	transparently and loose the GHOST hack
  *
+ *	Modified by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *	check resource allocation in sr_init and some cleanups - 2000/12/09
  */
 
 #include <linux/module.h>
@@ -70,10 +72,10 @@
 	sr_finish, sr_attach, sr_detach
 };
 
-Scsi_CD *scsi_CDs = NULL;
-static int *sr_sizes = NULL;
+Scsi_CD *scsi_CDs;
+static int *sr_sizes;
 
-static int *sr_blocksizes = NULL;
+static int *sr_blocksizes;
 
 static int sr_open(struct cdrom_device_info *, int);
 void get_sectorsize(int);
@@ -1113,16 +1115,21 @@
 	}
 	if (scsi_CDs)
 		return 0;
-	sr_template.dev_max =
-	    sr_template.dev_noticed + SR_EXTRA_DEVS;
-	scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
-	memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
-
-	sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
-	memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
-
-	sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
-						 sizeof(int), GFP_ATOMIC);
+	sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+	scsi_CDs = scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD),
+					GFP_ATOMIC);
+	if (!scsi_CDs)
+		goto cleanup_register;
+
+	sr_sizes = scsi_init_malloc(sr_template.dev_max * sizeof(int),
+					GFP_ATOMIC);
+	if (!sr_sizes)
+		goto cleanup_cds;
+
+	sr_blocksizes = scsi_init_malloc(sr_template.dev_max * sizeof(int),
+					GFP_ATOMIC);
+	if (!sr_blocksizes)
+		goto cleanup_sizes;
 
 	/*
 	 * These are good guesses for the time being.
@@ -1131,6 +1138,18 @@
 		sr_blocksizes[i] = 2048;
 	blksize_size[MAJOR_NR] = sr_blocksizes;
 	return 0;
+cleanup_sizes:
+	scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
+	sr_sizes = NULL;
+cleanup_cds:
+	scsi_init_free((char *) scsi_CDs,
+		       (sr_template.dev_noticed + SR_EXTRA_DEVS) *
+		       sizeof(Scsi_CD));
+	scsi_CDs = NULL;
+cleanup_register:
+	unregister_blkdev(MAJOR_NR, "sr");
+	sr_registered--;
+	return 1;
 }
 
 void sr_finish()
@@ -1241,7 +1260,7 @@
 		scsi_init_free((char *) scsi_CDs,
 			       (sr_template.dev_noticed + SR_EXTRA_DEVS)
 			       * sizeof(Scsi_CD));
-
+		scsi_CDs = NULL;
 		scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
 		sr_sizes = NULL;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/st.c linux.19p1/drivers/scsi/st.c
--- linux.vanilla/drivers/scsi/st.c	Mon Dec 11 22:12:55 2000
+++ linux.19p1/drivers/scsi/st.c	Wed Dec 13 21:31:48 2000
@@ -55,6 +55,8 @@
 
 #include "constants.h"
 
+#include "osst_detect.h"
+
 #ifdef MODULE
 MODULE_PARM(buffer_kbs, "i");
 MODULE_PARM(write_threshold_kbs, "i");
@@ -3407,6 +3409,18 @@
 #endif
 
 
+/* Returns zero for drives not supported by this driver */
+static int st_supported(Scsi_Device * SDp)
+{
+  /* These OnStream SC drives require a special driver. The OnStream ADR* drives
+     are supported by this driver */
+  if ( OSST_SUPPORTS(SDp) )
+      return 0;
+
+  return 1;
+}
+
+
 static struct file_operations st_fops = {
    NULL,            /* lseek - default */
    st_read,         /* read - general block-dev read */
@@ -3429,6 +3443,8 @@
 
    if (SDp->type != TYPE_TAPE)
        return 1;
+   if (!st_supported(SDp))
+       return 1;
 
    if (st_template.nr_dev >= st_template.dev_max) {
        SDp->attached--;
@@ -3502,6 +3518,8 @@
 static int st_detect(Scsi_Device * SDp)
 {
   if(SDp->type != TYPE_TAPE) return 0;
+  if (!st_supported(SDp))
+      return 0;
 
   printk(KERN_WARNING
 	 "Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n",
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cs46xx.c linux.19p1/drivers/sound/cs46xx.c
--- linux.vanilla/drivers/sound/cs46xx.c	Mon Dec 11 22:12:55 2000
+++ linux.19p1/drivers/sound/cs46xx.c	Wed Dec 13 21:55:26 2000
@@ -2499,7 +2499,7 @@
 	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, clkrun_hack},
 	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, clkrun_hack},
 	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL},
-	{0, 0, "Card without SSID set", NULL, NULL },
+	{0, 0, "Card without SSID set", amp_none, NULL },
 	{0, 0, NULL, NULL, NULL}
 };
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/usbserial.c linux.19p1/drivers/usb/serial/usbserial.c
--- linux.vanilla/drivers/usb/serial/usbserial.c	Mon Dec 11 22:13:01 2000
+++ linux.19p1/drivers/usb/serial/usbserial.c	Wed Dec 13 21:22:44 2000
@@ -1259,7 +1259,7 @@
 static struct tty_driver serial_tty_driver = {
 	magic:			TTY_DRIVER_MAGIC,
 	driver_name:		"usb-serial",
-	name:			"usb/tts/%d",
+	name:			"ttyUSB",
 	major:			SERIAL_TTY_MAJOR,
 	minor_start:		0,
 	num:			SERIAL_TTY_MINORS,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/Config.in linux.19p1/fs/Config.in
--- linux.vanilla/fs/Config.in	Mon Dec 11 22:13:02 2000
+++ linux.19p1/fs/Config.in	Wed Dec 13 22:43:46 2000
@@ -115,6 +115,7 @@
 
 bool 'BSD disklabel (BSD partition tables) support' CONFIG_BSD_DISKLABEL
 bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
+bool 'Minix subpartition support' CONFIG_MINIX_SUBPARTITION
 bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
 bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/file_table.c linux.19p1/fs/file_table.c
--- linux.vanilla/fs/file_table.c	Fri Apr 21 12:46:42 2000
+++ linux.19p1/fs/file_table.c	Wed Dec 13 21:48:45 2000
@@ -13,9 +13,7 @@
 static kmem_cache_t *filp_cache;
 
 /* sysctl tunables... */
-int nr_files = 0;	/* read only */
-int nr_free_files = 0;	/* read only */
-int max_files = NR_FILE;/* tunable */
+struct files_stat_struct files_stat = {0, 0, NR_FILE};
 
 /* Free list management, if you are here you must have f_count == 0 */
 static struct file * free_filps = NULL;
@@ -26,7 +24,7 @@
 		free_filps->f_pprev = &file->f_next;
 	free_filps = file;
 	file->f_pprev = &free_filps;
-	nr_free_files++;
+	files_stat.nr_free_files++;
 }
 
 /* The list of in-use filp's must be exported (ugh...) */
@@ -72,11 +70,11 @@
 	static int old_max = 0;
 	struct file * f;
 
-	if (nr_free_files > NR_RESERVED_FILES) {
+	if (files_stat.nr_free_files > NR_RESERVED_FILES) {
 	used_one:
 		f = free_filps;
 		remove_filp(f);
-		nr_free_files--;
+		files_stat.nr_free_files--;
 	new_one:
 		memset(f, 0, sizeof(*f));
 		f->f_count = 1;
@@ -89,23 +87,23 @@
 	/*
 	 * Use a reserved one if we're the superuser
 	 */
-	if (nr_free_files && !current->euid)
+	if (files_stat.nr_free_files && !current->euid)
 		goto used_one;
 	/*
 	 * Allocate a new one if we're below the limit.
 	 */
-	if (nr_files < max_files) {
+	if (files_stat.nr_files < files_stat.max_files) {
 		f = kmem_cache_alloc(filp_cache, SLAB_KERNEL);
 		if (f) {
-			nr_files++;
+			files_stat.nr_files++;
 			goto new_one;
 		}
 		/* Big problems... */
 		printk("VFS: filp allocation failed\n");
 
-	} else if (max_files > old_max) {
-		printk("VFS: file-max limit %d reached\n", max_files);
-		old_max = max_files;
+	} else if (files_stat.max_files > old_max) {
+		printk("VFS: file-max limit %d reached\n", files_stat.max_files);
+		old_max = files_stat.max_files;
 	}
 	return NULL;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/dir.c linux.19p1/fs/isofs/dir.c
--- linux.vanilla/fs/isofs/dir.c	Sat Sep  9 18:42:47 2000
+++ linux.19p1/fs/isofs/dir.c	Wed Dec 13 21:58:21 2000
@@ -67,14 +67,18 @@
 	NULL			/* permission */
 };
 
-static int isofs_name_translate(char * old, int len, char * new)
+int isofs_name_translate(struct iso_directory_record *de, char *new,
+			 struct inode *inode)
 {
-	int i, c;
+	char * old = de->name;
+	int len = de->name_len[0];
+	int i;
 			
 	for (i = 0; i < len; i++) {
-		c = old[i];
+		unsigned char c = old[i];
 		if (!c)
 			break;
+
 		if (c >= 'A' && c <= 'Z')
 			c |= 0x20;	/* lower case */
 
@@ -101,8 +105,7 @@
 {
 	int std;
 	unsigned char * chr;
-	int retnamlen = isofs_name_translate(de->name,
-				de->name_len[0], retname);
+	int retnamlen = isofs_name_translate(de, retname, inode);
 	if (retnamlen == 0) return 0;
 	std = sizeof(struct iso_directory_record) + de->name_len[0];
 	if (std & 1) std++;
@@ -122,7 +125,9 @@
 }
 
 /*
- * This should _really_ be cleaned up some day..
+ * Read directory and call the filldir() argument until either
+ * all entries were handled, or filldir() returns an error.
+ * Return 0.
  */
 static int do_isofs_readdir(struct inode *inode, struct file *filp,
 		void *dirent, filldir_t filldir,
@@ -132,7 +137,7 @@
 	unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
 	unsigned int block, offset;
 	int inode_number = 0;	/* Quiet GCC */
-	struct buffer_head *bh;
+	struct buffer_head *bh = NULL;
 	int len;
 	int map;
 	int high_sierra;
@@ -140,78 +145,59 @@
 	char *p = NULL;		/* Quiet GCC */
 	struct iso_directory_record *de;
 
- 	if (filp->f_pos >= inode->i_size)
-		return 0;
- 
 	offset = filp->f_pos & (bufsize - 1);
-	block = isofs_bmap(inode, filp->f_pos >> bufbits);
+	block = filp->f_pos >> bufbits;
 	high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 
-	if (!block)
-		return 0;
-
-	if (!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size)))
-		return 0;
-
 	while (filp->f_pos < inode->i_size) {
 		int de_len;
-#ifdef DEBUG
-		printk("Block, offset, f_pos: %x %x %x\n",
-		       block, offset, filp->f_pos);
-	        printk("inode->i_size = %x\n",inode->i_size);
-#endif
+
+		if (!bh) {
+			bh = isofs_bread(inode, bufsize, block);
+			if (!bh)
+				return 0;
+		}
+
 		de = (struct iso_directory_record *) (bh->b_data + offset);
-		if(first_de) inode_number = (block << bufbits) + (offset & (bufsize - 1));
+		if (first_de)
+			inode_number = (bh->b_blocknr << bufbits) + offset;
 
-		/* Check boundaries and get length. by GO! */
-		if (offset < bufsize) de_len = *(unsigned char *) de;
-#ifdef DEBUG
-		if (offset < bufsize) printk("de_len = %ld\n", de_len);
-		else printk("Move to next sector\n");
-#endif
-	    
+		de_len = *(unsigned char *) de;
 
 		/* If the length byte is zero, we should move on to the next
 		   CDROM sector.  If we are at the end of the directory, we
 		   kick out of the while loop. */
 
-		if ((offset >= bufsize) || (de_len == 0) ) {
+		if (de_len == 0) {
 			brelse(bh);
-			if (offset >= bufsize) {	/*Check first. by GO!*/
-				offset -= bufsize;
-				filp->f_pos += offset;
-			} else {
-				filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1))
-					       + ISOFS_BLOCK_SIZE);
-				offset = 0;
-			}
-
-			if (filp->f_pos >= inode->i_size)
-				return 0;
-
-			block = isofs_bmap(inode, (filp->f_pos) >> bufbits);
-			if (!block)
-				return 0;
-			bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size);
-			if (!bh)
-				return 0;
+			bh = NULL;
+			filp->f_pos = (filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1))
+					       + ISOFS_BLOCK_SIZE;
+			block = filp->f_pos >> bufbits;
+			offset = 0;
 			continue;
 		}
 
-		offset +=  de_len;
-		if (offset > bufsize) {
-			/*
-			 * This would only normally happen if we had
-			 * a buggy cdrom image.  All directory
-			 * entries should terminate with a null size
-			 * or end exactly at the end of the sector.
-			 */
-		        printk("next_offset (%x) > bufsize (%lx)\n",
-			       offset,bufsize);
-			break;
+		offset += de_len;
+
+		/* Make sure we have a full directory entry */
+		if (offset >= bufsize) {
+			int slop = bufsize - offset + de_len;
+			memcpy(tmpde, de, slop);
+			offset &= bufsize - 1;
+			block++;
+			brelse(bh);
+			bh = NULL;
+			if (offset) {
+				bh = isofs_bread(inode, bufsize, block);
+				if (!bh)
+					return 0;
+				memcpy((void *) tmpde + slop, bh->b_data, offset);
+			}
+			de = tmpde;
 		}
 
-		if(de->flags[-high_sierra] & 0x80) {
+		if (de->flags[-high_sierra] & 0x80) {
 			first_de = 0;
 			filp->f_pos += de_len;
 			continue;
@@ -250,7 +236,7 @@
 		map = 1;
 		if (inode->i_sb->u.isofs_sb.s_rock) {
 			len = get_rock_ridge_filename(de, tmpname, inode);
-			if (len != 0) {
+			if (len != 0) {		/* may be -1 */
 				p = tmpname;
 				map = 0;
 			}
@@ -258,7 +244,7 @@
 		if (map) {
 #ifdef CONFIG_JOLIET
 			if (inode->i_sb->u.isofs_sb.s_joliet_level) {
-				len = get_joliet_filename(de, inode, tmpname);
+				len = get_joliet_filename(de, tmpname, inode);
 				p = tmpname;
 			} else
 #endif
@@ -267,8 +253,7 @@
 				p = tmpname;
 			} else
 			if (inode->i_sb->u.isofs_sb.s_mapping == 'n') {
-				len = isofs_name_translate(de->name,
-					de->name_len[0], tmpname);
+				len = isofs_name_translate(de, tmpname, inode);
 				p = tmpname;
 			} else {
 				p = de->name;
@@ -283,7 +268,8 @@
 
 		continue;
 	}
-	brelse(bh);
+	if (bh)
+		brelse(bh);
 	return 0;
 }
 
@@ -296,17 +282,17 @@
 		void *dirent, filldir_t filldir)
 {
 	int result;
-	char * tmpname;
+	char * page;
 	struct iso_directory_record * tmpde;
 	struct inode *inode = filp->f_dentry->d_inode;
 
-	tmpname = (char *) __get_free_page(GFP_KERNEL);
-	if (!tmpname)
+	page = (char *) __get_free_page(GFP_KERNEL);
+	if (!page)
 		return -ENOMEM;
-	tmpde = (struct iso_directory_record *) (tmpname+1024);
+	tmpde = (struct iso_directory_record *) (page+1024);
 
-	result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
+	result = do_isofs_readdir(inode, filp, dirent, filldir, page, tmpde);
 
-	free_page((unsigned long) tmpname);
+	free_page((unsigned long) page);
 	return result;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/inode.c linux.19p1/fs/isofs/inode.c
--- linux.vanilla/fs/isofs/inode.c	Sat Sep  9 18:42:47 2000
+++ linux.19p1/fs/isofs/inode.c	Wed Dec 13 21:58:21 2000
@@ -54,7 +54,7 @@
 static int isofs_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
 #endif
 
-void isofs_put_super(struct super_block *sb)
+static void isofs_put_super(struct super_block *sb)
 {
 #ifdef CONFIG_JOLIET
 	if (sb->u.isofs_sb.s_nls_iocharset) {
@@ -72,6 +72,9 @@
 	return;
 }
 
+static void isofs_read_inode(struct inode *);
+static int isofs_statfs (struct super_block *, struct statfs *, int);
+
 static struct super_operations isofs_sops = {
 	isofs_read_inode,
 	NULL,			/* write_inode */
@@ -452,7 +455,7 @@
 				       (unsigned long) &ms_info);
       set_fs(old_fs);
 #if 0
-      printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
+      printk("isofs.inode: CDROMMULTISESSION: rc=%d\n", i);
       if (i==0)
 	{
 	  printk("isofs.inode: XA disk: %s\n", ms_info.xa_flag ? "yes":"no");
@@ -474,8 +477,8 @@
  * Note: a check_disk_change() has been done immediately prior
  * to this call, so we don't need to check again.
  */
-struct super_block *isofs_read_super(struct super_block *s, void *data,
-				     int silent)
+static struct super_block *isofs_read_super(struct super_block *s, void *data,
+					    int silent)
 {
 	kdev_t				dev = s->s_dev;
 	struct buffer_head	      * bh = NULL, *pri_bh = NULL;
@@ -521,15 +524,13 @@
  	 * that value.
  	 */
  	blocksize = get_hardblocksize(dev);
- 	if(    (blocksize != 0)
- 	    && (blocksize > opt.blocksize) )
- 	  {
+ 	if (blocksize > opt.blocksize) {
  	    /*
  	     * Force the blocksize we are going to use to be the
  	     * hardware blocksize.
  	     */
  	    opt.blocksize = blocksize;
- 	  }
+	}
  
 	blocksize_bits = 0;
 	{
@@ -625,9 +626,7 @@
 	pri_bh = NULL;
 
 root_found:
-	brelse(pri_bh);
-
-	if (joliet_level && opt.rock == 'n') {
+	if (joliet_level && (pri == NULL || opt.rock == 'n')) {
 	    /* This is the case of Joliet with the norock mount flag.
 	     * A disc with both Joliet and Rock Ridge is handled later
 	     */
@@ -724,6 +723,7 @@
 	 * We're all done using the volume descriptor, and may need
 	 * to change the device blocksize, so release the buffer now.
 	 */
+	brelse(pri_bh);
 	brelse(bh);
 
 	/*
@@ -776,8 +776,9 @@
 	s->u.isofs_sb.s_gid = opt.gid;
 	s->u.isofs_sb.s_utf8 = opt.utf8;
 	/*
-	 * It would be incredibly stupid to allow people to mark every file on the disk
-	 * as suid, so we merely allow them to set the default permissions.
+	 * It would be incredibly stupid to allow people to mark every file
+	 * on the disk as suid, so we merely allow them to set the default
+	 * permissions.
 	 */
 	s->u.isofs_sb.s_mode = opt.mode & 0777;
 
@@ -880,8 +881,8 @@
 	return NULL;
 }
 
-int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
-{
+static int
+isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) {
 	struct statfs tmp;
 
 	tmp.f_type = ISOFS_SUPER_MAGIC;
@@ -896,6 +897,15 @@
 	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
+struct buffer_head *isofs_bread(struct inode *inode, unsigned int bufsize,
+				unsigned int block)
+{
+	unsigned int blknr = isofs_bmap(inode, block);
+	if (!blknr)
+		return NULL;
+	return bread(inode->i_dev, blknr, bufsize);
+}
+
 int isofs_bmap(struct inode * inode,int block)
 {
 	off_t b_off, offset, size;
@@ -942,10 +952,6 @@
 	firstext = inode->u.isofs_i.i_first_extent;
 	size = inode->u.isofs_i.i_section_size;
 	nextino = inode->u.isofs_i.i_next_section_ino;
-#ifdef DEBUG
-	printk("first inode: inode=%x nextino=%x firstext=%u size=%lu\n",
-		inode->i_ino, nextino, firstext, size);
-#endif
 	i = 0;
 	if (nextino) {
 		while(b_off >= offset + size) {
@@ -956,10 +962,6 @@
 			if(!ino) return 0;
 			firstext = ino->u.isofs_i.i_first_extent;
 			size = ino->u.isofs_i.i_section_size;
-#ifdef DEBUG
-			printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n",
-			       inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size);
-#endif
 			nextino = ino->u.isofs_i.i_next_section_ino;
 			iput(ino);
 		
@@ -971,10 +973,6 @@
 			}
 		}
 	}
-#ifdef DEBUG
-	printk("isofs_bmap: mapped inode:block %x:%d to block %lu\n",
-		inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode));
-#endif
 	return firstext + ((b_off - offset) >> ISOFS_BUFFER_BITS(inode));
 }
 
@@ -995,118 +993,144 @@
 
 static int isofs_read_level3_size(struct inode * inode)
 {
-	unsigned long ino = inode->i_ino;
+	unsigned long f_pos = inode->i_ino;
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
 	int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 	struct buffer_head * bh = NULL;
-	int block = 0;
+	unsigned long block, offset;
 	int i = 0;
 	int more_entries = 0;
-	void *cpnt;
-	struct iso_directory_record * raw_inode;
+	struct iso_directory_record * tmpde = NULL;
 
 	inode->i_size = 0;
 	inode->u.isofs_i.i_next_section_ino = 0;
+
+	block = f_pos >> ISOFS_BUFFER_BITS(inode);
+	offset = f_pos & (bufsize-1);
+
 	do {
-		unsigned char *pnt;
-		unsigned int reclen;
-		int offset = (ino & (bufsize - 1));
-
-		cpnt = NULL;
-		/* Check whether to update our buffer */
-		if (block != ino >> ISOFS_BUFFER_BITS(inode)) {
-			block = ino >> ISOFS_BUFFER_BITS(inode);
+		struct iso_directory_record * de;
+		unsigned int de_len;
+
+		if (!bh) {
 			brelse(bh);
 			bh = bread(inode->i_dev, block, bufsize);
 			if (!bh)
 				goto out_noread;
 		}
-		pnt = ((unsigned char *) bh->b_data + offset);
-		/*
-		 * Note: this is invariant even if the record
-		 * spans buffers and must be copied ...
-		 */
-		reclen = *pnt;
 
-		/* N.B. this test doesn't trigger the i++ code ... */	
-		if(reclen == 0) {
-			ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
+		de = (struct iso_directory_record *) (bh->b_data + offset);
+		de_len = *(unsigned char *) de;
+
+		if (de_len == 0) {
+			brelse(bh);
+			bh = NULL;
+			f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
+			block = f_pos >> ISOFS_BUFFER_BITS(inode);
+			offset = 0;
 			continue;
 		}
-		raw_inode = ((struct iso_directory_record *) pnt);
 
-		/* Check whether the raw inode spans the buffer ... */	
-		if (offset + reclen > bufsize){
-		        int frag1 = bufsize - offset;
-	
-		        cpnt = kmalloc(reclen, GFP_KERNEL);
-			if (cpnt == NULL)
-				goto out_nomem;
-			memcpy(cpnt, pnt, frag1);
+		offset += de_len;
+
+		/* Make sure we have a full directory entry */
+		if (offset >= bufsize) {
+			int slop = bufsize - offset + de_len;
+			if (!tmpde) {
+				tmpde = kmalloc(256, GFP_KERNEL);
+				if (!tmpde)
+					goto out_nomem;
+			}
+			memcpy(tmpde, de, slop);
+			offset &= bufsize - 1;
+			block++;
 			brelse(bh);
-			bh = bread(inode->i_dev, ++block, bufsize);
-			if (!bh)
-				goto out_noread;
-			offset += reclen - bufsize;
-			memcpy((char *)cpnt+frag1, bh->b_data, offset);
-			raw_inode = ((struct iso_directory_record *) cpnt);
+			bh = NULL;
+			if (offset) {
+				bh = bread(inode->i_dev, ++block, bufsize);
+				if (!bh)
+					goto out_noread;
+				memcpy((void *) tmpde + slop, bh->b_data, offset);
+			}
+			de = tmpde;
 		}
 
-		inode->i_size += isonum_733 (raw_inode->size);
-		if(i == 1) inode->u.isofs_i.i_next_section_ino = ino;
+		inode->i_size += isonum_733 (de->size);
+		if(i == 1)
+			inode->u.isofs_i.i_next_section_ino = f_pos;
+
+		more_entries = de->flags[-high_sierra] & 0x80;
+
+		f_pos += de_len;
 
-		more_entries = raw_inode->flags[-high_sierra] & 0x80;
-		
-		ino += reclen;
-		if (cpnt)
-			kfree (cpnt);
 		i++;
 		if(i > 100)
 			goto out_toomany;
 	} while(more_entries);
 out:
-	brelse(bh);
+	if (tmpde)
+		kfree(tmpde);
+	if (bh)
+		brelse(bh);
 	return 0;
 
 out_nomem:
-	printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino);
-	brelse(bh);
-	return 1;
+	if (bh)
+		brelse(bh);
+	return -ENOMEM;
+
 out_noread:
-	printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block);
-	if (cpnt)
-		kfree(cpnt);
-	return 1;
+	printk(KERN_INFO "ISOFS: unable to read i-node block %lu\n", block);
+	if (tmpde)
+		kfree(tmpde);
+	return -EIO;
+
 out_toomany:
 	printk(KERN_INFO "isofs_read_level3_size: "
 		"More than 100 file sections ?!?, aborting...\n"
 	  	"isofs_read_level3_size: inode=%lu ino=%lu\n",
-		inode->i_ino, ino);
+		inode->i_ino, f_pos);
 	goto out;
 }
 
-void isofs_read_inode(struct inode * inode)
+static void isofs_read_inode(struct inode * inode)
 {
 	struct super_block *sb = inode->i_sb;
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
 	int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
 	int high_sierra = sb->u.isofs_sb.s_high_sierra;
-	struct buffer_head * bh;
-	struct iso_directory_record * raw_inode;
-	unsigned char *pnt;
-	int volume_seq_no, i;
+	struct buffer_head * bh = NULL;
+	struct iso_directory_record * de;
+	struct iso_directory_record * tmpde = NULL;
+	unsigned int de_len;
+	int volume_seq_no, i, offset;
 
 	bh = bread(inode->i_dev, block, bufsize);
-	if (!bh) {
-		printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
-		goto fail;
-	}
+	if (!bh)
+		goto out_badread;
 
-	pnt = ((unsigned char *) bh->b_data
-	       + (inode->i_ino & (bufsize - 1)));
-	raw_inode = ((struct iso_directory_record *) pnt);
+	offset = (inode->i_ino & (bufsize - 1));
+	de = (struct iso_directory_record *) (bh->b_data + offset);
+	de_len = *(unsigned char *) de;
+
+	if (offset + de_len > bufsize){
+		int frag1 = bufsize - offset;
+
+		tmpde = kmalloc(de_len, GFP_KERNEL);
+		if (tmpde == NULL) {
+			printk(KERN_INFO "isofs_read_inode: out of memory\n");
+			goto fail;
+		}
+		memcpy(tmpde, bh->b_data + offset, frag1);
+		brelse(bh);
+		bh = bread(inode->i_dev, ++block, bufsize);
+		if (!bh)
+			goto out_badread;
+		memcpy((char *)tmpde+frag1, bh->b_data, de_len - frag1);
+		de = tmpde;
+	}
 
-	if (raw_inode->flags[-high_sierra] & 2) {
+	if (de->flags[-high_sierra] & 2) {
 		inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
 		inode->i_nlink = 1; /* Set to 1.  We know there are 2, but
 				       the find utility tries to optimize
@@ -1121,10 +1145,10 @@
 		/* If there are no periods in the name,
 		 * then set the execute permission bit
 		 */
-		for(i=0; i< raw_inode->name_len[0]; i++)
-			if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
+		for(i=0; i< de->name_len[0]; i++)
+			if(de->name[i]=='.' || de->name[i]==';')
 				break;
-		if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';')
+		if(i == de->name_len[0] || de->name[i] == ';')
 			inode->i_mode |= S_IXUGO; /* execute permission */
 	}
 	inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
@@ -1132,49 +1156,54 @@
 	inode->i_blocks = inode->i_blksize = 0;
 
 
-	inode->u.isofs_i.i_section_size = isonum_733 (raw_inode->size);
-	if(raw_inode->flags[-high_sierra] & 0x80) {
+	inode->u.isofs_i.i_section_size = isonum_733 (de->size);
+	if(de->flags[-high_sierra] & 0x80) {
 		if(isofs_read_level3_size(inode)) goto fail;
 	} else {
-		inode->i_size = isonum_733 (raw_inode->size);
+		inode->i_size = isonum_733 (de->size);
 	}
 
 	/* There are defective discs out there - we do this to protect
 	   ourselves.  A cdrom will never contain more than 800Mb 
 	   .. but a DVD may be up to 1Gig (Ulrich Habel) */
+
 	if((inode->i_size < 0 || inode->i_size > 1073741824) &&
-	    inode->i_sb->u.isofs_sb.s_cruft == 'n') {
-	  printk("Warning: defective cdrom.  Enabling \"cruft\" mount option.\n");
-	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
+	   inode->i_sb->u.isofs_sb.s_cruft == 'n') {
+		printk(KERN_WARNING "Warning: defective cdrom.  "
+		       "Enabling \"cruft\" mount option.\n");
+		inode->i_sb->u.isofs_sb.s_cruft = 'y';
 	}
 
-/* Some dipshit decided to store some other bit of information in the high
-   byte of the file length.  Catch this and holler.  WARNING: this will make
-   it impossible for a file to be > 16Mb on the CDROM!!!*/
+	/*
+	 * Some dipshit decided to store some other bit of information
+	 * in the high byte of the file length.  Catch this and holler.
+	 * WARNING: this will make it impossible for a file to be > 16MB
+	 * on the CDROM.
+	 */
 
 	if(inode->i_sb->u.isofs_sb.s_cruft == 'y' &&
 	   inode->i_size & 0xff000000){
-/*	  printk("Illegal format on cdrom.  Pester manufacturer.\n"); */
-	  inode->i_size &= 0x00ffffff;
+		inode->i_size &= 0x00ffffff;
 	}
 
-	if (raw_inode->interleave[0]) {
+	if (de->interleave[0]) {
 		printk("Interleaved files not (yet) supported.\n");
 		inode->i_size = 0;
 	}
 
 	/* I have no idea what file_unit_size is used for, so
 	   we will flag it for now */
-	if(raw_inode->file_unit_size[0] != 0){
-		printk("File unit size != 0 for ISO file (%ld).\n",inode->i_ino);
+	if (de->file_unit_size[0] != 0){
+		printk("File unit size != 0 for ISO file (%ld).\n",
+		       inode->i_ino);
 	}
 
 	/* I have no idea what other flag bits are used for, so
 	   we will flag it for now */
 #ifdef DEBUG
-	if((raw_inode->flags[-high_sierra] & ~2)!= 0){
+	if ((de->flags[-high_sierra] & ~2)!= 0){
 		printk("Unusual flag settings for ISO file (%ld %x).\n",
-		       inode->i_ino, raw_inode->flags[-high_sierra]);
+		       inode->i_ino, de->flags[-high_sierra]);
 	}
 #endif
 
@@ -1184,32 +1213,25 @@
 #endif
 
 	inode->i_mtime = inode->i_atime = inode->i_ctime =
-	  iso_date(raw_inode->date, high_sierra);
+		iso_date(de->date, high_sierra);
 
-	inode->u.isofs_i.i_first_extent = (isonum_733 (raw_inode->extent) +
-					   isonum_711 (raw_inode->ext_attr_length));
+	inode->u.isofs_i.i_first_extent = (isonum_733 (de->extent) +
+					   isonum_711 (de->ext_attr_length));
 
-/* Now test for possible Rock Ridge extensions which will override some of
-   these numbers in the inode structure. */
+	/*
+	 * Now test for possible Rock Ridge extensions which will override
+	 * some of these numbers in the inode structure.
+	 */
 
 	if (!high_sierra) {
-	  parse_rock_ridge_inode(raw_inode, inode);
-	  /* hmm..if we want uid or gid set, override the rock ridge setting */
-	 test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
-         test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid);
+		parse_rock_ridge_inode(de, inode);
+		/* if we want uid/gid set, override the rock ridge setting */
+		test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
+		test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid);
 	}
 
-#ifdef DEBUG
-	printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent);
-#endif
-
 	/* get the volume sequence number */
-	volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
-
-	/*
-	 * All done with buffer ... no more references to buffer memory!
-	 */
-	brelse(bh);
+	volume_seq_no = isonum_723 (de->volume_sequence_number) ;
 
 	/*
 	 * Disable checking if we see any volume number other than 0 or 1.
@@ -1219,8 +1241,10 @@
 	 */
 	if (inode->i_sb->u.isofs_sb.s_cruft == 'n' &&
 	    (volume_seq_no != 0) && (volume_seq_no != 1)) {
-	  printk("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n");
-	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
+		printk("Warning: defective cdrom "
+		       "(volume sequence number %d). "
+		       "Enabling \"cruft\" mount option.\n", volume_seq_no);
+		inode->i_sb->u.isofs_sb.s_cruft = 'y';
 	}
 
 	/* Install the inode operations vector */
@@ -1228,26 +1252,34 @@
 #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
 	if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
 	    (volume_seq_no != 0) && (volume_seq_no != 1)) {
-		printk("Multi volume CD somehow got mounted.\n");
+		printk(KERN_WARNING "Multi volume CD somehow got mounted.\n");
 	} else
 #endif IGNORE_WRONG_MULTI_VOLUME_SPECS
 	{
-	  if (S_ISREG(inode->i_mode))
-	    inode->i_op = &isofs_file_inode_operations;
-	  else if (S_ISDIR(inode->i_mode))
-	    inode->i_op = &isofs_dir_inode_operations;
-	  else if (S_ISLNK(inode->i_mode))
-	    inode->i_op = &isofs_symlink_inode_operations;
-	  else if (S_ISCHR(inode->i_mode))
-	    inode->i_op = &chrdev_inode_operations;
-	  else if (S_ISBLK(inode->i_mode))
-	    inode->i_op = &blkdev_inode_operations;
-	  else if (S_ISFIFO(inode->i_mode))
-	    init_fifo(inode);
+		if (S_ISREG(inode->i_mode))
+			inode->i_op = &isofs_file_inode_operations;
+		else if (S_ISDIR(inode->i_mode))
+			inode->i_op = &isofs_dir_inode_operations;
+		else if (S_ISLNK(inode->i_mode))
+			inode->i_op = &isofs_symlink_inode_operations;
+		else if (S_ISCHR(inode->i_mode))
+			inode->i_op = &chrdev_inode_operations;
+		else if (S_ISBLK(inode->i_mode))
+			inode->i_op = &blkdev_inode_operations;
+		else if (S_ISFIFO(inode->i_mode))
+			init_fifo(inode);
 	}
+
+ out:
+	if (tmpde)
+		kfree(tmpde);
+	if (bh)
+		brelse(bh);
 	return;
 
-      fail:
+ out_badread:
+	printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
+ fail:
 	/* With a data error we return this information */
 	inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
 	inode->u.isofs_i.i_first_extent = 0;
@@ -1257,144 +1289,7 @@
 	inode->i_uid = inode->i_gid = 0;
 	inode->i_mode = S_IFREG;  /*Regular file, no one gets to read*/
 	inode->i_op = NULL;
-	return;
-}
-
-/* There are times when we need to know the inode number of a parent of
-   a particular directory.  When control passes through a routine that
-   has access to the parent information, it fills it into the inode structure,
-   but sometimes the inode gets flushed out of the queue, and someone
-   remembers the number.  When they try to open up again, we have lost
-   the information.  The '..' entry on the disc points to the data area
-   for a particular inode, so we can follow these links back up, but since
-   we do not know the inode number, we do not actually know how large the
-   directory is.  The disc is almost always correct, and there is
-   enough error checking on the drive itself, but an open ended search
-   makes me a little nervous.
-
-   The BSD iso filesystem uses the extent number for an inode, and this
-   would work really nicely for us except that the read_inode function
-   would not have any clean way of finding the actual directory record
-   that goes with the file.  If we had such info, then it would pay
-   to change the inode numbers and eliminate this function.
-*/
-
-int isofs_lookup_grandparent(struct inode * parent, int extent)
-{
-	unsigned long bufsize = ISOFS_BUFFER_SIZE(parent);
-	unsigned char bufbits = ISOFS_BUFFER_BITS(parent);
-	unsigned int block,offset;
-	int parent_dir, inode_number;
-	int result;
-	int directory_size;
-	struct buffer_head * bh;
-	struct iso_directory_record * de;
-
-	offset = 0;
-	block = extent << (ISOFS_ZONE_BITS(parent) - bufbits);
-	if (!(bh = bread(parent->i_dev, block, bufsize)))  return -1;
-
-	while (1 == 1) {
-		de = (struct iso_directory_record *) (bh->b_data + offset);
-		if (*((unsigned char *) de) == 0)
-		{
-			brelse(bh);
-			printk("Directory .. not found\n");
-			return -1;
-		}
-
-		offset += *((unsigned char *) de);
-
-		if (offset >= bufsize)
-		{
-			printk(".. Directory not in first block"
-			       " of directory.\n");
-			brelse(bh);
-			return -1;
-		}
-
-		if (de->name_len[0] == 1 && de->name[0] == 1)
-		{
-			parent_dir = find_rock_ridge_relocation(de, parent);
-			directory_size = isonum_733 (de->size);
-			brelse(bh);
-			break;
-		}
-	}
-#ifdef DEBUG
-	printk("Parent dir:%x\n",parent_dir);
-#endif
-	/* Now we know the extent where the parent dir starts on. */
-
-	result = -1;
-
-	offset = 0;
-	block = parent_dir << (ISOFS_ZONE_BITS(parent) - bufbits);
-	if (!block || !(bh = bread(parent->i_dev,block, bufsize)))
-	{
-		return -1;
-	}
-
-	for(;;)
-	{
-		de = (struct iso_directory_record *) (bh->b_data + offset);
-		inode_number = (block << bufbits)+(offset & (bufsize - 1));
-
-		/* If the length byte is zero, we should move on to the next
-		   CDROM sector.  If we are at the end of the directory, we
-		   kick out of the while loop. */
-
- 		if ((*((unsigned char *) de) == 0) || (offset == bufsize) )
-		{
-			brelse(bh);
-			offset = 0;
-			block++;
-			directory_size -= bufsize;
-			if(directory_size < 0) return -1;
-			if((block & 1) && (ISOFS_ZONE_BITS(parent) - bufbits) == 1)
-			{
-				return -1;
-			}
-			if((block & 3) && (ISOFS_ZONE_BITS(parent) - bufbits) == 2)
-			{
-				return -1;
-			}
-			if (!block
-			    || !(bh = bread(parent->i_dev,block, bufsize)))
-			{
-				return -1;
-			}
-			continue;
-		}
-
-		/* Make sure that the entire directory record is in the current
-		   bh block.  If not, we malloc a buffer, and put the two
-		   halves together, so that we can cleanly read the block.  */
-
-		offset += *((unsigned char *) de);
-
-		if (offset > bufsize)
-		{
- 			printk("Directory overrun\n");
- 			goto out;
-		}
-
-		if (find_rock_ridge_relocation(de, parent) == extent){
-			result = inode_number;
-			goto out;
-		}
-
-	}
-
-	/* We go here for any condition we cannot handle.
-	   We also drop through to here at the end of the directory. */
-
- out:
-	brelse(bh);
-#ifdef DEBUG
-	printk("Resultant Inode %d\n",result);
-#endif
-	return result;
+	goto out;
 }
 
 #ifdef LEAK_CHECK
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/joliet.c linux.19p1/fs/isofs/joliet.c
--- linux.vanilla/fs/isofs/joliet.c	Sun Jun 11 21:44:18 2000
+++ linux.19p1/fs/isofs/joliet.c	Wed Dec 13 21:58:21 2000
@@ -19,8 +19,6 @@
 	    struct nls_table *nls)
 {
 	unsigned char *ip, *op;
-	unsigned char ch, cl;
-	unsigned char *uni_page;
 
 	ip = uni;
 	op = ascii;
@@ -68,8 +66,8 @@
 }
 
 int
-get_joliet_filename(struct iso_directory_record * de, struct inode * inode,
-		    unsigned char *outname)
+get_joliet_filename(struct iso_directory_record * de, unsigned char *outname,
+		    struct inode * inode)
 {
 	unsigned char utf8;
 	struct nls_table *nls;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/namei.c linux.19p1/fs/isofs/namei.c
--- linux.vanilla/fs/isofs/namei.c	Fri Apr 21 12:46:43 2000
+++ linux.19p1/fs/isofs/namei.c	Wed Dec 13 21:58:21 2000
@@ -53,200 +53,130 @@
  *	isofs_find_entry()
  *
  * finds an entry in the specified directory with the wanted name. It
- * returns the cache buffer in which the entry was found, and the entry
- * itself (as an inode number). It does NOT read the inode of the
- * entry - you'll have to do that yourself if you want to.
+ * returns the inode number of the found entry, or 0 on error.
  */
-static struct buffer_head *
-isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
+static unsigned long
+isofs_find_entry(struct inode *dir, struct dentry *dentry,
+		 char *tmpname, struct iso_directory_record *tmpde)
 {
+	unsigned long inode_number;
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
 	unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
-	unsigned int block, i, f_pos, offset, 
-		inode_number = 0; /* shut gcc up */
-	struct buffer_head * bh , * retval = NULL;
-	unsigned int old_offset;
-	int dlen, match;
-	char * dpnt;
-	unsigned char *page = NULL;
-	struct iso_directory_record * de = NULL; /* shut gcc up */
-	char de_not_in_buf = 0;	  /* true if de is in kmalloc'd memory */
-	char c;
-
-	*ino = 0;
-	
-	if (!(block = dir->u.isofs_i.i_first_extent)) return NULL;
+	unsigned int block, f_pos, offset;
+	struct buffer_head * bh = NULL;
+
+	if (!dir->u.isofs_i.i_first_extent)
+		return 0;
   
 	f_pos = 0;
+	offset = 0;
+	block = 0;
 
-	offset = f_pos & (bufsize - 1);
-	block = isofs_bmap(dir,f_pos >> bufbits);
+	while (f_pos < dir->i_size) {
+		struct iso_directory_record * de;
+		int de_len, match, i, dlen;
+		char *dpnt;
 
-	if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL;
+		if (!bh) {
+			bh = isofs_bread(dir, bufsize, block);
+			if (!bh)
+				return 0;
+		}
 
-	while (f_pos < dir->i_size) {
+		de = (struct iso_directory_record *) (bh->b_data + offset);
+		inode_number = (bh->b_blocknr << bufbits) + offset;
 
-		/* if de is in kmalloc'd memory, do not point to the
-                   next de, instead we will move to the next sector */
-		if(!de_not_in_buf) {
-			de = (struct iso_directory_record *) 
-				(bh->b_data + offset);
-		}
-		inode_number = (block << bufbits) + (offset & (bufsize - 1));
-
-		/* If byte is zero, or we had to fetch this de past
-		   the end of the buffer, this is the end of file, or
-		   time to move to the next sector. Usually 2048 byte
-		   boundaries. */
-		
-		if (*((unsigned char *) de) == 0 || de_not_in_buf) {
-			if(de_not_in_buf) {
-				/* james@bpgc.com: Since we slopped
-                                   past the end of the last buffer, we
-                                   must start some way into the new
-                                   one */
-				de_not_in_buf = 0;
-				kfree(de);
-				f_pos += offset;
-			}
-			else { 
-				offset = 0;
-				f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1))
-					 + ISOFS_BLOCK_SIZE);
-			}
+		de_len = *(unsigned char *) de;
+		if (!de_len) {
 			brelse(bh);
 			bh = NULL;
-
-			if (f_pos >= dir->i_size) 
-				break;
-
-			block = isofs_bmap(dir,f_pos>>bufbits);
-			if (!block || !(bh = bread(dir->i_dev,block,bufsize)))
-				break;
-
-			continue; /* Will kick out if past end of directory */
+			f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
+			block = f_pos >> bufbits;
+			offset = 0;
+			continue;
 		}
 
-		old_offset = offset;
-		offset += *((unsigned char *) de);
-		f_pos += *((unsigned char *) de);
+		offset += de_len;
+		f_pos += de_len;
 
-		/* james@bpgc.com: new code to handle case where the
-		   directory entry spans two blocks.  Usually 1024
-		   byte boundaries */
+		/* Make sure we have a full directory entry */
 		if (offset >= bufsize) {
-			struct buffer_head *bh_next;
-
-			/* james@bpgc.com: read the next block, and
-                           copy the split de into a newly kmalloc'd
-                           buffer */
-			block = isofs_bmap(dir,f_pos>>bufbits);
-			if (!block || 
-			    !(bh_next = bread(dir->i_dev,block,bufsize)))
-				break;
-			
-			de = (struct iso_directory_record *)
-				kmalloc(offset - old_offset, GFP_KERNEL);
-			if(de==NULL)
-				break;
-			memcpy((char *)de, bh->b_data + old_offset, 
-			       bufsize - old_offset);
-			memcpy((char *)de + bufsize - old_offset,
-			       bh_next->b_data, offset - bufsize);
-			brelse(bh_next);
-			de_not_in_buf = 1;
-			offset -= bufsize;
+			int slop = bufsize - offset + de_len;
+			memcpy(tmpde, de, slop);
+			offset &= bufsize - 1;
+			block++;
+			brelse(bh);
+			bh = NULL;
+			if (offset) {
+				bh = isofs_bread(dir, bufsize, block);
+				if (!bh)
+					return 0;
+				memcpy((void *) tmpde + slop, bh->b_data, offset);
+			}
+			de = tmpde;
 		}
+
 		dlen = de->name_len[0];
 		dpnt = de->name;
 
-		if (dir->i_sb->u.isofs_sb.s_rock ||
-		    dir->i_sb->u.isofs_sb.s_joliet_level || 
-		    dir->i_sb->u.isofs_sb.s_mapping == 'n' ||
-		    dir->i_sb->u.isofs_sb.s_mapping == 'a') {
-			if (! page) {
-				page = (unsigned char *)
-					__get_free_page(GFP_KERNEL);
-				if (!page) break;
-			}
-		}
 		if (dir->i_sb->u.isofs_sb.s_rock &&
-		    ((i = get_rock_ridge_filename(de, page, dir)))) {
-			dlen = i;
-			dpnt = page;
+		    ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
+			dlen = i; 	/* possibly -1 */
+			dpnt = tmpname;
 #ifdef CONFIG_JOLIET
 		} else if (dir->i_sb->u.isofs_sb.s_joliet_level) {
-			dlen = get_joliet_filename(de, dir, page);
-			dpnt = page;
+			dlen = get_joliet_filename(de, tmpname, dir);
+			dpnt = tmpname;
 #endif
 		} else if (dir->i_sb->u.isofs_sb.s_mapping == 'a') {
-			dlen = get_acorn_filename(de, page, dir);
-			dpnt = page;
+			dlen = get_acorn_filename(de, tmpname, dir);
+			dpnt = tmpname;
 		} else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') {
-			for (i = 0; i < dlen; i++) {
-				c = dpnt[i];
-				/* lower case */
-				if (c >= 'A' && c <= 'Z') c |= 0x20;
-				if (c == ';' && i == dlen-2 && dpnt[i+1] == '1') {
-					dlen -= 2;
-					break;
-				}
-				if (c == ';') c = '.';
-				page[i] = c;
-			}
-			/* This allows us to match with and without
-			 * a trailing period. */
-			if(page[dlen-1] == '.' && dentry->d_name.len == dlen-1)
-				dlen--;
-			dpnt = page;
+			dlen = isofs_name_translate(de, tmpname, dir);
+			dpnt = tmpname;
 		}
+
 		/*
 		 * Skip hidden or associated files unless unhide is set 
 		 */
 		match = 0;
-		if ((!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
-		    || dir->i_sb->u.isofs_sb.s_unhide == 'y') && dlen)
+		if (dlen > 0 &&
+		    (!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
+		     || dir->i_sb->u.isofs_sb.s_unhide == 'y'))
 		{
 			match = (isofs_cmp(dentry,dpnt,dlen) == 0);
 		}
 		if (match) {
-			if(inode_number == -1) {
-				/* Should only happen for the '..' entry */
-				inode_number = 
-					isofs_lookup_grandparent(dir,
-					   find_rock_ridge_relocation(de,dir));
-			}
-			*ino = inode_number;
-			retval = bh;
-			bh = NULL;
-			break;
+			if (bh)
+				brelse(bh);
+			return inode_number;
 		}
 	}
-	if (page) free_page((unsigned long) page);
-	if (bh) brelse(bh);
-	if(de_not_in_buf) 
-		kfree(de);
-	return retval;
+	if (bh)
+		brelse(bh);
+	return 0;
 }
 
 struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry)
 {
 	unsigned long ino;
-	struct buffer_head * bh;
 	struct inode *inode;
+	char *page;
+	struct iso_directory_record * tmpde;
 
-#ifdef DEBUG
-	printk("lookup: %x %s\n",dir->i_ino, dentry->d_name.name);
-#endif
 	dentry->d_op = dir->i_sb->s_root->d_op;
 
-	bh = isofs_find_entry(dir, dentry, &ino);
+	page = (char *) __get_free_page(GFP_USER);
+	if (!page)
+		return ERR_PTR(-ENOMEM);
+	tmpde = (struct iso_directory_record *) (page+1024);
 
-	inode = NULL;
-	if (bh) {
-		brelse(bh);
+	ino = isofs_find_entry(dir, dentry, page, tmpde);
+	free_page((unsigned long) page);
 
-		inode = iget(dir->i_sb,ino);
+	inode = NULL;
+	if (ino) {
+		inode = iget(dir->i_sb, ino);
 		if (!inode)
 			return ERR_PTR(-EACCES);
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/rock.c linux.19p1/fs/isofs/rock.c
--- linux.vanilla/fs/isofs/rock.c	Fri Apr 21 12:46:43 2000
+++ linux.19p1/fs/isofs/rock.c	Wed Dec 13 21:58:21 2000
@@ -70,7 +70,7 @@
       cont_size = 0; \
       cont_offset = 0; \
       goto LABEL; \
-    };    \
+    }    \
     printk("Unable to read rock-ridge attributes\n");    \
   }}
 
@@ -118,22 +118,16 @@
 	CHECK_SP(goto out);
 	break;
       case SIG('C','L'):
-#ifdef DEBUG
-	printk("RR: CL\n");
-#endif
 	if (flag == 0) {
 	  retval = isonum_733(rr->u.CL.location);
 	  goto out;
-	};
+	}
 	break;
       case SIG('P','L'):
-#ifdef DEBUG
-	printk("RR: PL\n");
-#endif
 	if (flag != 0) {
 	  retval = isonum_733(rr->u.PL.location);
 	  goto out;
-	};
+	}
 	break;
       case SIG('C','E'):
 	CHECK_CE; /* This tells is if there is a continuation record */
@@ -141,8 +135,8 @@
       default:
 	break;
       }
-    };
-  };
+    }
+  }
   MAYBE_CONTINUE(repeat, inode);
   return retval;
  out:
@@ -150,6 +144,7 @@
   return retval;
 }
 
+/* return length of name field; 0: not found, -1: to be ignored */
 int get_rock_ridge_filename(struct iso_directory_record * de,
 			    char * retname, struct inode * inode)
 {
@@ -200,24 +195,21 @@
 	if (rr->u.NM.flags & ~1) {
 	  printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
 	  break;
-	};
+	}
 	if((strlen(retname) + rr->len - 5) >= 254) {
 	  truncate = 1;
 	  break;
-	};
+	}
 	strncat(retname, rr->u.NM.name, rr->len - 5);
 	retnamlen += rr->len - 5;
 	break;
       case SIG('R','E'):
-#ifdef DEBUG
-	printk("RR: RE (%x)\n", inode->i_ino);
-#endif
 	if (buffer) kfree(buffer);
 	return -1;
       default:
 	break;
       }
-    };
+    }
   }
   MAYBE_CONTINUE(repeat,inode);
   return retnamlen; /* If 0, this file did not have a NM field */
@@ -263,10 +255,10 @@
 	break;
       case SIG('E','R'):
 	inode->i_sb->u.isofs_sb.s_rock = 1;
-	printk(KERN_DEBUG"ISO 9660 Extensions: ");
+	printk(KERN_DEBUG "ISO 9660 Extensions: ");
 	{ int p;
 	  for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
-	};
+	}
 	  printk("\n");
 	break;
       case SIG('P','X'):
@@ -337,27 +329,24 @@
 	   slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
 
 	   if(slen < 2) {
-	     if(    ((rr->u.SL.flags & 1) != 0) 
-		    && ((oldslp->flags & 1) == 0) ) inode->i_size += 1;
+	     if((rr->u.SL.flags & 1) != 0 && (oldslp->flags & 1) == 0)
+		     inode->i_size += 1;
 	     break;
 	   }
 
 	   /*
 	    * If this component record isn't continued, then append a '/'.
 	    */
-	   if(   (!rootflag)
-		 && ((oldslp->flags & 1) == 0) ) inode->i_size += 1;
+	   if(!rootflag && (oldslp->flags & 1) == 0)
+		   inode->i_size += 1;
 	 }
 	}
 	symlink_len = inode->i_size;
 	break;
       case SIG('R','E'):
-	printk("Attempt to read inode for relocated directory\n");
+	printk(KERN_WARNING "Attempt to read inode for relocated directory\n");
 	goto out;
       case SIG('C','L'):
-#ifdef DEBUG
-	printk("RR CL (%x)\n",inode->i_ino);
-#endif
 	inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location);
 	reloc = iget(inode->i_sb,
 		     (inode->u.isofs_i.i_first_extent <<
@@ -378,7 +367,7 @@
       default:
 	break;
       }
-    };
+    }
   }
   MAYBE_CONTINUE(repeat,inode);
   return 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/util.c linux.19p1/fs/isofs/util.c
--- linux.vanilla/fs/isofs/util.c	Fri Apr 21 12:46:43 2000
+++ linux.19p1/fs/isofs/util.c	Wed Dec 13 21:58:21 2000
@@ -98,7 +98,7 @@
 
 int iso_date(char * p, int flag)
 {
-	int year, month, day, hour ,minute, second, tz;
+	int year, month, day, hour, minute, second, tz;
 	int crtime, days, i;
 
 	year = p[0] - 70;
@@ -114,7 +114,6 @@
 		crtime = 0;
 	} else {
 		int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
-		extern struct timezone sys_tz;
 
 		days = year * 365;
 		if (year > 2)
@@ -126,8 +125,6 @@
 		days += day - 1;
 		crtime = ((((days * 24) + hour) * 60 + minute) * 60)
 			+ second;
-		if (sys_tz.tz_dsttime)
-			crtime -= 3600;
 
 		/* sign extend */
 		if (tz & 0x80)
@@ -149,7 +146,7 @@
 		 * NOTE: mkisofs in versions prior to mkisofs-1.10 had
 		 * the sign wrong on the timezone offset.  This has now
 		 * been corrected there too, but if you are getting screwy
-		 * results this may be the explaination.  If enough people
+		 * results this may be the explanation.  If enough people
 		 * complain, a user configuration option could be added
 		 * to add the timezone offset in with the wrong sign
 		 * for 'compatibility' with older discs, but I cannot see how
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/e820.h linux.19p1/include/asm-i386/e820.h
--- linux.vanilla/include/asm-i386/e820.h	Thu Jan  1 01:00:00 1970
+++ linux.19p1/include/asm-i386/e820.h	Thu Dec 14 10:25:21 2000
@@ -0,0 +1,40 @@
+/*
+ * structures and definitions for the int 15, ax=e820 memory map
+ * scheme.
+ *
+ * In a nutshell, arch/i386/boot/setup.S populates a scratch table
+ * in the empty_zero_block that contains a list of usable address/size
+ * duples.   In arch/i386/kernel/setup.c, this information is
+ * transferred into the e820map, and in arch/i386/mm/init.c, that
+ * new information is used to mark pages reserved or not.
+ *
+ */
+#ifndef __E820_HEADER
+#define __E820_HEADER
+
+#define E820MAP	0x2d0		/* our map */
+#define E820MAX	32		/* number of entries in E820MAP */
+#define E820NR	0x1e8		/* # entries in E820MAP */
+
+#define E820_RAM	1
+#define E820_RESERVED	2
+#define E820_ACPI	3 /* usable as RAM once ACPI tables have been read */
+#define E820_NVS	4
+
+#define HIGH_MEMORY	(1024*1024)
+
+#ifndef __ASSEMBLY__
+
+struct e820map {
+    int nr_map;
+    struct e820entry {
+	unsigned long long addr;	/* start of memory segment */
+	unsigned long long size;	/* size of memory segment */
+	unsigned long type;		/* type of memory segment */
+    } map[E820MAX];
+};
+
+extern struct e820map e820;
+#endif/*!__ASSEMBLY__*/
+
+#endif/*__E820_HEADER*/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/msr.h linux.19p1/include/asm-i386/msr.h
--- linux.vanilla/include/asm-i386/msr.h	Fri Apr 21 12:46:49 2000
+++ linux.19p1/include/asm-i386/msr.h	Wed Dec 13 21:50:26 2000
@@ -28,3 +28,7 @@
 			  : "=a" (low), "=d" (high) \
 			  : "c" (counter))
 
+/* symbolic names for some interesting MSRs */
+#define MSR_IA32_PLATFORM_ID	0x17
+#define MSR_IA32_UCODE_WRITE	0x79
+#define MSR_IA32_UCODE_REV	0x8B
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/processor.h linux.19p1/include/asm-i386/processor.h
--- linux.vanilla/include/asm-i386/processor.h	Mon Dec 11 22:13:06 2000
+++ linux.19p1/include/asm-i386/processor.h	Wed Dec 13 22:57:09 2000
@@ -327,6 +327,23 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+/* '6' because it used to be for P6 only, now supports P15 too */
+#define MICROCODE_IOCFREE _IO('6',0)
+
+/* physical layour of IA32 microcode chunks */
+struct microcode {
+	unsigned int hdrver;
+	unsigned int rev;
+	unsigned int date;
+	unsigned int sig;
+	unsigned int cksum;
+	unsigned int ldrver;
+	unsigned int pf;
+	unsigned int reserved[5];
+	unsigned int bits[500];
+};
+
+
 /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */
 extern inline void rep_nop(void)
 {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/agp_backend.h linux.19p1/include/linux/agp_backend.h
--- linux.vanilla/include/linux/agp_backend.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/agp_backend.h	Wed Dec 13 21:32:45 2000
@@ -45,6 +45,7 @@
 	INTEL_BX,
 	INTEL_GX,
 	INTEL_I810,
+	INTEL_I815,
 	INTEL_I840,
 	VIA_GENERIC,
 	VIA_VP3,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/blk.h linux.19p1/include/linux/blk.h
--- linux.vanilla/include/linux/blk.h	Sun Jun 11 21:44:23 2000
+++ linux.19p1/include/linux/blk.h	Wed Dec 13 22:57:10 2000
@@ -193,6 +193,14 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+#elif (MAJOR_NR == OSST_MAJOR)
+
+#define DEVICE_NAME "onstream" 
+#define DEVICE_INTR do_osst
+#define DEVICE_NR(device) (MINOR(device) & 0x7f) 
+#define DEVICE_ON(device) 
+#define DEVICE_OFF(device) 
+
 #elif (MAJOR_NR == SCSI_CDROM_MAJOR)
 
 #define DEVICE_NAME "CD-ROM"
@@ -412,7 +420,7 @@
 
 #endif /* MAJOR_NR == whatever */
 
-#if (MAJOR_NR != SCSI_TAPE_MAJOR)
+#if (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR)
 #if !defined(IDE_DRIVER)
 
 #ifndef CURRENT
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/capi.h linux.19p1/include/linux/capi.h
--- linux.vanilla/include/linux/capi.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/capi.h	Wed Dec 13 23:18:51 2000
@@ -114,4 +114,18 @@
 	__u16 errcode;
 } capi_ioctl_struct;
 
+/*
+ * Middleware extension
+ */
+
+#define CAPIFLAG_HIGHJACKING	0x0001
+
+#define CAPI_GET_FLAGS		_IOR('C',0x23, unsigned)
+#define CAPI_SET_FLAGS		_IOR('C',0x24, unsigned)
+#define CAPI_CLR_FLAGS		_IOR('C',0x25, unsigned)
+
+#define CAPI_NCCI_OPENCOUNT	_IOR('C',0x26, unsigned)
+
+#define CAPI_NCCI_GETUNIT	_IOR('C',0x27, unsigned)
+
 #endif				/* __LINUX_CAPI_H__ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/fs.h linux.19p1/include/linux/fs.h
--- linux.vanilla/include/linux/fs.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/fs.h	Wed Dec 13 22:57:08 2000
@@ -47,7 +47,12 @@
 
 /* And dynamically-tunable limits and defaults: */
 extern int max_inodes;
-extern int max_files, nr_files, nr_free_files;
+struct files_stat_struct {
+	int nr_files;		/* read only */
+	int nr_free_files;	/* read only */
+	int max_files;		/* tunable */
+};
+extern struct files_stat_struct files_stat; /* fs/file_table.c */
 extern int max_super_blocks, nr_super_blocks;
 
 #define NR_FILE  4096	/* this can well be larger on a larger system */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdn.h linux.19p1/include/linux/isdn.h
--- linux.vanilla/include/linux/isdn.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/isdn.h	Wed Dec 13 23:17:54 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.107 2000/09/10 20:29:18 detabc Exp $
+/* $Id: isdn.h,v 1.110 2000/11/01 17:54:01 detabc Exp $
 
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -66,9 +66,7 @@
 #undef CONFIG_ISDN_WITH_ABC_CH_EXTINUSE
 #undef CONFIG_ISDN_WITH_ABC_CONN_ERROR
 #undef CONFIG_ISDN_WITH_ABC_RAWIPCOMPRESS
-#undef CONFIG_ISDN_WITH_ABC_FRAME_LIMIT
-#undef CONFIG_ISDN_WITH_ABC_IPV4_RW_SOCKADDR 
-#undef CONFIG_ISDN_WITH_ABC_IPV4_RWUDP_SOCKADDR 
+#undef CONFIG_ISDN_WITH_ABC_IPTABLES_NETFILTER
 
 
 /* New ioctl-codes */
@@ -368,7 +366,6 @@
   ulong                  sqfull_stamp; /* Start-Time of overload           */
   ulong                  slavedelay;   /* Dynamic bundling delaytime       */
   int                    triggercps;   /* BogoCPS needed for trigger slave */
-  struct device      *srobin;      /* Ptr to Master device for slaves  */
   isdn_net_phone         *phone[2];    /* List of remote-phonenumbers      */
 				       /* phone[0] = Incoming Numbers      */
 				       /* phone[1] = Outgoing Numbers      */
@@ -378,9 +375,15 @@
   struct isdn_net_local_s *next;       /* Ptr to next link in bundle       */
   struct isdn_net_local_s *last;       /* Ptr to last link in bundle       */
   struct isdn_net_dev_s  *netdev;      /* Ptr to netdev                    */
-  struct sk_buff         *first_skb;   /* Ptr to skb that triggers dialing */
-  struct sk_buff *volatile sav_skb;    /* Ptr to skb, rejected by LL-driver*/
+  struct sk_buff_head    super_tx_queue; /* List of supervisory frames to  */
+	                               /* be transmitted asap              */
+  atomic_t frame_cnt;                  /* number of frames currently       */
+                        	       /* queued in HL driver              */    
                                        /* Ptr to orig. hard_header_cache   */
+  spinlock_t             xmit_lock;    /* used to protect the xmit path of */
+                                       /* a particular channel (including  */
+                                       /* the frame_cnt                    */
+
   int                    (*org_hhc)(
 				    struct neighbour *neigh,
 				    struct hh_cache *hh);
@@ -400,13 +403,17 @@
   int  cisco_loop;                     /* Loop counter for Cisco-SLARP     */
   ulong cisco_myseq;                   /* Local keepalive seq. for Cisco   */
   ulong cisco_yourseq;                 /* Remote keepalive seq. for Cisco  */
+  struct tq_struct tqueue;
 } isdn_net_local;
 
 /* the interface itself */
 typedef struct isdn_net_dev_s {
   isdn_net_local *local;
-  isdn_net_local *queue;
-  void           *next;                /* Pointer to next isdn-interface   */
+  isdn_net_local *queue;               /* circular list of all bundled
+					  channels, which are currently
+					  online                           */
+  spinlock_t queue_lock;               /* lock to protect queue            */
+  void *next;                          /* Pointer to next isdn-interface   */
   struct device dev;               /* interface to upper levels        */
 #ifdef CONFIG_ISDN_PPP
   ippp_bundle * pb;		/* pointer to the common bundle structure
@@ -697,5 +704,13 @@
 {
 	dev->tbusy = 1;
 }
+
+struct pci_dev;
+
+static int __inline__ pci_enable_device(struct pci_dev * pdev)
+{
+	return 0;
+}
+
 #endif /* __KERNEL__ */
 #endif /* isdn_h */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdn_ppp.h linux.19p1/include/linux/isdn_ppp.h
--- linux.vanilla/include/linux/isdn_ppp.h	Sun Jun 11 21:44:23 2000
+++ linux.19p1/include/linux/isdn_ppp.h	Wed Dec 13 22:09:06 2000
@@ -30,8 +30,8 @@
 #define PPPIOCGIFNAME      _IOR('t',136, char [IFNAMSIZ] )
 
 #define PPP_MP          0x003d
-#define PPP_LINK_COMP   0x00fb
-#define PPP_LINK_CCP    0x80fb
+#define PPP_COMPFRAG    0x00fb
+#define PPP_CCPFRAG     0x80fb
 
 #define SC_MP_PROT       0x00000200
 #define SC_REJ_MP_PROT   0x00000400
@@ -67,8 +67,6 @@
 };
 
 #ifdef __KERNEL__
-
-#include <asm/spinlock.h>
 
 /*
  * We need a way for the decompressor to influence the generation of CCP
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdnif.h linux.19p1/include/linux/isdnif.h
--- linux.vanilla/include/linux/isdnif.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/isdnif.h	Wed Dec 13 23:17:54 2000
@@ -135,6 +135,20 @@
 /* STAT_INVOKE_BRD callback. The ll_id is set to 0, the other fields */
 /* are supplied by the network and not by the HL.                    */   
 /*********************************************************************/
+
+/*****************/
+/* NI1 commands */ 
+/*****************/
+#define NI1_CMD_INVOKE       ((0x00 << 8) | ISDN_PTYPE_NI1)   /* invoke a supplementary service */
+#define NI1_CMD_INVOKE_ABORT ((0x01 << 8) | ISDN_PTYPE_NI1)   /* abort a invoke cmd */
+
+/*******************************/
+/* NI1 Status callback values */
+/*******************************/
+#define NI1_STAT_INVOKE_RES  ((0x80 << 8) | ISDN_PTYPE_NI1)   /* Result for invocation */
+#define NI1_STAT_INVOKE_ERR  ((0x81 << 8) | ISDN_PTYPE_NI1)   /* Error Return for invocation */
+#define NI1_STAT_INVOKE_BRD  ((0x82 << 8) | ISDN_PTYPE_NI1)   /* Deliver invoke broadcast info */
+
 typedef struct
   { ulong ll_id; /* ID supplied by LL when executing    */
 		 /* a command and returned by HL for    */
@@ -150,7 +164,7 @@
                  /* error value when error callback     */
     int datalen; /* length of cmd or stat data          */
     u_char *data;/* pointer to data delivered or send   */
-  } dss1_cmd_stat;
+  } isdn_cmd_stat;
 
 /*
  * Commands from linklevel to lowlevel
@@ -412,13 +426,16 @@
 		setup_parm setup;/* For SETUP msg			*/
 		capi_msg cmsg;	/* For CAPI like messages		*/
 		char display[85];/* display message data		*/ 
-		dss1_cmd_stat dss1_io; /* DSS1 IO-parameter/result	*/
+		isdn_cmd_stat isdn_io; /* ISDN IO-parameter/result	*/
 		aux_s aux;	/* for modem commands/indications	*/
 #ifdef CONFIG_ISDN_TTY_FAX
 		T30_s	*fax;	/* Pointer to ttys fax struct		*/
 #endif
 	} parm;
 } isdn_ctrl;
+
+#define dss1_io    isdn_io
+#define ni1_io     isdn_io
 
 /*
  * The interface-struct itself (initialized at load-time of lowlevel-driver)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/iso_fs.h linux.19p1/include/linux/iso_fs.h
--- linux.vanilla/include/linux/iso_fs.h	Fri Apr 21 23:11:46 2000
+++ linux.19p1/include/linux/iso_fs.h	Wed Dec 13 21:58:21 2000
@@ -177,41 +177,22 @@
 
 extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
 extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
+extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *);
 
 extern char * get_rock_ridge_symlink(struct inode *);
-extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
 
-int get_joliet_filename(struct iso_directory_record *, struct inode *, unsigned char *);
+extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
+int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *);
 int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
 
-/* The stuff that follows may be totally unneeded. I have not checked to see 
- which prototypes we are still using.  */
-
-extern int isofs_open(struct inode * inode, struct file * filp);
-extern void isofs_release(struct inode * inode, struct file * filp);
 extern struct dentry *isofs_lookup(struct inode * dir, struct dentry *);
-extern unsigned long isofs_count_free_inodes(struct super_block *sb);
-extern int isofs_new_block(int dev);
-extern int isofs_free_block(int dev, int block);
 extern int isofs_bmap(struct inode *,int);
-
-extern void isofs_put_super(struct super_block *);
-extern struct super_block *isofs_read_super(struct super_block *,void *,int);
+extern struct buffer_head *isofs_bread(struct inode *, unsigned int, unsigned int);
 extern int init_iso9660_fs(void);
-extern void isofs_read_inode(struct inode *);
-extern void isofs_put_inode(struct inode *);
-extern int isofs_statfs(struct super_block *, struct statfs *, int);
-
-extern int isofs_lseek(struct inode *, struct file *, off_t, int);
-extern int isofs_read(struct inode *, struct file *, char *, int);
-extern int isofs_lookup_grandparent(struct inode *, int);
 
 extern struct inode_operations isofs_file_inode_operations;
 extern struct inode_operations isofs_dir_inode_operations;
 extern struct inode_operations isofs_symlink_inode_operations;
-extern struct inode_operations isofs_chrdev_inode_operations;
-extern struct inode_operations isofs_blkdev_inode_operations;
-extern struct inode_operations isofs_fifo_inode_operations;
 
 /* The following macros are used to check for memory leaks. */
 #ifdef LEAK_CHECK
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/kernelcapi.h linux.19p1/include/linux/kernelcapi.h
--- linux.vanilla/include/linux/kernelcapi.h	Fri Apr 21 12:46:59 2000
+++ linux.19p1/include/linux/kernelcapi.h	Wed Dec 13 22:09:17 2000
@@ -1,58 +1,17 @@
 /*
- * $Id: kernelcapi.h,v 1.5 2000/01/28 16:45:40 calle Exp $
+ * $Id: kernelcapi.h,v 1.7 2000/06/12 09:20:20 kai Exp $
  * 
  * Kernel CAPI 2.0 Interface for Linux
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
- * $Log: kernelcapi.h,v $
- * Revision 1.5  2000/01/28 16:45:40  calle
- * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard),
- * will search named driver and call the add_card function if one exist.
- *
- * Revision 1.4  1999/09/10 17:24:19  calle
- * Changes for proposed standard for CAPI2.0:
- * - AK148 "Linux Exention"
- *
- * Revision 1.3  1999/07/01 15:26:56  calle
- * complete new version (I love it):
- * + new hardware independed "capi_driver" interface that will make it easy to:
- *   - support other controllers with CAPI-2.0 (i.e. USB Controller)
- *   - write a CAPI-2.0 for the passive cards
- *   - support serial link CAPI-2.0 boxes.
- * + wrote "capi_driver" for all supported cards.
- * + "capi_driver" (supported cards) now have to be configured with
- *   make menuconfig, in the past all supported cards where included
- *   at once.
- * + new and better informations in /proc/capi/
- * + new ioctl to switch trace of capi messages per controller
- *   using "avmcapictrl trace [contr] on|off|...."
- * + complete testcircle with all supported cards and also the
- *   PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done.
- *
- * Revision 1.2  1999/06/21 15:24:26  calle
- * extend information in /proc.
- *
- * Revision 1.1  1997/03/04 21:27:33  calle
- * First version in isdn4linux
- *
- * Revision 2.2  1997/02/12 09:31:39  calle
- * new version
- *
- * Revision 1.1  1997/01/31 10:32:20  calle
- * Initial revision
- *
- * 
  */
+
 #ifndef __KERNELCAPI_H__
 #define __KERNELCAPI_H__
 
-#define CAPI_MAXAPPL	20	/*
-				   * maximum number of applications 
-				 */
-#define CAPI_MAXCONTR	10	/*
-				   * maximum number of controller 
-				 */
+#define CAPI_MAXAPPL	20	/* maximum number of applications  */
+#define CAPI_MAXCONTR	10	/* maximum number of controller    */
 #define CAPI_MAXDATAWINDOW	8
 
 
@@ -94,8 +53,8 @@
 	__u16 (*capi_put_message) (__u16 applid, struct sk_buff * msg);
 	__u16 (*capi_get_message) (__u16 applid, struct sk_buff ** msgp);
 	__u16 (*capi_set_signal) (__u16 applid,
-			      void (*signal) (__u16 applid, __u32 param),
-				  __u32 param);
+			      void (*signal) (__u16 applid, void *param),
+				  void *param);
 	__u16 (*capi_get_manufacturer) (__u32 contr, __u8 buf[CAPI_MANUFACTURER_LEN]);
 	__u16 (*capi_get_version) (__u32 contr, struct capi_version * verp);
 	 __u16(*capi_get_serial) (__u32 contr, __u8 serial[CAPI_SERIAL_LEN]);
@@ -108,8 +67,15 @@
 
 };
 
-#define	KCI_CONTRUP	0
-#define	KCI_CONTRDOWN	1
+struct capi_ncciinfo {
+	__u16 applid;
+	__u32 ncci;
+};
+
+#define	KCI_CONTRUP	0	/* struct capi_profile */
+#define	KCI_CONTRDOWN	1	/* NULL */
+#define	KCI_NCCIUP	2	/* struct capi_ncciinfo */
+#define	KCI_NCCIDOWN	3	/* struct capi_ncciinfo */
 
 struct capi_interface_user {
 	char name[20];
@@ -147,6 +113,47 @@
 #define CAPI_MSGNOTINSTALLED 	          0x1109
 #define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP  0x110a
 #define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b
+
+typedef enum {
+        CapiMessageNotSupportedInCurrentState = 0x2001,
+        CapiIllContrPlciNcci                  = 0x2002,
+        CapiNoPlciAvailable                   = 0x2003,
+        CapiNoNcciAvailable                   = 0x2004,
+        CapiNoListenResourcesAvailable        = 0x2005,
+        CapiNoFaxResourcesAvailable           = 0x2006,
+        CapiIllMessageParmCoding              = 0x2007,
+} RESOURCE_CODING_PROBLEM;
+
+typedef enum {
+        CapiB1ProtocolNotSupported                      = 0x3001,
+        CapiB2ProtocolNotSupported                      = 0x3002,
+        CapiB3ProtocolNotSupported                      = 0x3003,
+        CapiB1ProtocolParameterNotSupported             = 0x3004,
+        CapiB2ProtocolParameterNotSupported             = 0x3005,
+        CapiB3ProtocolParameterNotSupported             = 0x3006,
+        CapiBProtocolCombinationNotSupported            = 0x3007,
+        CapiNcpiNotSupported                            = 0x3008,
+        CapiCipValueUnknown                             = 0x3009,
+        CapiFlagsNotSupported                           = 0x300a,
+        CapiFacilityNotSupported                        = 0x300b,
+        CapiDataLengthNotSupportedByCurrentProtocol     = 0x300c,
+        CapiResetProcedureNotSupportedByCurrentProtocol = 0x300d,
+        CapiTeiAssignmentFailed                         = 0x300e,
+} REQUESTED_SERVICES_PROBLEM;
+
+typedef enum {
+	CapiSuccess                                     = 0x0000,
+	CapiSupplementaryServiceNotSupported            = 0x300e,
+	CapiRequestNotAllowedInThisState                = 0x3010,
+} SUPPLEMENTARY_SERVICE_INFO;
+
+typedef enum {
+	CapiProtocolErrorLayer1                         = 0x3301,
+	CapiProtocolErrorLayer2                         = 0x3302,
+	CapiProtocolErrorLayer3                         = 0x3303,
+	CapiTimeOut                                     = 0x3303, // SuppServiceReason
+	CapiCallGivenToOtherApplication                 = 0x3304,
+} CAPI_REASON;
 
 #endif				/* __KERNEL__ */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/major.h linux.19p1/include/linux/major.h
--- linux.vanilla/include/linux/major.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/major.h	Wed Dec 13 21:31:48 2000
@@ -133,6 +133,8 @@
 #define MSR_MAJOR 202
 #define CPUID_MAJOR 203
 
+#define OSST_MAJOR	206	/* OnStream-SCx0 SCSI tape */
+
 /*
  * Tests for SCSI devices.
  */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/miscdevice.h linux.19p1/include/linux/miscdevice.h
--- linux.vanilla/include/linux/miscdevice.h	Fri Apr 21 12:46:59 2000
+++ linux.19p1/include/linux/miscdevice.h	Wed Dec 13 21:50:26 2000
@@ -17,6 +17,7 @@
 #define SUN_OPENPROM_MINOR 139
 #define NVRAM_MINOR 144
 #define I2O_MINOR 166
+#define MICROCODE_MINOR 184
 #define MISC_DYNAMIC_MINOR 255
 
 #define SGI_GRAPHICS_MINOR   146
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/mm.h linux.19p1/include/linux/mm.h
--- linux.vanilla/include/linux/mm.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/mm.h	Wed Dec 13 22:57:09 2000
@@ -129,7 +129,11 @@
 	struct wait_queue *wait;
 	struct page **pprev_hash;
 	struct buffer_head * buffers;
+	int age;
 } mem_map_t;
+
+#define PAGE_AGE_INITIAL 1	/* age for pages just mapped */
+#define PAGE_AGE_YOUNG 2	/* age for pages recently referenced */
 
 /* Page flag bit values */
 #define PG_locked		 0
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/mtio.h linux.19p1/include/linux/mtio.h
--- linux.vanilla/include/linux/mtio.h	Fri Apr 21 23:10:54 2000
+++ linux.19p1/include/linux/mtio.h	Wed Dec 13 21:31:48 2000
@@ -105,6 +105,8 @@
 #define MT_ISEVEREX_FT40A	0x32	/* Everex FT40A (QIC-40) */
 #define MT_ISDDS1		0x51	/* DDS device without partitions */
 #define MT_ISDDS2		0x52	/* DDS device with partitions */
+#define MT_ISONSTREAM_SC        0x61	/* OnStream SCSI tape drives (SC-x0)
+					   and SCSI emulated (DI, DP, USB) */
 #define MT_ISSCSI1		0x71	/* Generic ANSI SCSI-1 tape unit */
 #define MT_ISSCSI2		0x72	/* Generic ANSI SCSI-2 tape unit */
 
@@ -134,6 +136,7 @@
 	{MT_ISWT5099EEN24,	"Wangtek 5099-een24, 60MB"}, \
 	{MT_ISTEAC_MT2ST,	"Teac MT-2ST 155mb data cassette drive"}, \
 	{MT_ISEVEREX_FT40A,	"Everex FT40A, QIC-40"}, \
+	{MT_ISONSTREAM_SC,      "OnStream SC-, DI-, DP-, or USB tape drive"}, \
 	{MT_ISSCSI1,		"Generic SCSI-1 tape"}, \
 	{MT_ISSCSI2,		"Generic SCSI-2 tape"}, \
 	{0, NULL} \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sched.h linux.19p1/include/linux/sched.h
--- linux.vanilla/include/linux/sched.h	Mon Dec 11 22:13:07 2000
+++ linux.19p1/include/linux/sched.h	Wed Dec 13 22:57:09 2000
@@ -79,6 +79,7 @@
 #define TASK_ZOMBIE		4
 #define TASK_STOPPED		8
 #define TASK_SWAPPING		16
+#define TASK_EXCLUSIVE		32
 
 /*
  * Scheduling policies
@@ -251,6 +252,7 @@
 	struct task_struct *next_task, *prev_task;
 	struct task_struct *next_run,  *prev_run;
 
+	unsigned int task_exclusive;	/* task wants wake-one semantics in __wake_up() */
 /* task state */
 	struct linux_binfmt *binfmt;
 	int exit_code, exit_signal;
@@ -370,6 +372,7 @@
 /* counter */	DEF_PRIORITY,DEF_PRIORITY,0, \
 /* SMP */	0,0,0,-1, \
 /* schedlink */	&init_task,&init_task, &init_task, &init_task, \
+/* task_exclusive */ 0, \
 /* binfmt */	NULL, \
 /* ec,brk... */	0,0,0,0,0,0, \
 /* pid etc.. */	0,0,0,0,0, \
@@ -496,8 +499,8 @@
 						    signed long timeout));
 extern void FASTCALL(wake_up_process(struct task_struct * tsk));
 
-#define wake_up(x)			__wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
-#define wake_up_interruptible(x)	__wake_up((x),TASK_INTERRUPTIBLE)
+#define wake_up(x)			__wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE)
+#define wake_up_interruptible(x)	__wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE)
 
 #define __set_current_state(state_value)	do { current->state = state_value; } while (0)
 #ifdef __SMP__
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/ip_masq.h linux.19p1/include/net/ip_masq.h
--- linux.vanilla/include/net/ip_masq.h	Fri Apr 21 23:12:18 2000
+++ linux.19p1/include/net/ip_masq.h	Wed Dec 13 22:59:35 2000
@@ -31,6 +31,32 @@
 #define MASQUERADE_EXPIRE_TCP     15*60*HZ
 #define MASQUERADE_EXPIRE_TCP_FIN  2*60*HZ
 #define MASQUERADE_EXPIRE_UDP      5*60*HZ
+
+/*
+ *	Debugging stuff
+ */
+
+extern int ip_masq_get_debug_level(void);
+
+#ifdef CONFIG_IP_MASQ_DEBUG
+#define IP_MASQ_DEBUG(level, msg...) do { \
+	if (level <= ip_masq_get_debug_level()) \
+		printk(KERN_DEBUG "IP_MASQ:" ## msg); \
+	} while (0)
+#else	/* NO DEBUGGING at ALL */
+#define IP_MASQ_DEBUG(level, msg...) do { } while (0)
+#endif
+
+#define IP_MASQ_INFO(msg...) \
+	printk(KERN_INFO "IP_MASQ:" ## msg)
+
+#define IP_MASQ_ERR(msg...) \
+	printk(KERN_ERR "IP_MASQ:" ## msg)
+
+#define IP_MASQ_WARNING(msg...) \
+	printk(KERN_WARNING "IP_MASQ:" ## msg)
+
+
 /* 
  * ICMP can no longer be modified on the fly using an ioctl - this
  * define is the only way to change the timeouts 
@@ -164,7 +190,7 @@
 {
         struct ip_masq_app *next;
 	char *name;		/* name of application proxy */
-        unsigned type;          /* type = proto<<16 | port (host byte order)*/
+        unsigned type;          /* type = flags | proto<<16 | port (host byte order)*/
         int n_attach;
         int (*masq_init_1)      /* ip_masq initializer */
                 (struct ip_masq_app *, struct ip_masq *);
@@ -182,20 +208,60 @@
 extern int ip_masq_app_init(void);
 
 /*
- * 	ip_masq_app object registration functions (port: host byte order)
+ * 	ip_masq_app object registration functions: register_ip_masq_app_type()
+ * 	can be used if ip_masq_app has been previously initialized
+ * 	(port: host byte order)
  */
-extern int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port);
+extern int register_ip_masq_app_type(struct ip_masq_app *mapp);
+extern int register_ip_masq_app(struct ip_masq_app *mapp, unsigned proto, __u16 port);
 extern int unregister_ip_masq_app(struct ip_masq_app *mapp);
 
 /*
+ * 	ip_masq_app flags[8bits] (ORed with protocol[8bits] ) 
+ */
+#define IP_MASQ_APP_OUTBOUND	0x01000000	/* dst port matched (in-out) */
+#define IP_MASQ_APP_INBOUND	0x02000000	/* src port matched (in-out) */
+#define IP_MASQ_APP_FWMARK	0x80000000	/* hook by (fwmark & 0xffff) */
+#define IP_MASQ_APP_FLAGS_MASK	0xff000000	/* mask for app flags */
+#define IP_MASQ_APP_PROTO_MASK	0x00ff0000	/* mask for proto value */
+#define IP_MASQ_APP_PORT_MASK	0x0000ffff	/* mask for port value */
+#define IP_MASQ_APP_FWMARK_MASK	0x00ffffff	/* mask for fwmark value (24bits only) */
+
+#define IP_MASQ_APP_TYPE_PP(flags, proto, port) ((flags)|((proto)<<16)|(port))
+#define IP_MASQ_APP_TYPE_FWMARK(flags, fwmark) ((flags)|(fwmark&0x00ffffff))
+#define IP_MASQ_APP_TYPE2PORT(type)        ( (type) & 0xffff )
+#define IP_MASQ_APP_TYPE2PROTO(type)       ( ((type)>>16) & 0x00ff )
+#define IP_MASQ_APP_TYPE2FWMARK(type)       ( (type) & 0x00ffffff )
+#define IP_MASQ_APP_TYPE2FLAGS(type)       ( (type) & 0xff000000 )
+
+/* Init functions for later register_ip_masq_app_type() */
+static __inline__ int ip_masq_app_init_proto_port(struct ip_masq_app* mapp, unsigned flags, unsigned proto, unsigned port) {
+        mapp->type = IP_MASQ_APP_TYPE_PP(flags, proto, port);
+	return 0;
+}
+static __inline__ int ip_masq_app_init_fwmark(struct ip_masq_app* mapp, unsigned flags, __u32 fwmark) {
+	if (fwmark&~(IP_MASQ_APP_FWMARK_MASK)) {
+		IP_MASQ_ERR("ip_masq_app_init_fwmark(): fwmark must be <= %d, sorry (shoot Juanjo)\n", IP_MASQ_APP_FWMARK_MASK);
+		return -EINVAL;
+	}
+        mapp->type = IP_MASQ_APP_TYPE_FWMARK(flags, fwmark);
+	return 0;
+}
+
+/*
  *	get ip_masq_app obj by proto,port(net_byte_order)
  */
-extern struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port);
+extern struct ip_masq_app * ip_masq_app_get_type(unsigned type);
+static __inline__ struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port) {
+        unsigned type = IP_MASQ_APP_TYPE_PP(0, proto, port);
+	return ip_masq_app_get_type(type);
+}
 
 /*
  *	ip_masq TO ip_masq_app (un)binding functions.
  */
 extern struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms);
+extern struct ip_masq_app * ip_masq_bind_app_fwmark(struct ip_masq *ms, __u32);
 extern int ip_masq_unbind_app(struct ip_masq *ms);
 
 /*
@@ -253,31 +319,6 @@
 /*
  *
  */
-
-/*
- *	Debugging stuff
- */
-
-extern int ip_masq_get_debug_level(void);
-
-#ifdef CONFIG_IP_MASQ_DEBUG
-#define IP_MASQ_DEBUG(level, msg...) do { \
-	if (level <= ip_masq_get_debug_level()) \
-		printk(KERN_DEBUG "IP_MASQ:" ## msg); \
-	} while (0)
-#else	/* NO DEBUGGING at ALL */
-#define IP_MASQ_DEBUG(level, msg...) do { } while (0)
-#endif
-
-#define IP_MASQ_INFO(msg...) \
-	printk(KERN_INFO "IP_MASQ:" ## msg)
-
-#define IP_MASQ_ERR(msg...) \
-	printk(KERN_ERR "IP_MASQ:" ## msg)
-
-#define IP_MASQ_WARNING(msg...) \
-	printk(KERN_WARNING "IP_MASQ:" ## msg)
-
 
 /*
  *	/proc/net entry
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/init/main.c linux.19p1/init/main.c
--- linux.vanilla/init/main.c	Mon Dec 11 22:13:08 2000
+++ linux.19p1/init/main.c	Wed Dec 13 21:31:48 2000
@@ -216,6 +216,7 @@
 #endif
 extern void floppy_setup(char *str, int *ints);
 extern void st_setup(char *str, int *ints);
+extern void osst_setup(char *str, int *ints);
 extern void st0x_setup(char *str, int *ints);
 extern void advansys_setup(char *str, int *ints);
 extern void tmc8xx_setup(char *str, int *ints);
@@ -840,6 +841,9 @@
 #endif
 #ifdef CONFIG_CHR_DEV_ST
 	{ "st=", st_setup },
+#endif
+#ifdef CONFIG_CHR_DEV_OSST
+	{ "osst=", osst_setup },
 #endif
 #ifdef CONFIG_BUSMOUSE
 	{ "bmouse=", bmouse_setup },
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/sched.c linux.19p1/kernel/sched.c
--- linux.vanilla/kernel/sched.c	Mon Dec 11 22:13:08 2000
+++ linux.19p1/kernel/sched.c	Wed Dec 13 22:21:20 2000
@@ -890,8 +890,9 @@
  */
 void __wake_up(struct wait_queue **q, unsigned int mode)
 {
-	struct task_struct *p;
+	struct task_struct *p, *best_exclusive;
 	struct wait_queue *head, *next;
+	unsigned int do_exclusive;
 
         if (!q)
 		goto out;
@@ -906,22 +907,23 @@
 	if (!next)
 		goto out_unlock;
 
+	best_exclusive = 0;
+	do_exclusive = mode & TASK_EXCLUSIVE;
 	while (next != head) {
 		p = next->task;
 		next = next->next;
 		if (p->state & mode) {
-			/*
-			 * We can drop the read-lock early if this
-			 * is the only/last process.
-			 */
-			if (next == head) {
-				read_unlock(&waitqueue_lock);
+			if (do_exclusive && p->task_exclusive) {
+				if (best_exclusive == NULL)
+					best_exclusive = p;
+			}
+			else {
 				wake_up_process(p);
-				goto out;
 			}
-			wake_up_process(p);
 		}
 	}
+	if (best_exclusive)
+		wake_up_process(best_exclusive);
 out_unlock:
 	read_unlock(&waitqueue_lock);
 out:
@@ -1943,7 +1945,7 @@
 		delay=(t.tv_nsec + 999) / 1000;
 		
 		if(delay>10000)
-			mdelay(delay);
+			mdelay((delay+999)/1000);
 		else
 			udelay(delay);
 		return 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/sysctl.c linux.19p1/kernel/sysctl.c
--- linux.vanilla/kernel/sysctl.c	Mon Dec 11 22:13:08 2000
+++ linux.19p1/kernel/sysctl.c	Wed Dec 13 21:48:45 2000
@@ -286,9 +286,9 @@
 	 0444, NULL, &proc_dointvec},
 	{FS_MAXINODE, "inode-max", &max_inodes, sizeof(int),
 	 0644, NULL, &proc_dointvec},
-	{FS_NRFILE, "file-nr", &nr_files, 3*sizeof(int),
+	{FS_NRFILE, "file-nr", &files_stat, 3*sizeof(int),
 	 0444, NULL, &proc_dointvec},
-	{FS_MAXFILE, "file-max", &max_files, sizeof(int),
+	{FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int),
 	 0644, NULL, &proc_dointvec},
 	{FS_NRSUPER, "super-nr", &nr_super_blocks, sizeof(int),
 	 0444, NULL, &proc_dointvec},
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/filemap.c linux.19p1/mm/filemap.c
--- linux.vanilla/mm/filemap.c	Mon Dec 11 22:13:08 2000
+++ linux.19p1/mm/filemap.c	Wed Dec 13 22:27:24 2000
@@ -158,8 +158,6 @@
  refresh_clock:
 	page = mem_map + clock;
 	do {
-		int referenced;
-
 		/* This works even in the presence of PageSkip because
 		 * the first two entries at the beginning of a hole will
 		 * be marked, not just the first.
@@ -176,32 +174,31 @@
 			clock = page - mem_map;
 		}
 		
+		if (test_and_clear_bit(PG_referenced, &page->flags)) {
+			page->age = PAGE_AGE_YOUNG;
+			continue;
+		}
+
+		if (page->age > 0) {
+			page->age--;
+			continue;
+		}
+
 		/* We can't free pages unless there's just one user */
 		if (atomic_read(&page->count) != 1)
 			continue;
 
-		referenced = test_and_clear_bit(PG_referenced, &page->flags);
-
 		if (PageLocked(page))
 			continue;
 
 		if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
 			continue;
 
-		/*
-		 * Is it a page swap page? If so, we want to
-		 * drop it if it is no longer used, even if it
-		 * were to be marked referenced..
-		 */
+		/* Is it a page swap page? Drop it, its old. */
 		if (PageSwapCache(page)) {
-			if (referenced && swap_count(page->offset) != 1)
-				continue;
 			delete_from_swap_cache(page);
 			return 1;
 		}	
-
-		if (referenced)
-			continue;
 
 		/* Is it a buffer page? */
 		if (page->buffers) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/page_alloc.c linux.19p1/mm/page_alloc.c
--- linux.vanilla/mm/page_alloc.c	Sat Sep  9 18:42:51 2000
+++ linux.19p1/mm/page_alloc.c	Wed Dec 13 22:27:24 2000
@@ -129,6 +129,7 @@
 		if (PageSwapCache(page))
 			panic ("Freeing swap cache page");
 		page->flags &= ~(1 << PG_referenced);
+		page->age = PAGE_AGE_INITIAL;
 		free_pages_ok(page - mem_map, order, PageDMA(page) ? 1 : 0);
 		return;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq.c linux.19p1/net/ipv4/ip_masq.c
--- linux.vanilla/net/ipv4/ip_masq.c	Fri Apr 21 12:47:14 2000
+++ linux.19p1/net/ipv4/ip_masq.c	Wed Dec 13 22:22:38 2000
@@ -236,7 +236,7 @@
 	
 tcp_state_out:
 	if (new_state!=ms->state)
-		IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08lX:%04X-%08lX:%04X state: %s->%s\n",
+		IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08X:%04X-%08X:%04X state: %s->%s\n",
 				masq_proto_name(ms->protocol),
 				output? "output" : "input ",
 				th->syn? 'S' : '.',
@@ -798,7 +798,7 @@
 	 */
 	atomic_inc(&ms->refcnt);
 
-	IP_MASQ_DEBUG(1, "Masqueraded %s %08lX:%04X expired\n",
+	IP_MASQ_DEBUG(1, "Masqueraded %s %08X:%04X expired\n",
 			masq_proto_name(ms->protocol),
 			ntohl(ms->saddr),ntohs(ms->sport));
 
@@ -846,7 +846,7 @@
 	}
 
 masq_expire_later:
-	IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08lX:%04X->%08lX:%04X masq.refcnt-1=%d masq.n_control=%d\n",
+	IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08X:%04X->%08X:%04X masq.refcnt-1=%d masq.n_control=%d\n",
 		masq_proto_name(ms->protocol),
 		ntohl(ms->saddr), ntohs(ms->sport),
 		ntohl(ms->daddr), ntohs(ms->dport),
@@ -1223,7 +1223,7 @@
 
 	/* h.raw = (char*) iph + iph->ihl * 4; */
 
- 	IP_MASQ_DEBUG(2, "Outgoing %s %08lX:%04X -> %08lX:%04X\n",
+ 	IP_MASQ_DEBUG(2, "Outgoing %s %08X:%04X -> %08X:%04X\n",
   		masq_proto_name(iph->protocol),
   		ntohl(iph->saddr), ntohs(h.portp[0]),
   		ntohl(iph->daddr), ntohs(h.portp[1]));
@@ -1294,6 +1294,8 @@
 					0);
                 if (ms == NULL)
 			return -1;
+		if (!ms->app && skb->fwmark)
+			ip_masq_bind_app_fwmark(ms, skb->fwmark);
  	}
 
 	/*
@@ -1387,7 +1389,7 @@
 	}
 	ip_send_check(iph);
 
-  	IP_MASQ_DEBUG(2, "O-routed from %08lX:%04X with masq.addr %08lX\n",
+  	IP_MASQ_DEBUG(2, "O-routed from %08X:%04X with masq.addr %08X\n",
 		ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr));
 
 	masq_set_state(ms, 1, iph, h.portp);
@@ -1486,7 +1488,7 @@
 	struct ip_masq	*ms;
 	unsigned short   len   = ntohs(iph->tot_len) - (iph->ihl * 4);
 
- 	IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %lX -> %lX\n",
+ 	IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %X -> %X\n",
 	        icmph->type, ntohs(icmp_id(icmph)),
  		ntohl(iph->saddr), ntohl(iph->daddr));
 
@@ -1496,7 +1498,7 @@
 	    (icmph->type == ICMP_INFO_REQUEST ) ||
 	    (icmph->type == ICMP_ADDRESS )) {
 
-		IP_MASQ_DEBUG(2, "icmp request rcv %lX->%lX  id %d type %d\n",
+		IP_MASQ_DEBUG(2, "icmp request rcv %X->%X  id %d type %d\n",
 		       ntohl(iph->saddr),
 		       ntohl(iph->daddr),
 		       ntohs(icmp_id(icmph)),
@@ -1547,7 +1549,7 @@
 		icmph->checksum = 0;
 		icmph->checksum = ip_compute_csum((unsigned char *)icmph, len);
 
-		IP_MASQ_DEBUG(2, "icmp request rwt %lX->%lX id %d type %d\n",
+		IP_MASQ_DEBUG(2, "icmp request rwt %X->%X id %d type %d\n",
 		       ntohl(iph->saddr),
 		       ntohl(iph->daddr),
 		       ntohs(icmp_id(icmph)),
@@ -1585,7 +1587,7 @@
 							     (ciph->ihl<<2));
 
 
-		IP_MASQ_DEBUG(2, "fw icmp/icmp rcv %lX->%lX id %d type %d\n",
+		IP_MASQ_DEBUG(2, "fw icmp/icmp rcv %X->%X id %d type %d\n",
 		       ntohl(ciph->saddr),
 		       ntohl(ciph->daddr),
 		       ntohs(icmp_id(cicmph)),
@@ -1621,7 +1623,7 @@
 		icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
 
 
-		IP_MASQ_DEBUG(2, "fw icmp/icmp rwt %lX->%lX id %d type %d\n",
+		IP_MASQ_DEBUG(2, "fw icmp/icmp rwt %X->%X id %d type %d\n",
 		       ntohl(ciph->saddr),
 		       ntohl(ciph->daddr),
 		       ntohs(icmp_id(cicmph)),
@@ -1657,7 +1659,7 @@
 	}
 
 
- 	IP_MASQ_DEBUG(2, "Handling forward ICMP for %08lX:%04X -> %08lX:%04X\n",
+ 	IP_MASQ_DEBUG(2, "Handling forward ICMP for %08X:%04X -> %08X:%04X\n",
 	       ntohl(ciph->saddr), ntohs(pptr[0]),
 	       ntohl(ciph->daddr), ntohs(pptr[1]));
 
@@ -1695,7 +1697,7 @@
 	icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
 
 
- 	IP_MASQ_DEBUG(2, "Rewrote forward ICMP to %08lX:%04X -> %08lX:%04X\n",
+ 	IP_MASQ_DEBUG(2, "Rewrote forward ICMP to %08X:%04X -> %08X:%04X\n",
 	       ntohl(ciph->saddr), ntohs(pptr[0]),
 	       ntohl(ciph->daddr), ntohs(pptr[1]));
 
@@ -1745,7 +1747,7 @@
 	unsigned short   len   = ntohs(iph->tot_len) - (iph->ihl * 4);
 
 
- 	IP_MASQ_DEBUG(2, "icmp in/rev (%d,%d) %lX -> %lX\n",
+ 	IP_MASQ_DEBUG(2, "icmp in/rev (%d,%d) %X -> %X\n",
 	        icmph->type, ntohs(icmp_id(icmph)),
  		ntohl(iph->saddr), ntohl(iph->daddr));
 
@@ -1756,7 +1758,7 @@
 	    (icmph->type == ICMP_INFO_REPLY) ||
 	    (icmph->type == ICMP_ADDRESSREPLY))	{
 
-		IP_MASQ_DEBUG(2, "icmp reply rcv %lX->%lX id %d type %d, req %d\n",
+		IP_MASQ_DEBUG(2, "icmp reply rcv %X->%X id %d type %d, req %d\n",
 		       ntohl(iph->saddr),
 		       ntohl(iph->daddr),
 		       ntohs(icmp_id(icmph)),
@@ -1793,7 +1795,7 @@
 
 
 
-		IP_MASQ_DEBUG(2, "icmp reply rwt %lX->%lX id %d type %d\n",
+		IP_MASQ_DEBUG(2, "icmp reply rwt %X->%X id %d type %d\n",
 		       ntohl(iph->saddr),
 		       ntohl(iph->daddr),
 		       ntohs(icmp_id(icmph)),
@@ -1830,7 +1832,7 @@
 							     (ciph->ihl<<2));
 
 
-		IP_MASQ_DEBUG(2, "rv icmp/icmp rcv %lX->%lX id %d type %d\n",
+		IP_MASQ_DEBUG(2, "rv icmp/icmp rcv %X->%X id %d type %d\n",
 		       ntohl(ciph->saddr),
 		       ntohl(ciph->daddr),
 		       ntohs(icmp_id(cicmph)),
@@ -1872,7 +1874,7 @@
 		icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
 
 
-		IP_MASQ_DEBUG(2, "rv icmp/icmp rwt %lX->%lX id %d type %d\n",
+		IP_MASQ_DEBUG(2, "rv icmp/icmp rwt %X->%X id %d type %d\n",
 		       ntohl(ciph->saddr),
 		       ntohl(ciph->daddr),
 		       ntohs(icmp_id(cicmph)),
@@ -1906,7 +1908,7 @@
 	}
 
 
- 	IP_MASQ_DEBUG(2, "Handling reverse ICMP for %08lX:%04X -> %08lX:%04X\n",
+ 	IP_MASQ_DEBUG(2, "Handling reverse ICMP for %08X:%04X -> %08X:%04X\n",
 	       ntohl(ciph->saddr), ntohs(pptr[0]),
 	       ntohl(ciph->daddr), ntohs(pptr[1]));
 
@@ -1948,7 +1950,7 @@
 	icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
 
 
- 	IP_MASQ_DEBUG(2, "Rewrote reverse ICMP to %08lX:%04X -> %08lX:%04X\n",
+ 	IP_MASQ_DEBUG(2, "Rewrote reverse ICMP to %08X:%04X -> %08X:%04X\n",
 	       ntohl(ciph->saddr), ntohs(pptr[0]),
 	       ntohl(ciph->daddr), ntohs(pptr[1]));
 
@@ -2067,7 +2069,7 @@
 
 
 
- 	IP_MASQ_DEBUG(2, "Incoming %s %08lX:%04X -> %08lX:%04X\n",
+ 	IP_MASQ_DEBUG(2, "Incoming %s %08X:%04X -> %08X:%04X\n",
  		masq_proto_name(iph->protocol),
  		ntohl(iph->saddr), ntohs(h.portp[0]),
  		ntohl(iph->daddr), ntohs(h.portp[1]));
@@ -2082,8 +2084,11 @@
  	 * 	Give additional modules a chance to create an entry
 	 */
 #ifdef CONFIG_IP_MASQUERADE_MOD
-	if (!ms) 
+	if (!ms) {
 		ms = ip_masq_mod_in_create(skb, iph, maddr);
+		if (ms && !ms->app && skb->fwmark)
+			ip_masq_bind_app_fwmark(ms, skb->fwmark);
+	}
 
 	/*
  	 * 	Call module's input update hook
@@ -2138,7 +2143,7 @@
 
 			write_unlock(&__ip_masq_lock);
 
-                        IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled daddr=%lX\n",
+                        IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled daddr=%X\n",
                                ntohl(ms->daddr));
 
                 }
@@ -2208,7 +2213,7 @@
 		}
                 ip_send_check(iph);
 
-                IP_MASQ_DEBUG(2, "I-routed to %08lX:%04X\n",ntohl(iph->daddr),ntohs(h.portp[1]));
+                IP_MASQ_DEBUG(2, "I-routed to %08X:%04X\n",ntohl(iph->daddr),ntohs(h.portp[1]));
 
 		masq_set_state (ms, 0, iph, h.portp);
 		ip_masq_put(ms);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq_app.c linux.19p1/net/ipv4/ip_masq_app.c
--- linux.vanilla/net/ipv4/ip_masq_app.c	Fri Apr 21 12:47:14 2000
+++ linux.19p1/net/ipv4/ip_masq_app.c	Wed Dec 13 22:22:39 2000
@@ -15,7 +15,8 @@
  * Fixes:
  *	JJC			: Implemented also input pkt hook
  *	Miquel van Smoorenburg	: Copy more stuff when resizing skb
- *
+ *	Juan Jose Ciarlante	: reworked register() (new _init_xxxx()  functions for nicer interface)
+ *	Juan Jose Ciarlante	: add INBOUND hooks (default to OUTBOUND)
  *
  * FIXME:
  *	- ip_masq_skb_replace(): use same skb if space available.
@@ -41,13 +42,27 @@
 
 #define IP_MASQ_APP_TAB_SIZE  16 /* must be power of 2 */
 
-#define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1))
-#define IP_MASQ_APP_TYPE(proto, port) ( proto<<16 | port )
-#define IP_MASQ_APP_PORT(type)        ( type & 0xffff )
-#define IP_MASQ_APP_PROTO(type)       ( (type>>16) & 0x00ff )
+/*
+ * 	ip_masq_app->type holds all the values needed to hook
+ * 	passing packet
+ * 		flags: 	(uses only upper 8bits: f_bits)
+ * 			INBOUND, OUTBOUND
+ * 			FWMARK
+ *
+ * 	if (flags & IP_MASQ_APP_FWMARK)
+ * 		[ f_bits][ fwmark (24 bits only)  ]
+ * 	else
+ * 		[ f_bits][ proto ][     port      ]
+ * 	        
+ * 	        <-----------  32 bits ------------>
+ * 	
+ */
+
+#define IP_MASQ_APP_HASH(type) (((type>>16)^(type)) & (IP_MASQ_APP_TAB_SIZE-1))
 
 
 EXPORT_SYMBOL(register_ip_masq_app);
+EXPORT_SYMBOL(register_ip_masq_app_type);
 EXPORT_SYMBOL(unregister_ip_masq_app);
 EXPORT_SYMBOL(ip_masq_skb_replace);
 
@@ -58,29 +73,53 @@
 struct ip_masq_app *ip_masq_app_base[IP_MASQ_APP_TAB_SIZE];
 
 /*
- * 	ip_masq_app registration routine
- *	port: host byte order.
+ * 	"raw" ip_masq_app registration routine if you 
+ * 	did ip_masq_app_init_xxxx() before to initialize (and check)
+ * 	type value
  */
 
-int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port)
-{
+int register_ip_masq_app_type(struct ip_masq_app *mapp)  {
         unsigned long flags;
         unsigned hash;
+#ifdef CONFIG_IP_MASQ_DEBUG
         if (!mapp) {
                 IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n");
                 return -EINVAL;
         }
-        mapp->type = IP_MASQ_APP_TYPE(proto, port);
+#endif
+	if (!mapp->type) {
+		IP_MASQ_DEBUG(1, "ip_masq_app_register_type(): ZERO type passed\n");
+		return -EINVAL;
+	}
+
         mapp->n_attach = 0;
-        hash = IP_MASQ_APP_HASH(proto, port);
+        hash = IP_MASQ_APP_HASH(mapp->type);
+	IP_MASQ_DEBUG(2, "ip_masq_app_register(): type=0x%x hash=0x%x\n", mapp->type, hash);
 
         save_flags(flags);
         cli();
         mapp->next = ip_masq_app_base[hash];
         ip_masq_app_base[hash] = mapp;
         restore_flags(flags);
+	return 0;
+}
 
-        return 0;
+/*
+ * 	ip_masq_app registration routine
+ *	port: host byte order.
+ *	proto:  (flags<<8) | ipproto 
+ */
+int register_ip_masq_app(struct ip_masq_app *mapp, unsigned proto, __u16 port)
+{
+#ifdef CONFIG_IP_MASQ_DEBUG
+        if (!mapp) {
+                IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n");
+                return -EINVAL;
+        }
+#endif
+
+	ip_masq_app_init_proto_port(mapp, IP_MASQ_APP_OUTBOUND, proto, port);
+        return register_ip_masq_app_type(mapp);
 }
 
 /*
@@ -104,7 +143,7 @@
                        mapp->n_attach);
                 return -EINVAL;
         }
-        hash = IP_MASQ_APP_HASH(IP_MASQ_APP_PROTO(mapp->type), IP_MASQ_APP_PORT(mapp->type));
+        hash = IP_MASQ_APP_HASH(mapp->type);
 
         save_flags(flags);
         cli();
@@ -116,24 +155,26 @@
                 }
 
         restore_flags(flags);
-        IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n",
-               masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type));
+        IP_MASQ_ERR("unregister_ip_masq_app(flags=0x%x, proto=%s,port=%u,fwmark=%d): not hashed!\n",
+			
+		IP_MASQ_APP_TYPE2FLAGS(mapp->type),
+		masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), 
+		IP_MASQ_APP_TYPE2PORT(mapp->type),
+		IP_MASQ_APP_TYPE2FWMARK(mapp->type));
         return -EINVAL;
 }
 
 /*
- *	get ip_masq_app object by its proto and port (net byte order).
+ *	get ip_masq_app object by its proto and port (host byte order).
  */
 
-struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port)
+struct ip_masq_app * ip_masq_app_get_type(unsigned type)
 {
         struct ip_masq_app *mapp;
         unsigned hash;
-        unsigned type;
 
-        port = ntohs(port);
-        type = IP_MASQ_APP_TYPE(proto,port);
-        hash = IP_MASQ_APP_HASH(proto,port);
+        hash = IP_MASQ_APP_HASH(type);
+	IP_MASQ_DEBUG(2, "ip_masq_app_get(): type=0x%x hash=0x%x\n", type, hash);
         for(mapp = ip_masq_app_base[hash]; mapp ; mapp = mapp->next) {
                 if (type == mapp->type) return mapp;
         }
@@ -159,8 +200,8 @@
         if (n_at < 0) {
                 restore_flags(flags);
                 IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n",
-                       masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
-                       IP_MASQ_APP_PORT(mapp->type));
+                       masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)),
+                       IP_MASQ_APP_TYPE2PORT(mapp->type));
                 return -1;
         }
         mapp->n_attach = n_at;
@@ -169,41 +210,91 @@
 }
 
 /*
+ *	Actually bind pointers and call contructor
+ */
+
+static struct ip_masq_app *do_bind_app(struct ip_masq *ms, struct ip_masq_app *mapp) 
+{
+	/*
+	 *	don't allow binding if already bound
+	 */
+	if (ms->app != NULL) {
+		IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n");
+		goto end;
+	}
+	IP_MASQ_DEBUG(1, "ip_masq_bind_app() : bound \"%s\" module\n", mapp->name);
+	ms->app = mapp;
+	if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
+	ip_masq_app_bind_chg(mapp, +1);
+end:
+        return ms->app;
+}
+
+/*
  *	Bind ip_masq to its ip_masq_app based on proto and dport ALREADY
- *	set in ip_masq struct. Also calls constructor.
+ *	set in ip_masq struct. 
+ *	Only called from ip_masq_new() when creating NEW masq tunnel,
  */
 
 struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms)
 {
         struct ip_masq_app * mapp;
+	unsigned type;
 
 	if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
 		return NULL;
 
-        mapp = ip_masq_app_get(ms->protocol, ms->dport);
+	IP_MASQ_DEBUG(1, "ip_masq_bind_app() : called for dst=%d.%d.%d.%d:%d src=%d.%d.%d.%d:%d masq=%d.%d.%d.%d:%d\n",
+			NIPQUAD(ms->daddr), ntohs(ms->dport),
+			NIPQUAD(ms->saddr), ntohs(ms->sport),
+			NIPQUAD(ms->maddr), ntohs(ms->mport));
 
-#if 0000
-/* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */
-	if (mapp == NULL)
-		mapp = ip_masq_app_get(ms->protocol, ms->sport);
-/* #endif */
-#endif
+	/*
+	 * 	Lookup "normal" case (client's active open inside)  	
+	 */
+	type = IP_MASQ_APP_TYPE_PP(IP_MASQ_APP_OUTBOUND, ms->protocol, ntohs(ms->dport));
+        mapp = ip_masq_app_get_type(type);
 
-        if (mapp != NULL) {
-                /*
-                 *	don't allow binding if already bound
-                 */
-
-                if (ms->app != NULL) {
-                        IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n");
-                        return ms->app;
-                }
+	if (mapp==NULL) {
+		/*	
+		 *	lookup "reverse" case (server waiting inside,
+		 *	for portfw and friends)
+		 */
+		type = IP_MASQ_APP_TYPE_PP(IP_MASQ_APP_INBOUND, ms->protocol, ntohs(ms->mport));
+		mapp = ip_masq_app_get_type(type);
+	}
 
-                ms->app = mapp;
-                if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
-                ip_masq_app_bind_chg(mapp, +1);
-        }
-        return mapp;
+        if (mapp != NULL) 
+		mapp=do_bind_app(ms, mapp);
+	return mapp;
+}
+/*
+ *	Bind ip_masq to its ip_masq_app based on firewall mark
+ */
+struct ip_masq_app * ip_masq_bind_app_fwmark(struct ip_masq *ms, __u32 fwmark) {
+	struct ip_masq_app *mapp;
+	unsigned type;
+
+	IP_MASQ_DEBUG(1, "ip_masq_bind_app_fwmark() : called for fwmark=0x%x, dst=%d.%d.%d.%d:%d src=%d.%d.%d.%d:%d masq=%d.%d.%d.%d:%d\n",
+			fwmark,
+			NIPQUAD(ms->daddr), ntohs(ms->dport),
+			NIPQUAD(ms->saddr), ntohs(ms->sport),
+			NIPQUAD(ms->maddr), ntohs(ms->mport));
+
+	type = IP_MASQ_APP_TYPE_FWMARK(IP_MASQ_APP_OUTBOUND, fwmark);
+        mapp = ip_masq_app_get_type(type);
+
+	if (mapp==NULL) {
+		/*	
+		 *	lookup "reverse" case (server waiting inside,
+		 *	for portfw and friends)
+		 */
+		type = IP_MASQ_APP_TYPE_FWMARK(IP_MASQ_APP_INBOUND, fwmark);
+		mapp = ip_masq_app_get_type(type);
+	}
+        if (mapp != NULL) 
+		mapp=do_bind_app(ms, mapp);
+	return mapp;
 }
 
 /*
@@ -269,10 +360,14 @@
          * Adjust ack_seq with delta-offset for
          * the packets AFTER most recent resized pkt has caused a shift
          * for packets before most recent resized pkt, use previous_delta
+	 *
+	 * Compare acks against  (remote SEQs space is +delta)
+	 * 	(ms_seq->init_seq + ms_seq->delta), not ms_seq->init_seq
+	 * 		-- R.R. 27/08/00
          */
 
         if (ms_seq->delta || ms_seq->previous_delta) {
-                if(after(ack_seq,ms_seq->init_seq)) {
+                if(after(ack_seq,ms_seq->init_seq + ms_seq->delta)) {
                         th->ack_seq = htonl(ack_seq-ms_seq->delta);
                         IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq->delta);
 
@@ -455,8 +550,8 @@
 				continue;
 
                         len += sprintf(buffer+len, "%-3s  %-7u %-7d  %-17s\n",
-                                       masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
-                                       IP_MASQ_APP_PORT(mapp->type), mapp->n_attach,
+                                       masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)),
+                                       IP_MASQ_APP_TYPE2PORT(mapp->type), mapp->n_attach,
 				       mapp->name);
 
                         if(len >= length)
@@ -497,7 +592,7 @@
  *	Replace a segment (of skb->data) with a new one.
  *	FIXME: Should re-use same skb if space available, this could
  *	       be done if n_len < o_len, unless some extra space
- *	       were already allocated at driver level :P .
+ *	       were already allocated at link level :P .
  */
 
 static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq_ftp.c linux.19p1/net/ipv4/ip_masq_ftp.c
--- linux.vanilla/net/ipv4/ip_masq_ftp.c	Fri Apr 21 12:47:14 2000
+++ linux.19p1/net/ipv4/ip_masq_ftp.c	Wed Dec 13 22:22:39 2000
@@ -2,7 +2,7 @@
  *		IP_MASQ_FTP ftp masquerading module
  *
  *
- * Version:	@(#)ip_masq_ftp.c 0.04   02/05/96
+ * Version:	@(#)ip_masq_ftp.c 0.10   20/09/00
  *
  * Author:	Wouter Gadeyne
  *		
@@ -17,12 +17,15 @@
  *	Juan Jose Ciarlante	:	use ip_masq_listen() 
  *	Juan Jose Ciarlante	: 	use private app_data for own flag(s)
  *  Bjarni R. Einarsson :	Added protection against "extended FTP ALG attack"
- *
+ *	Neil Toronto		: portfw FTP support
+ *	Juan Jose Ciarlante	: reimplemented parsing logic, merged portfw FTP support for PASV (new "in_ports" module param), use th->doff for data offset
+ *	Juan Jose Ciarlante	: safe_mem_eq2() and size adjustments for less CPU
+ *	Juan Jose Ciarlante	: fwmark hook-able
  *
  *	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.
+ *	2 of the License.
  *	
  * Multiple Port Support
  *	The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
@@ -34,6 +37,16 @@
  *	/etc/conf.modules (or /etc/modules.conf depending on your config)
  *	where modload will pick it up should you use modload to load your
  *	modules.
+ * Additional portfw Port Support
+ * 	Module parameter "in_ports" specifies the list of forwarded ports
+ * 	at firewall (portfw and friends) that must be hooked to allow 
+ * 	PASV connections to inside servers.
+ * 	Same as before:
+ * 		in_ports=fw1,fw2,...
+ *	Eg: 
+ *		ipmasqadm portfw -a -P tcp -L a.b.c.d 2021 -R 192.168.1.1 21
+ *		ipmasqadm portfw -a -P tcp -L a.b.c.d 8021 -R 192.168.1.1 21
+ *		modprobe ip_masq_ftp in_ports=2021,8021
  * 
  * Protection against the "extended FTP ALG vulnerability".
  *	This vulnerability was reported in:
@@ -66,22 +79,52 @@
 /* #define IP_MASQ_NDEBUG */
 #include <net/ip_masq.h>
 
+/*	
+ * 	paranoid, CPU care offset handling
+ */
+/* #define MASQ_FTP_RELAXED 1 */
+#ifdef MASQ_FTP_RELAXED
+#define _N(x) 	0
+#else
+#define _N(x)	(x)
+#endif
+
+#define IP_MASQ_FTP_RPAREN 0x01 /* stream has ')' char */
 
+/*
+ * 	Eat 1 port (last elem) for holding firewall mark instance
+ */
+#define MAX_MASQ_FTP_PORTS (MAX_MASQ_APP_PORTS-1)
+#define MAX_MASQ_FTP_PORTS_MODPARM 11
 /* 
  * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
  * First port is set to the default port.
  */
-static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */
-struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+static int ports[MAX_MASQ_FTP_PORTS] = {21}; /* I rely on the trailing items being set to zero */
+static struct ip_masq_app *masq_ftp_objs[MAX_MASQ_APP_PORTS];
+
+/*
+ * 	in (forwarded) ports
+ */
+static int in_ports[MAX_MASQ_FTP_PORTS] = {0}; 
+static struct ip_masq_app *masq_in_ftp_objs[MAX_MASQ_APP_PORTS];
+
+#define masq_ftp_mark masq_ftp_objs[MAX_MASQ_APP_PORTS-1]
+#define masq_in_ftp_mark masq_in_ftp_objs[MAX_MASQ_APP_PORTS-1]
 
 /*
  * List of ports (up to MAX_MASQ_APP_PORTS) we don't allow ftp-data 
  * connections to.  Default is to block connections to port 6000 (X servers).
  * This is in addition to all ports under 1024.
  */
-static int noport[MAX_MASQ_APP_PORTS] = {6000, 0}; /* I rely on the trailing items being set to zero */
+static int noport[MAX_MASQ_FTP_PORTS] = {6000, 0}; /* I rely on the trailing items being set to zero */
 
 /*
+ *	Firewall marks for "normal" and "forw" cases
+ */
+static int mark=0;
+static int in_mark=0;
+/*
  *	Debug level
  */
 #ifdef CONFIG_IP_MASQ_DEBUG
@@ -89,12 +132,56 @@
 MODULE_PARM(debug, "i");
 #endif
 
-MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
-MODULE_PARM(noport, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_FTP_PORTS_MODPARM) "i");
+MODULE_PARM(in_ports, "1-" __MODULE_STRING(MAX_MASQ_FTP_PORTS_MODPARM) "i");
+MODULE_PARM(noport, "1-" __MODULE_STRING(MAX_MASQ_FTP_PORTS_MODPARM) "i");
+MODULE_PARM(mark, "i");
+MODULE_PARM(in_mark, "i");
 
 /*	Dummy variable */
 static int masq_ftp_pasv;
 
+/*
+ * This function parses the IP address and Port number found in PORT commands
+ * and PASV responses.  This used to be done in-line, but with four cases it
+ * seemed worth encapsulating.  It returns the IP address, or zero if an
+ * error is detected.
+ */
+static __u32 parse_ip_port( char **datap, __u16 *portp )
+{
+	char	*data = *datap;
+#if CONFIG_IP_MASQ_DEBUG
+	char *data0=data;
+#endif
+	unsigned char p1,p2,p3,p4,p5,p6;
+
+	p1 = simple_strtoul(data, &data, 10);
+	if (*data != ',')
+		return 0;
+	p2 = simple_strtoul(data+1, &data, 10);
+	if (*data != ',')
+		return 0;
+	p3 = simple_strtoul(data+1, &data, 10);
+	if (*data != ',')
+		return 0;
+	p4 = simple_strtoul(data+1, &data, 10);
+	if (*data != ',')
+		return 0;
+	p5 = simple_strtoul(data+1, &data, 10);
+	if (*data != ',')
+		return 0;
+	p6 = simple_strtoul(data+1, &data, 10);
+
+	IP_MASQ_DEBUG(2-debug, "FTP: parse_ip_port() Ok: \"%*s\" size=%d\n",	
+			data-data0,
+			data0,
+			data-data0);
+	*datap = data;
+	*portp = (p5<<8) | p6;
+	return (p1<<24) | (p2<<16) | (p3<<8) | p4;
+}
+
+
 static int
 masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
 {
@@ -109,138 +196,221 @@
         return 0;
 }
 
+
+static int masq_ftp_unsafe(__u32 from_ip, __u16 from_port) {
+	int i;
+	if (from_port < 1024) 
+	{
+		IP_MASQ_DEBUG(1-debug, "Unsafe PORT %d.%d.%d.%d:%d detected, ignored\n",NIPQUAD(from_ip),from_port);
+		return 1;
+	}
+
+	for (i = 0; (i < MAX_MASQ_FTP_PORTS) && (noport[i]); i++)
+		if (from_port == noport[i])
+		{
+			IP_MASQ_DEBUG(1-debug, "Unsafe (module parm) PORT %d.%d.%d.%d:%d detected, ignored\n",NIPQUAD(from_ip),from_port);
+			return 1;
+		}
+	return 0;
+}
+/*
+ * carefully compare with any of these 2 strings, eating stream pointer
+ * as it proceeds
+ * Returns:
+ * 	NULL	not matched
+ * 	!NULL	last matched char*
+ */
+static char* safe_mem_eq2(char *data, const char *data_limit, int size, const char *str1, const char *str2) {
+#if CONFIG_IP_MASQ_DEBUG
+	const char *data0=data;
+	if (!data) {
+		IP_MASQ_ERR("FTP: NULL data passed to safe_mem_eq2()!!!");
+		return NULL;
+	}
+#endif
+	IP_MASQ_DEBUG(3-debug, "FTP: safe_mem_equal(): datalimit-data=%d, size=%d\n", data_limit-data, size);
+
+	/*	No point in going after data_limit for "size" comparison */
+	data_limit -= size;
+
+	while (data <= data_limit) {
+		if (memcmp(data,str1,size)==0) 
+			goto equal_ok;
+		if (str2 && memcmp(data,str2,size)==0) 
+			goto equal_ok;
+		data++;
+	}
+	IP_MASQ_DEBUG(2-debug, "FTP: safe_mem_equal(): \"%s\" not matched)\n", str1);
+	return NULL;
+equal_ok:
+	IP_MASQ_DEBUG(2-debug, "FTP: safe_mem_equal(): \"%s\" matched at offset %d\n",
+				str1, data-data0);
+	return data+size;
+}
 int
 masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
 {
         struct sk_buff *skb;
 	struct iphdr *iph;
 	struct tcphdr *th;
-	char *p, *data, *data_limit;
-	unsigned char p1,p2,p3,p4,p5,p6;
-	__u32 from;
+	char *p, *data, *data0, *data_limit;
+	__u32 from=0;
+	__u32 from_n;
 	__u16 port;
 	struct ip_masq *n_ms;
-	char buf[24];		/* xxx.xxx.xxx.xxx,ppp,ppp\000 */
+	char buf[25];		/* xxx.xxx.xxx.xxx,ppp,ppp)\000 */
+        unsigned flags=0;    	/* processing flags */
         unsigned buf_len;
-	int diff, i, unsafe;
+	int diff=0;
+
+	/*	Only useful for established  sessions */
+	if (ms->state != IP_MASQ_S_ESTABLISHED) 
+		return 0;
 
         skb = *skb_p;
 	iph = skb->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
-        data = (char *)&th[1];
+        data = (char *)th + (((struct tcphdr*)th)->doff << 2);
+	data0 = data;
+        data_limit = skb->h.raw + skb->len;
+
+	IP_MASQ_DEBUG(2-debug, "FTP: called masq_ftp_out() for type=0x%x datasize=%d\n",
+			mapp->type,
+			data_limit-data);
 
-        data_limit = skb->h.raw + skb->len - 18;
-        if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0))
-		ms->app_data = &masq_ftp_pasv;
+	/*	Only useful for actual data 	*/
+	if (data_limit<=data)
+		return 0;
 
-	while (data < data_limit)
-	{
-		if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5))
-		{
-			data ++;
-			continue;
+	/*
+	 * 	We are about to hack an OUT (from firewall) packet,
+	 *	check if 
+	 *		OUTBOUND: 	internal client stream
+	 *		INBOUND: 	internal server stream
+	 */
+	
+	if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_OUTBOUND) {
+		IP_MASQ_DEBUG(1-debug, "FTP: in->out client stream\n");
+
+		/* 
+		 * Minimum sizes ...
+		 *              PORT x,x,x,x,y,y+...  + \r\n
+		 *                   <---------- 11   + 2 = 13
+		 */
+		if (!(data=safe_mem_eq2(data, data_limit-_N(13),
+				5, "PORT ", "port ")) ) {
+			if (safe_mem_eq2(data0, data_limit, 6, "PASV\r\n", "pasv\r\n")) {
+				/*	Flags this tunnel as pasv, return */
+				ms->app_data = &masq_ftp_pasv;
+			}
+			return 0;
 		}
-		p = data+5;
- 		p1 = simple_strtoul(data+5,&data,10);
-		if (*data!=',')
-			continue;
-		p2 = simple_strtoul(data+1,&data,10);
-		if (*data!=',')
-			continue;
-		p3 = simple_strtoul(data+1,&data,10);
-		if (*data!=',')
-			continue;
-		p4 = simple_strtoul(data+1,&data,10);
-		if (*data!=',')
-			continue;
-		p5 = simple_strtoul(data+1,&data,10);
-		if (*data!=',')
-			continue;
-		p6 = simple_strtoul(data+1,&data,10);
-		if (*data!='\r' && *data!='\n')
-			continue;
+		p = data;
+		from = parse_ip_port(&data, &port);
+		if (masq_ftp_unsafe(from,port)) 
+			return 0;
+		IP_MASQ_DEBUG(1-debug,"FTP: out: PORT %d.%d.%d.%d:%d detected\n",
+				NIPQUAD(from), port);
 
-		from = (p1<<24) | (p2<<16) | (p3<<8) | p4;
-		port = (p5<<8) | p6;
+	} else if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_INBOUND) {
+		IP_MASQ_DEBUG(1-debug, "FTP: in->out server stream\n");
 
-		if (port < 1024) 
-		{
-			IP_MASQ_DEBUG(1-debug, "Unsafe PORT %X:%X detected, ignored\n",from,port);
-			continue;
-		}
+		/* 
+		 * Minimum sizes...
+		 *  	Entering Passive Mode (x,x,x,x,y,y)+...  + \r\n
+		 *  	         <------------------------- 26   + 2 = 28
+		 *  	                 <----------------- 18   + 2 = 20
+		 *  	                      <------------ 13   + 2 = 15
+		 */
+		if (!(data=safe_mem_eq2(data, data_limit-_N(28), 8, "ntering ", "NTERING ")))
+			return 0;
+		if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(20), 7, "assive ", "ASSIVE ")))
+			return 0;
+		if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(15), 4, "ode ", "ODE ")))
+			return 0;
+		do {
+			if (data >= data_limit)
+				return 0;
+		} while (*data++ != '(');
 
-		for (unsafe = i = 0; (i < MAX_MASQ_APP_PORTS) && (noport[i]); i++)
-		  if (port == noport[i])
-		{
-			IP_MASQ_DEBUG(1-debug, "Unsafe PORT %X:%X detected, ignored\n",from,port);
-			unsafe = 1;
-		}
-		if (unsafe) continue;
+		p = data;
+		from = parse_ip_port(&data, &port);
+		if ((from == 0) || (*data++ !=')'))
+			return 0;
 
-		IP_MASQ_DEBUG(1-debug, "PORT %X:%X detected\n",from,port);
+ 		flags |= IP_MASQ_FTP_RPAREN;
 
-		/*
-		 * Now update or create an masquerade entry for it
-		 */
+	}  else 
+		return 0; 
 
-		IP_MASQ_DEBUG(1-debug, "protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0);
+	/* no from detected, give up */
+	if (from == 0)
+		return 0;
 
-		n_ms = ip_masq_out_get(iph->protocol,
-					 htonl(from), htons(port),
-					 iph->daddr, 0);
-		if (!n_ms) {
-			n_ms = ip_masq_new(IPPROTO_TCP,
-					   maddr, 0,
-					   htonl(from), htons(port),
-					   iph->daddr, 0,
-					   IP_MASQ_F_NO_DPORT);
+	/* store from in network byte order */
+	from_n = htonl(from);
+	/*
+	 * Now update or create an masquerade entry for it
+	 */
 
-			if (n_ms==NULL)
-				return 0;
-			ip_masq_control_add(n_ms, ms);
-		}
+	IP_MASQ_DEBUG(1-debug, "FTP: out: %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", 
+			NIPQUAD(from_n), htons(port), 
+			NIPQUAD(iph->daddr), 0);
 
-		/*
-		 * Replace the old PORT with the new one
-		 */
-		from = ntohl(n_ms->maddr);
-		port = ntohs(n_ms->mport);
-		sprintf(buf,"%d,%d,%d,%d,%d,%d",
-			from>>24&255,from>>16&255,from>>8&255,from&255,
-			port>>8&255,port&255);
-		buf_len = strlen(buf);
+	n_ms = ip_masq_out_get(iph->protocol,
+				 from_n, htons(port),
+				 iph->daddr, 0);
+	if (!n_ms) {
+		n_ms = ip_masq_new(IPPROTO_TCP,
+				   maddr, 0,
+				   from_n, htons(port),
+				   iph->daddr, 0,
+				   IP_MASQ_F_NO_DPORT);
 
-		IP_MASQ_DEBUG(1-debug, "new PORT %X:%X\n",from,port);
+		if (n_ms==NULL)
+			return 0;
+		ip_masq_control_add(n_ms, ms);
+	}
 
-		/*
-		 * Calculate required delta-offset to keep TCP happy
-		 */
-		
-		diff = buf_len - (data-p);
-		
-		/*
-		 *	No shift.
-		 */
-		
-		if (diff==0) {
-			/*
-			 * simple case, just replace the old PORT cmd
- 			 */
- 			memcpy(p,buf,buf_len);
- 		} else {
+	/*
+	 * Replace the old PORT with the new one
+	 */
+	from = ntohl(n_ms->maddr);
+	port = ntohs(n_ms->mport);
+	sprintf(buf,"%d,%d,%d,%d,%d,%d%c",
+		from>>24&255,from>>16&255,from>>8&255,from&255,
+		port>>8&255,port&255,
+		(flags&IP_MASQ_FTP_RPAREN)? ')':0);
+	buf_len = strlen(buf);
 
-			*skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len);
-		}
-                /*
-                 * 	Move tunnel to listen state
-                 */
-		ip_masq_listen(n_ms);
-		ip_masq_put(n_ms);
+	IP_MASQ_DEBUG(1-debug, "FTP: new PORT %d.%d.%d.%d:%d\n",NIPQUAD(maddr),port);
+
+	/*
+	 * Calculate required delta-offset to keep TCP happy
+	 */
+
+	diff = buf_len - (data-p);
+
+	/*
+	 *	No shift.
+	 */
 
-                return diff;
+	if (diff==0) {
+		/*
+		 * simple case, just replace the old PORT cmd
+		 */
+		memcpy(p,buf,buf_len);
+	} else {
 
+		*skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len);
 	}
-	return 0;
+	/*
+	 * 	Move tunnel to listen state
+	 */
+	ip_masq_listen(n_ms);
+	ip_masq_put(n_ms);
+
+	return diff;
 
 }
 
@@ -269,53 +439,92 @@
 	struct iphdr *iph;
 	struct tcphdr *th;
 	char *data, *data_limit;
-	unsigned char p1,p2,p3,p4,p5,p6;
 	__u32 to;
+	__u32 from_n;
 	__u16 port;
 	struct ip_masq *n_ms;
 
-	if (ms->app_data != &masq_ftp_pasv)
-		return 0;	/* quick exit if no outstanding PASV */
+	/*	Only useful for established  sessions */
+	if (ms->state != IP_MASQ_S_ESTABLISHED) 
+		return 0;
 
 	skb = *skb_p;
 	iph = skb->nh.iph;
 	th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
-	data = (char *)&th[1];
+        data = (char *)th + (((struct tcphdr*)th)->doff << 2);
 	data_limit = skb->h.raw + skb->len;
 
-	while (data < data_limit && *data != ' ')
-		++data;	
-	while (data < data_limit && *data == ' ')
-		++data;	
-	data += 22;
-	if (data >= data_limit || *data != '(')
-		return 0;
-	p1 = simple_strtoul(data+1, &data, 10);
-	if (data >= data_limit || *data != ',')
-		return 0;
-	p2 = simple_strtoul(data+1, &data, 10);
-	if (data >= data_limit || *data != ',')
-		return 0;
-	p3 = simple_strtoul(data+1, &data, 10);
-	if (data >= data_limit || *data != ',')
-		return 0;
-	p4 = simple_strtoul(data+1, &data, 10);
-	if (data >= data_limit || *data != ',')
-		return 0;
-	p5 = simple_strtoul(data+1, &data, 10);
-	if (data >= data_limit || *data != ',')
-		return 0;
-	p6 = simple_strtoul(data+1, &data, 10);
-	if (data >= data_limit || *data != ')')
+	IP_MASQ_DEBUG(2-debug, "FTP: called masq_ftp_in() for type=0x%x datasize=%d\n", 
+			mapp->type,
+			data_limit-data);
+
+	/*	Only useful for actual data 	*/
+	if (data_limit<=data)
 		return 0;
 
-	to = (p1<<24) | (p2<<16) | (p3<<8) | p4;
-	port = (p5<<8) | p6;
+	/*
+	 * 	We are about to hack an IN (to firewall) packet,
+	 *	check if 
+	 *		OUTBOUND: 	internal client stream
+	 *		INBOUND: 	internal server stream
+	 */
+
+	if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_OUTBOUND) {
+		IP_MASQ_DEBUG(1-debug, "FTP: out->in client stream\n");
+		/* 	
+		 * 	For OUTBOUND only parse on input for linking PASV
+		 * 	data tunnel with control.
+		 * 	Exit quickly if no outstanding PASV 
+		 */
+		if (ms->app_data != &masq_ftp_pasv)
+			return 0;
+
+		/* 
+		 * Minimum sizes...
+		 *  	Entering Passive Mode (x,x,x,x,y,y)+...  + \r\n
+		 *  	         <------------------------- 26   + 2 = 28
+		 *  	                 <----------------- 18   + 2 = 20
+		 *  	                      <------------ 13   + 2 = 15
+		 */
+		if (!(data=safe_mem_eq2(data, data_limit-_N(28), 8, "ntering ", "NTERING ")))
+			return 0;
+		if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(20), 6, "ssive ", "SSIVE ")))
+			return 0;
+		if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(15), 4, "ode ", "ODE ")))
+			return 0;
+		do {
+			if (data >= data_limit)
+				return 0;
+		} while (*data++ != '(');
+
+		to = parse_ip_port(&data, &port);
+		if (to == 0 || *data != ')')
+			return 0;
+
+		from_n = ntohl(ms->saddr);
+		IP_MASQ_DEBUG(1-debug, "FTP: PASV response %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d detected\n", 
+			NIPQUAD(from_n), 0, 
+			NIPQUAD(to), port);
 
+	} else if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_INBOUND) {
+		IP_MASQ_DEBUG(1-debug, "FTP: out->in server stream\n");
+
+		if (!(data=safe_mem_eq2(data, data_limit-_N(13), 
+				5, "PORT ", "port ")) )
+			return 0;
+		to = parse_ip_port(&data, &port);
+		if (to == 0 || (*data != '\r' && *data != '\n'))
+			return 0;
+
+		from_n = ntohl(ms->saddr);
+		IP_MASQ_DEBUG(1-debug, "FTP: PORT  %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d detected\n", 
+			NIPQUAD(from_n), 0, 
+			NIPQUAD(to), port);
+	} else
+		return 0;
 	/*
 	 * Now update or create an masquerade entry for it
 	 */
-	IP_MASQ_DEBUG(1-debug, "PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port);
 
 	n_ms = ip_masq_out_get(iph->protocol,
 				 ms->saddr, 0,
@@ -356,58 +565,120 @@
         masq_ftp_out,           /* pkt_out */
         masq_ftp_in,            /* pkt_in */
 };
-
-/*
- * 	ip_masq_ftp initialization
- */
-
-__initfunc(int ip_masq_ftp_init(void))
+static struct ip_masq_app *make_instance(const struct ip_masq_app *mapp_class, int *err)
 {
-	int i, j;
+	struct ip_masq_app *mapp = NULL;
+	if ((mapp = kmalloc(sizeof(struct ip_masq_app), GFP_KERNEL)))
+		memcpy(mapp , mapp_class, sizeof(struct ip_masq_app));
+	else
+		*err= -ENOMEM;
+	return mapp;
+}
 
-	for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+/*	
+ *	Register all solicited ports (last array element is
+ * 	reserved for firewall "mark" object
+ */
+static int register_ports(struct ip_masq_app *mapp_instances[], int *ports, unsigned flags) {
+	int i;
+	int err=0;
+	struct ip_masq_app  *mapp;
+	for (i=0; (i<MAX_MASQ_FTP_PORTS_MODPARM); i++) {
 		if (ports[i]) {
-			if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
-							    GFP_KERNEL)) == NULL)
-				return -ENOMEM;
-			memcpy(masq_incarnations[i], &ip_masq_ftp, sizeof(struct ip_masq_app));
-			if ((j = register_ip_masq_app(masq_incarnations[i], 
-						      IPPROTO_TCP, 
-						      ports[i]))) {
-				return j;
-			}
-			IP_MASQ_DEBUG(1-debug, "Ftp: loaded support on port[%d] = %d\n",
-			       i, ports[i]);
+			if (!(mapp = make_instance(&ip_masq_ftp, &err)))
+				goto end;
+			if ((err=ip_masq_app_init_proto_port(mapp, flags, IPPROTO_TCP, ports[i]))) 
+				goto end_kfree;
+
+			if ((err=register_ip_masq_app_type(mapp)))
+				goto end_kfree;
+			mapp_instances[i]=mapp;
+			IP_MASQ_DEBUG(1-debug, "FTP: loaded support on %sport[%d] = %d, type=0x%x\n", 
+					(flags&IP_MASQ_APP_INBOUND)? "in_":"",
+					i, ports[i], mapp->type);
 		} else {
 			/* To be safe, force the incarnation table entry to NULL */
-			masq_incarnations[i] = NULL;
+			mapp_instances[i] = NULL;
 		}
 	}
 	return 0;
+end_kfree:
+	kfree_s(mapp, sizeof(struct ip_masq_app));
+end:
+	IP_MASQ_ERR("FTP: registration error, quitting\n");
+	return err;
 }
-
 /*
- * 	ip_masq_ftp fin.
+ * 	Unregister ALL ports, _including_ the (possible) firewall
+ * 	mark.
  */
-
-int ip_masq_ftp_done(void)
-{
-	int i, j, k;
-
-	k=0;
+static int unregister_ports(struct ip_masq_app *mapp_instances[]) {
+	int i, j, k=0;
 	for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
-		if (masq_incarnations[i]) {
-			if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+		if (mapp_instances[i]) {
+			if ((j = unregister_ip_masq_app(mapp_instances[i]))) {
 				k = j;
 			} else {
-				kfree(masq_incarnations[i]);
-				masq_incarnations[i] = NULL;
-				IP_MASQ_DEBUG(1-debug, "Ftp: unloaded support on port[%d] = %d\n",
-				       i, ports[i]);
+				IP_MASQ_DEBUG(1-debug, "FTP: unloaded support on port[%d] = %d, type=0x%x\n",
+				       i, ports[i], mapp_instances[i]->type);
+				kfree(mapp_instances[i]);
+				mapp_instances[i] = NULL;
 			}
 		}
 	}
 	return k;
+}
+
+/*
+ * 	ip_masq_ftp initialization
+ */
+
+__initfunc(int ip_masq_ftp_init(void))
+{
+	int ret=0;
+	struct ip_masq_app *mapp;
+	if (
+	(ret=register_ports(masq_ftp_objs, ports, IP_MASQ_APP_OUTBOUND)) ||
+	(ret=register_ports(masq_in_ftp_objs, in_ports, IP_MASQ_APP_INBOUND))
+	)
+		goto end;
+
+	if (mark) {
+		if (!(mapp=make_instance(&ip_masq_ftp, &ret)))
+			goto end;
+		if ((ret=ip_masq_app_init_fwmark(mapp, IP_MASQ_APP_OUTBOUND, mark))) {
+			kfree_s(mapp, sizeof (struct ip_masq_app));
+			goto end;
+		}
+		if ((ret=register_ip_masq_app_type(mapp)))
+			goto end;
+		masq_ftp_mark=mapp;
+	}
+	if (in_mark) {
+		if (!(mapp=make_instance(&ip_masq_ftp, &ret)))
+			goto end;
+		if ((ret=ip_masq_app_init_fwmark(mapp, IP_MASQ_APP_INBOUND, in_mark))) {
+			kfree_s(mapp, sizeof (struct ip_masq_app));
+			goto end;
+		}
+		if ((ret=register_ip_masq_app_type(mapp)))
+			goto end;
+		masq_in_ftp_mark=mapp;
+	}
+end:
+	return ret;
+}
+
+/*
+ * 	ip_masq_ftp fin.
+ */
+
+int ip_masq_ftp_done(void)
+{
+	int ret;
+	ret = unregister_ports(masq_ftp_objs);
+	ret += unregister_ports(masq_in_ftp_objs);
+	return ret;
 }
 
 #ifdef MODULE
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq_user.c linux.19p1/net/ipv4/ip_masq_user.c
--- linux.vanilla/net/ipv4/ip_masq_user.c	Mon Dec 11 22:13:10 2000
+++ linux.19p1/net/ipv4/ip_masq_user.c	Wed Dec 13 21:48:10 2000
@@ -35,7 +35,6 @@
  */
 static int debug=0;
 
-MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
 MODULE_PARM(debug, "i");
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/tcp.c linux.19p1/net/ipv4/tcp.c
--- linux.vanilla/net/ipv4/tcp.c	Mon Dec 11 22:13:10 2000
+++ linux.19p1/net/ipv4/tcp.c	Wed Dec 13 22:21:20 2000
@@ -1619,6 +1619,7 @@
 	struct wait_queue wait = { current, NULL };
 	struct open_request *req;
 
+	current->task_exclusive = 1;
 	add_wait_queue(sk->sleep, &wait);
 	for (;;) {
 		current->state = TASK_INTERRUPTIBLE;
@@ -1632,6 +1633,8 @@
 			break;
 	}
 	current->state = TASK_RUNNING;
+	wmb();
+	current->task_exclusive = 0;
 	remove_wait_queue(sk->sleep, &wait);
 	return req;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/netsyms.c linux.19p1/net/netsyms.c
--- linux.vanilla/net/netsyms.c	Mon Dec 11 22:13:10 2000
+++ linux.19p1/net/netsyms.c	Wed Dec 13 21:48:45 2000
@@ -208,7 +208,7 @@
 
 /* Needed by unix.o */
 EXPORT_SYMBOL(scm_fp_dup);
-EXPORT_SYMBOL(max_files);
+EXPORT_SYMBOL(files_stat);
 EXPORT_SYMBOL(do_mknod);
 EXPORT_SYMBOL(memcpy_toiovec);
 EXPORT_SYMBOL(csum_partial);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/unix/af_unix.c linux.19p1/net/unix/af_unix.c
--- linux.vanilla/net/unix/af_unix.c	Mon Dec 11 22:13:12 2000
+++ linux.19p1/net/unix/af_unix.c	Wed Dec 13 21:48:45 2000
@@ -417,7 +417,7 @@
 {
 	struct sock *sk;
 
-	if (atomic_read(&unix_nr_socks) >= 2*max_files)
+	if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files)
 		return NULL;
 
 	MOD_INC_USE_COUNT;
