diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/CREDITS linux.19p6/CREDITS
--- linux.vanilla/CREDITS	Mon Dec 11 22:12:31 2000
+++ linux.19p6/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.19p6/Documentation/Changes
--- linux.vanilla/Documentation/Changes	Mon Dec 11 22:12:31 2000
+++ linux.19p6/Documentation/Changes	Sat Dec 30 19:42:45 2000
@@ -517,6 +517,28 @@
 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 both a devfs regular file and as a normal (misc)
+character device.  If you are not using devfs you may need 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.
+
+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.19p6/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Mon Dec 11 22:12:31 2000
+++ linux.19p6/Documentation/Configure.help	Wed Jan  3 01:06:55 2001
@@ -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
@@ -5869,6 +5895,25 @@
   If you want to compile this as a module, say M and read
   Documentation/modules.txt. The module will be called comx-hw-mixcom.o.
 
+i810 TCO support
+CONFIG_I810_TCO
+  Hardware driver for the TCO timer built into the Intel i810 and i815
+  chipset family. The TCO (Total Cost of Ownership) timer is a watchdog
+  timer that will reboot the machine after it's second expiration. The
+  expiration time can be configured by commandline argument
+  "i810_margin=<n>" where <n> is the counter initial value. It is
+  decremented every 0.6 secs, the default is 50 which gives a timeout
+  of 30 seconds and one minute until reset.
+
+  On some motherboards the driver may fail to reset the chipset's
+  NO_REBOOT flag which prevents the watchdog from rebooting the machine.
+  If this is the case you will get a kernel message like
+  "i810tco init: failed to reset NO_REBOOT flag".
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called
+  i810-tco.o.
+
 MultiGate Cisco-HDLC and synchronous PPP protocol support
 CONFIG_COMX_PROTO_PPP
   Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards. 
@@ -6790,6 +6835,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.
@@ -7475,13 +7530,6 @@
   ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/mini. Probably the quota
   support is only useful for multi user systems. If unsure, say N.
 
-Acorn's ADFS filesystem support (read only) (EXPERIMENTAL)
-CONFIG_ADFS_FS
-  The Advanced Disk File System is the filesystem used on floppy and
-  hard disks by Acorn Systems.  Currently in development, as a read-
-  only driver for hard disks.  These should be the first partition
-  (eg. /dev/[sh]d?1) on each of your drives.  If unsure, say N.
-
 Support for USB
 CONFIG_USB
   Universal Serial Bus (USB) is a specification for a serial bus
@@ -7854,6 +7902,16 @@
   The module will be called digi_acceleport.o. If you want to compile 
   it as a module, say M here and read Documentation/modules.txt.
 
+USB Empeg empeg-car Mark I/II Driver
+CONFIG_USB_SERIAL_EMPEG
+  Say Y here if you want to connect to your Empeg empeg-car Mark I/II
+  mp3 player via USB.
+
+  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 empeg.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
 USB Serial Converter verbose debug
 CONFIG_USB_SERIAL_DEBUG
   Say Y here if you want verbose debug messages from the USB Serial
@@ -8730,6 +8788,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
@@ -9262,6 +9326,14 @@
   input/output character sets. Say Y here for the preferred Russian
   character set.
 
+nls koi8-ru
+CONFIG_NLS_KOI8_RU
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the preferred Russian
+  character set.
+
 Virtual terminal
 CONFIG_VT
   If you say Y here, you will get support for terminal devices with
@@ -9654,6 +9726,24 @@
   If you have more than 3 printers, you need to increase the LP_NO
   variable in lp.c.
 
+Amiga built-in parallel port support
+CONFIG_PARPORT_AMIGA
+  Say Y here if you need support for the parallel port hardware on
+  Amiga machines. This code is also available as a module (say M),
+  called parport_amiga.o. If in doubt, saying N is the safe plan.
+
+Atari built-in parallel port support
+CONFIG_PARPORT_ATARI
+  Say Y here if you need support for the parallel port hardware on
+  Atari machines. This code is also available as a module (say M),
+  called parport_atari.o. If in doubt, saying N is the safe plan.
+
+Multiface 3 parallel port card support
+CONFIG_PARPORT_MFC3
+  Say Y here if you need parallel port support for the MFC3 card.
+  This code is also available as a module (say M), called
+  parport_mfc3.o. If in doubt, saying N is the safe plan.
+
 Support IEEE1284 status readback
 CONFIG_PRINTER_READBACK
   If your printer conforms to IEEE 1284, it may be able to provide a
@@ -10411,12 +10501,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 +10515,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 +10568,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 +10577,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
@@ -10698,17 +10790,6 @@
   to it. For more information on how to use the driver please read
   Documentation/joystick.txt
 
-Atomwide Serial Support
-CONFIG_ATOMWIDE_SERIAL
-  If you have an Atomwide Serial card for an Acorn system, say Y to
-  this option. The driver can handle 1, 2, or 3 port cards.
-  If unsure, say N
-
-The Serial Port Dual Serial Port
-CONFIG_DUALSP_SERIAL
-  If you have the Serial Port's dual serial card for an Acorn system,
-  say Y to this option. If unsure, say N
-
 NetWinder Button
 CONFIG_NWBUTTON
   If you enable this driver and create a character device node
@@ -10976,8 +11057,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)
@@ -11377,7 +11457,15 @@
 CONFIG_SOUND_MAESTRO
   Say Y or M if you have a sound system driven by ESS's Maestro line
   of PCI sound chips.  These include the Maestro 1, Maestro 2, and
-  Maestro 2E.  See Documentation/sound/Maestro for more details.
+  Maestro 2E, but _does not_ include the Maestro 3 and Allegro 
+  line of chips.  See Documentation/sound/Maestro for more details
+  on the 1/2/2e line.
+
+ESS Maestro3/Allegro sound chipsets
+CONFIG_SOUND_MAESTRO3
+  Say Y or M if you have a sound system driven by ESS's Maestro3 line
+  of PCI sound chips.  This includes the Allegro sound chip that is
+  a lighter version of the Maestro3.
 
 Are you using a crosscompiler
 CONFIG_CROSSCOMPILE
@@ -11560,6 +11648,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 +11780,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 +11899,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
@@ -12763,22 +12859,6 @@
 ZOLTRIX I/O port (0x20c or 0x30c)
 CONFIG_RADIO_ZOLTRIX_PORT
   Enter the I/O port of your Zoltrix radio card.
-
-ADS Cadet AM/FM Tuner
-CONFIG_RADIO_CADET
-  Say Y here if this is your AM/FM radio card.
-
-  In order to control your radio card, you will need to use programs
-  that are compatible with the Video for Linux API. Information on 
-  this API and pointers to "v4l" programs may be found on the WWW at
-  http://roadrunner.swansea.uk.linux.org/v4l.shtml; to browse the WWW,
-  you need to have access to a machine on the Internet that has a 
-  program like lynx or netscape.
-
-  If you want to compile this driver as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt. The module will be
-  called radio-cadet.o
 
 Miro PCM20 Radio
 CONFIG_RADIO_MIROPCM20
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/devices.txt linux.19p6/Documentation/devices.txt
--- linux.vanilla/Documentation/devices.txt	Fri Apr 21 12:45:49 2000
+++ linux.19p6/Documentation/devices.txt	Sat Dec 30 19:25:47 2000
@@ -1,8 +1,8 @@
 
 		       LINUX ALLOCATED DEVICES
-	     Maintained by H. Peter Anvin <hpa@zytor.com>
+	     Maintained by H. Peter Anvin <device@lanana.org>
 
-		     Last revised: March 23, 2000
+		   Last revised: December 29, 2000
 
 This list is the Linux Device List, the official registry of allocated
 device numbers and /dev directory nodes for the Linux operating
@@ -37,7 +37,7 @@
 reply.
 
 
-     **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY ****
+	  **** DEVICE DRIVERS AUTHORS PLEASE READ THIS ****
 
 To have a major number allocated, or a minor number in situations
 where that applies (e.g. busmice), please contact me with the
@@ -45,24 +45,32 @@
 information regarding any of the devices listed below, or if I have
 made a mistake, I would greatly appreciate a note.
 
-I do, however, make two requests about the nature of your report.
+I do, however, make a few requests about the nature of your report.
 This is necessary for me to be able to keep this list up to date and
-correct in a timely manner.  First of all, *please* include the word
-"device" in the subject so your mail won't accidentally get buried!  I
-receive hundreds of email messages a day, so mail sent with other
-subjects may very well get lost in the avalanche.
+correct in a timely manner.  First of all, *please* send it to the
+correct address... <device@lanana.org>.  I receive hundreds of email
+messages a day, so mail sent to other addresses may very well get lost
+in the avalanche.  Please put in a descriptive subject, so I can find
+your mail again should I need to.  Too many people send me email
+saying just "device number request" in the subject.
 
 Second, please include a description of the device *in the same format
 as this list*.	The reason for this is that it is the only way I have
 found to ensure I have all the requisite information to publish your
 device and avoid conflicts.
 
+Third, please don't assume that the distributed version of the list is
+up to date.  Due to the number of registrations I have to maintain it
+in "batch mode", so there is likely additional registrations that
+haven't been listed yet.
+
 Finally, sometimes I have to play "namespace police."  Please don't be
 offended.  I often get submissions for /dev names that would be bound
 to cause conflicts down the road.  I am trying to avoid getting in a
 situation where we would have to suffer an incompatible forward
-change.
-
+change.  Therefore, please consult with me *before* you make your
+device names and numbers in any way public, at least to the point
+where it would be at all difficult to get them changed.
 
 Your cooperation is appreciated.
 
@@ -80,16 +88,18 @@
 		  7 = /dev/full		Returns ENOSPC on write
 		  8 = /dev/random	Nondeterministic random number gen.
 		  9 = /dev/urandom	Faster, less secure random number gen.
+		 10 = /dev/aio		Asyncronous I/O notification interface
     block	RAM disk
 		  0 = /dev/ram0		First RAM disk
+		  1 = /dev/ram1		Second RAM disk
 		    ...
-		  7 = /dev/ram7		Eighth RAM disk
-		250 = /dev/initrd	Initial RAM disk
+		250 = /dev/initrd	Initial RAM disk {2.6}
 
 		Older kernels had /dev/ramdisk (1, 1) here.
 		/dev/initrd refers to a RAM disk which was preloaded
-		by the boot loader.
-		    
+		by the boot loader; newer kernels use /dev/ram0 for
+		the initrd.
+
   2 char	Pseudo-TTY masters
 		  0 = /dev/ptyp0	First PTY master
 		  1 = /dev/ptyp1	Second PTY master
@@ -200,6 +210,8 @@
 		    ...
 		255 = /dev/ttyS191	192nd UART serial port
 
+		UART serial ports refer to 8250/16450/16550 series devices.
+
 		Older versions of the Linux kernel used this major
 		number for BSD PTY devices.  As of Linux 2.1.115, this
 		is no longer supported.	 Use major numbers 2 and 3.
@@ -311,6 +323,8 @@
 		 10 = /dev/adbmouse	Apple Desktop Bus mouse
 		 11 = /dev/vrtpanel	Vr41xx embedded touch panel
 		 13 = /dev/vpcmouse	Connectix Virtual PC Mouse
+		 14 = /dev/touchscreen/ucb1x00  UCB 1x00 touchscreen
+		 15 = /dev/touchscreen/mk712	MK712 touchscreen
 		128 = /dev/beep		Fancy beep device
 		129 = /dev/modreq	Kernel module load request {2.6}
 		130 = /dev/watchdog	Watchdog timer port
@@ -322,7 +336,7 @@
 		139 = /dev/openprom	SPARC OpenBoot PROM
 		140 = /dev/relay8	Berkshire Products Octal relay card
 		141 = /dev/relay16	Berkshire Products ISO-16 relay card
-		142 = /dev/msr		x86 model-specific registers
+		142 = /dev/msr		x86 model-specific registers {2.6}
 		143 = /dev/pciconf	PCI configuration space
 		144 = /dev/nvram	Non-volatile configuration RAM
 		145 = /dev/hfmodem	Soundcard shortwave modem control {2.6}
@@ -364,6 +378,26 @@
 		182 = /dev/perfctr	Performance-monitoring counters
 		183 = /dev/intel_rng	Intel i8x0 random number generator
 		184 = /dev/cpu/microcode CPU microcode update interface
+		186 = /dev/atomicps	Atomic shapshot of process state data
+		187 = /dev/irnet	IrNET device
+		188 = /dev/smbusbios	SMBus BIOS
+		189 = /dev/ussp_ctl	User space serial port control
+		190 = /dev/crash	Mission Critical Linux crash dump facility
+		191 = /dev/pcl181	<information missing>
+		192 = /dev/nas_xbus	NAS xbus LCD/buttons access
+		193 = /dev/d7s		SPARC 7-segment display
+		194 = /dev/zkshim	Zero-Knowledge network shim control
+		195 = /dev/elographics/e2201	Elographics touchscreen E271-2201
+		198 = /dev/sexec	Signed executable interface
+		199 = /dev/scanners/cuecat :CueCat barcode scanner
+		200 = /dev/net/tun	TAP/TUN network device
+		201 = /dev/button/gulpb	Transmeta GULP-B buttons
+		204 = /dev/video/em8300	    EM8300 DVD decoder control
+		205 = /dev/video/em8300_mv  EM8300 DVD decoder video
+		206 = /dev/video/em8300_ma  EM8300 DVD decoder audio
+		207 = /dev/video/em8300_sp  EM8300 DVD decoder subpicture
+		208 = /dev/compaq/cpqphpc Compaq PCI Hot Plug Controller
+		209 = /dev/compaq/cpqrid  Compaq Remote Insight Driver
 		240-255			Reserved for local use
 
  11 char	Raw keyboard device
@@ -902,29 +936,62 @@
 		  0 = /dev/ttyL0	First RISCom port
 		  1 = /dev/ttyL1	Second RISCom port
 		    ...
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; first controller
+		  0 = /dev/rd/c0d0	First disk, whole disk
+		  8 = /dev/rd/c0d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c0d15	16th disk, whole disk
+
+		For partitions add:
+		  0 = /dev/rd/c?d?	Whole disk
+		  1 = /dev/rd/c?d?p1	First partition
+		    ...
+		  7 = /dev/rd/c?d?p7	Seventh partition
 
  49 char	SDL RISCom serial card - alternate devices
 		  0 = /dev/cul0		Callout device for ttyL0
 		  1 = /dev/cul1		Callout device for ttyL1
 		    ...
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; second controller
+		  0 = /dev/rd/c1d0	First disk, whole disk
+		  8 = /dev/rd/c1d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c1d15	16th disk, whole disk
+
+		Partitions are handled as for major 48.
 
  50 char	Reserved for GLINT
-    block	Reserved for Mylex DAC960 PCI RAID controller
+
+    block	Mylex DAC960 PCI RAID controller; third controller
+		  0 = /dev/rd/c2d0	First disk, whole disk
+		  8 = /dev/rd/c2d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c2d15	16th disk, whole disk
 
  51 char	Baycom radio modem
 		  0 = /dev/bc0		First Baycom radio modem
 		  1 = /dev/bc1		Second Baycom radio modem
 		    ...
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; fourth controller
+		  0 = /dev/rd/c3d0	First disk, whole disk
+		  8 = /dev/rd/c3d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c3d15	16th disk, whole disk
+
+		Partitions are handled as for major 48.
 
  52 char	Spellcaster DataComm/BRI ISDN card
 		  0 = /dev/dcbri0	First DataComm card
 		  1 = /dev/dcbri1	Second DataComm card
 		  2 = /dev/dcbri2	Third DataComm card
 		  3 = /dev/dcbri3	Fourth DataComm card
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; fifth controller
+		  0 = /dev/rd/c4d0	First disk, whole disk
+		  8 = /dev/rd/c4d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c4d15	16th disk, whole disk
+
+		Partitions are handled as for major 48.
 
  53 char	BDM interface for remote debugging MC683xx microcontrollers
 		  0 = /dev/pd_bdm0	PD BDM interface on lp0
@@ -940,7 +1007,13 @@
 		Domain Interface and ICD is the commercial interface
 		by P&E.
 
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; sixth controller
+		  0 = /dev/rd/c5d0	First disk, whole disk
+		  8 = /dev/rd/c5d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c5d15	16th disk, whole disk
+
+		Partitions are handled as for major 48.
 
  54 char	Electrocardiognosis Holter serial card
 		  0 = /dev/holter0	First Holter port
@@ -951,11 +1024,23 @@
 		<mseritan@ottonel.pub.ro> to transfer data from Holter
 		24-hour heart monitoring equipment.
 
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; seventh controller
+		  0 = /dev/rd/c6d0	First disk, whole disk
+		  8 = /dev/rd/c6d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c6d15	16th disk, whole disk
+
+		Partitions are handled as for major 48.
 
  55 char	DSP56001 digital signal processor
 		  0 = /dev/dsp56k	First DSP56001
-    block	Reserved for Mylex DAC960 PCI RAID controller
+    block	Mylex DAC960 PCI RAID controller; eigth controller
+		  0 = /dev/rd/c7d0	First disk, whole disk
+		  8 = /dev/rd/c7d1	Second disk, whole disk
+		    ...
+		248 = /dev/rd/c7d15	16th disk, whole disk
+
+		Partitions are handled as for major 48.
 
  56 char	Apple Desktop Bus
 		  0 = /dev/adb		ADB bus control
@@ -1002,6 +1087,8 @@
 		running small fs translation drivers) through serial /
 		IRDA / parallel links.
 
+		NAMING CONFLICT -- PROPOSED REVISED NAME /dev/rpda0 etc
+
  60-63		LOCAL/EXPERIMENTAL USE
 		Allocated for local/experimental use.  For devices not
 		assigned official numbers, these ranges should be
@@ -1176,6 +1263,16 @@
 		    ...
 		255 = /dev/cuf255	Callout device for ttyF255
 
+    block	Compaq Intelligent Drive Array, first controller
+		  0 = /dev/ida/c0d0	First logical drive whole disk
+		 16 = /dev/ida/c0d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c0d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
  73 char	Computone IntelliPort II serial card - control devices
 		  0 = /dev/ip2ipl0	Loadware device for board 0
 		  1 = /dev/ip2stat0	Status device for board 0
@@ -1186,6 +1283,16 @@
 		 12 = /dev/ip2ipl3	Loadware device for board 3
 		 13 = /dev/ip2stat3	Status device for board 3
 
+    block	Compaq Intelligent Drive Array, second controller
+		  0 = /dev/ida/c1d0	First logical drive whole disk
+		 16 = /dev/ida/c1d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c1d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
  74 char	SCI bridge
 		  0 = /dev/SCI/0	SCI device 0
 		  1 = /dev/SCI/1	SCI device 1
@@ -1194,6 +1301,16 @@
 		Currently for Dolphin Interconnect Solutions' PCI-SCI
 		bridge.
 
+    block	Compaq Intelligent Drive Array, third controller
+		  0 = /dev/ida/c2d0	First logical drive whole disk
+		 16 = /dev/ida/c2d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c2d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
  75 char	Specialix IO8+ serial card
 		  0 = /dev/ttyW0	First IO8+ port, first card
 		  1 = /dev/ttyW1	Second IO8+ port, first card
@@ -1201,6 +1318,16 @@
 		  8 = /dev/ttyW8	First IO8+ port, second card
 		    ...
 
+    block	Compaq Intelligent Drive Array, fourth controller
+		  0 = /dev/ida/c3d0	First logical drive whole disk
+		 16 = /dev/ida/c3d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c3d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
  76 char	Specialix IO8+ serial card - alternate devices
 		  0 = /dev/cuw0		Callout device for ttyW0
 		  1 = /dev/cuw1		Callout device for ttyW1
@@ -1208,19 +1335,63 @@
 		  8 = /dev/cuw8		Callout device for ttyW8
 		    ...
 
+    block	Compaq Intelligent Drive Array, fifth controller
+		  0 = /dev/ida/c4d0	First logical drive whole disk
+		 16 = /dev/ida/c4d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c4d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
+
  77 char	ComScire Quantum Noise Generator
 		  0 = /dev/qng		ComScire Quantum Noise Generator
 
+    block	Compaq Intelligent Drive Array, sixth controller
+		  0 = /dev/ida/c5d0	First logical drive whole disk
+		 16 = /dev/ida/c5d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c5d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
+
  78 char	PAM Software's multimodem boards
 		  0 = /dev/ttyM0	First PAM modem
 		  1 = /dev/ttyM1	Second PAM modem
 		    ...
 
+    block	Compaq Intelligent Drive Array, seventh controller
+		  0 = /dev/ida/c6d0	First logical drive whole disk
+		 16 = /dev/ida/c6d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c6d15	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
+
  79 char	PAM Software's multimodem boards - alternate devices
 		  0 = /dev/cum0		Callout device for ttyM0
 		  1 = /dev/cum1		Callout device for ttyM1
 		    ...
 
+    block	Compaq Intelligent Drive Array, eigth controller
+		  0 = /dev/ida/c7d0	First logical drive whole disk
+		 16 = /dev/ida/c7d1	Second logical drive whole disk
+		    ...
+		240 = /dev/ida/c715	16th logical drive whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
+
  80 char	Photometrics AT200 CCD camera
 		  0 = /dev/at200	Photometrics AT200 CCD camera
 
@@ -1497,14 +1668,13 @@
 		  1 = /dev/parport1	Second parallel port
 		    ...
 
-100 char	POTS (analogue telephone) A/B port {2.6}
-		  0 = /dev/phone0	First telephone port
-		  1 = /dev/phone1	Second telephone port
-		    ...
+    block	JavaStation flash disk
+		  0 = /dev/jsfd		JavaStation flash disk
 
-		The names have been reallocated to Telephony For
-		Linux, major 159.  All use of major 100 should be
-		considered legacy and deprecated.
+100 char	Telephony for Linux
+		  0 = /dev/phone0	First telephony device
+		  1 = /dev/phone1	Second telephony device
+		    ...
 
 101 char	Motorola DSP 56xxx board
 		  0 = /dev/mdspstat	Status information
@@ -1512,6 +1682,19 @@
 		    ...
 		 16 = /dev/mdsp16	16th DSP board I/O controls
 
+    block	AMI HyperDisk RAID controller
+		  0 = /dev/amiraid/ar0	First array whole disk
+		 16 = /dev/amiraid/ar1	Second array whole disk
+		    ...
+		240 = /dev/amiraid/ar15	16th array whole disk
+
+		For each device, partitions are added as:
+		  0 = /dev/amiraid/ar?	  Whole disk
+		  1 = /dev/amiraid/ar?p1  First partition
+		  2 = /dev/amiraid/ar?p2  Second partition
+		    ...
+		 15 = /dev/amiraid/ar?p15 15th partition
+
 102 char	Philips SAA5249 Teletext signal decoder {2.6}
 		  0 = /dev/tlk0		First Teletext decoder
 		  1 = /dev/tlk1		Second Teletext decoder
@@ -1528,36 +1711,119 @@
 		to the arla announce mailing list by sending a mail to
 		<arla-announce-request@stacken.kth.se>.
 
+    block	Audit device
+		  0 = /dev/audit	Audit device
+
 104 char	Flash BIOS support
 
+    block	Compaq Next Generation Drive Array, first controller
+		  0 = /dev/cciss/c0d0	First logical drive, whole disk
+		 16 = /dev/cciss/c0d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c0d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 105 char	Comtrol VS-1000 serial controller
 		  0 = /dev/ttyV0	First VS-1000 port
 		  1 = /dev/ttyV1	Second VS-1000 port
 		    ...
 
+    block	Compaq Next Generation Drive Array, second controller
+		  0 = /dev/cciss/c1d0	First logical drive, whole disk
+		 16 = /dev/cciss/c1d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c1d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 106 char	Comtrol VS-1000 serial controller - alternate devices
 		  0 = /dev/cuv0		First VS-1000 port
 		  1 = /dev/cuv1		Second VS-1000 port
 		    ...
 
+    block	Compaq Next Generation Drive Array, third controller
+		  0 = /dev/cciss/c2d0	First logical drive, whole disk
+		 16 = /dev/cciss/c2d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c2d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 107 char	3Dfx Voodoo Graphics device
 		  0 = /dev/3dfx		Primary 3Dfx graphics device
 
+    block	Compaq Next Generation Drive Array, fourth controller
+		  0 = /dev/cciss/c3d0	First logical drive, whole disk
+		 16 = /dev/cciss/c3d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c3d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 108 char	Device independent PPP interface
 		  0 = /dev/ppp		Device independent PPP interface
 
+    block	Compaq Next Generation Drive Array, fifth controller
+		  0 = /dev/cciss/c4d0	First logical drive, whole disk
+		 16 = /dev/cciss/c4d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c4d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 109 char	Reserved for logical volume manager
 
+    block	Compaq Next Generation Drive Array, sixth controller
+		  0 = /dev/cciss/c5d0	First logical drive, whole disk
+		 16 = /dev/cciss/c5d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c5d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 110 char	miroMEDIA Surround board
 		  0 = /dev/srnd0	First miroMEDIA Surround board
 		  1 = /dev/srnd1	Second miroMEDIA Surround board
 		    ...
 
+    block	Compaq Next Generation Drive Array, seventh controller
+		  0 = /dev/cciss/c6d0	First logical drive, whole disk
+		 16 = /dev/cciss/c6d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c6d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 111 char	Philips SAA7146-based audio/video card {2.6}
 		  0 = /dev/av0		First A/V card
 		  1 = /dev/av1		Second A/V card
 		    ...
 
+    block	Compaq Next Generation Drive Array, eigth controller
+		  0 = /dev/cciss/c7d0	First logical drive, whole disk
+		 16 = /dev/cciss/c7d1	Second logical drive, whole disk
+		    ...
+		240 = /dev/cciss/c7d15	16th logical drive, whole disk
+
+		Partitions are handled the same way as for Mylex
+		DAC960 (see major number 48) except that the limit on
+		partitions is 15.
+
 112 char	ISI serial card
 		  0 = /dev/ttyM0	First ISI port
 		  1 = /dev/ttyM1	Second ISI port
@@ -1718,10 +1984,7 @@
 		  1 = /dev/gfax1	GammaLink channel 1
 		    ...
 
-159 char	Telephony for Linux
-		  0 = /dev/phone0	First telephony device
-		  1 = /dev/phone1	Second telephony device
-		    ...
+159		RESERVED
 
 160 char	General Purpose Instrument Bus (GPIB)
 		  0 = /dev/gpib0	First GPIB bus
@@ -1945,7 +2208,244 @@
 		240 = /dev/mvideo/status15	16th device 
 		    ...
 
-195-239		UNALLOCATED
+195 char	Nvidia graphics devices
+		  0 = /dev/nvidia0		First Nvidia card
+		  1 = /dev/nvidia1		Second Nvidia card
+		    ...
+		255 = /dev/nvidiactl		Nvidia card control device
+
+196-197		UNASSIGNED
+
+198 char	Total Impact TPMP2 quad coprocessor PCI card
+		  0 = /dev/tpmp2/0		First card
+		  1 = /dev/tpmp2/1		Second card
+		    ...
+
+199 char	Veritas volume manager (VxVM) volumes
+		  0 = /dev/vx/rdsk/*/*		First volume
+		  1 = /dev/vx/rdsk/*/*		Second volume
+		    ...
+    block	Veritas volume manager (VxVM) volumes
+		  0 = /dev/vx/dsk/*/*		First volume
+		  1 = /dev/vx/dsk/*/*		First volume
+		    ...
+
+		The namespace in these directories is maintained by
+		the user space VxVM software.
+
+200 char	Veritas VxVM configuration interface
+                  0 = /dev/vx/config		Configuration access node
+                  1 = /dev/vx/trace		Volume i/o trace access node
+                  2 = /dev/vx/iod		Volume i/o daemon access node
+                  3 = /dev/vx/info		Volume information access node
+                  4 = /dev/vx/task		Volume tasks access node
+                  5 = /dev/vx/taskmon		Volume tasks monitor daemon
+
+201 char	Veritas VxVM dynamic multipathing driver
+		  0 = /dev/vx/rdmp/*		First multipath device
+		  1 = /dev/vx/rdmp/*		Second multipath device
+		    ...
+    block	Veritas VxVM dynamic multipathing driver
+		  0 = /dev/vx/dmp/*		First multipath device
+		  1 = /dev/vx/dmp/*		Second multipath device
+		    ...
+
+		The namespace in these directories is maintained by
+		the user space VxVM software.
+
+202 char	CPU model-specific registers
+		  0 = /dev/cpu/0/msr		MSRs on CPU 0
+		  1 = /dev/cpu/1/msr		MSRs on CPU 1
+		    ...
+
+203 char	CPU CPUID information
+		  0 = /dev/cpu/0/cpuid		CPUID on CPU 0
+		  1 = /dev/cpu/1/cpuid		CPUID on CPU 1
+		    ...
+
+204 char	Low-density serial ports
+		  0 = /dev/ttyLU0		LinkUp Systems L72xx UART - port 0
+		  1 = /dev/ttyLU1		LinkUp Systems L72xx UART - port 1
+		  2 = /dev/ttyLU2		LinkUp Systems L72xx UART - port 2
+		  3 = /dev/ttyLU3		LinkUp Systems L72xx UART - port 3
+		  4 = /dev/ttyFB0		Intel Footbridge (ARM)
+		  5 = /dev/ttySA0		StrongARM builtin serial port 0
+		  6 = /dev/ttySA1		StrongARM builtin serial port 1
+		  7 = /dev/ttySA2		StrongARM builtin serial port 2
+		  8 = /dev/ttySC0		SCI serial port (SuperH) - port 0
+		  9 = /dev/ttySC1		SCI serial port (SuperH) - port 1
+		 10 = /dev/ttySC2		SCI serial port (SuperH) - port 2
+		 11 = /dev/ttySC3		SCI serial port (SuperH) - port 3
+		 12 = /dev/ttyFW0		Firmware console - port 0
+		 13 = /dev/ttyFW1		Firmware console - port 1
+		 14 = /dev/ttyFW2		Firmware console - port 2
+		 15 = /dev/ttyFW3		Firmware console - port 3
+		 16 = /dev/ttyAM0		ARM "AMBA" serial port 0
+		    ...
+		 31 = /dev/ttyAM15		ARM "AMBA" serial port 15
+
+205 char	Low-density serial ports (alternate device)
+		  0 = /dev/culu0		Callout device for ttyLU0
+		  1 = /dev/culu1		Callout device for ttyLU1
+		  2 = /dev/culu2		Callout device for ttyLU2
+		  3 = /dev/culu3		Callout device for ttyLU3
+		  4 = /dev/cufb0		Callout device for ttyFB0
+		  5 = /dev/cusa0		Callout device for ttySA0
+		  6 = /dev/cusa1		Callout device for ttySA1
+		  7 = /dev/cusa2		Callout device for ttySA2
+		  8 = /dev/cusc0		Callout device for ttySC0
+		  9 = /dev/cusc1		Callout device for ttySC1
+		 10 = /dev/cusc2		Callout device for ttySC2
+		 11 = /dev/cusc3		Callout device for ttySC3
+		 12 = /dev/cufw0		Callout device for ttyFW0
+		 13 = /dev/cufw1		Callout device for ttyFW1
+		 14 = /dev/cufw2		Callout device for ttyFW2
+		 15 = /dev/cufw3		Callout device for ttyFW3
+		 16 = /dev/cuam0		Callout device for ttyAM0
+		    ...
+		 31 = /dev/cuam15		Callout device for ttyAM15
+
+206 char	OnStream SC-x0 tape devices
+		  0 = /dev/osst0		First OnStream SCSI tape, mode 0
+		  1 = /dev/osst1		Second OnStream SCSI tape, mode 0
+		    ...
+		 32 = /dev/osst0l		First OnStream SCSI tape, mode 1
+		 33 = /dev/osst1l		Second OnStream SCSI tape, mode 1
+		    ...
+		 64 = /dev/osst0m		First OnStream SCSI tape, mode 2
+		 65 = /dev/osst1m		Second OnStream SCSI tape, mode 2
+		    ...
+		 96 = /dev/osst0a		First OnStream SCSI tape, mode 3
+		 97 = /dev/osst1a		Second OnStream SCSI tape, mode 3
+		    ...
+		128 = /dev/nosst0		No rewind version of /dev/osst0
+		129 = /dev/nosst1		No rewind version of /dev/osst1
+		    ...
+		160 = /dev/nosst0l		No rewind version of /dev/osst0l
+		161 = /dev/nosst1l		No rewind version of /dev/osst1l
+		    ...
+		192 = /dev/nosst0m		No rewind version of /dev/osst0m
+		193 = /dev/nosst1m		No rewind version of /dev/osst1m
+		    ...
+		224 = /dev/nosst0a		No rewind version of /dev/osst0a
+		225 = /dev/nosst1a		No rewind version of /dev/osst1a
+		    ...
+
+		The OnStream SC-x0 SCSI tapes do not support the
+		standard SCSI SASD command set and therefore need
+		their own driver "osst". Note that the IDE, USB (and
+		maybe ParPort) versions may be driven via ide-scsi or
+		usb-storage SCSI emulation and this osst device and
+		driver as well.  The ADR-x0 drives are QIC-157
+		compliant and don't need osst.
+
+207 char	Compaq ProLiant health feature indicate
+		  0 = /dev/cpqhealth/cpqw	Redirector interface
+		  1 = /dev/cpqhealth/crom	EISA CROM
+		  2 = /dev/cpqhealth/cdt	Data Table
+		  3 = /dev/cpqhealth/cevt	Event Log
+		  4 = /dev/cpqhealth/casr	Automatic Server Recovery
+		  5 = /dev/cpqhealth/cecc	ECC Memory
+		  6 = /dev/cpqhealth/cmca	Machine Check Architecture
+		  7 = /dev/cpqhealth/ccsm	Deprecated CDT
+		  8 = /dev/cpqhealth/cnmi	NMI Handling
+		  9 = /dev/cpqhealth/css	Sideshow Management
+		 10 = /dev/cpqhealth/cram	CMOS interface
+		 11 = /dev/cpqhealth/cpci	PCI IRQ interface
+
+208 char	User space serial ports
+		  0 = /dev/ttyU0		First user space serial port
+		  1 = /dev/ttyU1		Second user space serial port
+		    ...
+
+209 char	User space serial ports (alternate devices)
+		  0 = /dev/cuu0			Callout device for ttyU0
+		  1 = /dev/cuu1			Callout device for ttyU1
+		    ...
+
+210 char	SBE, Inc. sync/async serial card
+		  0 = /dev/sbei/wxcfg0		Configuration device for board 0
+		  1 = /dev/sbei/dld0		Download device for board 0
+		  2 = /dev/sbei/wan00		WAN device, port 0, board 0
+		  3 = /dev/sbei/wan01		WAN device, port 1, board 0
+		  4 = /dev/sbei/wan02		WAN device, port 2, board 0
+		  5 = /dev/sbei/wan03		WAN device, port 3, board 0
+		  6 = /dev/sbei/wanc00		WAN clone device, port 0, board 0
+		  7 = /dev/sbei/wanc01		WAN clone device, port 1, board 0
+		  8 = /dev/sbei/wanc02		WAN clone device, port 2, board 0
+		  9 = /dev/sbei/wanc03		WAN clone device, port 3, board 0
+		 10 = /dev/sbei/wxcfg1		Configuration device for board 1
+		 11 = /dev/sbei/dld1		Download device for board 1
+		 12 = /dev/sbei/wan10		WAN device, port 0, board 1
+		 13 = /dev/sbei/wan11		WAN device, port 1, board 1
+		 14 = /dev/sbei/wan12		WAN device, port 2, board 1
+		 15 = /dev/sbei/wan13		WAN device, port 3, board 1
+		 16 = /dev/sbei/wanc10		WAN clone device, port 0, board 1
+		 17 = /dev/sbei/wanc11		WAN clone device, port 1, board 1
+		 18 = /dev/sbei/wanc12		WAN clone device, port 2, board 1
+		 19 = /dev/sbei/wanc13		WAN clone device, port 3, board 1
+		    ...
+
+		Yes, each board is really spaced 10 (decimal) apart.
+
+211 char	Addinum CPCI1500 digital I/O card
+		  0 = /dev/addinum/cpci1500/0	First CPCI1500 card
+		  1 = /dev/addinum/cpci1500/1	Second CPCI1500 card
+		    ...
+
+216 char	USB BlueTooth devices
+		  0 = /dev/ttyUB0		First USB BlueTooth device
+		  1 = /dev/ttyUB1		Second USB BlueTooth device
+		    ...
+
+217 char	USB BlueTooth devices (alternate devices)
+		  0 = /dev/cuub0		Callout device for ttyUB0
+		  1 = /dev/cuub1		Callout device for ttyUB1
+		    ...
+
+218 char	The Logical Company bus Unibus/Qbus adapters
+		  0 = /dev/logicalco/bci/0	First bus adapter
+		  1 = /dev/logicalco/bci/1	First bus adapter
+		    ...
+
+219 char	The Logical Company DCI-1300 digital I/O card
+		  0 = /dev/logicalco/dci1300/0	First DCI-1300 card
+		  1 = /dev/logicalco/dci1300/1	Second DCI-1300 card
+		    ...
+
+220 char	Myricom Myrinet "GM" board
+		  0 = /dev/myricom/gm0		First Myrinet GM board
+		  1 = /dev/myricom/gmp0		First board "root access"
+		  2 = /dev/myricom/gm1		Second Myrinet GM board
+		  3 = /dev/myricom/gmp1		Second board "root access"
+		    ...
+
+221 char	VME bus
+		  0 = /dev/bus/vme/m0		First master image
+		  1 = /dev/bus/vme/m1		Second master image
+		  2 = /dev/bus/vme/m2		Third master image
+		  3 = /dev/bus/vme/m3		Fourth master image
+		  4 = /dev/bus/vme/s0		First slave image
+		  5 = /dev/bus/vme/s1		Second slave image
+		  6 = /dev/bus/vme/s2		Third slave image
+		  7 = /dev/bus/vme/s3		Fourth slave image
+		  8 = /dev/bus/vme/ctl		Control
+
+		It is expected that all VME bus drivers will use the
+		same interface.  For interface documentation see
+		http://www.vmelinux.org/.
+
+224 char	A2232 serial card
+		  0 = /dev/ttyY0		First A2232 port
+		  1 = /dev/cuy0			Second A2232 port
+		    ...
+
+225 char	A2232 serial card (alternate devices)
+		  0 = /dev/cuy0			Callout device for ttyY0
+		  1 = /dev/cuy1			Callout device for ttyY1
+		    ...
+
+226-239		UNASSIGNED
 
 240-254		LOCAL/EXPERIMENTAL USE
 
@@ -2026,6 +2526,14 @@
 /dev/log	socket		syslog local socket
 /dev/gpmdata	socket		gpm mouse multiplexer
 
+	Mount points
+
+The following names are reserved for mounting special filesystems
+under /dev.  These special filesystems provide kernel interfaces that
+cannot be provided with standard device nodes.
+
+/dev/pts	devpts		PTY slave filesystem
+/dev/shm	shmfs		POSIX shared memory maintenance access
 
  ****	TERMINAL DEVICES
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/minix-part.txt linux.19p6/Documentation/filesystems/minix-part.txt
--- linux.vanilla/Documentation/filesystems/minix-part.txt	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/Documentation/ioctl-number.txt
--- linux.vanilla/Documentation/ioctl-number.txt	Mon Dec 11 22:12:32 2000
+++ linux.19p6/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.19p6/Documentation/isdn/README.HiSax
--- linux.vanilla/Documentation/isdn/README.HiSax	Mon Dec 11 22:12:32 2000
+++ linux.19p6/Documentation/isdn/README.HiSax	Sat Dec 30 19:33:51 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
 ------------------
@@ -433,6 +442,7 @@
 	Enrik Berkhan (enrik@starfleet.inka.de) for S0BOX specific stuff
 	Ton van Rosmalen for Teles PCI
 	Petr Novak <petr.novak@i.cz> for Winbond W6692 support
+	Werner Cornelius <werner@isdn4linux.de> for HFC-PCI, HFC-S(+/P) and supplementary services support
         and more people who are hunting bugs. (If I forgot somebody, please
 	send me a mail).
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.hfc-pci linux.19p6/Documentation/isdn/README.hfc-pci
--- linux.vanilla/Documentation/isdn/README.hfc-pci	Fri Apr 21 12:45:49 2000
+++ linux.19p6/Documentation/isdn/README.hfc-pci	Sat Dec 30 19:33:51 2000
@@ -35,7 +35,7 @@
 supplied else the parameter is assumed 0 and a auto search for a free card is
 invoked which may not give the wanted result. 
 
-Comments and reports to werner@isdn4linux.de or werner@titro.de .
+Comments and reports to werner@isdn4linux.de or werner@isdn-development.de
 
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/ip_masq/rc.masqfw linux.19p6/Documentation/networking/ip_masq/rc.masqfw
--- linux.vanilla/Documentation/networking/ip_masq/rc.masqfw	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/Documentation/s390/cds.txt
--- linux.vanilla/Documentation/s390/cds.txt	Mon Dec 11 22:12:32 2000
+++ linux.19p6/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/Documentation/sound/ChangeLog.ymfsb linux.19p6/Documentation/sound/ChangeLog.ymfsb
--- linux.vanilla/Documentation/sound/ChangeLog.ymfsb	Sun Jun 11 21:44:08 2000
+++ linux.19p6/Documentation/sound/ChangeLog.ymfsb	Thu Jan  1 01:00:00 1970
@@ -1,19 +0,0 @@
-Sun May 21 15:14:37 2000  Daisuke Nagano  <breeze.nagano@nifty.ne.jp>
-
-	* ymf_sb.c: Add 'master_vol' module parameter to change
-	'PCM out Vol" of AC'97.
-	Should I support AC'97 mixer by implementing original mixer
-	codes... ?
-
-	* ymf_sb.c: remove native UART401 support. External MIDI port
-	should be supported by sb_midi driver.
-
-	*ymf_sb.c: add support for SPDIF OUT. Module parameter
-	'spdif_out' is now available.
-
-
-Tue May 16 19:29:29 2000  Daisuke Nagano  <breeze.nagano@nifty.ne.jp>
-
-	* ymf_sb.c (checkCodec): add a little delays for reset devices.
-	
-	* ymf_sb.c (readRegWord): fixed addressing bug.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/Maestro3 linux.19p6/Documentation/sound/Maestro3
--- linux.vanilla/Documentation/sound/Maestro3	Thu Jan  1 01:00:00 1970
+++ linux.19p6/Documentation/sound/Maestro3	Wed Jan  3 01:01:13 2001
@@ -0,0 +1,84 @@
+	An OSS/Lite Driver for the ESS Maestro3 family of sound chips
+
+			Zach Brown, December 2000
+
+Driver Status and Availability
+------------------------------
+
+The most recent version of this driver will hopefully always be available at
+	http://www.zabbo.net/maestro3/
+
+I will try and maintain the most recent stable version of the driver
+in both the stable and development kernel lines.
+
+Historically I've sucked pretty hard at actually doing that, however.
+
+ESS Maestro3 Chip Family
+-----------------------
+
+The 'Maestro3' is much like the Maestro2 chip.  The noted improvement
+is the removal of the silicon in the '2' that did PCM mixing.  All that
+work is now done through a custom DSP called the ASSP, the Asynchronus
+Specific Signal Processor.
+
+The 'Allegro' is a baby version of the Maestro3.  I'm not entirely clear
+on the extent of the differences, but the driver supports them both :)
+
+The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998,
+both under ESS's vendor ID of 0x125D.  The Maestro3 can also show up as
+0x199a when hardware strapping is used.
+
+The chip can also act as a multi function device.  The modem IDs follow
+the audio multimedia device IDs.  (so the modem part of an Allegro shows
+up as 0x1989)
+
+Driver OSS Behavior
+--------------------
+
+This OSS driver exports /dev/mixer and /dev/dsp to applications, which
+mostly adhere to the OSS spec.   This driver doesn't register itself
+with /dev/sndstat, so don't expect information to appear there.
+
+The /dev/dsp device exported behaves as expected.  Playback is
+supported in all the various lovely formats.  8/16bit stereo/mono from
+8khz to 48khz, with both read()/write(), and mmap().
+
+/dev/mixer is an interface to the AC'97 codec on the Maestro3.  It is
+worth noting that there are a variety of AC'97s that can be wired to
+the Maestro3.  Which is used is entirely up to the hardware implementor.
+This should only be visible to the user by the presence, or lack, of
+'Bass' and 'Treble' sliders in the mixer.  Not all AC'97s have them.
+The Allegro has an onchip AC'97.
+
+The driver doesn't support MIDI or FM playback at the moment.
+
+Compiling and Installing
+------------------------
+
+With the drivers inclusion into the kernel, compiling and installing
+is the same as most OSS/Lite modular sound drivers.  Compilation
+of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable
+in the config system.  
+
+It may be modular or statically linked.  If it is modular it should be
+installed with the rest of the modules for the kernel on the system.
+Typically this will be in /lib/modules/ somewhere.  'alias sound-slot-0
+maestro3' should also be added to your module configs (typically
+/etc/modules.conf) if you're using modular OSS/Lite sound and want to
+default to using a maestro3 chip.
+
+There are very few options to the driver.  One is 'debug' which will 
+tell the driver to print minimal debugging information as it runs.  This
+can be collected with 'dmesg' or through the klogd daemon.
+
+The other is 'external_amp', which tells the driver to attempt to enable
+an external amplifier.  This defaults to '1', you can tell the driver
+not to bother enabling such an amplifier by setting it to '0'.
+
+Power Management
+----------------
+
+This driver has a minimal understanding of PCI Power Management.  It will
+try and power down the chip when the system is suspended, and power
+it up with it is resumed.  It will also try and power down the chip
+when the machine is shut down.  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/README.ymfsb linux.19p6/Documentation/sound/README.ymfsb
--- linux.vanilla/Documentation/sound/README.ymfsb	Sat Sep  9 18:42:32 2000
+++ linux.19p6/Documentation/sound/README.ymfsb	Thu Jan  1 01:00:00 1970
@@ -1,127 +0,0 @@
-Legacy audio driver for YMF7xx PCI cards.
-
-
-FIRST OF ALL
-============
-
-  This code references YAMAHA's sample codes and data sheets.
-  I respect and thank for all people they made open the informations
-  about YMF7xx cards.
-
-  And this codes heavily based on Jeff Garzik <jgarzik@pobox.com>'s
-  old VIA 82Cxxx driver (via82cxxx.c). I also respect him.
-
-
-DISCLIMER
-=========
-
- This driver is currently at early ALPHA stage. It may cause serious
- damage to your computer when used.
- PLEASE USE IT AT YOUR OWN RISK.
-
-
-ABOUT THIS DRIVER
-=================
-
-  This code enables you to use your YMF724[A-F], YMF740[A-C], YMF744, YMF754
- cards as "SoundBlaster Pro" compatible card. It can only play 22.05kHz /
- 8bit / Stereo samples, control external MIDI port.
- And it can handle AC'97 mixer of these cards.
-
-  If you want to use your card as recent "16-bit" card, you should use
- Alsa or OSS/Linux driver. Ofcource you can write native PCI driver for
- your cards :)
-
-
-INSTALL
-=======
-
-  First, it requires some sound driver modules in Linux kernel 
- (soundcore.o , sound.o, sb.o and uart401.o), so you should enable
- these modules at kernel configuration (please refer some other 
- documentations for configuration of kernel).
- And add the following line in /etc/modules.conf:
-
-  options sb support=1
-
- (NOTE: There are no need any configuration of sb modules)
-
- To compile ymfsb module, type 'make'.
- And copy it to /lib/modules/(kernel_version)/misc, type 'depmod -a'.
- 
-  This module also requires /usr/src/linux/drivers/sound/ac97.[ch].
- If your kernel doesn't have these files, you should upgrade your kernel.
-
-
-USAGE
-=====
-
- # modprobe ymfsb (options)
-
-
-OPTIONS FOR YMFSB
-=================
-
-  io           : SB base address     (0x220, 0x240, 0x260, 0x280)
-  synth_io     : OPL3 base address   (0x388, 0x398, 0x3a0, 0x3a8)
-  dma          : DMA number          (0,1,3)
-  spdif_out    : SPDIF-out flag      (0:disable 1:enable)
-
- These options will change in future...
-
-
-FREQUENCY
-=========
-
-  When playing sounds via this driver, you will hear its pitch is slightly
- lower than original sounds. Since this driver recognizes your card acts
- with 21.739kHz sample rates rather than 22.050kHz (I think it must be
- hardware restriction). So many players become tone deafness.
- To prevent this, you should express some options to your sound player
- that specify correct sample frequency. For example, to play your MP3 file
- correctly with mpg123, specify the frequency like following:
-
-  %  mpg123 -r 21739 foo.mp3
-
-
-SPDIF OUT
-=========
-
-  With installing modules with option 'spdif_out=1', you can enjoy your
- sounds from SPDIF-out of your card (if it had).
- Its Fs is fixed to 48kHz (It never means the sample frequency become 
- up to 48kHz. All sounds via SPDIF-out also 22kHz samples). So your
- digital-in capable components has to be able to handle 48kHz Fs.
-
-
-COPYING
-=======
-
- 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, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
-TODO
-====
- * support for multiple cards
-   (set the different SB_IO,MPU_IO,OPL_IO for each cards)
-
- * support for OPL (dmfm) : There will be no requirements... :-<
-
-
-AUTHOR
-======
-
- Daisuke Nagano <breeze.nagano@nifty.ne.jp>
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/usb-serial.txt linux.19p6/Documentation/usb/usb-serial.txt
--- linux.vanilla/Documentation/usb/usb-serial.txt	Mon Dec 11 22:12:32 2000
+++ linux.19p6/Documentation/usb/usb-serial.txt	Tue Dec 19 02:30:27 2000
@@ -185,6 +185,15 @@
   -- Add everything else that is missing :)
 
 
+Empeg empeg-car Mark I/II Driver (empeg.c)
+
+  This is an experimental driver to provide connectivity support for the
+  client synchronization tools for an Empeg empeg-car mp3 player.
+
+  The driver is still pretty new, so some testing 'in the wild' would be
+  helpful. :)
+
+
 Generic Serial driver
 
   If your device is not one of the above listed devices, compatible with
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/MAINTAINERS linux.19p6/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Mon Dec 11 22:12:32 2000
+++ linux.19p6/MAINTAINERS	Sat Dec 30 19:46:46 2000
@@ -251,7 +251,7 @@
 
 DC390/AM53C974 SCSI driver
 P:	Kurt Garloff
-M:	kurt@garloff.de
+M:	garloff@suse.de
 W:	http://www.garloff.de/kurt/linux/dc390/
 S:	Maintained
 
@@ -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.19p6/Makefile
--- linux.vanilla/Makefile	Mon Dec 11 22:12:32 2000
+++ linux.19p6/Makefile	Wed Jan  3 13:25:16 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 18
-EXTRAVERSION =
+SUBLEVEL = 19
+EXTRAVERSION = pre6
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -25,7 +25,7 @@
 #
 #	foo-bar-gcc for cross builds
 #	gcc272 for Debian's old compiler for kernels
-#	kgcc for Conectiva and Red Hat 7
+#	kgcc for Conectiva, Mandrake and Red Hat 7
 #	otherwise 'cc'
 #
 CC	=$(shell if [ -n "$(CROSS_COMPILE)" ]; then echo $(CROSS_COMPILE)gcc; else \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/alpha_ksyms.c linux.19p6/arch/alpha/kernel/alpha_ksyms.c
--- linux.vanilla/arch/alpha/kernel/alpha_ksyms.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/alpha/kernel/alpha_ksyms.c	Wed Jan  3 13:34:35 2001
@@ -169,6 +169,7 @@
 EXPORT_SYMBOL(global_bh_lock);
 EXPORT_SYMBOL(global_bh_count);
 EXPORT_SYMBOL(synchronize_bh);
+EXPORT_SYMBOL(alpha_bh_lock);
 EXPORT_SYMBOL(global_irq_holder);
 EXPORT_SYMBOL(__global_cli);
 EXPORT_SYMBOL(__global_sti);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/irq.c linux.19p6/arch/alpha/kernel/irq.c
--- linux.vanilla/arch/alpha/kernel/irq.c	Sun Jun 11 21:44:09 2000
+++ linux.19p6/arch/alpha/kernel/irq.c	Wed Jan  3 13:34:35 2001
@@ -385,6 +385,7 @@
 /* This protects BH software state (masks, things like that). */
 atomic_t global_bh_lock = ATOMIC_INIT(0);
 atomic_t global_bh_count = ATOMIC_INIT(0);
+spinlock_t alpha_bh_lock = SPIN_LOCK_UNLOCKED;
 
 static void *previous_irqholder = NULL;
 
@@ -650,6 +651,7 @@
 void
 synchronize_bh(void)
 {
+	mb();
 	if (atomic_read(&global_bh_count) && !in_interrupt())
 			wait_on_bh();
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/process.c linux.19p6/arch/alpha/kernel/process.c
--- linux.vanilla/arch/alpha/kernel/process.c	Fri Apr 21 12:45:44 2000
+++ linux.19p6/arch/alpha/kernel/process.c	Wed Jan  3 13:37:59 2001
@@ -249,8 +249,11 @@
 	alpha_mv.kill_arch(LINUX_REBOOT_CMD_POWER_OFF, NULL);
 }
 
-void show_regs(struct pt_regs * regs)
+void __show_regs(struct pt_regs * regs)
 {
+	extern void dik_show_trace(unsigned long *);
+
+	printk("\nCPU: %d", smp_processor_id());
 	printk("\nps: %04lx pc: [<%016lx>]\n", regs->ps, regs->pc);
 	printk("rp: [<%016lx>] sp: %p\n", regs->r26, regs+1);
 	printk(" r0: %016lx  r1: %016lx  r2: %016lx  r3: %016lx\n",
@@ -265,6 +268,15 @@
 	       regs->r23, regs->r24, regs->r25, regs->r26);
 	printk("r27: %016lx r28: %016lx r29: %016lx hae: %016lx\n",
 	       regs->r27, regs->r28, regs->gp, regs->hae);
+	dik_show_trace(regs+1);
+}
+
+void show_regs(struct pt_regs * regs)
+{
+	__show_regs(regs);
+#ifdef CONFIG_SMP
+	smp_show_regs();
+#endif
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/ptrace.c linux.19p6/arch/alpha/kernel/ptrace.c
--- linux.vanilla/arch/alpha/kernel/ptrace.c	Fri Apr 21 12:45:44 2000
+++ linux.19p6/arch/alpha/kernel/ptrace.c	Fri Dec 22 12:07:48 2000
@@ -543,7 +543,7 @@
 		ret = -EPERM;
 		if (child == current)
 			goto out;
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		     (current->uid != child->euid) ||
 		     (current->uid != child->suid) ||
 		     (current->uid != child->uid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/smp.c linux.19p6/arch/alpha/kernel/smp.c
--- linux.vanilla/arch/alpha/kernel/smp.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/alpha/kernel/smp.c	Wed Jan  3 13:37:59 2001
@@ -95,8 +95,7 @@
 smp_store_cpu_info(int cpuid)
 {
 	cpu_data[cpuid].loops_per_jiffy = loops_per_jiffy;
-	cpu_data[cpuid].last_asn
-	  = (cpuid << WIDTH_HARDWARE_ASN) + ASN_FIRST_VERSION;
+	cpu_data[cpuid].last_asn = ASN_FIRST_VERSION;
 
         cpu_data[cpuid].irq_count = 0;
         cpu_data[cpuid].bh_count = 0;
@@ -739,7 +738,14 @@
 
 			/* At this point the structure may be gone unless
 			   wait is true.  */
-			(*func)(info);
+			{
+			static void ipi_show_regs(void *);
+
+			if (func != ipi_show_regs)
+				(*func)(info);
+			else
+				(*func)((void *) regs);
+			}
 
 			/* Notify the sending CPU that the task is done.  */
 			mb();
@@ -856,6 +862,21 @@
 }
 
 static void
+ipi_show_regs(void * param)
+{
+	struct pt_regs *regs = (struct pt_regs *) param;
+
+	__show_regs(regs);
+}
+
+void
+smp_show_regs(void)
+{
+	if (smp_call_function(ipi_show_regs, NULL, 1, 1))
+		printk(KERN_CRIT "smp_show_regs: timed out\n");
+}
+
+static void
 ipi_flush_tlb_all(void *ignored)
 {
 	tbia();
@@ -879,6 +900,8 @@
 	struct mm_struct *mm = (struct mm_struct *) x;
 	if (mm == current->mm)
 		flush_tlb_current(mm);
+	else
+		flush_tlb_other(mm);
 }
 
 void
@@ -886,10 +909,17 @@
 {
 	if (mm == current->mm) {
 		flush_tlb_current(mm);
-		if (atomic_read(&mm->count) == 1)
+		if (atomic_read(&mm->count) == 1) {
+			int i, cpu, this_cpu = smp_processor_id();
+			for (i = 0; i < smp_num_cpus; i++) {
+				cpu = cpu_logical_map(i);
+				if (cpu == this_cpu)
+					continue;
+				mm->context[cpu] = 0;
+			}
 			return;
-	} else
-		flush_tlb_other(mm);
+		}
+	}
 
 	if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) {
 		printk(KERN_CRIT "flush_tlb_mm: timed out\n");
@@ -906,8 +936,12 @@
 ipi_flush_tlb_page(void *x)
 {
 	struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x;
-	if (data->mm == current->mm)
-		flush_tlb_current_page(data->mm, data->vma, data->addr);
+	struct mm_struct * mm = data->mm;
+
+	if (mm == current->mm)
+		flush_tlb_current_page(mm, data->vma, data->addr);
+	else
+		flush_tlb_other(mm);
 }
 
 void
@@ -918,10 +952,17 @@
 
 	if (mm == current->mm) {
 		flush_tlb_current_page(mm, vma, addr);
-		if (atomic_read(&current->mm->count) == 1)
+		if (atomic_read(&current->mm->count) == 1) {
+			int i, cpu, this_cpu = smp_processor_id();
+			for (i = 0; i < smp_num_cpus; i++) {
+				cpu = cpu_logical_map(i);
+				if (cpu == this_cpu)
+					continue;
+				mm->context[cpu] = 0;
+			}
 			return;
-	} else
-		flush_tlb_other(mm);
+		}
+	}
        
 	data.vma = vma;
 	data.mm = mm;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/traps.c linux.19p6/arch/alpha/kernel/traps.c
--- linux.vanilla/arch/alpha/kernel/traps.c	Sun Jun 11 21:44:09 2000
+++ linux.19p6/arch/alpha/kernel/traps.c	Wed Jan  3 13:37:59 2001
@@ -269,7 +269,7 @@
 	}
 }
 
-static void
+void
 dik_show_trace(unsigned long *sp)
 {
 	int i = 1;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/mm/fault.c linux.19p6/arch/alpha/mm/fault.c
--- linux.vanilla/arch/alpha/mm/fault.c	Sat Sep  9 18:42:32 2000
+++ linux.19p6/arch/alpha/mm/fault.c	Wed Jan  3 13:33:37 2001
@@ -41,7 +41,7 @@
 get_new_mmu_context(struct task_struct *p, struct mm_struct *mm)
 {
 	unsigned long new = __get_new_mmu_context();
-	mm->context = new;
+	mm->context[smp_processor_id()] = new;
 	p->tss.asn = new & HARDWARE_ASN_MASK;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dec21285.c linux.19p6/arch/arm/kernel/dec21285.c
--- linux.vanilla/arch/arm/kernel/dec21285.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/dec21285.c	Wed Jan  3 00:57:43 2001
@@ -169,9 +169,14 @@
 __initfunc(void pcibios_init(void))
 {
 	unsigned int mem_size = (unsigned int)high_memory - PAGE_OFFSET;
+	unsigned int mem_mask;
 	unsigned long cntl;
 
-	*CSR_SDRAMBASEMASK    = (mem_size - 1) & 0x0ffc0000;
+	for (mem_mask = 0x00100000; mem_mask < 0x10000000; mem_mask <<= 1)
+		if (mem_mask >= mem_size)
+			break;		
+
+	*CSR_SDRAMBASEMASK    = (mem_mask - 1) & 0x0ffc0000;
 	*CSR_SDRAMBASEOFFSET  = 0;
 	*CSR_ROMBASEMASK      = 0x80000000;
 	*CSR_CSRBASEMASK      = 0;
@@ -224,7 +229,7 @@
 	*CSR_PCISDRAMBASE     = virt_to_bus((void *)PAGE_OFFSET);
 	*CSR_PCIROMBASE       = 0;
 	*CSR_PCICMD           = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-				PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK |
+				PCI_COMMAND_MASTER |
 				PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
 				(1 << 31) | (1 << 29) | (1 << 28) | (1 << 24);
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-armv.S linux.19p6/arch/arm/kernel/entry-armv.S
--- linux.vanilla/arch/arm/kernel/entry-armv.S	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/entry-armv.S	Wed Jan  3 00:57:43 2001
@@ -322,7 +322,8 @@
 
 		.macro	restore_user_regs
 		ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
-		msr	cpsr_c, #I_BIT | MODE_SVC	@ disable IRQs
+		mov	r1, #I_BIT | MODE_SVC		@ disable IRQs
+		msr	cpsr_c, r1
 		msr	spsr, r0			@ save in spsr_svc
 		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
 		mov	r0, r0
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/head-armv.S linux.19p6/arch/arm/kernel/head-armv.S
--- linux.vanilla/arch/arm/kernel/head-armv.S	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/head-armv.S	Wed Jan  3 00:57:43 2001
@@ -388,6 +388,7 @@
 __arch_types_end:
 
 #ifdef CONFIG_DEBUG_LL
+		.text
 /*
  * Some debugging routines (useful if you've got MM problems and
  * printk isn't working).  For DEBUGGING ONLY!!!  Do not leave
@@ -441,6 +442,10 @@
 		.endm
 
 #elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+
+#include <asm/dec21285.h>
+#include <asm/hardware.h>
+
 #ifndef CONFIG_DEBUG_DC21285_PORT
 	/* For NetWinder debugging */
 		.macro	addruart,rx
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/hw-footbridge.c linux.19p6/arch/arm/kernel/hw-footbridge.c
--- linux.vanilla/arch/arm/kernel/hw-footbridge.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/hw-footbridge.c	Wed Jan  3 00:57:43 2001
@@ -205,7 +205,8 @@
 
 		pci_read_config_word(dev, PCI_STATUS, &status);
 		if (status & 0xf900) {
-			printk(KERN_DEBUG "PCI: [%04X:%04X] status = %X\n",
+			printk(KERN_DEBUG "PCI: %02x:%02x.%d [%04X:%04X] status = 0x%04X\n",
+				dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
 				dev->vendor, dev->device, status);
 
 			pci_write_config_word(dev, PCI_STATUS, status & 0xf900);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/process.c linux.19p6/arch/arm/kernel/process.c
--- linux.vanilla/arch/arm/kernel/process.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/process.c	Wed Jan  3 00:57:43 2001
@@ -228,10 +228,8 @@
 
 void flush_thread(void)
 {
-	int i;
-
-	for (i = 0; i < NR_DEBUGS; i++)
-		current->tss.debug[i] = 0;
+	memset(&current->tss.debug, 0, sizeof(struct debug_info));
+	memset(&current->tss.fpstate, 0, sizeof(union fp_state));
 	current->used_math = 0;
 	current->flags &= ~PF_USEDFPU;
 }
@@ -263,12 +261,10 @@
  */
 int dump_fpu (struct pt_regs *regs, struct user_fp *fp)
 {
-	int fpvalid = 0;
-
 	if (current->used_math)
-		memcpy (fp, &current->tss.fpstate.soft, sizeof (fp));
+		memcpy (fp, &current->tss.fpstate.soft, sizeof (*fp));
 
-	return fpvalid;
+	return current->used_math;
 }
 
 /*
@@ -276,18 +272,20 @@
  */
 void dump_thread(struct pt_regs * regs, struct user * dump)
 {
-	int i;
-
+	struct task_struct *tsk = current;
 	dump->magic = CMAGIC;
-	dump->start_code = current->mm->start_code;
+	dump->start_code = tsk->mm->start_code;
 	dump->start_stack = regs->ARM_sp & ~(PAGE_SIZE - 1);
 
-	dump->u_tsize = (current->mm->end_code - current->mm->start_code) >> PAGE_SHIFT;
-	dump->u_dsize = (current->mm->brk - current->mm->start_data + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	dump->u_tsize = (tsk->mm->end_code - tsk->mm->start_code) >> PAGE_SHIFT;
+	dump->u_dsize = (tsk->mm->brk - tsk->mm->start_data + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	dump->u_ssize = 0;
 
-	for (i = 0; i < NR_DEBUGS; i++)
-		dump->u_debugreg[i] = current->tss.debug[i];  
+	dump->u_debugreg[0] = tsk->tss.debug.bp[0].address;
+	dump->u_debugreg[1] = tsk->tss.debug.bp[1].address;
+	dump->u_debugreg[2] = tsk->tss.debug.bp[0].insn;
+	dump->u_debugreg[3] = tsk->tss.debug.bp[1].insn;
+	dump->u_debugreg[4] = tsk->tss.debug.nsaved;
 
 	if (dump->start_stack < 0x04000000)
 		dump->u_ssize = (0x04000000 - dump->start_stack) >> PAGE_SHIFT;
@@ -310,14 +308,19 @@
 	pid_t __ret;
 
 	__asm__ __volatile__(
-	"mov	r0, %1		@ kernel_thread sys_clone\n"
+	"orr	r0, %1, %2	@ kernel_thread\n"
 "	mov	r1, #0\n"
 	__syscall(clone)"\n"
-"	mov	%0, r0"
+"	movs	%0, r0
+	bne	1f
+	mov	fp, r0
+	mov	r0, %4
+	mov	lr, pc
+	mov	pc, %3
+	b	sys_exit
+1:	"
         : "=r" (__ret)
-        : "Ir" (flags | CLONE_VM) : "r0", "r1");
-	if (__ret == 0)
-		sys_exit((fn)(arg));
+        : "Ir" (flags), "I" (CLONE_VM), "r" (fn), "r" (arg): "r0", "r1", "lr");
 	return __ret;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/ptrace.c linux.19p6/arch/arm/kernel/ptrace.c
--- linux.vanilla/arch/arm/kernel/ptrace.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/ptrace.c	Wed Jan  3 00:57:43 2001
@@ -16,6 +16,9 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 
+
+#define REG_PC	15
+#define REG_PSR	16
 /*
  * does not yet catch signals sent when the child dies.
  * in exit.c or in signal.c.
@@ -25,11 +28,12 @@
  * Breakpoint SWI instruction: SWI &9F0001
  */
 #define BREAKINST	0xef9f0001
-#define PTRACE_GETREGS		12
-#define PTRACE_SETREGS		13
-#define PTRACE_GETFPREGS	14
-#define PTRACE_SETFPREGS	15
 
+/*
+ * Get the address of the live pt_regs for the specified task.
+ * These are saved onto the top kernel stack when the process
+ * is not running.
+ */
 static inline struct pt_regs *
 get_user_regs(struct task_struct *task)
 {
@@ -299,38 +303,42 @@
 	return 0;
 }
 
+#define write_tsk_long(chld, addr, val) write_long((chld), (addr), (val))
+#define read_tsk_long(chld, addr, val)  read_long((chld), (addr), (val))
+
 /*
  * Get value of register `rn' (in the instruction)
  */
-static unsigned long ptrace_getrn (struct task_struct *child, unsigned long insn)
+static unsigned long
+ptrace_getrn(struct task_struct *child, unsigned long insn)
 {
 	unsigned int reg = (insn >> 16) & 15;
 	unsigned long val;
 
+	val = get_stack_long(child, reg);
 	if (reg == 15)
-		val = pc_pointer (get_stack_long (child, reg));
-	else
-		val = get_stack_long (child, reg);
+		val = pc_pointer(val + 8);
+
 
-printk ("r%02d=%08lX ", reg, val);
 	return val;
 }
 
 /*
  * Get value of operand 2 (in an ALU instruction)
  */
-static unsigned long ptrace_getaluop2 (struct task_struct *child, unsigned long insn)
+static unsigned long
+ptrace_getaluop2(struct task_struct *child, unsigned long insn)
 {
 	unsigned long val;
 	int shift;
 	int type;
 
-printk ("op2=");
+
 	if (insn & 1 << 25) {
 		val = insn & 255;
 		shift = (insn >> 8) & 15;
 		type = 3;
-printk ("(imm)");
+
 	} else {
 		val = get_stack_long (child, insn & 15);
 
@@ -340,9 +348,9 @@
 			shift = (insn >> 7) & 31;
 
 		type = (insn >> 5) & 3;
-printk ("(r%02ld)", insn & 15);
+
 	}
-printk ("sh%dx%d", type, shift);
+
 	switch (type) {
 	case 0:	val <<= shift;	break;
 	case 1:	val >>= shift;	break;
@@ -350,27 +358,28 @@
 		val = (((signed long)val) >> shift);
 		break;
 	case 3:
-		__asm__ __volatile__("mov %0, %0, ror %1" : "=r" (val) : "0" (val), "r" (shift));
+		val = (val >> shift) | (val << (32 - shift));
 		break;
 	}
-printk ("=%08lX ", val);
+
 	return val;
 }
 
 /*
  * Get value of operand 2 (in a LDR instruction)
  */
-static unsigned long ptrace_getldrop2 (struct task_struct *child, unsigned long insn)
+static unsigned long
+ptrace_getldrop2(struct task_struct *child, unsigned long insn)
 {
 	unsigned long val;
 	int shift;
 	int type;
 
-	val = get_stack_long (child, insn & 15);
+	val = get_stack_long(child, insn & 15);
 	shift = (insn >> 7) & 31;
 	type = (insn >> 5) & 3;
 
-printk ("op2=r%02ldsh%dx%d", insn & 15, shift, type);
+
 	switch (type) {
 	case 0:	val <<= shift;	break;
 	case 1:	val >>= shift;	break;
@@ -378,115 +387,88 @@
 		val = (((signed long)val) >> shift);
 		break;
 	case 3:
-		__asm__ __volatile__("mov %0, %0, ror %1" : "=r" (val) : "0" (val), "r" (shift));
+		val = (val >> shift) | (val << (32 - shift));
 		break;
 	}
-printk ("=%08lX ", val);
+
 	return val;
 }
-#undef pc_pointer
-#define pc_pointer(x) ((x) & 0x03fffffc)
-int ptrace_set_bpt (struct task_struct *child)
-{
-	unsigned long insn, pc, alt;
-	int i, nsaved = 0, res;
-
-	pc = pc_pointer (get_stack_long (child, 15/*REG_PC*/));
-
-	res = read_long (child, pc, &insn);
-	if (res < 0)
-		return res;
-
-	child->tss.debug[nsaved++] = alt = pc + 4;
-printk ("ptrace_set_bpt: insn=%08lX pc=%08lX ", insn, pc);
-	switch (insn & 0x0e100000) {
+
+static unsigned long
+get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
+{
+	unsigned long alt = 0;
+
+	switch (insn & 0x0e000000) {
 	case 0x00000000:
-	case 0x00100000:
-	case 0x02000000:
-	case 0x02100000: /* data processing */
-		printk ("data ");
-		switch (insn & 0x01e0f000) {
-		case 0x0000f000:
-			alt = ptrace_getrn(child, insn) & ptrace_getaluop2(child, insn);
-			break;
-		case 0x0020f000:
-			alt = ptrace_getrn(child, insn) ^ ptrace_getaluop2(child, insn);
-			break;
-		case 0x0040f000:
-			alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn);
-			break;
-		case 0x0060f000:
-			alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn);
-			break;
-		case 0x0080f000:
-			alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn);
-			break;
-		case 0x00a0f000:
-			alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn) +
-				(get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
-			break;
-		case 0x00c0f000:
-			alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn) +
-				(get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
-			break;
-		case 0x00e0f000:
-			alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn) +
-				(get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
-			break;
-		case 0x0180f000:
-			alt = ptrace_getrn(child, insn) | ptrace_getaluop2(child, insn);
-			break;
-		case 0x01a0f000:
-			alt = ptrace_getaluop2(child, insn);
-			break;
-		case 0x01c0f000:
-			alt = ptrace_getrn(child, insn) & ~ptrace_getaluop2(child, insn);
-			break;
-		case 0x01e0f000:
-			alt = ~ptrace_getaluop2(child, insn);
+	case 0x02000000: {
+		/*
+		 * data processing
+		 */
+		long aluop1, aluop2, ccbit;
+
+		if ((insn & 0xf000) != 0xf000)
 			break;
+
+
+
+		aluop1 = ptrace_getrn(child, insn);
+		aluop2 = ptrace_getaluop2(child, insn);
+		ccbit  = get_stack_long(child, REG_PSR) & CC_C_BIT ? 1 : 0;
+
+		switch (insn & 0x01e00000) {
+		case 0x00000000: alt = aluop1 & aluop2;		break;
+		case 0x00200000: alt = aluop1 ^ aluop2;		break;
+		case 0x00400000: alt = aluop1 - aluop2;		break;
+		case 0x00600000: alt = aluop2 - aluop1;		break;
+		case 0x00800000: alt = aluop1 + aluop2;		break;
+		case 0x00a00000: alt = aluop1 + aluop2 + ccbit;	break;
+		case 0x00c00000: alt = aluop1 - aluop2 + ccbit;	break;
+		case 0x00e00000: alt = aluop2 - aluop1 + ccbit;	break;
+		case 0x01800000: alt = aluop1 | aluop2;		break;
+		case 0x01a00000: alt = aluop2;			break;
+		case 0x01c00000: alt = aluop1 & ~aluop2;	break;
+		case 0x01e00000: alt = ~aluop2;			break;
 		}
 		break;
+	}
 
-	case 0x04100000: /* ldr */
-		if ((insn & 0xf000) == 0xf000) {
-printk ("ldr ");
-			alt = ptrace_getrn(child, insn);
+	case 0x04000000:
+	case 0x06000000:
+		/*
+		 * ldr
+		 */
+		if ((insn & 0x0010f000) == 0x0010f000) {
+			unsigned long base;
+
+			base = ptrace_getrn(child, insn);
 			if (insn & 1 << 24) {
-				if (insn & 1 << 23)
-					alt += ptrace_getldrop2 (child, insn);
+				long aluop2;
+
+				if (insn & 0x02000000)
+					aluop2 = ptrace_getldrop2(child, insn);
 				else
-					alt -= ptrace_getldrop2 (child, insn);
-			}
-			if (read_long (child, alt, &alt) < 0)
-				alt = pc + 4; /* not valid */
-			else
-				alt = pc_pointer (alt);
-		}
-		break;
+					aluop2 = insn & 0xfff;
 
-	case 0x06100000: /* ldr imm */
-		if ((insn & 0xf000) == 0xf000) {
-printk ("ldrimm ");
-			alt = ptrace_getrn(child, insn);
-			if (insn & 1 << 24) {
 				if (insn & 1 << 23)
-					alt += insn & 0xfff;
+					base += aluop2;
 				else
-					alt -= insn & 0xfff;
+					base -= aluop2;
 			}
-			if (read_long (child, alt, &alt) < 0)
-				alt = pc + 4; /* not valid */
-			else
-				alt = pc_pointer (alt);
+
+			if (read_tsk_long(child, base, &alt) == 0)
+				alt = pc_pointer(alt);
 		}
 		break;
 
-	case 0x08100000: /* ldm */
-		if (insn & (1 << 15)) {
+	case 0x08000000:
+		/*
+		 * ldm
+		 */
+		if ((insn & 0x00108000) == 0x00108000) {
 			unsigned long base;
-			int nr_regs;
-printk ("ldm ");
+			unsigned int nr_regs;
+
 
 			if (insn & (1 << 23)) {
 				nr_regs = insn & 65535;
@@ -506,23 +488,23 @@
 					nr_regs = 0;
 			}
 
-			base = ptrace_getrn (child, insn);
+			base = ptrace_getrn(child, insn);
 
-			if (read_long (child, base + nr_regs, &alt) < 0)
-				alt = pc + 4; /* not valid */
-			else
-				alt = pc_pointer (alt);
+			if (read_tsk_long(child, base + nr_regs, &alt) == 0)
+				alt = pc_pointer(alt);
 			break;
 		}
 		break;
 
-	case 0x0a000000:
-	case 0x0a100000: { /* bl or b */
+	case 0x0a000000: {
+		/*
+		 * bl or b
+		 */
 		signed long displ;
-printk ("b/bl ");
+
 		/* It's a branch/branch link: instead of trying to
 		 * figure out whether the branch will be taken or not,
-		 * we'll put a breakpoint at either location.  This is
+		 * we'll put a breakpoint at both locations.  This is
 		 * simpler, more reliable, and probably not a whole lot
 		 * slower than the alternative approach of emulating the
 		 * branch.
@@ -596,7 +578,7 @@
 	if (request == PTRACE_ATTACH) {
 		if (child == current)
 			goto out;
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->suid) ||
 		    (current->uid != child->uid) ||
@@ -737,44 +719,103 @@
 					ret = 0;
 				}
 			}
-			goto out;
+			break;
 		}
 
+		/*
+		 * Get the child FPU state.
+		 */
 		case PTRACE_GETFPREGS: 
-			/* Get the child FPU state. */
-			ret = 0;
-			if (copy_to_user((void *)data, &child->tss.fpstate,
-					 sizeof(struct user_fp)))
-				ret = -EFAULT;
-			goto out;
-		
-		case PTRACE_SETFPREGS:
-			/* Set the child FPU state. */
-			ret = 0;
-			if (copy_from_user(&child->tss.fpstate, (void *)data,
-					   sizeof(struct user_fp)))
-				ret = -EFAULT;
-			goto out;
+			ret = -EIO;
+			if (!access_ok(VERIFY_WRITE, (void *)data, sizeof(struct user_fp)))
+				break;
 
-		case PTRACE_DETACH:				/* detach a process that was attached. */
+			/* we should check child->used_math here */
+			ret = __copy_to_user((void *)data, &child->tss.fpstate,
+					     sizeof(struct user_fp)) ? -EFAULT : 0;
+			break;
+
+		/*
+		 * Set the child FPU state.
+		 */
+		case PTRACE_SETFPREGS:
 			ret = -EIO;
-			if ((unsigned long) data > _NSIG)
-				goto out;
-			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
-			wake_up_process (child);
-			child->exit_code = data;
-			REMOVE_LINKS(child);
-			child->p_pptr = child->p_opptr;
-			SET_LINKS(child);
-			/* make sure single-step breakpoint is gone. */
-			ptrace_cancel_bpt (child);
-			ret = 0;
-			goto out;
+			if (!access_ok(VERIFY_READ, (void *)data, sizeof(struct user_fp)))
+				break;
+
+			child->used_math = 1;
+			ret = __copy_from_user(&child->tss.fpstate, (void *)data,
+					   sizeof(struct user_fp)) ? -EFAULT : 0;
+			break;
 
 		default:
 			ret = -EIO;
+			break;
+	}
+
+	return ret;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+	struct task_struct *child;
+	int ret;
+
+	lock_kernel();
+	ret = -EPERM;
+	if (request == PTRACE_TRACEME) {
+		/* are we already being traced? */
+		if (current->flags & PF_PTRACED)
+			goto out;
+		/* set the ptrace bit in the process flags. */
+		current->flags |= PF_PTRACED;
+		ret = 0;
+		goto out;
+	}
+	ret = -ESRCH;
+	if (!(child = find_task_by_pid(pid)))
+		goto out;
+
+	ret = -EPERM;
+	if (pid == 1)		/* you may not mess with init */
+		goto out;
+
+	if (request == PTRACE_ATTACH) {
+		if (child == current)
+			goto out;
+		if ((!child->dumpable ||
+		    (current->uid != child->euid) ||
+		    (current->uid != child->suid) ||
+		    (current->uid != child->uid) ||
+	 	    (current->gid != child->egid) ||
+	 	    (current->gid != child->sgid) ||
+	 	    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
 			goto out;
+		/* the same process cannot be attached many times */
+		if (child->flags & PF_PTRACED)
+			goto out;
+		child->flags |= PF_PTRACED;
+
+		if (child->p_pptr != current) {
+			REMOVE_LINKS(child);
+			child->p_pptr = current;
+			SET_LINKS(child);
+		}
+
+		send_sig(SIGSTOP, child, 1);
+		ret = 0;
+		goto out;
 	}
+	ret = -ESRCH;
+	if (!(child->flags & PF_PTRACED))
+		goto out;
+	if (child->state != TASK_STOPPED && request != PTRACE_KILL)
+		goto out;
+	if (child->p_pptr != current)
+		goto out;
+
+	ret = do_ptrace(request, child, addr, data);
+
 out:
 	unlock_kernel();
 	return ret;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/signal.c linux.19p6/arch/arm/kernel/signal.c
--- linux.vanilla/arch/arm/kernel/signal.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/kernel/signal.c	Wed Jan  3 00:57:43 2001
@@ -539,6 +539,7 @@
 				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
+				single_stepping |= ptrace_cancel_bpt(current);
 				continue;
 
 			case SIGQUIT: case SIGILL: case SIGTRAP:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/delay.S linux.19p6/arch/arm/lib/delay.S
--- linux.vanilla/arch/arm/lib/delay.S	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/lib/delay.S	Wed Jan  3 00:57:43 2001
@@ -9,17 +9,24 @@
 
 LC0:		.word	SYMBOL_NAME(loops_per_jiffy)
 
+/*
+ * 0 <= r0 <= 2000
+ */
 ENTRY(udelay)
-		mov	r2, #0x1000
-		orr	r2, r2, #0x00c6
+		mov	r2,     #0x00006800
+		orr	r2, r2, #0x000000db
 		mul	r1, r0, r2
 		ldr	r2, LC0
 		ldr	r2, [r2]
 		mov	r1, r1, lsr #11
 		mov	r2, r2, lsr #11
 		mul	r0, r1, r2
-		movs	r0, r0, lsr #10
+		movs	r0, r0, lsr #6
 		RETINSTR(moveq,pc,lr)
+
+/*
+ * loops = (r0 * 0x10c6 * 100 * loops_per_jiffie) / 2^32
+ */
 
 @ Delay routine
 ENTRY(__delay)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/memset.S linux.19p6/arch/arm/lib/memset.S
--- linux.vanilla/arch/arm/lib/memset.S	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/arm/lib/memset.S	Wed Jan  3 00:57:43 2001
@@ -1,88 +1,80 @@
 /*
- * linux/arch/arm/lib/memset.S
+ *  linux/arch/arm/lib/memset.S
  *
- * Copyright (C) 1995-1999 Russell King
+ *  Copyright (C) 1995-2000 Russell King
  *
- * ASM optimised string functions
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  *
+ *  ASM optimised string functions
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
-#include "constants.h"
 
-		.text
-		.align	5
-ENTRY(memset)
-		mov	r3, r0
-		cmp	r2, #16
-		blt	6f
-		ands	ip, r3, #3
-		beq	1f
-		cmp	ip, #2
-		strltb	r1, [r3], #1			@ Align destination
-		strleb	r1, [r3], #1
-		strb	r1, [r3], #1
-		rsb	ip, ip, #4
-		sub	r2, r2, ip
-1:		orr	r1, r1, r1, lsl #8
-		orr	r1, r1, r1, lsl #16
-		cmp	r2, #256
-		blt	4f
-		stmfd	sp!, {r4, r5, lr}
-		mov	r4, r1
-		mov	r5, r1
-		mov	lr, r1
-		mov	ip, r2, lsr #6
-		sub	r2, r2, ip, lsl #6
-2:		stmia	r3!, {r1, r4, r5, lr}		@ 64 bytes at a time.
-		stmia	r3!, {r1, r4, r5, lr}
-		stmia	r3!, {r1, r4, r5, lr}
-		stmia	r3!, {r1, r4, r5, lr}
-		subs	ip, ip, #1
-		bne	2b
-		teq	r2, #0
-		LOADREGS(eqfd, sp!, {r4, r5, pc})	@ Now <64 bytes to go.
-		tst	r2, #32
-		stmneia	r3!, {r1, r4, r5, lr}
-		stmneia	r3!, {r1, r4, r5, lr}
-		tst	r2, #16
-		stmneia	r3!, {r1, r4, r5, lr}
-		ldmia	sp!, {r4, r5}
-3:		tst	r2, #8
-		stmneia	r3!, {r1, lr}
-		tst	r2, #4
-		strne	r1, [r3], #4
-		tst	r2, #2
-		strneb	r1, [r3], #1
-		strneb	r1, [r3], #1
-		tst	r2, #1
-		strneb	r1, [r3], #1
-		LOADREGS(fd, sp!, {pc})
+	.text
+	.align	5
+	.word	0
 
-4:		movs	ip, r2, lsr #3
-		beq	3b
-		sub	r2, r2, ip, lsl #3
-		stmfd	sp!, {lr}
-		mov	lr, r1
-		subs	ip, ip, #4
-5:		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		subges	ip, ip, #4
-		bge	5b
-		tst	ip, #2
-		stmneia	r3!, {r1, lr}
-		stmneia	r3!, {r1, lr}
-		tst	ip, #1
-		stmneia	r3!, {r1, lr}
-		teq	r2, #0
-		LOADREGS(eqfd, sp!, {pc})
-		b	3b
+1:	subs	r2, r2, #4		@ 1 do we have enough
+	blt	5f			@ 1 bytes to align with?
+	cmp	r3, #2			@ 1
+	strltb	r1, [r0], #1		@ 1
+	strleb	r1, [r0], #1		@ 1
+	strb	r1, [r0], #1		@ 1
+	add	r2, r2, r3		@ 1 (r2 = r2 - (4 - r3))
+/*
+ * The pointer is now aligned and the length is adjusted.  Try doing the
+ * memzero again.
+ */
 
-6:		subs	r2, r2, #1
-		strgeb	r1, [r3], #1
-		bgt	6b
-		RETINSTR(mov, pc, lr)
+ENTRY(memset)
+	ands	r3, r0, #3		@ 1 unaligned?
+	bne	1b			@ 1
+/*
+ * we know that the pointer in r0 is aligned to a word boundary.
+ */
+	orr	r1, r1, r1, lsl #8
+	orr	r1, r1, r1, lsl #16
+	mov	r3, r1
+	cmp	r2, #16
+	blt	4f
+/*
+ * We need an extra register for this loop - save the return address and
+ * use the LR
+ */
+	str	lr, [sp, #-4]!
+	mov	ip, r1
+	mov	lr, r1
 
+2:	subs	r2, r2, #64
+	stmgeia	r0!, {r1, r3, ip, lr}	@ 64 bytes at a time.
+	stmgeia	r0!, {r1, r3, ip, lr}
+	stmgeia	r0!, {r1, r3, ip, lr}
+	stmgeia	r0!, {r1, r3, ip, lr}
+	bgt	2b
+	LOADREGS(eqfd, sp!, {pc})	@ Now <64 bytes to go.
+/*
+ * No need to correct the count; we're only testing bits from now on
+ */
+	tst	r2, #32
+	stmneia	r0!, {r1, r3, ip, lr}
+	stmneia	r0!, {r1, r3, ip, lr}
+	tst	r2, #16
+	stmneia	r0!, {r1, r3, ip, lr}
+	ldr	lr, [sp], #4
 
+4:	tst	r2, #8
+	stmneia	r0!, {r1, r3}
+	tst	r2, #4
+	strne	r1, [r0], #4
+/*
+ * When we get here, we've got less than 4 bytes to zero.  We
+ * may have an unaligned pointer as well.
+ */
+5:	tst	r2, #2
+	strneb	r1, [r0], #1
+	strneb	r1, [r0], #1
+	tst	r2, #1
+	strneb	r1, [r0], #1
+	RETINSTR(mov,pc,lr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/boot/setup.S linux.19p6/arch/i386/boot/setup.S
--- linux.vanilla/arch/i386/boot/setup.S	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/i386/boot/setup.S	Sat Dec 30 19:49:42 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:
+	xor     ebx, ebx
+	mov     di,#0x2d0
+
+jmpe820:
+	mov     eax,#0x0000e820
+	mov     edx,#0x534d4150		! Some biosen trash edx each call
+	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.19p6/arch/i386/config.in
--- linux.vanilla/arch/i386/config.in	Mon Dec 11 22:12:33 2000
+++ linux.19p6/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/cpuid.c linux.19p6/arch/i386/kernel/cpuid.c
--- linux.vanilla/arch/i386/kernel/cpuid.c	Mon Dec 11 22:12:33 2000
+++ linux.19p6/arch/i386/kernel/cpuid.c	Thu Dec 21 23:02:19 2000
@@ -152,6 +152,7 @@
 
 static void cpuid_exit(void)
 {
+  unregister_chrdev(CPUID_MAJOR, "cpu/cpuid");
 }
 
 module_init(cpuid_init);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/entry.S linux.19p6/arch/i386/kernel/entry.S
--- linux.vanilla/arch/i386/kernel/entry.S	Sat Sep  9 18:42:32 2000
+++ linux.19p6/arch/i386/kernel/entry.S	Tue Dec 19 02:14:13 2000
@@ -267,16 +267,18 @@
 	pushl %ebx
 	cld
 	movl %es,%cx
-	xchgl %eax, ORIG_EAX(%esp)	# orig_eax (get the error code. )
+	movl ORIG_EAX(%esp), %esi	# get the error code
+	movl ES(%esp), %edi		# get the function address
+	movl %eax, ORIG_EAX(%esp)
+	movl %ecx, ES(%esp)
 	movl %esp,%edx
-	xchgl %ecx, ES(%esp)		# get the address and save es.
-	pushl %eax			# push the error code
-	pushl %edx
+	pushl %esi			# push the error code
+	pushl %edx			# push the pt_regs pointer
 	movl $(__KERNEL_DS),%edx
 	movl %dx,%ds
 	movl %dx,%es
 	GET_CURRENT(%ebx)
-	call *%ecx
+	call *%edi
 	addl $8,%esp
 	jmp ret_from_exception
 
@@ -331,11 +333,6 @@
 ENTRY(coprocessor_segment_overrun)
 	pushl $0
 	pushl $ SYMBOL_NAME(do_coprocessor_segment_overrun)
-	jmp error_code
-
-ENTRY(reserved)
-	pushl $0
-	pushl $ SYMBOL_NAME(do_reserved)
 	jmp error_code
 
 ENTRY(double_fault)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/microcode.c linux.19p6/arch/i386/kernel/microcode.c
--- linux.vanilla/arch/i386/kernel/microcode.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/i386/kernel/microcode.c	Sat Dec 30 19:42:45 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,12 @@
  *		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
+ *	1.07	13 Dec 2000, Tigran Aivazian <tigran@veritas.com>
+ *		More bugfixes backported from 2.4
+ *	1.08	27 Dec 2000, Tigran Aivazian <tigran@veritas.com>
+ *		Fix: X86_FEATURE_30 was used incorrectly (in a 2.4 manner)
  */
 
 #include <linux/init.h>
@@ -37,7 +44,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 +51,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.08"
 
-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 +81,6 @@
 	write:		microcode_write,
 	ioctl:		microcode_ioctl,
 	open:		microcode_open,
-	release:	microcode_release,
 };
 
 static struct miscdevice microcode_dev = {
@@ -106,26 +91,23 @@
 
 int __init microcode_init(void)
 {
-	int error = 0;
-
 	if (misc_register(&microcode_dev) < 0) {
-		printk(KERN_WARNING 
-			"microcode: can't misc_register on minor=%d\n",
+		printk(KERN_ERR "microcode: can't misc_register on minor=%d\n",
 			MICROCODE_MINOR);
-		error = 1;
+		return -EINVAL;
 	}
-	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 +115,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 +136,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 +156,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 ||
+		(c->x86_capability & X86_FEATURE_30) ) { /* 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 +182,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 +209,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,19 +229,27 @@
 		}
 
 	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)
 {
+	ssize_t err = 0;
+
+	down(&microcode_sem);
 	if (*ppos >= mc_fsize)
-		return 0;
+		goto out;
 	if (*ppos + len > mc_fsize)
 		len = mc_fsize - *ppos;
+	err = -EFAULT;
 	if (copy_to_user(buf, mc_applied + *ppos, len))
-		return -EFAULT;
+		goto out;
 	*ppos += len;
-	return len;
+	err = len;
+out:
+	up(&microcode_sem);
+	return err;
 }
 
 static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
@@ -279,17 +261,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 +293,7 @@
 out_fsize:
 	vfree(microcode);
 out_unlock:
-	unlock_kernel();
+	up(&microcode_sem);
 	return ret;
 }
 
@@ -319,15 +302,19 @@
 {
 	switch(cmd) {
 		case MICROCODE_IOCFREE:
+			down(&microcode_sem);
 			if (mc_applied) {
+				int bytes = smp_num_cpus * sizeof(struct microcode);
+
 				memset(mc_applied, 0, mc_fsize);
 				kfree(mc_applied);
 				mc_applied = NULL;
-				printk(KERN_WARNING 
-					"microcode: freed %d bytes\n", mc_fsize);
 				mc_fsize = 0;
+				printk(KERN_WARNING "microcode: freed %d bytes\n", bytes);
+				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/msr.c linux.19p6/arch/i386/kernel/msr.c
--- linux.vanilla/arch/i386/kernel/msr.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/i386/kernel/msr.c	Thu Dec 21 23:02:03 2000
@@ -260,6 +260,7 @@
 
 static void msr_exit(void)
 {
+  unregister_chrdev(MSR_MAJOR, "cpu/msr");
 }
 
 module_init(msr_init);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/ptrace.c linux.19p6/arch/i386/kernel/ptrace.c
--- linux.vanilla/arch/i386/kernel/ptrace.c	Fri Apr 21 12:45:45 2000
+++ linux.19p6/arch/i386/kernel/ptrace.c	Fri Dec 22 12:07:36 2000
@@ -413,7 +413,7 @@
 	if (request == PTRACE_ATTACH) {
 		if (child == current)
 			goto out;
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->suid) ||
 		    (current->uid != child->uid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/setup.c linux.19p6/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/i386/kernel/setup.c	Sat Dec 30 19:43:39 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 __initdata = { 0, };
 
 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))
@@ -269,18 +273,128 @@
 	}
 #endif
 
+static 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 */
+
+unsigned long i386_endbase __initdata =  0;
+
+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 > i386_endbase) {
+                               if (start < i386_endbase)
+                                       add_memory_region(start, i386_endbase-start, type);
+                               if (end <= 0x100000ULL)
+                                       continue;
+                               start = 0x100000ULL;
+                               size = end - start;
+                       }
+               }
+               add_memory_region(start, size, type);
+       } while (biosmap++,--nr_map);
+       return 0;
+}
+
+static void __init print_memory_map(char *who)
+{
+	int i;
+
+	for (i = 0; i < e820.nr_map; i++) {
+		printk(" %s: %08lx @ %08lx ", who,
+		       (unsigned long) e820.map[i].size,
+		       (unsigned long) e820.map[i].addr);
+		switch (e820.map[i].type) {
+		case E820_RAM:	printk("(usable)\n");
+				break;
+		case E820_RESERVED:
+				printk("(reserved)\n");
+				break;
+		case E820_ACPI:
+				printk("(ACPI data)\n");
+				break;
+		case E820_NVS:
+				printk("(ACPI NVS)\n");
+				break;
+		default:	printk("type %lu\n", e820.map[i].type);
+				break;
+		}
+	}
+}
+
+static 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, i386_endbase, E820_RAM);
+               add_memory_region(HIGH_MEMORY, (mem_size << 10), E820_RAM);
+       }
+       printk("BIOS-provided physical RAM map:\n");
+       print_memory_map(who);
+} /* setup_memory_region */
+
 
 static char command_line[COMMAND_LINE_SIZE] = { 0, };
        char saved_command_line[COMMAND_LINE_SIZE];
-unsigned long i386_endbase __initdata =  0;
 
 __initfunc(void setup_arch(char **cmdline_p,
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
-	unsigned long memory_start, memory_end;
+	unsigned long memory_start, memory_end = 0;
 	char c = ' ', *to = command_line, *from = COMMAND_LINE;
 	int len = 0;
 	int read_endbase_from_BIOS = 1;
+	int i;
+	unsigned long user_mem = 0;
 
 #ifdef CONFIG_VISWS
 	visws_get_board_type_and_rev();
@@ -297,17 +411,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);
@@ -337,12 +441,12 @@
 				from += 9+4;
 				boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE;
 			} else {
-				memory_end = simple_strtoul(from+4, &from, 0);
+				user_mem = simple_strtoul(from+4, &from, 0);
 				if ( *from == 'K' || *from == 'k' ) {
-					memory_end = memory_end << 10;
+					user_mem = user_mem << 10;
 					from++;
 				} else if ( *from == 'M' || *from == 'm' ) {
-					memory_end = memory_end << 20;
+					user_mem = user_mem << 20;
 					from++;
 				}
 			}
@@ -351,7 +455,6 @@
 		{
 			if (to != command_line) to--;
 			i386_endbase = simple_strtoul(from+8, &from, 0);
-			i386_endbase += PAGE_OFFSET;
 			read_endbase_from_BIOS = 0;
 		}
 		c = *(from++);
@@ -387,7 +490,35 @@
 			}
 			i386_endbase = BIOS_ENDBASE;
 		}
-		i386_endbase += PAGE_OFFSET;
+	}
+
+	if (!user_mem)
+		setup_memory_region();
+	else {
+		e820.nr_map = 0;
+		add_memory_region(0, i386_endbase, E820_RAM);
+		add_memory_region(HIGH_MEMORY, user_mem-HIGH_MEMORY, E820_RAM);
+		printk("USER-provided physical RAM map:\n");
+		print_memory_map("USER");
+	}
+
+#define PFN_UP(x)      (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+#define PFN_DOWN(x)    ((x) >> PAGE_SHIFT)
+	for (i = 0; i < e820.nr_map; i++) {
+		unsigned long long start, end;
+		/* RAM? */
+		if (e820.map[i].type != E820_RAM)
+			continue;
+		start = e820.map[i].addr;
+		if (start >= 0xffffffff)
+			continue;
+		end = e820.map[i].addr + e820.map[i].size;
+		if (start >= end)
+			continue;
+		if (end > 0xffffffff)
+			end = 0xffffffff;
+		if (end > memory_end)
+			memory_end = end;
 	}
 
 #define VMALLOC_RESERVE	(64 << 20)	/* 64MB for vmalloc */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/smp.c linux.19p6/arch/i386/kernel/smp.c
--- linux.vanilla/arch/i386/kernel/smp.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/i386/kernel/smp.c	Thu Dec 21 22:49:00 2000
@@ -680,7 +680,7 @@
  */
 unsigned long __init smp_alloc_memory(unsigned long mem_base)
 {
-	if (mem_base + PAGE_SIZE > i386_endbase)
+	if (mem_base + PAGE_SIZE > i386_endbase + PAGE_OFFSET)
 		panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.", mem_base);
 	trampoline_base = (void *)mem_base;
 	return mem_base + PAGE_SIZE;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/time.c linux.19p6/arch/i386/kernel/time.c
--- linux.vanilla/arch/i386/kernel/time.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/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/arch/i386/kernel/traps.c linux.19p6/arch/i386/kernel/traps.c
--- linux.vanilla/arch/i386/kernel/traps.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/i386/kernel/traps.c	Wed Jan  3 13:40:32 2001
@@ -106,7 +106,6 @@
 asmlinkage void general_protection(void);
 asmlinkage void page_fault(void);
 asmlinkage void coprocessor_error(void);
-asmlinkage void reserved(void);
 asmlinkage void alignment_check(void);
 asmlinkage void spurious_interrupt_bug(void);
 asmlinkage void machine_check(void);
@@ -156,7 +155,7 @@
 		printk("\nStack: ");
 		stack = (unsigned long *) esp;
 		for(i=0; i < kstack_depth_to_print; i++) {
-			if (((long) stack & 4095) == 0)
+			if (((long) stack & 8191) == 0)
 				break;
 			if (i && ((i % 8) == 0))
 				printk("\n       ");
@@ -171,7 +170,7 @@
 			module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT);
 			module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
 			module_end = module_start + MODULE_RANGE;
-			while (((long) stack & 4095) != 0) {
+			while (((long) stack & 8191) != 0) {
 				addr = *stack++;
 				/*
 				 * If the address is either in the text segment of the
@@ -246,7 +245,6 @@
 DO_ERROR(11, SIGBUS,  "segment not present", segment_not_present, current)
 DO_ERROR(12, SIGBUS,  "stack segment", stack_segment, current)
 DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current)
-DO_ERROR(18, SIGSEGV, "reserved", reserved, current)
 /* I don't have documents for this but it does seem to cover the cache
    flush from user space exception some people get. */
 DO_ERROR(19, SIGSEGV, "cache flush denied", cache_flush_denied, current)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/init.c linux.19p6/arch/i386/mm/init.c
--- linux.vanilla/arch/i386/mm/init.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/i386/mm/init.c	Thu Dec 21 22:49:00 2000
@@ -27,6 +27,7 @@
 #include <asm/pgtable.h>
 #include <asm/dma.h>
 #include <asm/fixmap.h>
+#include <asm/e820.h>
 
 extern void show_net_buffers(void);
 extern unsigned long init_smp_mappings(unsigned long);
@@ -384,8 +385,6 @@
 		printk(".\n");
 }
 
-extern unsigned long i386_endbase;
-
 __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
 {
 	unsigned long start_low_mem = PAGE_SIZE;
@@ -417,15 +416,36 @@
 #endif
 	start_mem = PAGE_ALIGN(start_mem);
 
-	while (start_low_mem < i386_endbase) {
-		clear_bit(PG_reserved, &mem_map[MAP_NR(start_low_mem)].flags);
-		start_low_mem += PAGE_SIZE;
-	}
+	for (tmp = __pa(start_low_mem); tmp < __pa(end_mem);
+	     tmp += PAGE_SIZE) {
+		struct page * page = mem_map + (tmp >> PAGE_SHIFT);
+		int i;
+		extern struct e820map e820;
 
-	while (start_mem < end_mem) {
-		clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
-		start_mem += PAGE_SIZE;
+		/* don't include kernel memory and bootmem allocations */
+		if (tmp >= 0x100000 && tmp < __pa(start_mem))
+			continue;
+
+		for (i = 0; i < e820.nr_map; i++) {
+			unsigned long long start, end;
+			/* RAM? */
+			if (e820.map[i].type != E820_RAM)
+				continue;
+			start = e820.map[i].addr;
+			if (start >= 0xffffffff)
+				continue;
+			end = e820.map[i].addr + e820.map[i].size;
+			if (start >= end)
+				continue;
+			if (end > 0xffffffff)
+				end = 0xffffffff;
+
+			/* start and end are valid here */
+			if (start <= tmp && tmp+PAGE_SIZE <= end)
+				clear_bit(PG_reserved, &page->flags);
+		}
 	}
+
 	for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) {
 		if (tmp >= MAX_DMA_ADDRESS)
 			clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/ptrace.c linux.19p6/arch/m68k/kernel/ptrace.c
--- linux.vanilla/arch/m68k/kernel/ptrace.c	Mon Dec 11 22:12:34 2000
+++ linux.19p6/arch/m68k/kernel/ptrace.c	Fri Dec 22 12:08:24 2000
@@ -339,7 +339,7 @@
 	if (request == PTRACE_ATTACH) {
 		if (child == current)
 			goto out;
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->suid) ||
 		    (current->uid != child->uid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips/kernel/ptrace.c linux.19p6/arch/mips/kernel/ptrace.c
--- linux.vanilla/arch/mips/kernel/ptrace.c	Fri Apr 21 12:45:47 2000
+++ linux.19p6/arch/mips/kernel/ptrace.c	Fri Dec 22 12:08:51 2000
@@ -292,7 +292,7 @@
 			res = -EPERM;
 			goto out;
 		}
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->suid) ||
 		    (current->uid != child->uid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/pmac_setup.c linux.19p6/arch/ppc/kernel/pmac_setup.c
--- linux.vanilla/arch/ppc/kernel/pmac_setup.c	Mon Dec 11 22:12:35 2000
+++ linux.19p6/arch/ppc/kernel/pmac_setup.c	Thu Dec 21 22:53:58 2000
@@ -658,6 +658,10 @@
 	ppc_md.get_rtc_time   = pmac_get_rtc_time;
 	ppc_md.calibrate_decr = pmac_calibrate_decr;
 
+	ppc_md.pci_dev_root_bridge	= pmac_pci_dev_root_bridge;
+	ppc_md.pci_dev_mem_base		= pmac_pci_dev_mem_base;
+	ppc_md.pci_dev_io_base		= pmac_pci_dev_io_base;
+
 #ifdef CONFIG_VT
 #ifdef CONFIG_MAC_KEYBOARD
 	ppc_md.kbd_setkeycode    = mackbd_setkeycode;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/ptrace.c linux.19p6/arch/ppc/kernel/ptrace.c
--- linux.vanilla/arch/ppc/kernel/ptrace.c	Fri Apr 21 12:45:47 2000
+++ linux.19p6/arch/ppc/kernel/ptrace.c	Fri Dec 22 12:09:10 2000
@@ -360,7 +360,7 @@
 	if (request == PTRACE_ATTACH) {
 		if (child == current)
 			goto out;
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->suid) ||
 		    (current->uid != child->uid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/config.in linux.19p6/arch/s390/config.in
--- linux.vanilla/arch/s390/config.in	Mon Dec 11 22:12:36 2000
+++ linux.19p6/arch/s390/config.in	Thu Dec 21 22:50:29 2000
@@ -44,11 +44,14 @@
 endmenu
 
 source drivers/s390/Config.in
+
+mainmenu_option next_comment
 comment 'Character devices'
 bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
 if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
   int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
 fi
+endmenu
 
 if [ "$CONFIG_NET" = "y" ]; then
   source net/Config.in
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/ptrace.c linux.19p6/arch/s390/kernel/ptrace.c
--- linux.vanilla/arch/s390/kernel/ptrace.c	Mon Dec 11 22:12:36 2000
+++ linux.19p6/arch/s390/kernel/ptrace.c	Fri Dec 22 12:09:44 2000
@@ -452,7 +452,7 @@
 	{
 		if (child == current)
 			goto out;
-		if ((!child->dumpable ||
+		if ((child->dumpable != 1 ||
 		     (current->uid != child->euid) ||
 		     (current->uid != child->uid) ||
 		     (current->gid != child->egid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/s390_ksyms.c linux.19p6/arch/s390/kernel/s390_ksyms.c
--- linux.vanilla/arch/s390/kernel/s390_ksyms.c	Mon Dec 11 22:12:36 2000
+++ linux.19p6/arch/s390/kernel/s390_ksyms.c	Thu Dec 21 22:50:29 2000
@@ -41,7 +41,9 @@
 EXPORT_SYMBOL(s390_device_register);
 EXPORT_SYMBOL(s390_device_unregister);
 
+#ifdef	__SMP__
 EXPORT_SYMBOL(s390_bh_lock);
+#endif
 
 EXPORT_SYMBOL(ccw_alloc_request);
 EXPORT_SYMBOL(ccw_free_request);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/setup.c linux.19p6/arch/s390/kernel/setup.c
--- linux.vanilla/arch/s390/kernel/setup.c	Mon Dec 11 22:12:36 2000
+++ linux.19p6/arch/s390/kernel/setup.c	Thu Dec 21 22:50:29 2000
@@ -36,6 +36,9 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include "cpcmd.h"
+
+extern void reipl(int ipl_device);
 
 /*
  * Machine setup..
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/ptrace.c linux.19p6/arch/sparc/kernel/ptrace.c
--- linux.vanilla/arch/sparc/kernel/ptrace.c	Fri Apr 21 12:45:48 2000
+++ linux.19p6/arch/sparc/kernel/ptrace.c	Fri Dec 22 12:10:03 2000
@@ -567,7 +567,7 @@
 			pt_error_return(regs, EPERM);
 			goto out;
 		}
-		if((!child->dumpable ||
+		if((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
 		    (current->gid != child->egid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/ptrace.c linux.19p6/arch/sparc64/kernel/ptrace.c
--- linux.vanilla/arch/sparc64/kernel/ptrace.c	Fri Apr 21 12:45:48 2000
+++ linux.19p6/arch/sparc64/kernel/ptrace.c	Fri Dec 22 12:10:15 2000
@@ -605,7 +605,7 @@
 			pt_error_return(regs, EPERM);
 			goto out;
 		}
-		if((!child->dumpable ||
+		if((child->dumpable != 1 ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
 		    (current->gid != child->egid) ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sparc32.c linux.19p6/arch/sparc64/kernel/sys_sparc32.c
--- linux.vanilla/arch/sparc64/kernel/sys_sparc32.c	Mon Dec 11 22:12:37 2000
+++ linux.19p6/arch/sparc64/kernel/sys_sparc32.c	Thu Dec 21 22:57:16 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.107.2.14 2000/10/10 04:50:50 davem Exp $
+/* $Id: sys_sparc32.c,v 1.107.2.16 2000/12/20 02:45:00 anton Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -3462,7 +3462,7 @@
 	set_fs (KERNEL_DS);
 	sys_get_kernel_syms(tbl);
 	set_fs (old_fs);
-	for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) {
+	for (i = 0; i < len; i++, table++) {
 		if (put_user (tbl[i].value, &table->value) ||
 		    copy_to_user (table->name, tbl[i].name, 60))
 			break;
@@ -3553,6 +3553,12 @@
 	s32			gf32_version;
 };
 
+struct nfsctl_fdparm32 {
+	struct sockaddr		gd32_addr;
+	s8			gd32_path[NFS_MAXPATHLEN+1];
+	s32			gd32_version;
+};
+
 struct nfsctl_arg32 {
 	s32			ca32_version;	/* safeguard */
 	union {
@@ -3561,6 +3567,7 @@
 		struct nfsctl_export32	u32_export;
 		struct nfsctl_uidmap32	u32_umap;
 		struct nfsctl_fhparm32	u32_getfh;
+		struct nfsctl_fdparm32  u32_getfd;
 		u32			u32_debug;
 	} u;
 #define ca32_svc	u.u32_svc
@@ -3569,6 +3576,7 @@
 #define ca32_umap	u.u32_umap
 #define ca32_getfh	u.u32_getfh
 #define ca32_authd	u.u32_authd
+#define ca32_getfd	u.u32_getfd
 #define ca32_debug	u.u32_debug
 };
 
@@ -3698,6 +3706,22 @@
 	return err;
 }
 
+static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = __get_user(karg->ca_version, &arg32->ca32_version);
+	err |= copy_from_user(&karg->ca_getfd.gd_addr,
+			  &arg32->ca32_getfd.gd32_addr,
+			  (sizeof(struct sockaddr)));
+	err |= copy_from_user(&karg->ca_getfd.gd_path,
+			  &arg32->ca32_getfd.gd32_path,
+			  (NFS_MAXPATHLEN+1));
+	err |= __get_user(karg->ca_getfd.gd_version,
+		      &arg32->ca32_getfd.gd32_version);
+	return err;
+}
+
 static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32)
 {
 	int err;
@@ -3739,6 +3763,7 @@
 		err = nfs_clnt32_trans(karg, arg32);
 		break;
 	case NFSCTL_EXPORT:
+	case NFSCTL_UNEXPORT:
 		err = nfs_exp32_trans(karg, arg32);
 		break;
 	/* This one is unimplemented, be we're ready for it. */
@@ -3748,6 +3773,9 @@
 	case NFSCTL_GETFH:
 		err = nfs_getfh32_trans(karg, arg32);
 		break;
+	case NFSCTL_GETFD:
+		err = nfs_getfd32_trans(karg, arg32);
+		break;
 	case NFSCTL_LOCKD:
 		/* We still have to copy over the version
 		 * because nfsctl still checks that.
@@ -3765,7 +3793,11 @@
 	err = sys_nfsservctl(cmd, karg, kres);
 	set_fs(oldfs);
 
-	if(!err && cmd == NFSCTL_GETFH)
+	if (err)
+		goto done;
+
+	if((cmd == NFSCTL_GETFH) ||
+	   (cmd == NFSCTL_GETFD))
 		err = nfs_getfh32_res_trans(kres, res32);
 
 done:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/genhd.c linux.19p6/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c	Mon Dec 11 22:12:40 2000
+++ linux.19p6/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/block/rd.c linux.19p6/drivers/block/rd.c
--- linux.vanilla/drivers/block/rd.c	Mon Dec 11 22:12:40 2000
+++ linux.19p6/drivers/block/rd.c	Thu Dec 21 22:51:11 2000
@@ -173,7 +173,7 @@
 	if (CURRENT->cmd == READ) 
 		memset(CURRENT->buffer, 0, len); 
 	else	
-		set_bit(BH_Protected, &CURRENT->bh->b_state);
+		mark_buffer_protected(CURRENT->bh);
 
 	end_request(1);
 	goto repeat;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Config.in linux.19p6/drivers/char/Config.in
--- linux.vanilla/drivers/char/Config.in	Mon Dec 11 22:12:40 2000
+++ linux.19p6/drivers/char/Config.in	Thu Dec 21 23:06:08 2000
@@ -99,7 +99,14 @@
   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 '  Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO
+  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 +114,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 +130,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.19p6/drivers/char/Makefile
--- linux.vanilla/drivers/char/Makefile	Mon Dec 11 22:12:40 2000
+++ linux.19p6/drivers/char/Makefile	Thu Dec 21 23:05:01 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
@@ -678,6 +686,14 @@
 else
   ifeq ($(CONFIG_TOSHIBA),m)
   M_OBJS += toshiba.o
+  endif
+endif
+
+ifeq ($(CONFIG_I810_TCO),y)
+O_OBJS += i810-tco.o
+else
+  ifeq ($(CONFIG_I810_TCO),m)
+  M_OBJS += i810-tco.o
   endif
 endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/advantechwdt.c linux.19p6/drivers/char/advantechwdt.c
--- linux.vanilla/drivers/char/advantechwdt.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/drivers/char/agp/agp_backend.h
--- linux.vanilla/drivers/char/agp/agp_backend.h	Mon Dec 11 22:12:40 2000
+++ linux.19p6/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.19p6/drivers/char/agp/agpgart_be.c
--- linux.vanilla/drivers/char/agp/agpgart_be.c	Mon Dec 11 22:12:40 2000
+++ linux.19p6/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.19p6/drivers/char/console.c
--- linux.vanilla/drivers/char/console.c	Mon Dec 11 22:12:41 2000
+++ linux.19p6/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.19p6/drivers/char/drm/gamma_drv.c
--- linux.vanilla/drivers/char/drm/gamma_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p6/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.19p6/drivers/char/drm/i810_drv.c
--- linux.vanilla/drivers/char/drm/i810_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p6/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.19p6/drivers/char/drm/mga_drv.c
--- linux.vanilla/drivers/char/drm/mga_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p6/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.19p6/drivers/char/drm/r128_drv.c
--- linux.vanilla/drivers/char/drm/r128_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p6/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.19p6/drivers/char/drm/tdfx_drv.c
--- linux.vanilla/drivers/char/drm/tdfx_drv.c	Mon Dec 11 22:12:42 2000
+++ linux.19p6/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/i810-tco.c linux.19p6/drivers/char/i810-tco.c
--- linux.vanilla/drivers/char/i810-tco.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/char/i810-tco.c	Thu Dec 21 23:31:09 2000
@@ -0,0 +1,343 @@
+/*
+ *	i810-tco 0.02:	TCO timer driver for i810 chipsets
+ *
+ *	(c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
+ *				http://www.kernelconcepts.de
+ *
+ *	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 kernel concepts nor Nils Faerber admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
+ *
+ *	(c) Copyright 2000	kernel concepts <nils@kernelconcepts.de>
+ *				developed for
+ *                              Jentro AG, Haar/Munich (Germany)
+ *
+ *	TCO timer driver for i810/i815 chipsets
+ *	based on softdog.c by Alan Cox <alan@redhat.com>
+ *
+ *	The TCO timer is implemented in the 82801AA (82801AB) chip,
+ *	see intel documentation from http://developer.intel.com,
+ *	order number 290655-003
+ *
+ *  20000710 Nils Faerber
+ *	Initial Version 0.01
+ *  20000728 Nils Faerber
+ *      0.02 Fix for SMI_EN->TCO_EN bit, some cleanups
+ */
+ 
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include "i810-tco.h"
+
+
+/* Just in case that the PCI vendor and device IDs are not yet defined */
+#ifndef PCI_DEVICE_ID_INTEL_82801AA_0
+#define PCI_DEVICE_ID_INTEL_82801AA_0	0x2410
+#endif
+
+/* Default expire timeout */
+#define TIMER_MARGIN	50	/* steps of 0.6sec, 2<n<64. Default is 30 seconds */
+
+static unsigned int ACPIBASE;
+static spinlock_t tco_lock;	/* Guards the hardware */
+
+static int i810_margin = TIMER_MARGIN;	/* steps of 0.6sec */
+
+MODULE_PARM (i810_margin, "i");
+
+/*
+ *	Timer active flag
+ */
+
+static int timer_alive;
+static int boot_status;
+
+/*
+ * Some i810 specific functions
+ */
+
+
+/*
+ * Start the timer countdown
+ */
+static int tco_timer_start (void)
+{
+	unsigned char val;
+
+	spin_lock(&tco_lock);
+	val = inb (TCO1_CNT + 1);
+	val &= 0xf7;
+	outb (val, TCO1_CNT + 1);
+	val = inb (TCO1_CNT + 1);
+	spin_unlock(&tco_lock);
+	
+	if (val & 0x08)
+		return -1;
+	return 0;
+}
+
+/*
+ * Stop the timer countdown
+ */
+static int tco_timer_stop (void)
+{
+	unsigned char val;
+
+	spin_lock(&tco_lock);
+	val = inb (TCO1_CNT + 1);
+	val |= 0x08;
+	outb (val, TCO1_CNT + 1);
+	val = inb (TCO1_CNT + 1);
+	spin_unlock(&tco_lock);
+	
+	if ((val & 0x08) == 0)
+		return -1;
+	return 0;
+}
+
+/*
+ * Set the timer reload value
+ */
+static int tco_timer_settimer (unsigned char tmrval)
+{
+	unsigned char val;
+
+	/* from the specs: */
+	/* "Values of 0h-3h are ignored and should not be attempted" */
+	if (tmrval > 0x3f || tmrval < 0x03)
+		return -1;
+	
+	spin_lock(&tco_lock);
+	val = inb (TCO1_TMR);
+	val &= 0xc0;
+	val |= tmrval;
+	outb (val, TCO1_TMR);
+	val = inb (TCO1_TMR);
+	spin_unlock(&tco_lock);
+	
+	if ((val & 0x3f) != tmrval)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Reload (trigger) the timer. Lock is needed so we dont reload it during
+ * a reprogramming event
+ */
+ 
+static void tco_timer_reload (void)
+{
+	spin_lock(&tco_lock);
+	outb (0x01, TCO1_RLD);
+	spin_unlock(&tco_lock);
+}
+
+/*
+ * Read the current timer value
+ */
+static unsigned char tco_timer_read (void)
+{
+	return (inb (TCO1_RLD));
+}
+
+
+/*
+ *	Allow only one person to hold it open
+ */
+
+static int i810tco_open (struct inode *inode, struct file *file)
+{
+	if (timer_alive)
+		return -EBUSY;
+
+	/*
+	 *      Reload and activate timer
+	 */
+	tco_timer_reload ();
+	tco_timer_start ();
+	timer_alive = 1;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int i810tco_release (struct inode *inode, struct file *file)
+{
+	/*
+	 *      Shut off the timer.
+	 */
+	MOD_INC_USE_COUNT;
+	tco_timer_stop ();
+	timer_alive = 0;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static ssize_t i810tco_write (struct file *file, const char *data,
+			      size_t len, loff_t * ppos)
+{
+	/*  Can't seek (pwrite) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	/*
+	 *      Refresh the timer.
+	 */
+	if (len) {
+		tco_timer_reload ();
+		return 1;
+	}
+	return 0;
+}
+
+static int i810tco_ioctl (struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	static struct watchdog_info ident = {
+		0,
+		0,
+		"i810 TCO timer"
+	};
+	switch (cmd) {
+	default:
+		return -ENOIOCTLCMD;
+	case WDIOC_GETSUPPORT:
+		if (copy_to_user
+		    ((struct watchdog_info *) arg, &ident, sizeof (ident)))
+			return -EFAULT;
+		return 0;
+	case WDIOC_GETSTATUS:
+		return put_user (tco_timer_read (),
+				 (unsigned int *) (int) arg);
+	case WDIOC_GETBOOTSTATUS:
+		return put_user (boot_status, (int *) arg);
+	case WDIOC_KEEPALIVE:
+		tco_timer_reload ();
+		return 0;
+	}
+}
+
+static struct pci_dev *i810tco_pci;
+
+static unsigned char i810tco_getdevice (void)
+{
+	u8 val1, val2;
+	u16 badr;
+	/*
+	 *      Find the PCI device which has vendor id 0x8086
+	 *      and device ID 0x2410
+	 */
+	i810tco_pci = pci_find_device (PCI_VENDOR_ID_INTEL,
+				       PCI_DEVICE_ID_INTEL_82801AA_0, NULL);
+	if (i810tco_pci) {
+		/*
+		 *      Find the ACPI base I/O address which is the base
+		 *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
+		 *      ACPIBASE is bits [15:7] from 0x40-0x43
+		 */
+		pci_read_config_byte (i810tco_pci, 0x40, &val1);
+		pci_read_config_byte (i810tco_pci, 0x41, &val2);
+		badr = ((val2 << 1) | (val1 >> 7)) << 7;
+		ACPIBASE = badr;
+		/* Something's wrong here, ACPIBASE has to be set */
+		if (badr == 0x0001 || badr == 0x0000) {
+			printk (KERN_ERR "i810tco init: failed to get TCOBASE address\n");
+			return 0;
+		}
+		/*
+		 * Check chipset's NO_REBOOT bit
+		 */
+		pci_read_config_byte (i810tco_pci, 0xd4, &val1);
+		if (val1 & 0x02) {
+			val1 &= 0xfd;
+			pci_write_config_byte (i810tco_pci, 0xd4, val1);
+			pci_read_config_byte (i810tco_pci, 0xd4, &val1);
+			if (val1 & 0x02) {
+				printk (KERN_ERR "i810tco init: failed to reset NO_REBOOT flag\n");
+				return 0;	/* Cannot reset NO_REBOOT bit */
+			}
+		}
+		/* Set the TCO_EN bit in SMI_EN register */
+		val1 = inb (SMI_EN + 1);
+		val1 &= 0xdf;
+		outb (val1, SMI_EN + 1);
+		/* Clear out the (probably old) status */
+		outb (0, TCO1_STS);
+		boot_status = (int) inb (TCO2_STS);
+		outb (3, TCO2_STS);
+		return 1;
+	}
+	return 0;
+}
+
+static struct file_operations i810tco_fops = {
+	write:		i810tco_write,
+	ioctl:		i810tco_ioctl,
+	open:		i810tco_open,
+	release:	i810tco_release,
+};
+
+static struct miscdevice i810tco_miscdev = {
+	WATCHDOG_MINOR,
+	"watchdog",
+	&i810tco_fops
+};
+
+static int __init watchdog_init (void)
+{
+	spin_lock_init(&tco_lock);
+	if (!i810tco_getdevice () || i810tco_pci == NULL)
+		return -ENODEV;
+	if (!check_region (TCOBASE, 0x10)) {
+		printk (KERN_ERR
+			"i810 TCO timer: I/O address 0x%04x already in use\n",
+			TCOBASE);
+		return -EIO;
+	}
+	request_region (TCOBASE, 0x10, "i810 TCO");
+	if (misc_register (&i810tco_miscdev) != 0) {
+		release_region (TCOBASE, 0x10);
+		printk (KERN_ERR "i810 TCO timer: cannot register miscdev\n");
+		return -EIO;
+	}
+	tco_timer_settimer ((unsigned char) i810_margin);
+	tco_timer_reload ();
+
+	printk (KERN_INFO
+		"i810 TCO timer: V0.02, timer margin: %d sec (0x%04x)\n",
+		(int) (i810_margin * 6 / 10), TCOBASE);
+	return 0;
+}
+
+static void watchdog_cleanup (void)
+{
+	u8 val;
+
+	/* Reset the timer before we leave */
+	tco_timer_reload ();
+	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+	pci_read_config_byte (i810tco_pci, 0xd4, &val);
+	val |= 0x02;
+	pci_write_config_byte (i810tco_pci, 0xd4, val);
+	release_region (TCOBASE, 0x10);
+	misc_deregister (&i810tco_miscdev);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_cleanup);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/i810-tco.h linux.19p6/drivers/char/i810-tco.h
--- linux.vanilla/drivers/char/i810-tco.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/char/i810-tco.h	Thu Dec 21 23:29:25 2000
@@ -0,0 +1,45 @@
+/*
+ *	i810-tco 0.02:	TCO timer driver for i810 chipsets
+ *
+ *	(c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
+ *				http://www.kernelconcepts.de
+ *
+ *	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 kernel concepts nor Nils Faerber admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
+ *
+ *	(c) Copyright 2000	kernel concepts <nils@kernelconcepts.de>
+ *				developed for
+ *                              Jentro AG, Haar/Munich (Germany)
+ *
+ *	TCO timer driver for i810 chipsets
+ *	based on softdog.c by Alan Cox <alan@redhat.com>
+ *
+ *      The TCO timer is implemented in the 82801AA (82801AB) chip,
+ *	see intel documentation from http://developer.intel.com,
+ *	order number 290655-003
+ *
+ *	For history see i810-tco.c
+ */
+
+
+/*
+ * Some address definitions for the i810 TCO
+ */
+
+#define	TCOBASE		ACPIBASE + 0x60	/* TCO base address		*/
+#define TCO1_RLD	TCOBASE + 0x00	/* TCO Timer Reload and Current Value */
+#define TCO1_TMR	TCOBASE + 0x01	/* TCO Timer Initial Value	*/
+#define	TCO1_DAT_IN	TCOBASE + 0x02	/* TCO Data In Register		*/
+#define	TCO1_DAT_OUT	TCOBASE + 0x03	/* TCO Data Out Register	*/
+#define	TCO1_STS	TCOBASE + 0x04	/* TCO1 Status Register		*/
+#define	TCO2_STS	TCOBASE + 0x06	/* TCO2 Status Register		*/
+#define TCO1_CNT	TCOBASE + 0x08	/* TCO1 Control Register	*/
+#define TCO2_CNT	TCOBASE + 0x0a	/* TCO2 Control Register	*/
+
+#define	SMI_EN		ACPIBASE + 0x30	/* SMI Control and Enable Register */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/misc.c linux.19p6/drivers/char/misc.c
--- linux.vanilla/drivers/char/misc.c	Mon Dec 11 22:12:43 2000
+++ linux.19p6/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/char/n_hdlc.c linux.19p6/drivers/char/n_hdlc.c
--- linux.vanilla/drivers/char/n_hdlc.c	Mon Dec 11 22:12:43 2000
+++ linux.19p6/drivers/char/n_hdlc.c	Tue Dec 19 02:09:06 2000
@@ -9,7 +9,7 @@
  *	Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
  *
  * Original release 01/11/99
- * ==FILEDATE 20000515==
+ * $Id: n_hdlc.c,v 2.2 2000/11/08 17:08:29 paul Exp $
  *
  * This code is released under the GNU General Public License (GPL)
  *
@@ -78,7 +78,7 @@
  */
 
 #define HDLC_MAGIC 0x239e
-#define HDLC_VERSION "1.15"
+#define HDLC_VERSION "2.2"
 
 #include <linux/version.h>
 #include <linux/config.h>
@@ -93,14 +93,7 @@
 #undef VERSION
 #define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
 
-#if LINUX_VERSION_CODE < VERSION(2,1,14)
-#include <linux/ioport.h>
-#endif
-
-#if LINUX_VERSION_CODE >= VERSION(2,1,23)
 #include <linux/poll.h>
-#endif
-
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/tty.h>
@@ -118,79 +111,23 @@
 #include <linux/kerneld.h>
 #endif
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,4)
+#if LINUX_VERSION_CODE < VERSION(2,2,18) 
+typedef struct wait_queue *wait_queue_head_t;
+#define DECLARE_WAITQUEUE(name,task) struct wait_queue (name) = {(task),NULL}
+#define init_waitqueue_head(head) *(head) = NULL
+#define set_current_state(a) current->state = (a)
+#endif
+
 #include <asm/segment.h>
 #define GET_USER(error,value,addr) error = get_user(value,addr)
 #define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
 #define PUT_USER(error,value,addr) error = put_user(value,addr)
 #define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,5)
 #include <asm/uaccess.h>
-#endif
-
-#else  /* 2.0.x and 2.1.x before 2.1.4 */
-
-#define GET_USER(error,value,addr)					  \
-do {									  \
-	error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \
-	if (error == 0)							  \
-		value = get_user(addr);					  \
-} while (0)
-
-#define COPY_FROM_USER(error,dest,src,size)				  \
-do {									  \
-	error = verify_area (VERIFY_READ, (void *) src, size);		  \
-	if (error == 0)							  \
-		memcpy_fromfs (dest, src, size);			  \
-} while (0)
-
-#define PUT_USER(error,value,addr)					   \
-do {									   \
-	error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \
-	if (error == 0)							   \
-		put_user (value, addr);					   \
-} while (0)
-
-#define COPY_TO_USER(error,dest,src,size)				  \
-do {									  \
-	error = verify_area (VERIFY_WRITE, (void *) dest, size);		  \
-	if (error == 0)							  \
-		memcpy_tofs (dest, src, size);				  \
-} while (0)
 
-#endif
-
-#if LINUX_VERSION_CODE < VERSION(2,1,0)
-#define __init
-typedef int spinlock_t;
-#define spin_lock_init(a)
-#define spin_lock_irqsave(a,b) {save_flags((b));cli();}
-#define spin_unlock_irqrestore(a,b) {restore_flags((b));}
-#define spin_lock(a)
-#define spin_unlock(a)
-#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();}
-#endif
-
-#if LINUX_VERSION_CODE < VERSION(2,1,37)
-#define test_and_set_bit(nr, addr)	set_bit(nr, addr)
-#endif
-
-#if LINUX_VERSION_CODE < VERSION(2,1,57)
-#define signal_pending(p)	((p)->signal & ~(p)->blocked)
-#endif
-
-#if LINUX_VERSION_CODE < VERSION(2,1,25)
-#define net_device_stats	enet_statistics
-#endif
-
-#if LINUX_VERSION_CODE < VERSION(2,1,60)
-typedef int		rw_ret_t;
-typedef unsigned int	rw_count_t;
-#else
 typedef ssize_t		rw_ret_t;
 typedef size_t		rw_count_t;
-#endif
 
 /*
  * Buffers for individual HDLC frames
@@ -254,10 +191,8 @@
 
 static struct n_hdlc *n_hdlc_alloc (void);
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,19) 
 MODULE_PARM(debuglevel, "i");
 MODULE_PARM(maxframe, "i");
-#endif
 
 /* debug level can be set by insmod for debugging purposes */
 #define DEBUG_LEVEL_INFO	1
@@ -274,13 +209,8 @@
 	struct file *, const __u8 *, rw_count_t);
 static int n_hdlc_tty_ioctl(struct tty_struct *,
 	struct file *, unsigned int, unsigned long);
-#if LINUX_VERSION_CODE < VERSION(2,1,23)
-static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode,
-		      struct file *filp, int sel_type, select_table * wait);
-#else
 static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, struct file *filp,
 				  poll_table * wait);
-#endif
 static int n_hdlc_tty_open (struct tty_struct *);
 static void n_hdlc_tty_close (struct tty_struct *);
 static int n_hdlc_tty_room (struct tty_struct *tty);
@@ -653,10 +583,10 @@
 	wake_up_interruptible (&n_hdlc->read_wait);
 	wake_up_interruptible (&n_hdlc->poll_wait);
 	if (n_hdlc->tty->fasync != NULL)
-#if LINUX_VERSION_CODE < VERSION(2,3,0) 
-		kill_fasync (n_hdlc->tty->fasync, SIGIO);
-#else
+#if LINUX_VERSION_CODE >= VERSION(2,2,14) && defined(__rh_config_h__)
 		kill_fasync (n_hdlc->tty->fasync, SIGIO, POLL_IN);
+#else
+		kill_fasync (n_hdlc->tty->fasync, SIGIO);
 #endif
 }	/* end of n_hdlc_tty_receive() */
 
@@ -886,73 +816,6 @@
 	
 }	/* end of n_hdlc_tty_ioctl() */
 
-#if LINUX_VERSION_CODE < VERSION(2,1,23)
-/* n_hdlc_tty_select()
- * 
- * 	Device select method. Determine if operation requires
- * 	blocking and if so put appropriate wait queue in select
- * 	table and return 0, otherwise return 1.
- * 	
- * Arguments:
- * 
- * 	tty		pointer to tty device instance data
- * 	inode		pointer to inode for device
- * 	filp		pointer to file object
- * 	sel_type	identified the select type (read/write/exception)
- * 	wait		select table for adding wait queue if appropriate
- * 	
- * Return Value:
- * 
- * 	1 if no need to block on operation
- * 	0 if must block and wait queue added to select table
- */
-static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode,
-		struct file *filp, int sel_type, select_table * wait)
-{
-	struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
-	int result = 1;
-	
-	if (debuglevel >= DEBUG_LEVEL_INFO)	
-		printk("%s(%d)n_hdlc_tty_select() called\n",__FILE__,__LINE__);
-		
-	/* Verify the status of the device */
-	if (!n_hdlc)
-		return -EBADF;
-
-	if (n_hdlc->magic != HDLC_MAGIC || tty != n_hdlc->tty)
-		return -EBADF;
-
-	switch (sel_type) {
-	case SEL_IN:
-		if (n_hdlc->rx_buf_list.head)
-			break;
-
-	case SEL_EX:	/* Exceptions or read errors */
-		/* Is this a pty link and the remote disconnected? */
-		if (tty->flags & (1 << TTY_OTHER_CLOSED))
-			break;
-
-		/* Is this a local link and the modem disconnected? */
-		if (tty_hung_up_p (filp))
-			break;
-
-		select_wait (&n_hdlc->read_wait, wait);
-		result = 0;
-		break;
-		
-	/* Write mode. A write is allowed if there is no current transmission */
-	case SEL_OUT:
-		if (!n_hdlc->tx_free_buf_list.head) {
-			select_wait (&n_hdlc->write_wait, wait);
-			result = 0;
-		}
-		break;
-	}
-	return result;
-}	/* end of n_hdlc_tty_select() */
-
-#else	/* 2.1.23 or later */
-
 /* n_hdlc_tty_poll()
  * 
  * 	TTY callback for poll system call. Determine which 
@@ -981,11 +844,8 @@
 	if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {
 		/* queue current process into any wait queue that */
 		/* may awaken in the future (read and write) */
-#if LINUX_VERSION_CODE < VERSION(2,1,89)
-		poll_wait(&n_hdlc->poll_wait, wait);
-#else
 		poll_wait(filp, &n_hdlc->poll_wait, wait);
-#endif
+
 		/* set bits for operations that wont block */
 		if(n_hdlc->rx_buf_list.head)
 			mask |= POLLIN | POLLRDNORM;	/* readable */
@@ -999,8 +859,6 @@
 	return mask;
 }	/* end of n_hdlc_tty_poll() */
 
-#endif
-
 /* n_hdlc_alloc()
  * 
  * 	Allocate an n_hdlc instance data structure
@@ -1153,19 +1011,13 @@
 	
 	memset(&n_hdlc_ldisc, 0, sizeof (n_hdlc_ldisc));
 	n_hdlc_ldisc.magic		= TTY_LDISC_MAGIC;
-#if LINUX_VERSION_CODE >= VERSION(2,1,28)
 	n_hdlc_ldisc.name          	= "hdlc";
-#endif
 	n_hdlc_ldisc.open		= n_hdlc_tty_open;
 	n_hdlc_ldisc.close		= n_hdlc_tty_close;
 	n_hdlc_ldisc.read		= n_hdlc_tty_read;
 	n_hdlc_ldisc.write		= n_hdlc_tty_write;
 	n_hdlc_ldisc.ioctl		= n_hdlc_tty_ioctl;
-#if LINUX_VERSION_CODE < VERSION(2,1,23)
-	n_hdlc_ldisc.select		= n_hdlc_tty_select;
-#else
 	n_hdlc_ldisc.poll		= n_hdlc_tty_poll;
-#endif
 	n_hdlc_ldisc.receive_room	= n_hdlc_tty_room;
 	n_hdlc_ldisc.receive_buf	= n_hdlc_tty_receive;
 	n_hdlc_ldisc.write_wakeup	= n_hdlc_tty_wakeup;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/synclink.c linux.19p6/drivers/char/synclink.c
--- linux.vanilla/drivers/char/synclink.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/char/synclink.c	Tue Dec 19 02:09:21 2000
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/char/synclink.c
  *
- * ==FILEDATE 20000821==
+ * $Id: synclink.c,v 2.4 2000/12/11 20:08:18 paul Exp $
  *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
@@ -79,17 +79,10 @@
 #include <linux/ioport.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
-
 #include <linux/netdevice.h>
-
-#if LINUX_VERSION_CODE >= VERSION(2,1,0) 
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <asm/serial.h>
-#else
-#include <linux/bios32.h>
-#endif
-
 #include <linux/delay.h>
 #include <linux/ioctl.h>
 
@@ -102,90 +95,34 @@
 #include <linux/termios.h>
 #include <linux/tqueue.h>
 
+#if LINUX_VERSION_CODE < VERSION(2,2,18) 
+typedef struct wait_queue *wait_queue_head_t;
+#define DECLARE_WAITQUEUE(name,task) struct wait_queue (name) = {(task),NULL}
+#define init_waitqueue_head(head) *(head) = NULL
+#define DECLARE_MUTEX(name) struct semaphore (name) = MUTEX
+#define set_current_state(a) current->state = (a)
+#endif
+
 #ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
 #define CONFIG_SYNCLINK_SYNCPPP 1
 #endif
 
 #ifdef CONFIG_SYNCLINK_SYNCPPP
-#if LINUX_VERSION_CODE < VERSION(2,3,43) 
 #include "../net/syncppp.h"
 #define net_device device
 #define netif_stop_queue(a) (a)->tbusy = 1
 #define netif_start_queue(a) (a)->tbusy = 0
 #define netif_wake_queue(a) (a)->tbusy = 0; mark_bh(NET_BH)
 #define netif_queue_stopped(a) ((a)->tbusy)
-#else
-#include "../net/wan/syncppp.h"
-#endif
 #endif
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,4)
 #include <asm/segment.h>
 #define GET_USER(error,value,addr) error = get_user(value,addr)
 #define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
 #define PUT_USER(error,value,addr) error = put_user(value,addr)
 #define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,5)
 #include <asm/uaccess.h>
-#endif
-
-#else  /* 2.0.x and 2.1.x before 2.1.4 */
-
-#define GET_USER(error,value,addr)					  \
-do {									  \
-	error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \
-	if (error == 0)							  \
-		value = get_user(addr);					  \
-} while (0)
-
-#define COPY_FROM_USER(error,dest,src,size)				  \
-do {									  \
-	error = verify_area (VERIFY_READ, (void *) src, size);		  \
-	if (error == 0)							  \
-		memcpy_fromfs (dest, src, size);			  \
-} while (0)
-
-#define PUT_USER(error,value,addr)					   \
-do {									   \
-	error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \
-	if (error == 0)							   \
-		put_user (value, addr);					   \
-} while (0)
-
-#define COPY_TO_USER(error,dest,src,size)				  \
-do {									  \
-	error = verify_area (VERIFY_WRITE, (void *) dest, size);		  \
-	if (error == 0)							  \
-		memcpy_tofs (dest, src, size);				  \
-} while (0)
-
-#endif
-
-#if LINUX_VERSION_CODE < VERSION(2,1,0)
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
-	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
-
-#define __init
-#define ioremap(a,b) vremap((a),(b))
-#define iounmap(a) vfree((a))
-#define SERIAL_TYPE_NORMAL	1
-#define SERIAL_TYPE_CALLOUT	2
-typedef int spinlock_t;
-#define spin_lock_init(a)
-#define spin_lock_irqsave(a,b) {save_flags((b));cli();}
-#define spin_unlock_irqrestore(a,b) {restore_flags((b));}
-#define spin_lock(a)
-#define spin_unlock(a)
-#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();}
-#define signal_pending(a) ((a)->signal & ~(a)->blocked)
-#endif
-
-
 
 #include "linux/synclink.h"
 
@@ -944,7 +881,6 @@
 static int maxframe[MAX_TOTAL_DEVICES] = {0,};
 static int dosyncppp[MAX_TOTAL_DEVICES] = {0,};
 	
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 MODULE_PARM(break_on_load,"i");
 MODULE_PARM(ttymajor,"i");
 MODULE_PARM(cuamajor,"i");
@@ -954,10 +890,9 @@
 MODULE_PARM(debug_level,"i");
 MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
 MODULE_PARM(dosyncppp,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
-#endif
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "1.22";
+static char *driver_version = "2.3";
 
 static struct tty_driver serial_driver, callout_driver;
 static int serial_refcount;
@@ -969,9 +904,9 @@
 static void mgsl_change_params(struct mgsl_struct *info);
 static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout);
 
-static struct tty_struct **serial_table = NULL;
-static struct termios **serial_termios = NULL;
-static struct termios **serial_termios_locked = NULL;
+static struct tty_struct *serial_table[MAX_TOTAL_DEVICES];
+static struct termios *serial_termios[MAX_TOTAL_DEVICES];
+static struct termios *serial_termios_locked[MAX_TOTAL_DEVICES];
 
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
@@ -1372,11 +1307,6 @@
 #endif
 			} else
 				info->input_signal_events.dcd_down++;
-#ifdef CONFIG_HARD_PPS
-			if ((info->flags & ASYNC_HARDPPS_CD) &&
-			    (status & MISCSTATUS_DCD_LATCHED))
-				hardpps();
-#endif
 		}
 		if (status & MISCSTATUS_CTS_LATCHED)
 		{
@@ -1583,14 +1513,8 @@
 			icount->parity,icount->frame,icount->overrun);
 	}
 			
-	if ( tty->flip.count ) {
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+	if ( tty->flip.count )
 		tty_flip_buffer_push(tty);
-#else		
-		queue_task(&tty->flip.tqueue, &tq_timer); 
-#endif		
-	}
-	
 
 }	/* end of mgsl_isr_receive_data() */
 
@@ -1787,11 +1711,7 @@
 		retval = mgsl_adapter_test(info);
 		
 	if ( retval ) {
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
   		if (capable(CAP_SYS_ADMIN) && info->tty)
-#else
-  		if (suser() && info->tty)
-#endif		
 			set_bit(TTY_IO_ERROR, &info->tty->flags);
 		mgsl_release_resources(info);
   		return retval;
@@ -1967,19 +1887,7 @@
 	 * current data rate.
 	 */
 	if (info->params.data_rate <= 460800) {
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 		info->params.data_rate = tty_get_baud_rate(info->tty);
-#else
-		int i = cflag & CBAUD;
-		if (i & CBAUDEX) {
-			i &= ~CBAUDEX;
-			if (i < 1 || i > 4) 
-				info->tty->termios->c_cflag &= ~CBAUDEX;
-			else
-				i += 15;
-		}
-		info->params.data_rate = baud_table[i];
-#endif	
 	}
 	
 	if ( info->params.data_rate ) {
@@ -2942,7 +2850,6 @@
 	
 }	/* end of set_modem_info() */
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 /* mgsl_break()		Set or clear transmit break condition
  *
  * Arguments:		tty		pointer to tty instance data
@@ -2969,7 +2876,6 @@
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
 }	/* end of mgsl_break() */
-#endif
 
 /* mgsl_ioctl()	Service an IOCTL request
  * 	
@@ -3092,7 +2998,6 @@
 			if (error) return error;
 			PUT_USER(error,cnow.dcd, &p_cuser->dcd);
 			if (error) return error;
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 			PUT_USER(error,cnow.rx, &p_cuser->rx);
 			if (error) return error;
 			PUT_USER(error,cnow.tx, &p_cuser->tx);
@@ -3107,7 +3012,6 @@
 			if (error) return error;
 			PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun);
 			if (error) return error;
-#endif			
 			return 0;
 		default:
 			return -ENOIOCTLCMD;
@@ -3346,7 +3250,6 @@
 		}
 	}
       
-	set_current_state(TASK_RUNNING);
 exit:
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_wait_until_sent(%s) exit\n",
@@ -3589,9 +3492,7 @@
 			tmp_buf = (unsigned char *) page;
 	}
 	
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-#endif
 
 	spin_lock_irqsave(&info->netlock, flags);
 	if (info->netcount) {
@@ -3978,27 +3879,19 @@
 
 }	/* end of mgsl_alloc_buffer_list_memory() */
 
-/*
- * mgsl_free_buffer_list_memory()
- * 
- * 	Free the common DMA buffer allocated for use as the
- * 	receive and transmit buffer lists. The associated Memory
- * 	Descriptor List (MDL) is also freed.
- * 
+/* Free DMA buffers allocated for use as the
+ * receive and transmit buffer lists.
  * Warning:
  * 
  * 	The data transfer buffers associated with the buffer list
  * 	MUST be freed before freeing the buffer list itself because
  * 	the buffer list contains the information necessary to free
  * 	the individual buffers!
- * 
- * Arguments:		info	pointer to device extension
- * Return Value:	None
  */
 void mgsl_free_buffer_list_memory( struct mgsl_struct *info )
 {
 	if ( info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI )
-		kfree_s(info->buffer_list, BUFFERLISTSIZE);
+		kfree(info->buffer_list);
 		
 	info->buffer_list = NULL;
 	info->rx_buffer_list = NULL;
@@ -4072,7 +3965,7 @@
 		for ( i = 0 ; i < Buffercount ; i++ ) {
 			if ( BufferList[i].virt_addr ) {
 				if ( info->bus_type != MGSL_BUS_TYPE_PCI )
-					kfree_s(BufferList[i].virt_addr, DMABUFFERSIZE);
+					kfree(BufferList[i].virt_addr);
 				BufferList[i].virt_addr = NULL;
 			}
 		}
@@ -4131,19 +4024,12 @@
 void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info)
 {
 	if ( info->intermediate_rxbuffer )
-		kfree_s( info->intermediate_rxbuffer, info->max_frame_size);
+		kfree(info->intermediate_rxbuffer);
 
 	info->intermediate_rxbuffer = NULL;
 
 }	/* end of mgsl_free_intermediate_rxbuffer_memory() */
 
-/* mgsl_claim_resources()
- * 
- * 	Claim all resources used by a device
- * 	
- * Arguments:		info	pointer to device instance data
- * Return Value:	0 if success, otherwise -ENODEV
- */
 int mgsl_claim_resources(struct mgsl_struct *info)
 {
 	/* claim 16C32 I/O base address */
@@ -4168,7 +4054,7 @@
 	info->irq_requested = 1;
 	
 	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-		/* claim shared memory range */
+
 		info->memory_base = ioremap(info->phys_memory_base,0x40000);
 		if (!info->memory_base) {
 			printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n",
@@ -4177,7 +4063,6 @@
 			return -ENODEV;
 		}
 		
-		/* test the shared memory range */
 		if ( !mgsl_memory_test(info) ) {
 			printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n",
 				__FILE__,__LINE__,info->device_name, info->phys_memory_base );
@@ -4185,7 +4070,6 @@
 			return -ENODEV;
 		}
 		
-		/* claim LCR memory range */
 		info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset;
 		if (!info->lcr_base) {
 			printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n",
@@ -4221,13 +4105,6 @@
 		
 }	/* end of mgsl_claim_resources() */
 
-/* mgsl_release_resources()
- * 
- * 	Release all resources used by a device
- * 	
- * Arguments:		info	pointer to device instance data
- * Return Value:	None
- */
 void mgsl_release_resources(struct mgsl_struct *info)
 {
 	if ( debug_level >= DEBUG_LEVEL_INFO )
@@ -4380,9 +4257,7 @@
 			printk("ISA device specified io=%04X,irq=%d,dma=%d\n",
 				io[i], irq[i], dma[i] );
 		
-		info = mgsl_allocate_device();
-		if ( !info ) {
-			/* error allocating device instance data */
+		if (!(info = mgsl_allocate_device())) {
 			if ( debug_level >= DEBUG_LEVEL_ERROR )
 				printk( "can't allocate device instance data.\n");
 			continue;
@@ -4391,12 +4266,7 @@
 		/* Copy user configuration info to device instance data */
 		info->io_base = (unsigned int)io[i];
 		info->irq_level = (unsigned int)irq[i];
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 		info->irq_level = irq_cannonicalize(info->irq_level);
-#else		
-		if (info->irq_level == 2)
-			info->irq_level = 9;
-#endif			
 		info->dma_level = (unsigned int)dma[i];
 		info->bus_type = MGSL_BUS_TYPE_ISA;
 		info->io_addr_size = 16;
@@ -4412,73 +4282,29 @@
 	if ( pcibios_present() ) {
 		unsigned char bus;
 		unsigned char func;
-		unsigned int  shared_mem_base;
-		unsigned int  lcr_mem_base;
-		unsigned int  io_base;
-		unsigned char irq_line;
 		
 		for(i=0;;i++){
 			if ( PCIBIOS_SUCCESSFUL == pcibios_find_device(
 				MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) {
 				
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 				struct pci_dev *pdev = pci_find_slot(bus,func);
-				irq_line = pdev->irq;				
-#else												
-				if (pcibios_read_config_byte(bus,func,
-					PCI_INTERRUPT_LINE,&irq_line) ) {
-					printk( "%s(%d):USC I/O addr not set.\n",
-						__FILE__,__LINE__);
-					continue;
-				}
-#endif
-
-				if (pcibios_read_config_dword(bus,func,
-					PCI_BASE_ADDRESS_3,&shared_mem_base) ) {
-					printk( "%s(%d):Shared mem addr not set.\n",
-						__FILE__,__LINE__);
-					continue;
-				}
-							
-				if (pcibios_read_config_dword(bus,func,
-					PCI_BASE_ADDRESS_0,&lcr_mem_base) ) {
-					printk( "%s(%d):LCR mem addr not set.\n",
-						__FILE__,__LINE__);
-					continue;
-				}
 				
-				if (pcibios_read_config_dword(bus,func,
-					PCI_BASE_ADDRESS_2,&io_base) ) {
-					printk( "%s(%d):USC I/O addr not set.\n",
-						__FILE__,__LINE__);
-					continue;
-				}
-				
-				info = mgsl_allocate_device();
-				if ( !info ) {
-					/* error allocating device instance data */
+				if (!(info = mgsl_allocate_device())) {
 					if ( debug_level >= DEBUG_LEVEL_ERROR )
 						printk( "can't allocate device instance data.\n");
 					continue;
 				}
 		
 				/* Copy user configuration info to device instance data */
+				info->irq_level = pdev->irq;				
+				info->phys_lcr_base = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
+				info->io_base = pdev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+				info->phys_memory_base = pdev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK;
 		
-				info->io_base = io_base & PCI_BASE_ADDRESS_IO_MASK;
-				info->irq_level = (unsigned int)irq_line;
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
-				info->irq_level = irq_cannonicalize(info->irq_level);
-#else		
-				if (info->irq_level == 2)
-					info->irq_level = 9;
-#endif			
-				info->phys_memory_base = shared_mem_base & PCI_BASE_ADDRESS_MEM_MASK;
-				
 				/* Because veremap only works on page boundaries we must map
 				 * a larger area than is actually implemented for the LCR
 				 * memory range. We map a full page starting at the page boundary.
 				 */
-				info->phys_lcr_base = lcr_mem_base & PCI_BASE_ADDRESS_MEM_MASK;
 				info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
 				info->phys_lcr_base &= ~(PAGE_SIZE-1);
 				
@@ -4508,30 +4334,9 @@
 	}
 #endif
 
-	/*
-	 * Allocate memory to hold the following tty/termios arrays
-	 * with an element for each enumerated device.
-	 */	
-	
-	serial_table = (struct tty_struct**)kmalloc(sizeof(struct tty_struct*)*mgsl_device_count, GFP_KERNEL);
-	serial_termios = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL);
-	serial_termios_locked = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL);
-	
-	if (!serial_table || !serial_termios || !serial_termios_locked){
-		printk("%s(%d):Can't allocate tty/termios arrays.\n",
-			__FILE__,__LINE__);
-		if (serial_table)
-			kfree(serial_table);
-		if (serial_termios)
-			kfree(serial_termios);
-		if (serial_termios_locked)
-			kfree(serial_termios_locked);
-		return -ENOMEM;
-	}
-	
-	memset(serial_table,0,sizeof(struct tty_struct*)*mgsl_device_count);
-	memset(serial_termios,0,sizeof(struct termios*)*mgsl_device_count);
-	memset(serial_termios_locked,0,sizeof(struct termios*)*mgsl_device_count);
+	memset(serial_table,0,sizeof(struct tty_struct*)*MAX_TOTAL_DEVICES);
+	memset(serial_termios,0,sizeof(struct termios*)*MAX_TOTAL_DEVICES);
+	memset(serial_termios_locked,0,sizeof(struct termios*)*MAX_TOTAL_DEVICES);
 
 	return 0;
 	
@@ -4548,11 +4353,7 @@
 {
 	struct mgsl_struct *info;
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 	EXPORT_NO_SYMBOLS;
-#else
-	register_symtab(NULL);
-#endif		
 	
  	printk("%s version %s\n", driver_name, driver_version);
 	
@@ -4567,9 +4368,7 @@
 	
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 	serial_driver.driver_name = "synclink";
-#endif	
 	serial_driver.name = "ttySL";
 	serial_driver.major = ttymajor;
 	serial_driver.minor_start = 64;
@@ -4596,12 +4395,10 @@
 	serial_driver.ioctl = mgsl_ioctl;
 	serial_driver.throttle = mgsl_throttle;
 	serial_driver.unthrottle = mgsl_unthrottle;
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 	serial_driver.send_xchar = mgsl_send_xchar;
 	serial_driver.break_ctl = mgsl_break;
 	serial_driver.wait_until_sent = mgsl_wait_until_sent;
  	serial_driver.read_proc = mgsl_read_proc;
-#endif	
 	serial_driver.set_termios = mgsl_set_termios;
 	serial_driver.stop = mgsl_stop;
 	serial_driver.start = mgsl_start;
@@ -4615,10 +4412,8 @@
 	callout_driver.name = "cuaSL";
 	callout_driver.major = cuamajor;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
 	callout_driver.read_proc = 0;
 	callout_driver.proc_entry = 0;
-#endif	
 
 	if (tty_register_driver(&serial_driver) < 0)
 		printk("%s(%d):Couldn't register serial driver\n",
@@ -4645,8 +4440,7 @@
 	
 }	/* end of mgsl_init() */
 
-#ifdef MODULE
-int init_module(void)
+int __init init_module(void)
 {
 /* Uncomment this to kernel debug module.
  * mgsl_get_text_ptr() leaves the .text address in eax
@@ -4692,20 +4486,8 @@
 		tmp_buf = NULL;
 	}
 	
-	if (serial_table)
-		kfree_s(serial_table,sizeof(struct tty_struct*)*mgsl_device_count);
-		
-	if (serial_termios)
-		kfree_s(serial_termios,sizeof(struct termios*)*mgsl_device_count);
-		
-	if (serial_termios_locked)
-		kfree_s(serial_termios_locked,sizeof(struct termios*)*mgsl_device_count);
-	
 }	/* end of cleanup_module() */
 
-#endif /* MODULE */
-
-
 /*
  * usc_RTCmd()
  *
@@ -6952,7 +6734,6 @@
 	while( EndTime-- && !info->irq_occurred ) {
 		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(jiffies_from_ms(10));
-		set_current_state(TASK_RUNNING);
 	}
 	
 	spin_lock_irqsave(&info->irq_spinlock,flags);
@@ -7575,7 +7356,7 @@
 	sppp_attach(&info->pppdev);
 
 	d = info->netdev;
-	strcpy(d->name, info->netname);
+	d->name = info->netname;
 	d->base_addr = info->io_base;
 	d->irq = info->irq_level;
 	d->dma = info->dma_level;
@@ -7586,10 +7367,6 @@
 	d->hard_start_xmit = mgsl_sppp_tx;
 	d->do_ioctl = mgsl_sppp_ioctl;
 	d->get_stats = mgsl_net_stats;
-#if LINUX_VERSION_CODE >= VERSION(2,3,43) 
-	d->tx_timeout = mgsl_sppp_tx_timeout;
-	d->watchdog_timeo = 10*HZ;
-#endif
 	dev_init_buffers(d);
 
 	if (register_netdev(d) == -1) {
@@ -7679,7 +7456,6 @@
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("mgsl_sppp_tx(%s)\n",info->netname);	
 
-#if LINUX_VERSION_CODE < VERSION(2,3,43) 
 	if (dev->tbusy) { 
 		if (time_before(jiffies, dev->trans_start+10*HZ))
 			return -EBUSY;	/* 10 seconds timeout */
@@ -7687,9 +7463,6 @@
 	}
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
 		return -EBUSY;
-#else
-	netif_stop_queue(dev);
-#endif
 
 	info->xmit_cnt = skb->len;
 	mgsl_load_tx_dma_buffer(info, skb->data, skb->len);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/Config.in linux.19p6/drivers/isdn/Config.in
--- linux.vanilla/drivers/isdn/Config.in	Fri Apr 21 12:46:08 2000
+++ linux.19p6/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.19p6/drivers/isdn/Makefile
--- linux.vanilla/drivers/isdn/Makefile	Fri Apr 21 12:46:12 2000
+++ linux.19p6/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.19p6/drivers/isdn/act2000/Makefile
--- linux.vanilla/drivers/isdn/act2000/Makefile	Fri Apr 21 12:46:07 2000
+++ linux.19p6/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.19p6/drivers/isdn/act2000/act2000.h
--- linux.vanilla/drivers/isdn/act2000/act2000.h	Wed May  3 22:34:32 2000
+++ linux.19p6/drivers/isdn/act2000/act2000.h	Wed Jan  3 14:15:16 2001
@@ -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.19p6/drivers/isdn/act2000/act2000_isa.c
--- linux.vanilla/drivers/isdn/act2000/act2000_isa.c	Fri Apr 21 12:46:07 2000
+++ linux.19p6/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.19p6/drivers/isdn/act2000/act2000_isa.h
--- linux.vanilla/drivers/isdn/act2000/act2000_isa.h	Fri Apr 21 12:46:07 2000
+++ linux.19p6/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.19p6/drivers/isdn/act2000/capi.c
--- linux.vanilla/drivers/isdn/act2000/capi.c	Fri Apr 21 12:46:07 2000
+++ linux.19p6/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.19p6/drivers/isdn/act2000/capi.h
--- linux.vanilla/drivers/isdn/act2000/capi.h	Fri Apr 21 12:46:07 2000
+++ linux.19p6/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.19p6/drivers/isdn/act2000/module.c
--- linux.vanilla/drivers/isdn/act2000/module.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/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.19p6/drivers/isdn/avmb1/Makefile
--- linux.vanilla/drivers/isdn/avmb1/Makefile	Fri Apr 21 12:46:08 2000
+++ linux.19p6/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.19p6/drivers/isdn/avmb1/avm_cs.c
--- linux.vanilla/drivers/isdn/avmb1/avm_cs.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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/avmcard.h linux.19p6/drivers/isdn/avmb1/avmcard.h
--- linux.vanilla/drivers/isdn/avmb1/avmcard.h	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/avmb1/avmcard.h	Sat Dec 30 19:33:51 2000
@@ -1,9 +1,12 @@
 /*
- * $Id: avmcard.h,v 1.7 2000/01/25 14:33:38 calle Exp $
+ * $Id: avmcard.h,v 1.8 2000/10/10 17:44:19 kai Exp $
  *
  * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: avmcard.h,v $
+ * Revision 1.8  2000/10/10 17:44:19  kai
+ * changes from/for 2.2.18
+ *
  * Revision 1.7  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
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1.c linux.19p6/drivers/isdn/avmb1/b1.c
--- linux.vanilla/drivers/isdn/avmb1/b1.c	Sat Sep  9 18:42:38 2000
+++ linux.19p6/drivers/isdn/avmb1/b1.c	Sat Dec 30 19:33:51 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.20 $";
 
 /* ------------------------------------------------------------- */
 
@@ -507,7 +510,7 @@
 	struct sk_buff *skb;
 
 	unsigned ApplId;
-	signed MsgLen;
+	unsigned MsgLen;
 	unsigned DataB3Len;
 	unsigned NCCI;
 	unsigned WindowSize;
@@ -593,25 +596,29 @@
 		ctrl->ready(ctrl);
 		break;
 
-        case RECEIVE_TASK_READY:
+	case RECEIVE_TASK_READY:
 		ApplId = (unsigned) b1_get_word(card->port);
 		MsgLen = b1_get_slice(card->port, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
 		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
 				card->name, ApplId, card->msgbuf);
 		break;
 
-        case RECEIVE_DEBUGMSG:
+	case RECEIVE_DEBUGMSG:
 		MsgLen = b1_get_slice(card->port, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
 		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
 		break;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1dma.c linux.19p6/drivers/isdn/avmb1/b1dma.c
--- linux.vanilla/drivers/isdn/avmb1/b1dma.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/avmb1/b1dma.c	Sat Dec 30 19:33:51 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.11 $";
 
 /* ------------------------------------------------------------- */
 
@@ -462,8 +468,7 @@
 	struct capi_ctr *ctrl = cinfo->capi_ctrl;
 	struct sk_buff *skb;
 	void *p = dma->recvbuf+4;
-	__u32 ApplId, DataB3Len, NCCI, WindowSize;
-	__s32 MsgLen;
+	__u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
 	__u8 b1cmd =  _get_byte(&p);
 
 #ifdef CONFIG_B1DMA_DEBUG
@@ -552,22 +557,26 @@
 	case RECEIVE_TASK_READY:
 		ApplId = (unsigned) _get_word(&p);
 		MsgLen = _get_slice(&p, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
 		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
 				card->name, ApplId, card->msgbuf);
 		break;
 
 	case RECEIVE_DEBUGMSG:
 		MsgLen = _get_slice(&p, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
 		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
 		break;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1isa.c linux.19p6/drivers/isdn/avmb1/b1isa.c
--- linux.vanilla/drivers/isdn/avmb1/b1isa.c	Fri Apr 21 12:46:07 2000
+++ linux.19p6/drivers/isdn/avmb1/b1isa.c	Sat Dec 30 19:33:51 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.10 $";
 
 /* ------------------------------------------------------------- */
 
@@ -240,20 +244,20 @@
 /* ------------------------------------------------------------- */
 
 static struct capi_driver b1isa_driver = {
-    "b1isa",
-    "0.0",
-    b1_load_firmware,
-    b1_reset_ctr,
-    b1isa_remove_ctr,
-    b1_register_appl,
-    b1_release_appl,
-    b1_send_message,
-
-    b1isa_procinfo,
-    b1ctl_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "b1isa",
+    revision: "0.0",
+    load_firmware: b1_load_firmware,
+    reset_ctr: b1_reset_ctr,
+    remove_ctr: b1isa_remove_ctr,
+    register_appl: b1_register_appl,
+    release_appl: b1_release_appl,
+    send_message: b1_send_message,
+
+    procinfo: b1isa_procinfo,
+    ctr_read_proc: b1ctl_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    b1isa_add_card,
+    add_card: b1isa_add_card,
 };
 
 #ifdef MODULE
@@ -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.19p6/drivers/isdn/avmb1/b1pci.c
--- linux.vanilla/drivers/isdn/avmb1/b1pci.c	Fri Apr 21 12:46:07 2000
+++ linux.19p6/drivers/isdn/avmb1/b1pci.c	Sat Dec 30 19:33:51 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,22 +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 $";
-
-/* ------------------------------------------------------------- */
-
-#ifndef PCI_VENDOR_ID_AVM
-#define PCI_VENDOR_ID_AVM	0x1244
-#endif
-
-#ifndef PCI_DEVICE_ID_AVM_B1
-#define PCI_DEVICE_ID_AVM_B1	0x700
-#endif
+static char *revision = "$Revision: 1.29.6.1 $";
 
 /* ------------------------------------------------------------- */
 
@@ -252,20 +260,20 @@
 /* ------------------------------------------------------------- */
 
 static struct capi_driver b1pci_driver = {
-    "b1pci",
-    "0.0",
-    b1_load_firmware,
-    b1_reset_ctr,
-    b1pci_remove_ctr,
-    b1_register_appl,
-    b1_release_appl,
-    b1_send_message,
-
-    b1pci_procinfo,
-    b1ctl_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "b1pci",
+    revision: "0.0",
+    load_firmware: b1_load_firmware,
+    reset_ctr: b1_reset_ctr,
+    remove_ctr: b1pci_remove_ctr,
+    register_appl: b1_register_appl,
+    release_appl: b1_release_appl,
+    send_message: b1_send_message,
+
+    procinfo: b1pci_procinfo,
+    ctr_read_proc: b1ctl_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    0, /* no add_card function */
+    add_card: 0, /* no add_card function */
 };
 
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
@@ -431,20 +439,20 @@
 
 
 static struct capi_driver b1pciv4_driver = {
-    "b1pciv4",
-    "0.0",
-    b1dma_load_firmware,
-    b1dma_reset_ctr,
-    b1pciv4_remove_ctr,
-    b1dma_register_appl,
-    b1dma_release_appl,
-    b1dma_send_message,
-
-    b1pciv4_procinfo,
-    b1dmactl_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "b1pciv4",
+    revision: "0.0",
+    load_firmware: b1dma_load_firmware,
+    reset_ctr: b1dma_reset_ctr,
+    remove_ctr: b1pciv4_remove_ctr,
+    register_appl: b1dma_register_appl,
+    release_appl: b1dma_release_appl,
+    send_message: b1dma_send_message,
+
+    procinfo: b1pciv4_procinfo,
+    ctr_read_proc: b1dmactl_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    0, /* no add_card function */
+    add_card: 0, /* no add_card function */
 };
 
 #endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
@@ -469,6 +477,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 +507,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 +542,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 +563,7 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -534,6 +576,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 +588,7 @@
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
     		detach_capi_driver(driverv4);
 #endif
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -554,6 +598,7 @@
 #ifdef MODULE
 			cleanup_module();
 #endif
+			MOD_DEC_USE_COUNT;
 			return retval;
 		}
 		ncards++;
@@ -561,12 +606,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.19p6/drivers/isdn/avmb1/b1pcmcia.c
--- linux.vanilla/drivers/isdn/avmb1/b1pcmcia.c	Fri Apr 21 12:46:07 2000
+++ linux.19p6/drivers/isdn/avmb1/b1pcmcia.c	Sat Dec 30 19:33:51 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.12 $";
 
 /* ------------------------------------------------------------- */
 
@@ -232,20 +245,20 @@
 /* ------------------------------------------------------------- */
 
 static struct capi_driver b1pcmcia_driver = {
-    "b1pcmcia",
-    "0.0",
-    b1_load_firmware,
-    b1_reset_ctr,
-    b1pcmcia_remove_ctr,
-    b1_register_appl,
-    b1_release_appl,
-    b1_send_message,
-
-    b1pcmcia_procinfo,
-    b1ctl_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "b1pcmcia",
+    revision: "0.0",
+    load_firmware: b1_load_firmware,
+    reset_ctr: b1_reset_ctr,
+    remove_ctr: b1pcmcia_remove_ctr,
+    register_appl: b1_register_appl,
+    release_appl: b1_release_appl,
+    send_message: b1_send_message,
+
+    procinfo: b1pcmcia_procinfo,
+    ctr_read_proc: b1ctl_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    0,
+    add_card: 0,
 };
 
 /* ------------------------------------------------------------- */
@@ -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.19p6/drivers/isdn/avmb1/c4.c
--- linux.vanilla/drivers/isdn/avmb1/c4.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/avmb1/c4.c	Sat Dec 30 19:33:51 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,35 +67,16 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.20.6.1 $";
 
 #undef CONFIG_C4_DEBUG
 #undef CONFIG_C4_POLLDEBUG
 
 /* ------------------------------------------------------------- */
 
-#ifndef PCI_VENDOR_ID_DEC
-#define PCI_VENDOR_ID_DEC	0x1011
-#endif
-
-#ifndef PCI_DEVICE_ID_DEC_21285
-#define PCI_DEVICE_ID_DEC_21285	0x1065
-#endif
-
-#ifndef PCI_VENDOR_ID_AVM
-#define PCI_VENDOR_ID_AVM	0x1244
-#endif
-
-#ifndef PCI_DEVICE_ID_AVM_C4
-#define PCI_DEVICE_ID_AVM_C4	0x0800
-#endif
-
-/* ------------------------------------------------------------- */
-
-static int suppress_pollack = 0;
+static int suppress_pollack;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
-
 MODULE_PARM(suppress_pollack, "0-1i");
 
 /* ------------------------------------------------------------- */
@@ -535,8 +540,7 @@
 	avmctrl_info *cinfo;
 	struct sk_buff *skb;
 	void *p = dma->recvbuf;
-	__u32 ApplId, DataB3Len, NCCI, WindowSize;
-	__s32 MsgLen;
+	__u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
 	__u8 b1cmd =  _get_byte(&p);
 	__u32 cidx;
 
@@ -655,22 +659,26 @@
 	case RECEIVE_TASK_READY:
 		ApplId = (unsigned) _get_word(&p);
 		MsgLen = _get_slice(&p, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
 		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
 				card->name, ApplId, card->msgbuf);
 		break;
 
 	case RECEIVE_DEBUGMSG:
 		MsgLen = _get_slice(&p, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
 		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
 		break;
 
@@ -774,45 +782,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 +861,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 +906,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 +948,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 +1164,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;
@@ -1231,20 +1270,20 @@
 /* ------------------------------------------------------------- */
 
 static struct capi_driver c4_driver = {
-    "c4",
-    "0.0",
-    c4_load_firmware,
-    c4_reset_ctr,
-    c4_remove_ctr,
-    c4_register_appl,
-    c4_release_appl,
-    c4_send_message,
-
-    c4_procinfo,
-    c4_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "c4",
+    revision: "0.0",
+    load_firmware: c4_load_firmware,
+    reset_ctr: c4_reset_ctr,
+    remove_ctr: c4_remove_ctr,
+    register_appl: c4_register_appl,
+    release_appl: c4_release_appl,
+    send_message: c4_send_message,
+
+    procinfo: c4_procinfo,
+    ctr_read_proc: c4_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    0, /* no add_card function */
+    add_card: 0, /* no add_card function */
 };
 
 #ifdef MODULE
@@ -1282,6 +1321,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 +1336,7 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -1302,6 +1344,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 +1357,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 +1380,7 @@
 #ifdef MODULE
 			cleanup_module();
 #endif
+			MOD_DEC_USE_COUNT;
 			return retval;
 		}
 		ncards++;
@@ -1332,12 +1388,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.19p6/drivers/isdn/avmb1/capi.c
--- linux.vanilla/drivers/isdn/avmb1/capi.c	Sun Jun 11 21:44:13 2000
+++ linux.19p6/drivers/isdn/avmb1/capi.c	Sat Dec 30 19:33:51 2000
@@ -1,11 +1,114 @@
 /*
- * $Id: capi.c,v 1.23 2000/02/26 01:00:53 keil Exp $
+ * $Id: capi.c,v 1.44.6.3 2000/12/17 22:45:08 kai Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capi.c,v $
+ * Revision 1.44.6.3  2000/12/17 22:45:08  kai
+ * That's hopefully it for test13-4
+ *
+ * Revision 1.44.6.2  2000/12/14 23:04:12  kai
+ * Makefile changes and the like for 2.4.0-test13-pre1
+ * No compatiblity code for older kernels yet, but note the branch
+ *
+ * Revision 1.45  2000/12/02 19:47:29  kai
+ * Change the Makefiles to new style.
+ * There may be problems there that I missed, so this shouldn't go into
+ * an offical kernel any time soon.
+ * However, if I didn't commit it, we wouldn't find the bugs...
+ *
+ * Revision 1.44  2000/11/25 17:00:59  kai
+ * compatibility cleanup - final part for the time being
+ *
+ * Revision 1.43  2000/11/23 20:45:14  kai
+ * fixed module_init/exit stuff
+ * Note: compiled-in kernel doesn't work pre 2.2.18 anymore.
+ *
+ * Revision 1.42  2000/11/19 17:03:55  kai
+ * compatibility cleanup - part 5
+ *
+ * Revision 1.41  2000/11/01 14:05:02  calle
+ * - use module_init/module_exit from linux/init.h.
+ * - all static struct variables are initialized with "membername:" now.
+ * - avm_cs.c, let it work with newer pcmcia-cs.
+ *
+ * Revision 1.40  2000/10/24 15:15:04  calle
+ * Workaround: pppd calls restoretty before reseting the ldisc and
+ *   ldisc "ppp_sync" didn't support this. So we call n_tty_ioctl
+ *   in the driver ioctl function. (remember: driver ioctl function is
+ *   only called if ldisc ioctl function did not handle the call)
+ *
+ * Revision 1.39  2000/07/24 13:42:50  calle
+ * - lock_kernel/unlock_kernel for _release functions. (from 2.4)
+ *
+ * Revision 1.38  2000/07/24 08:49:09  calle
+ * - Bugfix: capiminor_del_all_ack completely wrong :-(
+ *
+ * Revision 1.37  2000/07/20 10:22:27  calle
+ * - Made procfs function cleaner and removed variable "begin".
+ *
+ * Revision 1.36  2000/06/29 13:59:35  calle
+ * - call to devfs_register was wrong
+ *
+ * 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 +229,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.44.6.3 $";
 
 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 +936,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 +997,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 +1152,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 +1161,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 (file->private_data)
+		return -EEXIST;
 
-	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++;
-
-	} 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) {
+	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 +1892,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 +1940,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 +1968,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.19p6/drivers/isdn/avmb1/capicmd.h
--- linux.vanilla/drivers/isdn/avmb1/capicmd.h	Fri Apr 21 12:46:08 2000
+++ linux.19p6/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.19p6/drivers/isdn/avmb1/capidev.h
--- linux.vanilla/drivers/isdn/avmb1/capidev.h	Fri Apr 21 12:46:08 2000
+++ linux.19p6/drivers/isdn/avmb1/capidev.h	Sat Dec 30 19:33:51 2000
@@ -1,11 +1,25 @@
 /*
- * $Id: capidev.h,v 1.4 1999/07/01 15:26:32 calle Exp $
+ * $Id: capidev.h,v 1.6 2000/11/25 17:00:59 kai Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidev.h,v $
+ * Revision 1.6  2000/11/25 17:00:59  kai
+ * compatibility cleanup - final part for the time being
+ *
+ * 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 +54,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.19p6/drivers/isdn/avmb1/capidrv.c
--- linux.vanilla/drivers/isdn/avmb1/capidrv.c	Fri Apr 21 12:46:08 2000
+++ linux.19p6/drivers/isdn/avmb1/capidrv.c	Sat Dec 30 19:33:51 2000
@@ -1,11 +1,58 @@
 /*
- * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $
+ * $Id: capidrv.c,v 1.39 2000/11/23 20:45:14 kai Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.39  2000/11/23 20:45:14  kai
+ * fixed module_init/exit stuff
+ * Note: compiled-in kernel doesn't work pre 2.2.18 anymore.
+ *
+ * Revision 1.38  2000/11/14 08:43:07  calle
+ * Bugfix for v110. Connectparamters where setup for sync ...
+ *
+ * Revision 1.37  2000/11/01 14:05:02  calle
+ * - use module_init/module_exit from linux/init.h.
+ * - all static struct variables are initialized with "membername:" now.
+ * - avm_cs.c, let it work with newer pcmcia-cs.
+ *
+ * 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 +218,8 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.29 $";
-int debugmode = 0;
+static char *revision = "$Revision: 1.39 $";
+static int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 MODULE_PARM(debugmode, "i");
@@ -280,6 +327,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 +353,8 @@
 		return 2;
         case ISDN_PROTO_L2_FAX:
 		return 4;
+	case ISDN_PROTO_L2_MODEM:
+		return 8;
 	}
 }
 
@@ -321,6 +371,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 +389,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 +491,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 +1555,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 +1599,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 +2038,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 +2195,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 +2222,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 +2246,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 +2255,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 +2320,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;
 }
 
@@ -2421,8 +2475,8 @@
 }
 
 static struct capi_interface_user cuser = {
-	"capidrv",
-	lower_callback
+	name: "capidrv",
+	callback: lower_callback
 };
 
 #ifdef MODULE
@@ -2438,10 +2492,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 +2514,7 @@
 	errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
 	if (errcode) {
 		detach_capi_interface(&cuser);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -2463,6 +2522,7 @@
 	if (errcode != CAPI_NOERROR) {
 		(void) (*capifuncs->capi_release) (global.appid);
 		detach_capi_interface(&cuser);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -2477,17 +2537,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 +2557,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.19p6/drivers/isdn/avmb1/capifs.c
--- linux.vanilla/drivers/isdn/avmb1/capifs.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/avmb1/capifs.c	Sat Dec 30 19:33:51 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.19p6/drivers/isdn/avmb1/capifs.h
--- linux.vanilla/drivers/isdn/avmb1/capifs.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/drivers/isdn/avmb1/capiutil.c
--- linux.vanilla/drivers/isdn/avmb1/capiutil.c	Fri Apr 21 12:46:08 2000
+++ linux.19p6/drivers/isdn/avmb1/capiutil.c	Sat Dec 30 19:33:51 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: capiutil.c,v 1.10 1999/08/31 11:19:54 paul Exp $
+ * $Id: capiutil.c,v 1.13 2000/11/23 20:45:14 kai Exp $
  *
  * CAPI 2.0 convert capi message to capi message struct
  *
@@ -7,6 +7,26 @@
  * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capiutil.c,v $
+ * Revision 1.13  2000/11/23 20:45:14  kai
+ * fixed module_init/exit stuff
+ * Note: compiled-in kernel doesn't work pre 2.2.18 anymore.
+ *
+ * Revision 1.12  2000/11/01 14:05:02  calle
+ * - use module_init/module_exit from linux/init.h.
+ * - all static struct variables are initialized with "membername:" now.
+ * - avm_cs.c, let it work with newer pcmcia-cs.
+ *
+ * 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 +814,7 @@
     /*15 */ "Class",
     /*16 */ "ConnectedNumber",
     /*17 */ "ConnectedSubaddress",
-    /*18 */ "Data",
+    /*18 */ "Data32",
     /*19 */ "DataHandle",
     /*1a */ "DataLength",
     /*1b */ "FacilityConfirmationParameter",
@@ -892,13 +912,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:
@@ -972,7 +986,6 @@
 	protocol_message_2_pars(cmsg, 1);
 	return buf;
 }
-
 
 EXPORT_SYMBOL(capi_cmsg2message);
 EXPORT_SYMBOL(capi_message2cmsg);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/capiutil.h linux.19p6/drivers/isdn/avmb1/capiutil.h
--- linux.vanilla/drivers/isdn/avmb1/capiutil.h	Fri Apr 21 23:23:03 2000
+++ linux.19p6/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.19p6/drivers/isdn/avmb1/kcapi.c
--- linux.vanilla/drivers/isdn/avmb1/kcapi.c	Sun Jun 11 21:44:13 2000
+++ linux.19p6/drivers/isdn/avmb1/kcapi.c	Sat Dec 30 19:33:51 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.21.6.1 $";
 
 /* ------------------------------------------------------------- */
 
@@ -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,8 +1755,9 @@
 	char *p;
 	char rev[10];
 
+	MOD_INC_USE_COUNT;
+
 	skb_queue_head_init(&recv_queue);
-	/* init_bh(CAPI_BH, do_capi_bh); */
 
 	tq_state_notify.routine = notify_handler;
 	tq_state_notify.data = 0;
@@ -1606,6 +1797,7 @@
 	(void)c4_init();
 #endif
 #endif
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1623,7 +1815,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.19p6/drivers/isdn/avmb1/t1isa.c
--- linux.vanilla/drivers/isdn/avmb1/t1isa.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/avmb1/t1isa.c	Sat Dec 30 19:33:51 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.16 $";
 
 /* ------------------------------------------------------------- */
 
@@ -190,7 +194,7 @@
 	struct sk_buff *skb;
 
 	unsigned ApplId;
-	signed MsgLen;
+	unsigned MsgLen;
 	unsigned DataB3Len;
 	unsigned NCCI;
 	unsigned WindowSize;
@@ -278,25 +282,30 @@
 		case RECEIVE_TASK_READY:
 			ApplId = (unsigned) b1_get_word(card->port);
 			MsgLen = t1_get_slice(card->port, card->msgbuf);
-			card->msgbuf[MsgLen--] = 0;
-			while (    MsgLen >= 0
-			       && (   card->msgbuf[MsgLen] == '\n'
-				   || card->msgbuf[MsgLen] == '\r'))
-				card->msgbuf[MsgLen--] = 0;
+			card->msgbuf[MsgLen] = 0;
+			while (    MsgLen > 0
+			       && (   card->msgbuf[MsgLen-1] == '\n'
+				   || card->msgbuf[MsgLen-1] == '\r')) {
+				card->msgbuf[MsgLen-1] = 0;
+				MsgLen--;
+			}
 			printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
 					card->name, ApplId, card->msgbuf);
 			break;
 
 		case RECEIVE_DEBUGMSG:
 			MsgLen = t1_get_slice(card->port, card->msgbuf);
-			card->msgbuf[MsgLen--] = 0;
-			while (    MsgLen >= 0
-			       && (   card->msgbuf[MsgLen] == '\n'
-				   || card->msgbuf[MsgLen] == '\r'))
-				card->msgbuf[MsgLen--] = 0;
+			card->msgbuf[MsgLen] = 0;
+			while (    MsgLen > 0
+			       && (   card->msgbuf[MsgLen-1] == '\n'
+				   || card->msgbuf[MsgLen-1] == '\r')) {
+				card->msgbuf[MsgLen-1] = 0;
+				MsgLen--;
+			}
 			printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
 			break;
 
+
 		case 0xff:
 			printk(KERN_ERR "%s: card reseted ?\n", card->name);
 			return;
@@ -573,20 +582,20 @@
 /* ------------------------------------------------------------- */
 
 static struct capi_driver t1isa_driver = {
-    "t1isa",
-    "0.0",
-    t1isa_load_firmware,
-    t1isa_reset_ctr,
-    t1isa_remove_ctr,
-    b1_register_appl,
-    b1_release_appl,
-    t1isa_send_message,
-
-    t1isa_procinfo,
-    b1ctl_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "t1isa",
+    revision: "0.0",
+    load_firmware: t1isa_load_firmware,
+    reset_ctr: t1isa_reset_ctr,
+    remove_ctr: t1isa_remove_ctr,
+    register_appl: b1_register_appl,
+    release_appl: b1_release_appl,
+    send_message: t1isa_send_message,
+
+    procinfo: t1isa_procinfo,
+    ctr_read_proc: b1ctl_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    t1isa_add_card,
+    add_card: t1isa_add_card,
 };
 
 #ifdef MODULE
@@ -598,6 +607,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 +624,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.19p6/drivers/isdn/avmb1/t1pci.c
--- linux.vanilla/drivers/isdn/avmb1/t1pci.c	Fri Apr 21 12:46:08 2000
+++ linux.19p6/drivers/isdn/avmb1/t1pci.c	Sat Dec 30 19:33:51 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,28 +56,19 @@
 #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.13.6.1 $";
 
 #undef CONFIG_T1PCI_DEBUG
 #undef CONFIG_T1PCI_POLLDEBUG
 
 /* ------------------------------------------------------------- */
 
-#ifndef PCI_VENDOR_ID_AVM
-#define PCI_VENDOR_ID_AVM	0x1244
-#endif
-
-#ifndef PCI_DEVICE_ID_AVM_T1
-#define PCI_DEVICE_ID_AVM_T1	0x1200
-#endif
-
-/* ------------------------------------------------------------- */
-
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 
 /* ------------------------------------------------------------- */
@@ -164,7 +169,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);
@@ -234,20 +243,20 @@
 /* ------------------------------------------------------------- */
 
 static struct capi_driver t1pci_driver = {
-    "t1pci",
-    "0.0",
-    b1dma_load_firmware,
-    b1dma_reset_ctr,
-    t1pci_remove_ctr,
-    b1dma_register_appl,
-    b1dma_release_appl,
-    b1dma_send_message,
-
-    t1pci_procinfo,
-    b1dmactl_read_proc,
-    0,	/* use standard driver_read_proc */
+    name: "t1pci",
+    revision: "0.0",
+    load_firmware: b1dma_load_firmware,
+    reset_ctr: b1dma_reset_ctr,
+    remove_ctr: t1pci_remove_ctr,
+    register_appl: b1dma_register_appl,
+    release_appl: b1dma_release_appl,
+    send_message: b1dma_send_message,
+
+    procinfo: t1pci_procinfo,
+    ctr_read_proc: b1dmactl_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
 
-    0, /* no add_card function */
+    add_card: 0, /* no add_card function */
 };
 
 #ifdef MODULE
@@ -264,6 +273,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 +288,7 @@
 	if (!di) {
 		printk(KERN_ERR "%s: failed to attach capi_driver\n",
 				driver->name);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -284,6 +296,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 +304,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 +330,7 @@
 #ifdef MODULE
 			cleanup_module();
 #endif
+			MOD_DEC_USE_COUNT;
 			return retval;
 		}
 		ncards++;
@@ -312,12 +338,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_init.c linux.19p6/drivers/isdn/divert/divert_init.c
--- linux.vanilla/drivers/isdn/divert/divert_init.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/divert/divert_init.c	Sat Dec 30 19:37:11 2000
@@ -1,5 +1,5 @@
 /* 
- * $Id: divert_init.c,v 1.5 2000/11/13 22:51:47 kai Exp $
+ * $Id: divert_init.c,v 1.5.6.1 2000/12/17 16:47:16 kai Exp $
  *
  * Module init for DSS1 diversion services for i4l.
  *
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/divert/divert_procfs.c linux.19p6/drivers/isdn/divert/divert_procfs.c
--- linux.vanilla/drivers/isdn/divert/divert_procfs.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/divert/divert_procfs.c	Sat Dec 30 19:33:51 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: divert_procfs.c,v 1.10 2000/11/13 22:51:47 kai Exp $
+ * $Id: divert_procfs.c,v 1.11 2000/11/25 17:01:00 kai Exp $
  *
  * Filesystem handling for the diversion supplementary services.
  *
@@ -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/divert/isdn_divert.c linux.19p6/drivers/isdn/divert/isdn_divert.c
--- linux.vanilla/drivers/isdn/divert/isdn_divert.c	Fri Apr 21 12:46:08 2000
+++ linux.19p6/drivers/isdn/divert/isdn_divert.c	Sat Dec 30 19:37:57 2000
@@ -1,5 +1,5 @@
 /* 
- * $Id: isdn_divert.c,v 1.5 1999/08/31 11:20:04 paul Exp $
+ * $Id: isdn_divert.c,v 1.6 2000/11/13 22:51:47 kai Exp $
  *
  * DSS1 main diversion supplementary handling for i4l.
  *
@@ -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: isdn_divert.c,v $
- * Revision 1.5  1999/08/31 11:20:04  paul
- * various spelling corrections (new checksums may be needed, Karsten!)
- *
- * Revision 1.4  1999/08/25 20:02:21  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 1.3  1999/08/22 20:26:35  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.2  1999/07/04 21:37:32  werner
- * Ported from kernel version 2.0
- *
- *
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/divert/isdn_divert.h linux.19p6/drivers/isdn/divert/isdn_divert.h
--- linux.vanilla/drivers/isdn/divert/isdn_divert.h	Fri Apr 21 23:22:31 2000
+++ linux.19p6/drivers/isdn/divert/isdn_divert.h	Wed Jan  3 14:14:19 2001
@@ -1,5 +1,5 @@
 /* 
- * $Id: isdn_divert.h,v 1.4 1999/09/02 13:24:12 paul Exp $
+ * $Id: isdn_divert.h,v 1.5 2000/11/13 22:51:47 kai Exp $
  *
  * Header for the diversion supplementary ioctl interface.
  *
@@ -18,19 +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: isdn_divert.h,v $
- * Revision 1.4  1999/09/02 13:24:12  paul
- * cosmetics; text following #endif is not ANSI C
- *
- * Revision 1.3  1999/08/22 20:26:37  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.2  1999/07/04 21:37:33  werner
- * Ported from kernel version 2.0
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/Makefile linux.19p6/drivers/isdn/hisax/Makefile
--- linux.vanilla/drivers/isdn/hisax/Makefile	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/amd7930.c
--- linux.vanilla/drivers/isdn/hisax/amd7930.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/amd7930.c	Sat Dec 30 19:33:51 2000
@@ -1,25 +1,9 @@
-/* $Id: amd7930.c,v 1.3 1999/07/12 21:04:52 keil Exp $
+/* $Id: amd7930.c,v 1.5 2000/11/24 17:05:37 kai 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.5 $";
 
 #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.19p6/drivers/isdn/hisax/arcofi.c
--- linux.vanilla/drivers/isdn/hisax/arcofi.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/arcofi.c	Sat Dec 30 19:33:51 2000
@@ -1,43 +1,10 @@
-/* $Id: arcofi.c,v 1.10 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: arcofi.c,v 1.12 2000/11/25 17:01:00 kai 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.19p6/drivers/isdn/hisax/arcofi.h
--- linux.vanilla/drivers/isdn/hisax/arcofi.h	Fri Apr 21 12:46:09 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/asuscom.c
--- linux.vanilla/drivers/isdn/hisax/asuscom.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/asuscom.c	Sat Dec 30 19:33:51 2000
@@ -1,39 +1,12 @@
-/* $Id: asuscom.c,v 1.9 1999/12/19 13:09:41 keil Exp $
-
+/* $Id: asuscom.c,v 1.11 2000/11/24 17:05:37 kai 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.11 $";
 
 #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.19p6/drivers/isdn/hisax/avm_a1.c
--- linux.vanilla/drivers/isdn/hisax/avm_a1.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/avm_a1.c	Sat Dec 30 19:33:51 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.13 2000/11/24 17:05:37 kai 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.13 $";
 
 #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.19p6/drivers/isdn/hisax/avm_a1p.c
--- linux.vanilla/drivers/isdn/hisax/avm_a1p.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/avm_a1p.c	Sat Dec 30 19:33:51 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.7 2000/11/24 17:05:37 kai 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.7 $";
 
 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.19p6/drivers/isdn/hisax/avm_pci.c
--- linux.vanilla/drivers/isdn/hisax/avm_pci.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/avm_pci.c	Sat Dec 30 19:33:51 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.22.6.2 2000/11/29 16:00:14 kai 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,14 +17,11 @@
 #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.22.6.2 $";
 
 #define  AVM_FRITZ_PCI		1
 #define  AVM_FRITZ_PNP		2
 
-#define  PCI_VENDOR_AVM		0x1244
-#define  PCI_FRITZPCI_ID	0xa00
-
 #define  HDLC_FIFO		0x0
 #define  HDLC_STATUS		0x4
 
@@ -339,7 +290,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 +354,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 +718,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 +780,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_A1,  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.19p6/drivers/isdn/hisax/bkm_a4t.c
--- linux.vanilla/drivers/isdn/hisax/bkm_a4t.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/bkm_a4t.c	Sat Dec 30 19:33:51 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.13.6.2 2000/11/29 16:00:14 kai 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.13.6.2 $";
 
 
 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 == PCI_DEVICE_ID_BERKOM_A4T) && (sub_vendor == PCI_VENDOR_ID_BERKOM)) {
+			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.19p6/drivers/isdn/hisax/bkm_a8.c
--- linux.vanilla/drivers/isdn/hisax/bkm_a8.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/bkm_a8.c	Sat Dec 30 19:33:51 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.14.6.2 2000/11/29 16:00:14 kai 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.14.6.2 $";
 
 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 != PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) ||
+		(sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
 		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 == PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) &&
+				(sub_vendor_id == PCI_VENDOR_ID_BERKOM)) {
+				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,8 @@
 		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");
+	return (0);
+#endif /* CONFIG_PCI */
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/bkm_ax.h linux.19p6/drivers/isdn/hisax/bkm_ax.h
--- linux.vanilla/drivers/isdn/hisax/bkm_ax.h	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/bkm_ax.h	Sat Dec 30 19:33:51 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.5.6.1 2000/11/28 12:02:46 kai 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
  *
  */
 
@@ -20,19 +16,6 @@
 #define	SCT_3		3
 #define	SCT_4		4
 #define BKM_A4T		5
-
-
-/* A4T																*/
-#define I20_DEVICE_ID       	0x6120  /* I20 PCI device ID */
-#define I20_VENDOR_ID       	0x11DE  /* I20 PCI vendor ID */
-#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
-#define	SCT_SUBVEN_ID		0x0871
-#define	SCT_SUBSYS_ID		0xFFA8
-
 
 #define	PLX_ADDR_PLX		0x14	/* Addr PLX configuration */
 #define	PLX_ADDR_ISAC		0x18	/* Addr ISAC */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/callc.c linux.19p6/drivers/isdn/hisax/callc.c
--- linux.vanilla/drivers/isdn/hisax/callc.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/callc.c	Sat Dec 30 19:33:51 2000
@@ -1,5 +1,5 @@
-/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $
-
+/* $Id: callc.c,v 2.51 2000/11/24 17:05:37 kai 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.51 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -177,10 +30,8 @@
 static int init_b_st(struct Channel *chanp, int incoming);
 static void release_b_st(struct Channel *chanp);
 
-static struct Fsm callcfsm =
-{NULL, 0, 0, NULL, NULL};
-
-static int chancount = 0;
+static struct Fsm callcfsm;
+static int chancount;
 
 /* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
 #define ALERT_REJECT 0
@@ -349,6 +200,8 @@
 {
 	isdn_ctrl ic;
 
+	if (!chanp->proc)
+		return;
 	if (chanp->proc->para.cause == NO_CAUSE)
 		return;
 	ic.driver = chanp->cs->myid;
@@ -511,7 +364,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 +381,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 +498,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 +514,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 +547,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 +797,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 +960,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 +1110,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 +1183,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 +1215,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 +1245,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 +1520,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 +1540,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 +1737,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.19p6/drivers/isdn/hisax/cert.c
--- linux.vanilla/drivers/isdn/hisax/cert.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/config.c
--- linux.vanilla/drivers/isdn/hisax/config.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/hisax/config.c	Sat Dec 30 19:33:51 2000
@@ -1,161 +1,9 @@
-/* $Id: config.c,v 2.44 2000/02/26 00:35:12 keil Exp $
-
+/* $Id: config.c,v 2.57.6.7 2000/12/17 16:47:17 kai 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, \
@@ -476,18 +338,14 @@
 	EMPTY_CARD,
 };
 
-static char HiSaxID[64] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
-char *HiSax_id HISAX_INITDATA = HiSaxID;
+static char HiSaxID[64]  = { 0, };
+
+char *HiSax_id  = HiSaxID;
 #ifdef MODULE
 /* Variables for insmod */
-static int type[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
-static int protocol[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
-static int io[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
+static int type[8]  = { 0, };
+static int protocol[8]  = { 0, };
+static int io[8]  = { 0, };
 #undef IO0_IO1
 #ifdef CONFIG_HISAX_16_3
 #define IO0_IO1
@@ -497,16 +355,12 @@
 #define IO0_IO1
 #endif
 #ifdef IO0_IO1
-static int io0[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
-static int io1[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
-#endif
-static int irq[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
-static int mem[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0};
-static char *id HISAX_INITDATA = HiSaxID;
+static int io0[8]  = { 0, };
+static int io1[8]  = { 0, };
+#endif
+static int irq[8]  = { 0, };
+static int mem[8]  = { 0, };
+static char *id  = HiSaxID;
 
 MODULE_AUTHOR("Karsten Keil");
 MODULE_PARM(type, "1-8i");
@@ -551,9 +405,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 +543,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 +595,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 +801,7 @@
 	ic.command = ISDN_STAT_STOP;
 	ic.driver = cs->myid;
 	cs->iif.statcallb(&ic);
-	CallcFreeChan(cs);
+//	CallcFreeChan(cs);
 }
 
 static void
@@ -1012,7 +870,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 +924,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 +1070,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 +1136,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 +1260,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);
@@ -1420,7 +1288,7 @@
 	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
 		(ulong) & HiSax_reportcard);
 	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
-	printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc1 flg %x\n",
+	printk(KERN_DEBUG "HiSax: HW_Flags %lx bc0 flg %lx bc1 flg %lx\n",
 		cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
 	printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
 		cs->bcs[0].mode, cs->bcs[0].channel);
@@ -1453,10 +1321,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 +1352,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 +1422,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 +1566,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 +1712,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.19p6/drivers/isdn/hisax/diva.c
--- linux.vanilla/drivers/isdn/hisax/diva.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/diva.c	Sat Dec 30 19:33:51 2000
@@ -1,5 +1,5 @@
-/* $Id: diva.c,v 1.18 1999/12/19 13:09:41 keil Exp $
-
+/* $Id: diva.c,v 1.25.6.2 2000/11/29 16:00:14 kai 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.25.6.2 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -107,14 +46,6 @@
 #define DIVA_IPAC_ISA	3
 #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
-
 /* CTRL (Read) */
 #define DIVA_IRQ_STAT	0x01
 #define DIVA_EEPROM_SDA	0x02
@@ -131,10 +62,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 +120,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 +691,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 +868,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.19p6/drivers/isdn/hisax/elsa.c
--- linux.vanilla/drivers/isdn/hisax/elsa.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/elsa.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: elsa.c,v 2.20 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: elsa.c,v 2.26.6.1 2000/11/28 12:02:46 kai 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.26.6.1 $";
 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,6 @@
 #define ELSA_PCMCIA_IPAC 11
 
 /* PCI stuff */
-#define PCI_VENDOR_ELSA	0x1048
-#define PCI_QS1000_ID	0x1000
-#define PCI_QS3000_ID	0x3000
 #define ELSA_PCI_IRQ_MASK	0x04
 
 /* ITAC Registeradressen (only Microlink PC) */
@@ -582,10 +505,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 +712,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 +720,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 {
@@ -932,8 +856,8 @@
 	return (CARD_portlist[i]);
 }
 
-static 	struct pci_dev *dev_qs1000 __initdata = NULL;
-static 	struct pci_dev *dev_qs3000 __initdata = NULL;
+static 	struct pci_dev *dev_qs1000  = NULL;
+static 	struct pci_dev *dev_qs3000  = NULL;
 
 int
 setup_elsa(struct IsdnCard *card)
@@ -1056,22 +980,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_MICROLINK, 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.19p6/drivers/isdn/hisax/elsa_ser.c
--- linux.vanilla/drivers/isdn/hisax/elsa_ser.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/elsa_ser.c	Sat Dec 30 19:33:52 2000
@@ -1,3 +1,10 @@
+/* $Id: elsa_ser.c,v 2.10 2000/11/19 17:02:47 kai 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.19p6/drivers/isdn/hisax/fsm.c
--- linux.vanilla/drivers/isdn/hisax/fsm.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/fsm.c	Sat Dec 30 19:33:52 2000
@@ -1,48 +1,12 @@
-/* $Id: fsm.c,v 1.11 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: fsm.c,v 1.14 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/gazel.c
--- linux.vanilla/drivers/isdn/hisax/gazel.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/gazel.c	Sat Dec 30 19:33:52 2000
@@ -1,32 +1,11 @@
-/* $Id: gazel.c,v 2.6 1999/08/22 20:27:03 calle Exp $
-
+/* $Id: gazel.c,v 2.11.6.2 2000/11/29 16:00:14 kai 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,19 +18,13 @@
 #include <linux/pci.h>
 
 extern const char *CardType[];
-const char *gazel_revision = "$Revision: 2.6 $";
+const char *gazel_revision = "$Revision: 2.11.6.2 $";
 
 #define R647      1
 #define R685      2
 #define R753      3
 #define R742      4
 
-/* Gazel R685 stuff */
-#define GAZEL_MANUFACTURER     0x10b5
-#define GAZEL_R685             0x1030
-#define GAZEL_R753             0x1152
-#define GAZEL_DJINN_ITOO       0x1151
-
 #define PLX_CNTRL    0x50	/* registre de controle PLX */
 #define RESET_GAZEL  0x4
 #define RESET_9050   0x40000000
@@ -586,24 +559,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 +606,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 +617,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.19p6/drivers/isdn/hisax/hfc_2bds0.c
--- linux.vanilla/drivers/isdn/hisax/hfc_2bds0.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/hfc_2bds0.c	Sat Dec 30 19:33:52 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.15 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/hfc_2bds0.h
--- linux.vanilla/drivers/isdn/hisax/hfc_2bds0.h	Fri Apr 21 12:46:09 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/hfc_2bs0.c
--- linux.vanilla/drivers/isdn/hisax/hfc_2bs0.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/hfc_2bs0.c	Sat Dec 30 19:33:52 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.17 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/hfc_2bs0.h
--- linux.vanilla/drivers/isdn/hisax/hfc_2bs0.h	Fri Apr 21 12:46:09 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/hfc_pci.c
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/hisax/hfc_pci.c	Sat Dec 30 19:33:52 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.34.6.3 2000/12/10 23:39:19 kai 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.34.6.3 $";
 
 /* table entry in the PCI devices list */
 typedef struct {
@@ -139,26 +44,32 @@
 	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[] =
 {
-	{0x1397, 0x2BD0, "CCD/Billion/Asuscom", "2BD0"},
-	{0x1397, 0xB000, "Billion", "B000"},
-	{0x1397, 0xB006, "Billion", "B006"},
-	{0x1397, 0xB007, "Billion", "B007"},
-	{0x1397, 0xB008, "Billion", "B008"},
-	{0x1397, 0xB009, "Billion", "B009"},
-	{0x1397, 0xB00A, "Billion", "B00A"},
-	{0x1397, 0xB00B, "Billion", "B00B"},
-	{0x1397, 0xB00C, "Billion", "B00C"},
-	{0x1043, 0x0675, "Asuscom/Askey", "675"},
-	{0x0871, 0xFFA2, "German telekom", "T-Concept"},
-	{0x0871, 0xFFA1, "German telekom", "A1T"},
-	{0x1051, 0x0100, "Motorola MC145575", "MC145575"},
-	{0x1397, 0xB100, "Seyeon", "B100"},
-	{0x15B0, 0x2BD0, "Zoltrix", "2BD0"},
-	{0x114f, 0x71,   "Digi intl.","Digicom"}, 
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"},
+	{PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"},
+	{PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"},
+	{PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"},
+	{PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"},
 	{0, 0, NULL, NULL},
 };
 
@@ -180,7 +91,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 +122,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 +136,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 +260,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 +329,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 +518,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 +755,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 +1611,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 +1641,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 +1662,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 +1687,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 +1720,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.19p6/drivers/isdn/hisax/hfc_pci.h
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/hfc_sx.c
--- linux.vanilla/drivers/isdn/hisax/hfc_sx.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/hfc_sx.c	Sat Dec 30 19:33:52 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.9 2000/11/24 17:05:37 kai 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.9 $";
 
 /***************************************/
 /* 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.19p6/drivers/isdn/hisax/hfc_sx.h
--- linux.vanilla/drivers/isdn/hisax/hfc_sx.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/hfcscard.c
--- linux.vanilla/drivers/isdn/hisax/hfcscard.c	Fri Apr 21 12:46:09 2000
+++ linux.19p6/drivers/isdn/hisax/hfcscard.c	Sat Dec 30 19:33:52 2000
@@ -1,29 +1,10 @@
-/* $Id: hfcscard.c,v 1.6 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: hfcscard.c,v 1.8 2000/11/24 17:05:37 kai 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.8 $";
 
 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.19p6/drivers/isdn/hisax/hisax.h
--- linux.vanilla/drivers/isdn/hisax/hisax.h	Wed May  3 22:33:45 2000
+++ linux.19p6/drivers/isdn/hisax/hisax.h	Wed Jan  3 14:14:21 2001
@@ -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.52.6.1 2000/12/06 16:59:19 kai 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,12 +195,13 @@
 #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;
 	struct BCState *bcs;
 	struct PStack **stlistp;
-	int Flags;
+	long Flags;
 	struct FsmInst l1m;
 	struct FsmTimer	timer;
 	void (*l1l2) (struct PStack *, int, void *);
@@ -362,7 +240,7 @@
 	int tei;
 	int sap;
 	int maxlen;
-	unsigned int flag;
+	unsigned long flag;
 	unsigned int vs, va, vr;
 	int rc;
 	unsigned int window;
@@ -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;
 };
 
@@ -481,7 +365,7 @@
 };
 
 struct isar_reg {
-	unsigned int Flags;
+	unsigned long Flags;
 	volatile u_char bstat;
 	volatile u_char iis;
 	volatile u_char cmsb;
@@ -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 {
@@ -590,7 +482,7 @@
 struct BCState {
 	int channel;
 	int mode;
-	int Flag;
+	long Flag; /* long req'd for set_bit --RR */
 	struct IsdnCardState *cs;
 	int tx_cnt;		/* B-Channel transmit counter */
 	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
@@ -635,7 +527,8 @@
 	int data_open;
 	struct l3_process *proc;
 	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
-	int Flags;		/* for remembering action done in l4 */
+	long Flags;		/* for remembering action done in l4 */
+				/* long req'd for set_bit --RR */
 	int leased;
 };
 
@@ -938,6 +831,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 +857,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;
@@ -955,7 +865,7 @@
 	int protocol;
 	unsigned int irq;
 	unsigned long irq_flags;
-	int HW_Flags;
+	long HW_Flags;
 	int *busy_flag;
         int chanlimit; /* limited number of B-chans to use */
         int logecho; /* log echo if supported by card */
@@ -1016,12 +926,13 @@
 		struct hfcpci_chip hfcpci;
 		struct hfcsx_chip hfcsx;
 		struct w6692_chip w6692;
+		struct icc_chip icc;
 	} dc;
 	u_char *rcvbuf;
 	int rcvidx;
 	struct sk_buff *tx_skb;
 	int tx_cnt;
-	int event;
+	long event;
 	struct tq_struct tqueue;
 	struct timer_list dbusytimer;
 #ifdef ERROR_STATISTIC
@@ -1057,7 +968,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 +986,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 +1137,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 +1250,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 +1286,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 +1363,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.19p6/drivers/isdn/hisax/hscx.c
--- linux.vanilla/drivers/isdn/hisax/hscx.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/hscx.c	Sat Dec 30 19:33:52 2000
@@ -1,61 +1,10 @@
-/* $Id: hscx.c,v 1.17 1999/07/01 08:11:41 keil Exp $
-
+/* $Id: hscx.c,v 1.21 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/hscx.h
--- linux.vanilla/drivers/isdn/hisax/hscx.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/hscx_irq.c
--- linux.vanilla/drivers/isdn/hisax/hscx_irq.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/hscx_irq.c	Sat Dec 30 19:33:52 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.16 2000/11/19 17:02:47 kai 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.19p6/drivers/isdn/hisax/icc.c
--- linux.vanilla/drivers/isdn/hisax/icc.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/hisax/icc.c	Sat Dec 30 19:33:52 2000
@@ -0,0 +1,685 @@
+// $Id: icc.c,v 1.5 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/icc.h
--- linux.vanilla/drivers/isdn/hisax/icc.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/drivers/isdn/hisax/ipac.h
--- linux.vanilla/drivers/isdn/hisax/ipac.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/isac.c
--- linux.vanilla/drivers/isdn/hisax/isac.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/isac.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: isac.c,v 1.24 1999/10/14 20:25:28 keil Exp $
-
+/* $Id: isac.c,v 1.28 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/isac.h
--- linux.vanilla/drivers/isdn/hisax/isac.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/isar.c
--- linux.vanilla/drivers/isdn/hisax/isar.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/isar.c	Sat Dec 30 19:33:52 2000
@@ -1,39 +1,10 @@
-/* $Id: isar.c,v 1.9 2000/01/20 19:47:45 keil Exp $
-
+/* $Id: isar.c,v 1.17 2000/11/24 17:05:37 kai 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.19p6/drivers/isdn/hisax/isar.h
--- linux.vanilla/drivers/isdn/hisax/isar.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/isdnl1.c
--- linux.vanilla/drivers/isdn/hisax/isdnl1.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/isdnl1.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnl1.c,v 2.37 2000/01/20 19:51:46 keil Exp $
-
+/* $Id: isdnl1.c,v 2.41.6.1 2000/12/10 22:01:04 kai 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.41.6.1 $";
 
 #define __NO_VERSION__
 #include "hisax.h"
@@ -150,13 +23,8 @@
 
 #define TIMER3_VALUE 7000
 
-static
-struct Fsm l1fsm_b =
-{NULL, 0, 0, NULL, NULL};
-
-static
-struct Fsm l1fsm_d =
-{NULL, 0, 0, NULL, NULL};
+static struct Fsm l1fsm_b;
+static struct Fsm l1fsm_s;
 
 enum {
 	ST_L1_F2,
@@ -168,9 +36,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 +49,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 +450,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 +460,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 +490,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 +504,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 +529,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 +561,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 +579,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 +596,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 +627,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 +738,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 +759,10 @@
 
 void Isdnl1Free(void)
 {
-	FsmFree(&l1fsm_d);
+#ifdef HISAX_UINTERFACE
+	FsmFree(&l1fsm_u);
+#endif
+	FsmFree(&l1fsm_s);
 	FsmFree(&l1fsm_b);
 }
 
@@ -804,7 +780,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 +860,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 +879,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.19p6/drivers/isdn/hisax/isdnl1.h
--- linux.vanilla/drivers/isdn/hisax/isdnl1.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/isdnl2.c
--- linux.vanilla/drivers/isdn/hisax/isdnl2.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/isdnl2.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnl2.c,v 2.20 1999/08/25 16:52:04 keil Exp $
-
+/* $Id: isdnl2.c,v 2.25 2000/11/24 17:05:38 kai Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
@@ -10,86 +10,16 @@
  * 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.25 $";
 
 static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
 
-static
-struct Fsm l2fsm =
-{NULL, 0, 0, NULL, NULL};
+static struct Fsm l2fsm;
 
 enum {
 	ST_L2_1,
@@ -361,7 +291,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 +301,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.19p6/drivers/isdn/hisax/isdnl2.h
--- linux.vanilla/drivers/isdn/hisax/isdnl2.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/isdnl3.c
--- linux.vanilla/drivers/isdn/hisax/isdnl3.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/isdnl3.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $
-
+/* $Id: isdnl3.c,v 2.17 2000/11/24 17:05:38 kai Exp $
+ *
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
@@ -10,69 +10,16 @@
  * 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.17 $";
 
-static
-struct Fsm l3fsm =
-{NULL, 0, 0, NULL, NULL};
+static struct Fsm l3fsm;
 
 enum {
 	ST_L3_LC_REL,
@@ -234,7 +181,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 +233,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 +322,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 +355,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 +543,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.19p6/drivers/isdn/hisax/isdnl3.h
--- linux.vanilla/drivers/isdn/hisax/isdnl3.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/isurf.c
--- linux.vanilla/drivers/isdn/hisax/isurf.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/isurf.c	Sat Dec 30 19:33:52 2000
@@ -1,38 +1,10 @@
-/* $Id: isurf.c,v 1.8 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: isurf.c,v 1.10 2000/11/24 17:05:38 kai 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.10 $";
 
 #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.19p6/drivers/isdn/hisax/ix1_micro.c
--- linux.vanilla/drivers/isdn/hisax/ix1_micro.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/ix1_micro.c	Sat Dec 30 19:33:52 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.10 2000/11/24 17:05:38 kai 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.10 $";
 
 #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.19p6/drivers/isdn/hisax/jade.c
--- linux.vanilla/drivers/isdn/hisax/jade.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/jade.c	Sat Dec 30 19:33:52 2000
@@ -1,13 +1,10 @@
-/* $Id: jade.c,v 1.2 1999/07/01 08:07:57 keil Exp $
+/* $Id: jade.c,v 1.6 2000/11/24 17:05:38 kai 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.19p6/drivers/isdn/hisax/jade.h
--- linux.vanilla/drivers/isdn/hisax/jade.h	Fri Apr 21 12:46:10 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/jade_irq.c
--- linux.vanilla/drivers/isdn/hisax/jade_irq.c	Fri Apr 21 12:46:10 2000
+++ linux.19p6/drivers/isdn/hisax/jade_irq.c	Sat Dec 30 19:33:52 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.5 2000/11/19 17:02:48 kai 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.19p6/drivers/isdn/hisax/l3_1tr6.c
--- linux.vanilla/drivers/isdn/hisax/l3_1tr6.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/l3_1tr6.c	Sat Dec 30 19:33:52 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.13 2000/11/19 17:02:48 kai 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.13 $";
 
 #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.19p6/drivers/isdn/hisax/l3_1tr6.h
--- linux.vanilla/drivers/isdn/hisax/l3_1tr6.h	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/l3dss1.c
--- linux.vanilla/drivers/isdn/hisax/l3dss1.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/l3dss1.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: l3dss1.c,v 2.23 2000/02/26 01:38:14 keil Exp $
-
+/* $Id: l3dss1.c,v 2.30 2000/11/19 17:02:48 kai 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.30 $";
 
 #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.19p6/drivers/isdn/hisax/l3dss1.h
--- linux.vanilla/drivers/isdn/hisax/l3dss1.h	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/l3ni1.c
--- linux.vanilla/drivers/isdn/hisax/l3ni1.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/hisax/l3ni1.c	Sat Dec 30 19:40:39 2000
@@ -0,0 +1,3175 @@
+// $Id: l3ni1.c,v 2.5.6.1 2000/12/06 16:59:19 kai Exp $
+//
+//-----------------------------------------------------------------------------
+//
+// NI1 D-channel protocol
+//
+// Authors:
+// 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.
+// NI-1 Hall of Fame - Thanks to....
+// Ragnar Paulson - for some handy code fragments
+// Will Scales - beta tester extraordinaire
+// Brett Whittacre - beta tester and remote devel system in Vegas
+//
+// This file is (c) under GNU PUBLIC LICENSE
+//
+//-----------------------------------------------------------------------------
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3ni1.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *ni1_revision = "$Revision: 2.5.6.1 $";
+
+#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.19p6/drivers/isdn/hisax/l3ni1.h
--- linux.vanilla/drivers/isdn/hisax/l3ni1.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/hisax/l3ni1.h	Sat Dec 30 19:33:52 2000
@@ -0,0 +1,137 @@
+// $Id: l3ni1.h,v 2.3 2000/11/16 13:50:43 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	5000 /* was 2000 - Guy Ellis */
+
+/*
+ * 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.19p6/drivers/isdn/hisax/lmgr.c
--- linux.vanilla/drivers/isdn/hisax/lmgr.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/md5sums.asc
--- linux.vanilla/drivers/isdn/hisax/md5sums.asc	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/mic.c
--- linux.vanilla/drivers/isdn/hisax/mic.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/mic.c	Sat Dec 30 19:33:52 2000
@@ -1,38 +1,12 @@
-/* $Id: mic.c,v 1.8 1999/07/12 21:05:20 keil Exp $
-
+/* $Id: mic.c,v 1.10 2000/11/24 17:05:38 kai 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.10 $";
 
 #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.19p6/drivers/isdn/hisax/netjet.c
--- linux.vanilla/drivers/isdn/hisax/netjet.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/netjet.c	Sat Dec 30 19:33:52 2000
@@ -1,67 +1,12 @@
-/* $Id: netjet.c,v 1.17 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: netjet.c,v 1.24.6.2 2000/12/17 22:45:11 kai 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.19p6/drivers/isdn/hisax/netjet.h
--- linux.vanilla/drivers/isdn/hisax/netjet.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/hisax/netjet.h	Sat Dec 30 19:33:52 2000
@@ -0,0 +1,70 @@
+// $Id: netjet.h,v 2.5.6.1 2000/11/28 12:02:46 kai 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)
+
+#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.19p6/drivers/isdn/hisax/niccy.c
--- linux.vanilla/drivers/isdn/hisax/niccy.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/niccy.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $
-
+/* $Id: niccy.c,v 1.15.6.2 2000/11/29 16:00:14 kai 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.15.6.2 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -59,8 +38,6 @@
 #define NICCY_PCI	2
 
 /* PCI stuff */
-#define PCI_VENDOR_DR_NEUHAUS	0x1267
-#define PCI_NICCY_ID		0x1016
 #define PCI_IRQ_CTRL_REG	0x38
 #define PCI_IRQ_ENABLE		0x1f00
 #define PCI_IRQ_DISABLE		0xff0000
@@ -228,12 +205,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 +282,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.19p6/drivers/isdn/hisax/nj_s.c
--- linux.vanilla/drivers/isdn/hisax/nj_s.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/hisax/nj_s.c	Sat Dec 30 19:33:52 2000
@@ -0,0 +1,258 @@
+// $Id: nj_s.c,v 2.7.6.1 2000/11/29 16:00:14 kai 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.7.6.1 $";
+
+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);
+
+#if CONFIG_PCI
+	for ( ;; )
+	{
+
+
+		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.19p6/drivers/isdn/hisax/nj_u.c
--- linux.vanilla/drivers/isdn/hisax/nj_u.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/isdn/hisax/nj_u.c	Sat Dec 30 19:41:05 2000
@@ -0,0 +1,261 @@
+/* $Id: nj_u.c,v 2.8.6.1 2000/11/29 16:00:14 kai 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.8.6.1 $";
+
+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);
+
+#if CONFIG_PCI
+	for ( ;; )
+	{
+
+
+		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.19p6/drivers/isdn/hisax/q931.c
--- linux.vanilla/drivers/isdn/hisax/q931.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/rawhdlc.c
--- linux.vanilla/drivers/isdn/hisax/rawhdlc.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/rawhdlc.h
--- linux.vanilla/drivers/isdn/hisax/rawhdlc.h	Fri Apr 21 12:46:11 2000
+++ linux.19p6/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.19p6/drivers/isdn/hisax/s0box.c
--- linux.vanilla/drivers/isdn/hisax/s0box.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/s0box.c	Sat Dec 30 19:33:52 2000
@@ -1,9 +1,10 @@
-/* $Id: s0box.c,v 2.2 1999/07/12 21:05:25 keil Exp $
-
+/* $Id: s0box.c,v 2.4 2000/11/24 17:05:38 kai 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.4 $";
 
 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.19p6/drivers/isdn/hisax/saphir.c
--- linux.vanilla/drivers/isdn/hisax/saphir.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/saphir.c	Sat Dec 30 19:33:52 2000
@@ -1,27 +1,12 @@
-/* $Id: saphir.c,v 1.5 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: saphir.c,v 1.8 2000/11/24 17:05:38 kai 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.8 $";
 
 #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.19p6/drivers/isdn/hisax/sedlbauer.c
--- linux.vanilla/drivers/isdn/hisax/sedlbauer.c	Fri Apr 21 12:46:11 2000
+++ linux.19p6/drivers/isdn/hisax/sedlbauer.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: sedlbauer.c,v 1.20 2000/01/20 19:47:45 keil Exp $
-
+/* $Id: sedlbauer.c,v 1.25.6.2 2000/11/29 17:48:59 kai 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,17 @@
 
 extern const char *CardType[];
 
-const char *Sedlbauer_revision = "$Revision: 1.20 $";
+const char *Sedlbauer_revision = "$Revision: 1.25.6.2 $";
 
 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
-#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 +69,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 +282,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 +357,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 +396,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 +432,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 +458,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 +507,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 +516,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 +528,7 @@
 	return(0);
 }
 
-#ifdef SEDLBAUER_PCI
 static 	struct pci_dev *dev_sedl __initdata = NULL;
-#endif
 
 __initfunc(int
 setup_sedlbauer(struct IsdnCard *card))
@@ -633,42 +566,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 +629,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 +688,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.19p6/drivers/isdn/hisax/sportster.c
--- linux.vanilla/drivers/isdn/hisax/sportster.c	Fri Apr 21 12:46:12 2000
+++ linux.19p6/drivers/isdn/hisax/sportster.c	Sat Dec 30 19:33:52 2000
@@ -1,49 +1,12 @@
-/* $Id: sportster.c,v 1.12 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: sportster.c,v 1.14 2000/11/24 17:05:38 kai 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.14 $";
 
 #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.19p6/drivers/isdn/hisax/tei.c
--- linux.vanilla/drivers/isdn/hisax/tei.c	Fri Apr 21 12:46:12 2000
+++ linux.19p6/drivers/isdn/hisax/tei.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $
-
+/* $Id: tei.c,v 2.17 2000/11/24 17:05:38 kai 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.17 $";
 
 #define ID_REQUEST	1
 #define ID_ASSIGNED	2
@@ -90,9 +28,7 @@
 
 #define TEI_ENTITY_ID	0xf
 
-static
-struct Fsm teifsm =
-{NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsm;
 
 void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb);
 
@@ -216,7 +152,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 +179,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.19p6/drivers/isdn/hisax/teleint.c
--- linux.vanilla/drivers/isdn/hisax/teleint.c	Fri Apr 21 12:46:12 2000
+++ linux.19p6/drivers/isdn/hisax/teleint.c	Sat Dec 30 19:33:52 2000
@@ -1,49 +1,10 @@
-/* $Id: teleint.c,v 1.12 1999/12/19 13:09:42 keil Exp $
-
+/* $Id: teleint.c,v 1.14 2000/11/24 17:05:38 kai 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.14 $";
 
 #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.19p6/drivers/isdn/hisax/teles0.c
--- linux.vanilla/drivers/isdn/hisax/teles0.c	Fri Apr 21 12:46:12 2000
+++ linux.19p6/drivers/isdn/hisax/teles0.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: teles0.c,v 2.11 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: teles0.c,v 2.13 2000/11/24 17:05:38 kai 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.13 $";
 
 #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.19p6/drivers/isdn/hisax/teles3.c
--- linux.vanilla/drivers/isdn/hisax/teles3.c	Fri Apr 21 12:46:12 2000
+++ linux.19p6/drivers/isdn/hisax/teles3.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: teles3.c,v 2.15 2000/02/03 16:40:10 keil Exp $
-
+/* $Id: teles3.c,v 2.17 2000/11/24 17:05:38 kai 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.17 $";
 
 #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.19p6/drivers/isdn/hisax/telespci.c
--- linux.vanilla/drivers/isdn/hisax/telespci.c	Fri Apr 21 12:46:12 2000
+++ linux.19p6/drivers/isdn/hisax/telespci.c	Sat Dec 30 19:33:52 2000
@@ -1,44 +1,11 @@
-/* $Id: telespci.c,v 2.11 1999/12/23 15:09:32 keil Exp $
-
+/* $Id: telespci.c,v 2.16.6.2 2000/11/29 16:00:14 kai 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.16.6.2 $";
 
 #define ZORAN_PO_RQ_PEN	0x02000000
 #define ZORAN_PO_WR	0x00800000
@@ -315,6 +282,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 +294,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.19p6/drivers/isdn/hisax/w6692.c
--- linux.vanilla/drivers/isdn/hisax/w6692.c	Sun Jun 11 21:44:14 2000
+++ linux.19p6/drivers/isdn/hisax/w6692.c	Sat Dec 30 19:33:52 2000
@@ -1,5 +1,5 @@
-/* $Id: w6692.c,v 1.4 2000/03/16 23:24:11 werner Exp $
-
+/* $Id: w6692.c,v 1.12.6.2 2000/11/29 16:00:14 kai 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,6 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 
-#define	PCI_VEND_ASUSCOM	0x675
-#define	PCI_DEV_ASUSCOMPCI1	0x1702
-#ifndef PCI_VENDOR_ID_WINBOND2
-#define PCI_VENDOR_ID_WINBOND2	0x1050
-#endif
-#define	PCI_DEVICE_W6692	0x6692
-
 /* table entry in the PCI devices list */
 typedef struct {
 	int vendor_id;
@@ -51,18 +27,18 @@
 
 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_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},
+	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},
 	{0, 0, NULL, NULL}
 };
 
 extern const char *CardType[];
 
-const char *w6692_revision = "$Revision: 1.4 $";
+const char *w6692_revision = "$Revision: 1.12.6.2 $";
 
 #define DBUSY_TIMER_VALUE 80
 
-static char *W6692Ver[] HISAX_INITDATA =
+static char *W6692Ver[] __initdata =
 {"W6692 V00", "W6692 V01", "W6692 V10",
  "W6692 V11"};
 
@@ -256,7 +232,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 +240,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 +283,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 +301,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 +392,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 +706,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 +884,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 +966,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 +982,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 +994,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 +1005,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 +1044,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.19p6/drivers/isdn/hisax/w6692.h
--- linux.vanilla/drivers/isdn/hisax/w6692.h	Fri Apr 21 12:46:12 2000
+++ linux.19p6/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.19p6/drivers/isdn/isdn_common.c
--- linux.vanilla/drivers/isdn/isdn_common.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/isdn_common.c	Sat Dec 30 19:33:52 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.113 2000/11/01 17:54:00 detabc Exp $
+/* $Id: isdn_common.c,v 1.114.6.3 2000/12/18 22:14:09 kai Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -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_common.h linux.19p6/drivers/isdn/isdn_common.h
--- linux.vanilla/drivers/isdn/isdn_common.h	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/isdn_common.h	Sat Dec 30 19:33:52 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.h,v 1.20 2000/06/16 13:00:27 keil Exp $
+/* $Id: isdn_common.h,v 1.21 2000/11/25 17:00:59 kai Exp $
 
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_concap.c linux.19p6/drivers/isdn/isdn_concap.c
--- linux.vanilla/drivers/isdn/isdn_concap.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/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.19p6/drivers/isdn/isdn_net.c
--- linux.vanilla/drivers/isdn/isdn_net.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/isdn_net.c	Sat Dec 30 19:33:52 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.6.1 2000/12/10 22:01:04 kai 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.6.1 $";
 
  /*
   * 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.19p6/drivers/isdn/isdn_net.h
--- linux.vanilla/drivers/isdn/isdn_net.h	Mon Dec 11 22:12:45 2000
+++ linux.19p6/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.19p6/drivers/isdn/isdn_ppp.c
--- linux.vanilla/drivers/isdn/isdn_ppp.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/isdn_ppp.c	Sat Dec 30 19:35:13 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.85 2000/11/25 17:00:59 kai 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>
@@ -38,8 +36,6 @@
 #define PPP_IPX 0x002b
 #endif
 
-/* set this if you use dynamic addressing */
-
 /* Prototypes */
 static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
 static int isdn_ppp_closewait(int slot);
@@ -85,11 +81,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.85 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
@@ -124,55 +118,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 +150,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 +166,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 +206,6 @@
 	}
 	
 	lp->ppp_slot = i;
-
 	is = ippp_table[i];
 	is->lp = lp;
 	is->unit = unit;
@@ -270,7 +232,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 +767,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 +789,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 +904,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 +950,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 +1049,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 +1108,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 +1140,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 +1166,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 +1240,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 +1251,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 +1271,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 +1295,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 +1329,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 +1342,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 +1351,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 +1384,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 +1432,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 +1591,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 +1615,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 +1661,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 +1723,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 +1737,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 +1979,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 +2020,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 +2270,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 +2315,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 +2355,6 @@
 		ipc->incomp(stat,skb,proto);
 		return skb;
 	}
-#endif
 }
 
 /*
@@ -2474,13 +2373,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 +2576,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.19p6/drivers/isdn/isdn_tty.c
--- linux.vanilla/drivers/isdn/isdn_tty.c	Mon Dec 11 22:12:45 2000
+++ linux.19p6/drivers/isdn/isdn_tty.c	Sat Dec 30 19:33:52 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.93 2000/08/05 09:58:26 armin Exp $
+/* $Id: isdn_tty.c,v 1.94 2000/11/25 17:00:59 kai Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -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.94 $";
 
 
 /* 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.19p6/drivers/isdn/isdn_tty.h
--- linux.vanilla/drivers/isdn/isdn_tty.h	Mon Dec 11 22:12:45 2000
+++ linux.19p6/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.19p6/drivers/isdn/sc/debug.c
--- linux.vanilla/drivers/isdn/sc/debug.c	Fri Apr 21 12:46:13 2000
+++ linux.19p6/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.19p6/drivers/isdn/sc/debug.h
--- linux.vanilla/drivers/isdn/sc/debug.h	Fri Apr 21 12:46:13 2000
+++ linux.19p6/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.19p6/drivers/isdn/sc/init.c
--- linux.vanilla/drivers/isdn/sc/init.c	Fri Apr 21 12:46:13 2000
+++ linux.19p6/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.19p6/drivers/isdn/sc/message.c
--- linux.vanilla/drivers/isdn/sc/message.c	Fri Apr 21 12:46:14 2000
+++ linux.19p6/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.19p6/drivers/isdn/sc/timer.c
--- linux.vanilla/drivers/isdn/sc/timer.c	Fri Apr 21 12:46:14 2000
+++ linux.19p6/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.19p6/drivers/net/3c527.c
--- linux.vanilla/drivers/net/3c527.c	Mon Dec 11 22:12:47 2000
+++ linux.19p6/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.19p6/drivers/net/3c527.h
--- linux.vanilla/drivers/net/3c527.h	Fri Apr 21 12:46:15 2000
+++ linux.19p6/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.19p6/drivers/net/Config.in
--- linux.vanilla/drivers/net/Config.in	Mon Dec 11 22:12:47 2000
+++ linux.19p6/drivers/net/Config.in	Tue Dec 19 02:02:42 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
@@ -182,7 +183,7 @@
       tristate 'Packet Engines GNIC-II (Hamachi) support' CONFIG_HAMACHI
       tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN
     fi
-    tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN
+    dep_tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN $CONFIG_PCI
 endmenu
 
 bool 'FDDI driver support' CONFIG_FDDI
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Makefile linux.19p6/drivers/net/Makefile
--- linux.vanilla/drivers/net/Makefile	Mon Dec 11 22:12:47 2000
+++ linux.19p6/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.19p6/drivers/net/Space.c
--- linux.vanilla/drivers/net/Space.c	Mon Dec 11 22:12:47 2000
+++ linux.19p6/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/cs89x0.c linux.19p6/drivers/net/cs89x0.c
--- linux.vanilla/drivers/net/cs89x0.c	Fri Apr 21 12:46:16 2000
+++ linux.19p6/drivers/net/cs89x0.c	Thu Dec 21 22:58:31 2000
@@ -27,6 +27,8 @@
                     : is running from all accounts.
                     
   Alan Cox          : Removed 1.2 support, added 2.1 extra counters.
+
+  Arnaldo Melo	    : release resources on failure in cs89x0_probe1
 */
 
 static char *version =
@@ -249,13 +251,17 @@
 	struct net_local *lp;
 	static unsigned version_printed = 0;
 	int i;
+	char priv_alloced_here = 0;
 	unsigned rev_type = 0;
 	int eeprom_buff[CHKSUM_LEN];
 
 	/* Initialize the device structure. */
 	if (dev->priv == NULL) {
 		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+		if (!dev->priv)
+			return -ENOMEM;
                 memset(dev->priv, 0, sizeof(struct net_local));
+		priv_alloced_here = 1;
         }
 	lp = (struct net_local *)dev->priv;
 
@@ -265,12 +271,12 @@
 	if (ioaddr & 1) {
 		ioaddr &= ~1;
 		if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
-			return ENODEV;
+			goto err;
 		outw(PP_ChipID, ioaddr + ADD_PORT);
 	}
 
 	if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
-		return ENODEV;
+		goto err;
 
 	/* Fill in the 'dev' fields. */
 	dev->base_addr = ioaddr;
@@ -388,6 +394,11 @@
 
 	printk("\n");
 	return 0;
+err:	if (priv_alloced_here) {
+		kfree(dev->priv);
+		dev->priv = NULL;
+	}
+	return -ENODEV;
 }
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lance.c linux.19p6/drivers/net/lance.c
--- linux.vanilla/drivers/net/lance.c	Fri Apr 21 12:46:17 2000
+++ linux.19p6/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/lanstreamer.c linux.19p6/drivers/net/lanstreamer.c
--- linux.vanilla/drivers/net/lanstreamer.c	Fri Apr 21 12:46:17 2000
+++ linux.19p6/drivers/net/lanstreamer.c	Thu Dec 21 22:49:57 2000
@@ -58,6 +58,7 @@
  *            First release to the public
  *  03/03/00 - Merged to kernel, indented -kr -i8 -bri0, fixed some missing
  *		malloc free checks, reviewed code. <alan@redhat.com>
+ *  12/18/00 - Made it SMP safe   - George Staikos <staikos@0wned.org>
  *  
  *  To Do:
  *
@@ -108,6 +109,7 @@
 #include <net/checksum.h>
 
 #include <asm/io.h>
+#include <asm/spinlock.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 
@@ -285,6 +287,8 @@
 	streamer_priv = (struct streamer_private *) dev->priv;
 	streamer_mmio = streamer_priv->streamer_mmio;
 
+        streamer_priv->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+
 	printk("%s \n", version);
 	printk(KERN_INFO "%s: IBM PCI tokenring card. I/O at %hx, MMIO at %p, using irq %d\n",
 			dev->name, (unsigned int) dev->base_addr,
@@ -823,6 +827,7 @@
 	__u16 misr;
 	__u16 sisrmask;
 
+        spin_lock(&(streamer_priv->lock));
 	sisrmask = SISR_MI;
 	writew(~sisrmask, streamer_mmio + SISR_MASK_RUM);
 	sisr = readw(streamer_mmio + SISR);
@@ -831,6 +836,7 @@
 	writew(~misr, streamer_mmio + MISR_RUM);
 
 	if (!sisr) {		/* Interrupt isn't for us */
+                spin_unlock(&(streamer_priv->lock));
 		return;
 	}
 
@@ -919,6 +925,7 @@
 
 	dev->interrupt = 0;
 	writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+        spin_unlock(&(streamer_priv->lock));
 }
 
 
@@ -927,11 +934,14 @@
 	struct streamer_private *streamer_priv =
 	    (struct streamer_private *) dev->priv;
 	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+        int flags;
 
 	if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
 		return 1;
 	}
 
+        spin_lock_irqsave(&(streamer_priv->lock), flags);
+
 	if (streamer_priv->free_tx_ring_entries) {
 		streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0;
 		streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len;
@@ -956,8 +966,10 @@
 
 		streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
 		dev->tbusy = 0;
+                spin_unlock_irqrestore(&(streamer_priv->lock), flags);
 		return 0;
 	} else {
+                spin_unlock_irqrestore(&(streamer_priv->lock), flags);
 		return 1;
 	}
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lanstreamer.h linux.19p6/drivers/net/lanstreamer.h
--- linux.vanilla/drivers/net/lanstreamer.h	Fri Apr 21 12:46:17 2000
+++ linux.19p6/drivers/net/lanstreamer.h	Thu Dec 21 22:50:02 2000
@@ -283,6 +283,7 @@
 	__u16 streamer_addr_table_addr, streamer_parms_addr;
 	__u16 mac_rx_buffer;
 	__u8 streamer_laa[6];
+        spinlock_t lock;
 };
 
 struct streamer_adapter_addr_table {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lp486e.c linux.19p6/drivers/net/lp486e.c
--- linux.vanilla/drivers/net/lp486e.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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/ne2k-pci.c linux.19p6/drivers/net/ne2k-pci.c
--- linux.vanilla/drivers/net/ne2k-pci.c	Fri Apr 21 12:46:19 2000
+++ linux.19p6/drivers/net/ne2k-pci.c	Sat Dec 30 19:45:15 2000
@@ -12,19 +12,31 @@
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
 
-	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
-	Center of Excellence in Space Data and Information Sciences
-	Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+	Drivers based on or derived from this code fall under the GPL and must
+	retain the authorship, copyright and license notice.  This file is not
+	a complete program and may only be used when the entire operating
+	system is licensed under the GPL.
+
+	The author may be reached as becker@scyld.com, or C/O
+	Scyld Computing Corporation
+	410 Severn Ave., Suite 210
+	Annapolis MD 21403
 
+	Issues remaining:
 	People are making PCI ne2000 clones! Oh the horror, the horror...
+	Limited full-duplex support.
 
-	Issues remaining:
-	No full-duplex support.
+	ChangeLog:
+
+	12/15/2000 Merged Scyld v1.02 into 2.2.18
+									J.A. Magallon <jamagallon@able.es>
 */
 
-/* Our copyright info must remain in the binary. */
-static const char *version =
-"ne2k-pci.c:vpre-1.00e 5/27/99 D. Becker/P. Gortmaker http://cesdis.gsfc.nasa.gov/linux/drivers/ne2k-pci.html\n";
+/* These identify the driver base version and may not be removed. */
+static const char version1[] =
+"ne2k-pci.c:v1.02 10/19/2000 D. Becker/P. Gortmaker\n";
+static const char version2[] =
+"  http://www.scyld.com/network/ne2k-pci.html\n";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -49,7 +61,18 @@
 #endif
 
 /* Set statically or when loading the driver module. */
-static int debug = 1;
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+/* More are supported, limit only on options */
+#define MAX_UNITS 6
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[MAX_UNITS] = {0, };
+static int options[MAX_UNITS] = {0, };
+
+MODULE_AUTHOR("Donald Becker / Paul Gortmaker");
+MODULE_DESCRIPTION("PCI NE2000 clone driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 
 /* Some defines that people can play with if so inclined. */
 
@@ -62,12 +85,13 @@
 /* Do we have a non std. amount of memory? (in units of 256 byte pages) */
 /* #define PACKETBUF_MEMSIZE	0x40 */
 
-#define ne2k_flags reg0			/* Rename an existing field to store flags! */
-
-/* Only the low 8 bits are usable for non-init-time flags! */
+/* Flags.  We rename an existing ei_status field to store flags! */
+/* Thus only the low 8 bits are usable for non-init-time flags. */
+#define ne2k_flags reg0
 enum {
-	HOLTEK_FDX=1, 		/* Full duplex -> set 0x80 at offset 0x20. */
-	ONLY_16BIT_IO=2, ONLY_32BIT_IO=4,	/* Chip can do only 16/32-bit xfers. */
+	ONLY_16BIT_IO=8, ONLY_32BIT_IO=4,   /* Chip can do only 16/32-bit xfers. */
+	FORCE_FDX=0x20,                     /* User override. */
+	REALTEK_FDX=0x40, HOLTEK_FDX=0x80,
 	STOP_PG_0x60=0x100,
 };
 
@@ -79,18 +103,17 @@
 	int flags;
 }
 pci_clone_list[] __initdata = {
-	{0x10ec, 0x8029, "RealTek RTL-8029", 0},
-	{0x1050, 0x0940, "Winbond 89C940", 0},
-	{0x11f6, 0x1401, "Compex RL2000", 0},
-	{0x8e2e, 0x3000, "KTI ET32P2", 0},
-	{0x4a14, 0x5000, "NetVin NV5000SC", 0},
-	{0x1106, 0x0926, "Via 86C926", ONLY_16BIT_IO},
-	{0x10bd, 0x0e34, "SureCom NE34", 0},
-	{0x1050, 0x5a5a, "Winbond", 0},
-	{0x12c3, 0x0058, "Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
-	{0x12c3, 0x5598, "Holtek HT80229",
-	 ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
-	{0,}
+{0x10ec, 0x8029, "RealTek RTL-8029", REALTEK_FDX},
+{0x1050, 0x0940, "Winbond 89C940", 0},
+{0x1050, 0x5a5a, "Winbond w89c940", 0},
+{0x8e2e, 0x3000, "KTI ET32P2", 0},
+{0x4a14, 0x5000, "NetVin NV5000SC", 0},
+{0x1106, 0x0926, "Via 86C926", ONLY_16BIT_IO},
+{0x10bd, 0x0e34, "SureCom NE34", 0},
+{0x12c3, 0x0058, "Holtek HT80232", ONLY_16BIT_IO|HOLTEK_FDX},
+{0x12c3, 0x5598, "Holtek HT80229", ONLY_32BIT_IO|HOLTEK_FDX|STOP_PG_0x60 },
+{0x11f6, 0x1401, "Compex RL2000", 0},
+{0,}
 };
 
 /* ---- No user-serviceable parts below ---- */
@@ -119,7 +142,7 @@
 static void ne2k_pci_block_output(struct device *dev, const int count,
 		const unsigned char *buf, const int start_page);
 
-
+
 
 /* No room in the standard 8390 structure for extra info we need. */
 struct ne2k_pci_card {
@@ -137,7 +160,7 @@
 {
 	/* We must emit version information. */
 	if (debug)
-		printk(KERN_INFO "%s", version);
+		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
 
 	if (ne2k_pci_probe(0)) {
 		printk(KERN_NOTICE "ne2k-pci.c: No useable cards found, driver NOT installed.\n");
@@ -220,7 +243,7 @@
 		{
 			static unsigned version_printed = 0;
 			if (version_printed++ == 0)
-				printk(KERN_INFO "%s", version);
+				printk(KERN_INFO "%s %s", version1, version2);
 		}
 #endif
 
@@ -263,8 +286,8 @@
 	return cards_found ? 0 : -ENODEV;
 }
 
-__initfunc (static struct device *ne2k_pci_probe1(struct device *dev, long ioaddr, int irq,
-									  int chip_idx))
+__initfunc (static struct device *ne2k_pci_probe1(struct device *dev,
+			long ioaddr, int irq, int chip_idx))
 {
 	int i;
 	unsigned char SA_prom[32];
@@ -291,6 +314,8 @@
 
 	dev = init_etherdev(dev, 0);
 
+	if (!dev) return 0;
+
 	/* Reset card. Who knows what dain-bramaged state it was left in. */
 	{
 		unsigned long reset_start_time = jiffies;
@@ -342,7 +367,7 @@
 	/* Note: all PCI cards have at least 16 bit access, so we don't have
 	   to check for 8 bit cards.  Most cards permit 32 bit access. */
 	if (pci_clone_list[chip_idx].flags & ONLY_32BIT_IO) {
-		for (i = 0; i < 4 ; i++)
+		for (i = 0; i < 8 ; i++)
 			((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT));
 	} else
 		for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
@@ -379,6 +404,10 @@
 	ei_status.stop_page = stop_page;
 	ei_status.word16 = 1;
 	ei_status.ne2k_flags = pci_clone_list[chip_idx].flags;
+	if (chip_idx < MAX_UNITS) {
+		if (full_duplex[chip_idx]  ||  (options[chip_idx] & FORCE_FDX))
+			ei_status.ne2k_flags |= FORCE_FDX;
+	}
 
 	ei_status.rx_start_page = start_page + TX_PAGES;
 #ifdef PACKETBUF_MEMSIZE
@@ -401,8 +430,17 @@
 {
 	if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev))
 		return -EAGAIN;
-	ei_open(dev);
 	MOD_INC_USE_COUNT;
+	/* Set full duplex for the chips that we know about. */
+	if (ei_status.ne2k_flags & FORCE_FDX) {
+		long ioaddr = dev->base_addr;
+		if (ei_status.ne2k_flags & REALTEK_FDX) {
+			outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */
+			outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20);
+		} else if (ei_status.ne2k_flags & HOLTEK_FDX)
+			outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20);
+	}
+	ei_open(dev);
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/strip.c linux.19p6/drivers/net/strip.c
--- linux.vanilla/drivers/net/strip.c	Fri Apr 21 12:46:20 2000
+++ linux.19p6/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/net/tulip.c linux.19p6/drivers/net/tulip.c
--- linux.vanilla/drivers/net/tulip.c	Mon Dec 11 22:12:50 2000
+++ linux.19p6/drivers/net/tulip.c	Tue Dec 19 01:56:44 2000
@@ -15,10 +15,14 @@
 
 	Support and updates available at
 	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
-	
+
 	This driver also contains updates by Wolfgang Walter and others.
 	For this specific driver variant please use linux-kernel for 
 	bug reports.
+
+	Updated 12/17/2000 by Jim McQuillan <jam@McQuil.com> to
+	include support for the Linksys LNE100TX card based on the
+	Admtek 985 Centaur-P chipset.
 */
 
 #define SMP_CHECK
@@ -398,6 +402,8 @@
 	HAS_MII | HAS_NWAY143 | HAS_8023X, t21142_timer },
   { "ADMtek Comet", 256, 0x0001abef,
 	MC_HASH_ONLY, comet_timer },
+  { "ADMtek Centaur-P", 256, 0x0001abef,
+  	MC_HASH_ONLY, comet_timer },
   { "Compex 9881 PMAC", 128, 0x0001ebef,
 	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
   { "Intel DS21145 Tulip", 128, 0x0801fbff,
@@ -411,8 +417,8 @@
 /* This matches the table above.  Note 21142 == 21143. */
 enum chips {
 	DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
-	LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881,
-	I21145,
+	LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMET5,
+        COMPEX9881, I21145, XIRCOM,
 };
 
 /* A full-duplex map for media types. */
@@ -423,7 +429,7 @@
 {0,0,0,16,  3,19,16,24,  27,4,7,5, 0,20,23,20 };
 static u8 t21040_csr13[] = {2,0x0C,8,4,  4,0,0,0, 0,0,0,0, 4,0,0,0};
 /* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
-static u16 t21041_csr13[] = { 0xEF05, 0xEF0D, 0xEF0D, 0xEF05, 0xEF05, };
+static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
 static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
 static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
@@ -584,7 +590,7 @@
 		return -ENODEV;
 
 	for (;pci_index < 0xff; pci_index++) {
-		u16 vendor, device, pci_command, new_command, subvendor;
+		u16 vendor, device, pci_command, new_command;
 		int chip_idx;
 		int irq;
 		long ioaddr;
@@ -602,13 +608,6 @@
 								 PCI_VENDOR_ID, &vendor);
 		pcibios_read_config_word(pci_bus, pci_device_fn,
 								 PCI_DEVICE_ID, &device);
-		pcibios_read_config_word(pci_bus, pci_device_fn,
-								 PCI_SUBSYSTEM_VENDOR_ID, &subvendor);
-		
-	        if( subvendor == 0x1376 ){
-			printk("tulip: skipping LMC card.\n");
-			continue;
-		}	
 
 		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
 			if (vendor == pci_tbl[chip_idx].vendor_id
@@ -750,7 +749,13 @@
 			put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
 			sum += value & 0xffff;
 		}
-	} else if (chip_idx == COMET) {
+	} else if (chip_idx == COMET){
+		/* No need to read the EEPROM. */
+		put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
+		put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
+		for (i = 0; i < 6; i ++)
+			sum += dev->dev_addr[i];
+	} else if (chip_idx == COMET5){
 		/* No need to read the EEPROM. */
 		put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
 		put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
@@ -995,6 +1000,9 @@
 	case COMET:
 		/* No initialization necessary. */
 		break;
+	case COMET5:
+		/* No initialization necessary. */
+		break;
 	}
 
 	if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
@@ -1321,6 +1329,17 @@
 		}
 		return 0xffff;
 	}
+	if (tp->chip_id == COMET5) {
+		if (phy_id == 1) {
+			if (location < 7)
+				return inl(ioaddr + 0xB4 + (location<<2));
+			else if (location == 17)
+				return inl(ioaddr + 0xD0);
+			else if (location >= 29 && location <= 31)
+				return inl(ioaddr + 0xD4 + ((location-29)<<2));
+		}
+		return 0xffff;
+	}
 
 	/* Establish sync by sending at least 32 logic ones. */
 	for (i = 32; i >= 0; i--) {
@@ -1379,6 +1398,18 @@
 		return;
 	}
 
+	if (tp->chip_id == COMET5) {
+		if (phy_id != 1)
+			return;
+		if (location < 7)
+			outl(value, ioaddr + 0xB4 + (location<<2));
+		else if (location == 17)
+			outl(value, ioaddr + 0xD0);
+		else if (location >= 29 && location <= 31)
+			outl(value, ioaddr + 0xD4 + ((location-29)<<2));
+		return;
+	}
+
 	/* Establish sync by sending 32 logic ones. */
 	for (i = 32; i >= 0; i--) {
 		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
@@ -1462,6 +1493,11 @@
 			outl(addr_high, ioaddr + 0xA8);
 			outl(0, ioaddr + 0xAC);
 			outl(0, ioaddr + 0xB0);
+		} else if (tp->chip_id == COMET5) {
+			outl(addr_low,  ioaddr + 0xA4);
+			outl(addr_high, ioaddr + 0xA8);
+			outl(0, ioaddr + 0xAC);
+			outl(0, ioaddr + 0xB0);
 		}
 	} else {
 		/* This is set_rx_mode(), but without starting the transmitter. */
@@ -1575,6 +1611,9 @@
 	} else if (tp->chip_id == COMET) {
 		dev->if_port = 0;
 		tp->csr6 = 0x00040000;
+	} else if (tp->chip_id == COMET5) {
+		dev->if_port = 0;
+		tp->csr6 = 0x00040000;
 	} else if (tp->chip_id == AX88140) {
 		tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100;
 	} else
@@ -3014,6 +3053,8 @@
 			data[0] = 32;
 		else if (tp->chip_id == COMET)
 			data[0] = 1;
+		else if (tp->chip_id == COMET5)
+			data[0] = 1;
 		else
 			return -ENODEV;
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
@@ -3140,6 +3181,9 @@
 				outl(3, ioaddr + CSR13);
 				outl(mc_filter[1], ioaddr + CSR14);
 			} else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
+				outl(mc_filter[0], ioaddr + 0xAC);
+				outl(mc_filter[1], ioaddr + 0xB0);
+			} else if (tp->chip_id == COMET5) { /* Has a simple hash filter. */
 				outl(mc_filter[0], ioaddr + 0xAC);
 				outl(mc_filter[1], ioaddr + 0xB0);
 			}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/via-rhine.c linux.19p6/drivers/net/via-rhine.c
--- linux.vanilla/drivers/net/via-rhine.c	Mon Dec 11 22:12:50 2000
+++ linux.19p6/drivers/net/via-rhine.c	Tue Dec 19 02:03:44 2000
@@ -1,35 +1,44 @@
 /* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */
 /*
-	Written 1998-1999 by Donald Becker.
+	Written 1998-2000 by Donald Becker.
 
-	This software may be used and distributed according to the terms
-	of the GNU Public License (GPL), incorporated herein by reference.
-	Drivers derived from this code also fall under the GPL and must retain
-	this authorship and copyright notice.
+	This software may be used and distributed according to the terms of
+	the GNU General Public License (GPL), incorporated herein by reference.
+	Drivers based on or derived from this code fall under the GPL and must
+	retain the authorship, copyright and license notice.  This file is not
+	a complete program and may only be used when the entire operating
+	system is licensed under the GPL.
 
 	This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet
 	controller.  It also works with the older 3043 Rhine-I chip.
 
-	The author may be reached as becker@cesdis.edu, or
-	Donald Becker
-	312 Severn Ave. #W302
+	The author may be reached as becker@scyld.com, or C/O
+	Scyld Computing Corporation
+	410 Severn Ave., Suite 210
 	Annapolis MD 21403
 
 	Support and updates available at
-	http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html
+	http://www.scyld.com/network/via-rhine.html
+
+
+	Linux kernel version history:
+
+	LK1.0.0:
+	- Urban Widmark: merges from Beckers 1.08b version and 2.4.0 (VT6102)
 */
 
-static const char *versionA =
-"via-rhine.c:v1.01 2/27/99  Written by Donald Becker\n";
-static const char *versionB =
-"  http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+/* These identify the driver base version and may not be removed. */
+static const char version1[] =
+"via-rhine.c:v1.08b-LK1.0.0 12/14/2000  Written by Donald Becker\n";
+static const char version2[] =
+"  http://www.scyld.com/network/via-rhine.html\n";
 
-/* A few user-configurable values.   These may be modified when a driver
-   module is loaded.*/
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
 
 static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
 static int max_interrupt_work = 20;
-static int min_pci_latency = 64;
+static int min_pci_latency = 32;
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature. */
@@ -55,7 +64,8 @@
    Making the Tx ring too large decreases the effectiveness of channel
    bonding and packet priority.
    There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE	8
+#define TX_RING_SIZE	16
+#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
 #define RX_RING_SIZE	16
 
 /* Operational parameters that usually are not changed. */
@@ -64,9 +74,15 @@
 
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
+
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error  You must compile this driver with "-O".
+#endif
+
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/errno.h>
@@ -82,15 +98,17 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
-/* This driver was written to use PCI memory space, however some x86
-   motherboards only configure I/O space accesses correctly. */
-#if defined(__i386__)  &&  !defined(VIA_USE_MEMORY)
-#define VIA_USE_IO
-#endif
-#if defined(__alpha__)
-#define VIA_USE_IO
-#endif
-#ifdef VIA_USE_IO
+/* Condensed bus+endian portability operations. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* This driver was written to use PCI memory space, however most versions
+   of the Rhine only work correctly with I/O space accesses. */
+#if defined(VIA_USE_MEMORY)
+#warning Many adapters using the VIA Rhine chip are not configured to work
+#warning with PCI memory space accesses.
+#else
+#define USE_IO_OPS
 #undef readb
 #undef readw
 #undef readl
@@ -105,21 +123,7 @@
 #define writel outl
 #endif
 
-/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
-   This is only in the support-all-kernels source code. */
-
-#define RUN_AT(x) (jiffies + (x))
-
-#if (LINUX_VERSION_CODE >= 0x20100)
-static char kernel_version[] = UTS_RELEASE;
-#else
-#ifndef __alpha__
-#define ioremap vremap
-#define iounmap vfree
-#endif
-#endif
-#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(min_pci_latency, "i");
@@ -127,28 +131,6 @@
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-#endif
-#if LINUX_VERSION_CODE < 0x20123
-#define test_and_set_bit(val, addr) set_bit(val, addr)
-#endif
-#if LINUX_VERSION_CODE <= 0x20139
-#define	net_device_stats enet_statistics
-#else
-#define NETSTATS_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
-/* Grrrr, the PCI code changed, but did not consider CardBus... */
-#include <linux/bios32.h>
-#define PCI_SUPPORT_VER1
-#else
-#define PCI_SUPPORT_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20159
-#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
-#else
-#define dev_free_skb(skb) dev_kfree_skb(skb);
-#endif
-
 
 /*
 				Theory of Operation
@@ -226,13 +208,13 @@
 IVb. References
 
 Preliminary VT86C100A manual from http://www.via.com.tw/
-http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
 
 IVc. Errata
 
 The VT86C100A manual is not reliable information.
-The chip does not handle unaligned transmit or receive buffers, resulting
+The 3043 chip does not handle unaligned transmit or receive buffers, resulting
 in significant performance degradation for bounce buffer copies on transmit
 and unaligned IP headers on receive.
 The chip does not pad to minimum transmit length.
@@ -250,6 +232,15 @@
 	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
 	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
 };
+
+#if defined(VIA_USE_MEMORY)
+#define RHINE_IOTYPE (PCI_USES_MEM | PCI_USES_MASTER | PCI_ADDR0)
+#define RHINEII_IOSIZE 4096
+#else
+#define RHINE_IOTYPE (PCI_USES_IO  | PCI_USES_MASTER | PCI_ADDR1)
+#define RHINEII_IOSIZE 256
+#endif
+
 struct pci_id_info {
 	const char *name;
 	u16	vendor_id, device_id, device_id_mask, flags;
@@ -264,34 +255,41 @@
 
 static struct pci_id_info pci_tbl[] __initdata = {
 	{ "VIA VT86C100A Rhine-II", 0x1106, 0x6100, 0xffff,
-	  PCI_USES_MEM|PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
+	  RHINE_IOTYPE, 128, via_probe1},
+	{ "VIA VT6102 Rhine-II", 0x1106, 0x3065, 0xffff,
+	  RHINE_IOTYPE, RHINEII_IOSIZE, via_probe1},
 	{ "VIA VT3043 Rhine", 0x1106, 0x3043, 0xffff,
-	  PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
+	  RHINE_IOTYPE, 128, via_probe1},
 	{0,},						/* 0 terminated list. */
 };
 
 
 /* A chip capabilities table, matching the entries in pci_tbl[] above. */
-enum chip_capability_flags {CanHaveMII=1, };
+enum chip_capability_flags {
+	CanHaveMII=1, HasESIPhy=2, HasDavicomPhy=4,
+	ReqTxAlign=0x10, HasWOL=0x20,
+};
+
 struct chip_info {
 	int io_size;
 	int flags;
 } static cap_tbl[] __initdata = {
-	{128, CanHaveMII, },
-	{128, CanHaveMII, },
+	{128, CanHaveMII | ReqTxAlign, },
+	{128, CanHaveMII | HasWOL, },
+	{128, CanHaveMII | ReqTxAlign, },
 };
 
 
-/* Offsets to the device registers.
-*/
+/* Offsets to the device registers. */
 enum register_offsets {
 	StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08,
 	IntrStatus=0x0C, IntrEnable=0x0E,
 	MulticastFilter0=0x10, MulticastFilter1=0x14,
 	RxRingPtr=0x18, TxRingPtr=0x1C,
-	MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E,
+	MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E,
 	MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72,
-	Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E,
+	Config=0x78, ConfigA=0x7A, RxMissed=0x7C, RxCRCErrs=0x7E,
+	StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC,
 };
 
 /* Bits in the interrupt status/mask registers. */
@@ -303,21 +301,18 @@
 	IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
 	IntrTxAborted=0x2000, IntrLinkChange=0x4000,
 	IntrRxWakeUp=0x8000,
-	IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260,
+	IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260,
 };
 
-
 /* The Rx and Tx buffer descriptors. */
 struct rx_desc {
-	u16 rx_status;
-	u16 rx_length;
+	s32 rx_status;
 	u32 desc_length;
 	u32 addr;
 	u32 next_desc;
 };
 struct tx_desc {
-	u16 tx_status;
-	u16 tx_own;
+	s32 tx_status;
 	u32 desc_length;
 	u32 addr;
 	u32 next_desc;
@@ -325,9 +320,10 @@
 
 /* Bits in *_desc.status */
 enum rx_status_bits {
-	RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F};
+	RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F
+};
 enum desc_status_bits {
-	DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000,
+	DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000,
 };
 
 /* Bits in ChipCmd. */
@@ -338,6 +334,7 @@
 	CmdNoTxPoll=0x0800, CmdReset=0x8000,
 };
 
+#define PRIV_ALIGN	15	/* Required alignment mask */
 struct netdev_private {
 	/* Descriptor rings first for alignment. */
 	struct rx_desc rx_ring[RX_RING_SIZE];
@@ -349,12 +346,12 @@
 	unsigned char *tx_buf[TX_RING_SIZE];	/* Tx bounce buffers */
 	unsigned char *tx_bufs;				/* Tx bounce buffer region. */
 	struct device *next_module;			/* Link for devices of this type. */
+	void *priv_addr;					/* Unaligned address for kfree */
 	struct net_device_stats stats;
 	struct timer_list timer;	/* Media monitoring timer. */
 	unsigned char pci_bus, pci_devfn;
 	/* Frequently used values: keep some adjacent for cache effect. */
-	int chip_id;
-	long in_interrupt;			/* Word-long for SMP locks. */
+	int chip_id, drv_flags;
 	struct rx_desc *rx_head_desc;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
 	unsigned int cur_tx, dirty_tx;
@@ -388,6 +385,7 @@
 static struct net_device_stats *get_stats(struct device *dev);
 static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
 static int  netdev_close(struct device *dev);
+static inline void clear_tally_counters(long ioaddr);
 
 
 
@@ -432,30 +430,13 @@
 			continue;
 
 		{
-#if defined(PCI_SUPPORT_VER2)
 			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
-#ifdef VIA_USE_IO
+#ifdef USE_IO_OPS
 			pciaddr = pdev->base_address[0];
 #else
 			pciaddr = pdev->base_address[1];
 #endif
 			irq = pdev->irq;
-#else
-			u32 pci_memaddr;
-			u8 pci_irq_line;
-			pcibios_read_config_byte(pci_bus, pci_device_fn,
-									 PCI_INTERRUPT_LINE, &pci_irq_line);
-#ifdef VIA_USE_IO
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-									  PCI_BASE_ADDRESS_0, &pci_memaddr);
-			pciaddr = pci_memaddr;
-#else
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-									  PCI_BASE_ADDRESS_1, &pci_memaddr);
-			pciaddr = pci_memaddr;
-#endif
-			irq = pci_irq_line;
-#endif
 		}
 
 		if (debug > 2)
@@ -511,7 +492,7 @@
 {
 	static int did_version = 0;
 	if (!did_version++)
-		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
 	return pci_etherdev_probe(dev, pci_tbl);
 }
 #endif
@@ -521,6 +502,7 @@
 								 int chip_id, int card_idx)
 {
 	struct netdev_private *np;
+	void *priv_mem;
 	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 
 	dev = init_etherdev(dev, 0);
@@ -528,14 +510,20 @@
 	printk(KERN_INFO "%s: %s at 0x%lx, ",
 		   dev->name, pci_tbl[chip_id].name, ioaddr);
 
-	/* Ideally we would be read the EEPROM but access may be locked. */
-	for (i = 0; i <6; i++)
+	/* We would prefer to read the EEPROM but access may be locked. */
+	for (i = 0; i < 6; i++)
 		dev->dev_addr[i] = readb(ioaddr + StationAddr + i);
 	for (i = 0; i < 5; i++)
 			printk("%2.2x:", dev->dev_addr[i]);
 	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
 
-#ifdef VIA_USE_IO
+	/* Allocate driver private memory for descriptor lists.
+	   Check for the very unlikely case of no memory. */
+	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL);
+	if (priv_mem == NULL)
+		return NULL;
+
+#ifdef USE_IO_OPS
 	request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name);
 #endif
 
@@ -546,9 +534,9 @@
 	dev->irq = irq;
 
 	/* Make certain the descriptor lists are cache-aligned. */
-	np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31);
+	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN);
 	memset(np, 0, sizeof(*np));
-	dev->priv = np;
+	np->priv_addr = priv_mem;
 
 	np->next_module = root_net_dev;
 	root_net_dev = dev;
@@ -556,6 +544,7 @@
 	np->pci_bus = pci_bus;
 	np->pci_devfn = pci_devfn;
 	np->chip_id = chip_id;
+	np->drv_flags = cap_tbl[chip_id].flags;
 
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -582,7 +571,7 @@
 	dev->set_multicast_list = &set_rx_mode;
 	dev->do_ioctl = &mii_ioctl;
 
-	if (cap_tbl[np->chip_id].flags & CanHaveMII) {
+	if (np->drv_flags & CanHaveMII) {
 		int phy, phy_idx = 0;
 		np->phys[0] = 1;		/* Standard for this chip. */
 		for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
@@ -625,9 +614,23 @@
 
 static void mdio_write(struct device *dev, int phy_id, int regnum, int value)
 {
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int boguscnt = 1024;
 
+	if (phy_id == np->phys[0]) {
+		switch (regnum) {
+		case 0:					/* Is user forcing speed/duplex? */
+			if (value & 0x9000)	/* Autonegotiation. */
+				np->duplex_lock = 0;
+			else
+				np->full_duplex = (value & 0x0100) ? 1 : 0;
+			break;
+		case 4:
+			np->advertising = value;
+			break;
+		}
+	}
 	/* Wait for a previous command to complete. */
 	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
 		;
@@ -649,15 +652,17 @@
 	/* Reset the chip. */
 	writew(CmdReset, ioaddr + ChipCmd);
 
-	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev))
+	MOD_INC_USE_COUNT;
+
+	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
+		MOD_DEC_USE_COUNT;
 		return -EAGAIN;
+	}
 
 	if (debug > 1)
 		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
 			   dev->name, dev->irq);
 
-	MOD_INC_USE_COUNT;
-
 	init_ring(dev);
 
 	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
@@ -667,7 +672,7 @@
 		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
 	/* Initialize other registers. */
-	writew(0x0006, ioaddr + PCIConfig);	/* Tune configuration??? */
+	writew(0x0006, ioaddr + PCIBusConfig);	/* Tune configuration??? */
 	/* Configure the FIFO thresholds. */
 	writeb(0x20, ioaddr + TxConfig);	/* Initial threshold 32 bytes */
 	np->tx_thresh = 0x20;
@@ -678,7 +683,6 @@
 
 	dev->tbusy = 0;
 	dev->interrupt = 0;
-	np->in_interrupt = 0;
 
 	set_rx_mode(dev);
 
@@ -696,6 +700,11 @@
 	writew(np->chip_cmd, ioaddr + ChipCmd);
 
 	check_duplex(dev);
+	/* The LED outputs of various MII xcvrs should be configured.  */
+	/* For NS or Mison phys, turn on bit 1 in register 0x17 */
+	/* For ESI phys, turn on bit 7 in register 0x17. */
+	mdio_write(dev, np->phys[0], 0x17, mdio_read(dev, np->phys[0], 0x17) |
+			   (np->drv_flags & HasESIPhy) ? 0x0080 : 0x0001);
 
 	if (debug > 2)
 		printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x "
@@ -705,7 +714,7 @@
 
 	/* Set the timer to check for link beat. */
 	init_timer(&np->timer);
-	np->timer.expires = RUN_AT(1);
+	np->timer.expires = jiffies + 2;
 	np->timer.data = (unsigned long)dev;
 	np->timer.function = &netdev_timer;				/* timer handler */
 	add_timer(&np->timer);
@@ -718,11 +727,12 @@
 	struct netdev_private *np = (struct netdev_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+	int negotiated = mii_reg5 & np->advertising;
 	int duplex;
 
 	if (np->duplex_lock  ||  mii_reg5 == 0xffff)
 		return;
-	duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
 	if (np->full_duplex != duplex) {
 		np->full_duplex = duplex;
 		if (debug)
@@ -748,9 +758,14 @@
 		printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n",
 			   dev->name, readw(ioaddr + IntrStatus));
 	}
+	if (test_bit(0, (void*)&dev->tbusy) != 0
+		&& np->cur_tx - np->dirty_tx > 1
+		&& jiffies - dev->trans_start > TX_TIMEOUT)
+		tx_timeout(dev);
+
 	check_duplex(dev);
 
-	np->timer.expires = RUN_AT(next_tick);
+	np->timer.expires = jiffies + next_tick;
 	add_timer(&np->timer);
 }
 
@@ -764,15 +779,15 @@
 		   dev->name, readw(ioaddr + IntrStatus),
 		   mdio_read(dev, np->phys[0], 1));
 
-  /* Perhaps we should reinitialize the hardware here. */
-  dev->if_port = 0;
-  /* Stop and restart the chip's Tx processes . */
-
-  /* Trigger an immediate transmit demand. */
-
-  dev->trans_start = jiffies;
-  np->stats.tx_errors++;
-  return;
+	/* Perhaps we should reinitialize the hardware here. */
+	dev->if_port = 0;
+	/* Stop and restart the chip's Tx processes . */
+
+	/* Trigger an immediate transmit demand. */
+
+	dev->trans_start = jiffies;
+	np->stats.tx_errors++;
+	return;
 }
 
 
@@ -791,35 +806,33 @@
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		np->rx_ring[i].rx_status = 0;
-		np->rx_ring[i].rx_length = 0;
-		np->rx_ring[i].desc_length = np->rx_buf_sz;
-		np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]);
+		np->rx_ring[i].desc_length = cpu_to_le32(np->rx_buf_sz);
+		np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]);
 		np->rx_skbuff[i] = 0;
 	}
 	/* Mark the last entry as wrapping the ring. */
-	np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]);
+	np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]);
 
-	/* Fill in the Rx buffers. */
+	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
 		np->rx_skbuff[i] = skb;
 		if (skb == NULL)
 			break;
 		skb->dev = dev;			/* Mark as being used by this device. */
-		np->rx_ring[i].addr = virt_to_bus(skb->tail);
-		np->rx_ring[i].rx_status = 0;
-		np->rx_ring[i].rx_length = DescOwn;
+		np->rx_ring[i].addr = virt_to_le32desc(skb->tail);
+		np->rx_ring[i].rx_status = cpu_to_le32(DescOwn);
 	}
 	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		np->tx_skbuff[i] = 0;
-		np->tx_ring[i].tx_own = 0;
-		np->tx_ring[i].desc_length = 0x00e08000;
-		np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]);
+		np->tx_ring[i].tx_status = 0;
+		np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000);
+		np->tx_ring[i].next_desc = virt_to_le32desc(&np->tx_ring[i+1]);
 		np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL);
 	}
-	np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]);
+	np->tx_ring[i-1].next_desc = virt_to_le32desc(&np->tx_ring[0]);
 
 	return;
 }
@@ -832,41 +845,45 @@
 	/* 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) {
-		if (jiffies - dev->trans_start < TX_TIMEOUT)
-			return 1;
-		tx_timeout(dev);
+		/* This watchdog code is redundant with the media monitor timer. */
+		if (jiffies - dev->trans_start > TX_TIMEOUT)
+			tx_timeout(dev);
 		return 1;
 	}
 
-	/* Caution: the write order is important here, set the field
-	   with the "ownership" bits last. */
+	/* Explicitly flush packet data cache lines here. */
+
+	/* Caution: the write order is important here, set the descriptor word
+	   with the "ownership" bit last.  No SMP locking is needed if the
+	   cur_tx is incremented after the descriptor is consistent.  */
 
 	/* Calculate the next Tx descriptor entry. */
 	entry = np->cur_tx % TX_RING_SIZE;
 
 	np->tx_skbuff[entry] = skb;
 
-	if ((long)skb->data & 3) {			/* Must use alignment buffer. */
+	if ((np->drv_flags & ReqTxAlign)  && ((long)skb->data & 3)) {
+		/* Must use alignment buffer. */
 		if (np->tx_buf[entry] == NULL &&
 			(np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL)
 			return 1;
 		memcpy(np->tx_buf[entry], skb->data, skb->len);
-		np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]);
+		np->tx_ring[entry].addr = virt_to_le32desc(np->tx_buf[entry]);
 	} else
-		np->tx_ring[entry].addr = virt_to_bus(skb->data);
+		np->tx_ring[entry].addr = virt_to_le32desc(skb->data);
 
-	np->tx_ring[entry].desc_length = 0x00E08000 |
-		(skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
-	np->tx_ring[entry].tx_own = DescOwn;
+	np->tx_ring[entry].desc_length =
+		cpu_to_le32(0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+	np->tx_ring[entry].tx_status = cpu_to_le32(DescOwn);
 
 	np->cur_tx++;
 
-	/* Non-x86 Todo: explicitly flush cache lines here. */
+	/* Explicitly flush descriptor cache lines here. */
 
 	/* Wake the potentially-idle transmit channel. */
 	writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
 
-	if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1)
+	if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1)
 		clear_bit(0, (void*)&dev->tbusy);		/* Typical path */
 	else
 		np->tx_full = 1;
@@ -884,26 +901,9 @@
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
 {
 	struct device *dev = (struct device *)dev_instance;
-	struct netdev_private *np;
-	long ioaddr, boguscnt = max_interrupt_work;
-
-	ioaddr = dev->base_addr;
-	np = (struct netdev_private *)dev->priv;
-#if defined(__i386__)
-	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
-	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
-		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
-			   dev->name);
-		dev->interrupt = 0;	/* Avoid halting machine. */
-		return;
-	}
-#else
-	if (dev->interrupt) {
-		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
-		return;
-	}
-	dev->interrupt = 1;
-#endif
+	struct netdev_private *np = (void *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int boguscnt = max_interrupt_work;
 
 	do {
 		u32 intr_status = readw(ioaddr + IntrStatus);
@@ -924,10 +924,9 @@
 
 		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
 			int entry = np->dirty_tx % TX_RING_SIZE;
-			int txstatus;
-			if (np->tx_ring[entry].tx_own)
+			int txstatus = le32_to_cpu(np->tx_ring[entry].tx_status);
+			if (txstatus & DescOwn)
 				break;
-			txstatus = np->tx_ring[entry].tx_status;
 			if (debug > 6)
 				printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
 					   entry, txstatus);
@@ -951,16 +950,16 @@
 #endif
 				np->stats.collisions += (txstatus >> 3) & 15;
 #if defined(NETSTATS_VER2)
-				np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
+				np->stats.tx_bytes += np->tx_skbuff[entry]->len;
 #endif
 				np->stats.tx_packets++;
 			}
 			/* Free the original skb. */
-			dev_free_skb(np->tx_skbuff[entry]);
+			dev_kfree_skb(np->tx_skbuff[entry]);
 			np->tx_skbuff[entry] = 0;
 		}
 		if (np->tx_full && dev->tbusy
-			&& np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) {
+			&& np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
 			/* The ring is no longer full, clear tbusy. */
 			np->tx_full = 0;
 			clear_bit(0, (void*)&dev->tbusy);
@@ -984,11 +983,6 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
 			   dev->name, readw(ioaddr + IntrStatus));
 
-#if defined(__i386__)
-	clear_bit(0, (void*)&dev->interrupt);
-#else
-	dev->interrupt = 0;
-#endif
 	return;
 }
 
@@ -1001,15 +995,15 @@
 	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
 
 	if (debug > 4) {
-		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
-			   entry, np->rx_head_desc->rx_length);
+		printk(KERN_DEBUG " In netdev_rx(), entry %d status %8.8x.\n",
+			   entry, np->rx_head_desc->rx_status);
 	}
 
 	/* If EOP is set on the next entry, it's a new packet. Send it up. */
-	while ( ! (np->rx_head_desc->rx_length & DescOwn)) {
+	while ( ! (np->rx_head_desc->rx_status & cpu_to_le32(DescOwn))) {
 		struct rx_desc *desc = np->rx_head_desc;
-		int data_size = desc->rx_length;
-		u16 desc_status = desc->rx_status;
+		u32 desc_status = le32_to_cpu(desc->rx_status);
+		int data_size = desc_status >> 16;
 
 		if (debug > 4)
 			printk(KERN_DEBUG "  netdev_rx() status is %4.4x.\n",
@@ -1039,7 +1033,7 @@
 		} else {
 			struct sk_buff *skb;
 			/* Length should omit the CRC */
-			u16 pkt_len = data_size - 4;
+			int pkt_len = data_size - 4;
 
 			/* Check if the packet is long enough to accept without copying
 			   to a minimally-sized skbuff. */
@@ -1047,21 +1041,23 @@
 				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-#if ! defined(__alpha__) || USE_IP_COPYSUM		/* Avoid misaligned on Alpha */
-				eth_copy_and_sum(skb, bus_to_virt(desc->addr),
-								 pkt_len, 0);
+#if HAS_IP_COPYSUM			/* Call copy + cksum if available. */
+				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
 #else
-				memcpy(skb_put(skb,pkt_len), bus_to_virt(desc->addr), pkt_len);
+				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail,
+					   pkt_len);
 #endif
 			} else {
 				skb_put(skb = np->rx_skbuff[entry], pkt_len);
 				np->rx_skbuff[entry] = NULL;
 			}
 			skb->protocol = eth_type_trans(skb, dev);
-			np->stats.rx_bytes+=skb->len;
 			netif_rx(skb);
 			dev->last_rx = jiffies;
+#if defined(NETSTATS_VER2)
+			np->stats.rx_bytes += skb->len;
+#endif
 			np->stats.rx_packets++;
 		}
 		entry = (++np->cur_rx) % RX_RING_SIZE;
@@ -1078,10 +1074,9 @@
 			if (skb == NULL)
 				break;			/* Better luck next round. */
 			skb->dev = dev;			/* Mark as being used by this device. */
-			np->rx_ring[entry].addr = virt_to_bus(skb->tail);
+			np->rx_ring[entry].addr = virt_to_le32desc(skb->tail);
 		}
-		np->rx_ring[entry].rx_status = 0;
-		np->rx_ring[entry].rx_length = DescOwn;
+		np->rx_ring[entry].rx_status = cpu_to_le32(DescOwn);
 	}
 
 	/* Pre-emptively restart Rx engine. */
@@ -1095,10 +1090,11 @@
 	long ioaddr = dev->base_addr;
 
 	if (intr_status & (IntrMIIChange | IntrLinkChange)) {
-		if (readb(ioaddr + MIIStatus) & 0x02)
+		if (readb(ioaddr + MIIStatus) & 0x02) {
 			/* Link failed, restart autonegotiation. */
-			mdio_write(dev, np->phys[0], 0, 0x3300);
-		else
+			if (np->drv_flags & HasDavicomPhy)
+				mdio_write(dev, np->phys[0], 0, 0x3300);
+		} else
 			check_duplex(dev);
 		if (debug)
 			printk(KERN_ERR "%s: MII status changed: Autonegotiation "
@@ -1109,7 +1105,7 @@
 	if (intr_status & IntrStatsMax) {
 		np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
 		np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed);
-		writel(0, RxMissed);
+		clear_tally_counters(ioaddr);
 	}
 	if (intr_status & IntrTxAbort) {
 		/* Stats counted in Tx-done handler, just restart Tx. */
@@ -1122,7 +1118,8 @@
 			printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
 				   "threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
 	}
-	if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug) {
+	if ((intr_status & ~(IntrLinkChange | IntrStatsMax |
+						 IntrTxAbort|IntrTxAborted))  &&  debug) {
 		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
 			   dev->name, intr_status);
 		/* Recovery for other fault sources not known. */
@@ -1140,28 +1137,39 @@
 	   non-critical. */
 	np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
 	np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed);
-	writel(0, RxMissed);
+	clear_tally_counters(ioaddr);
 
 	return &np->stats;
 }
 
+/* Clears the "tally counters" for CRC errors and missed frames(?).
+   It has been reported that some chips need a write of 0 to clear
+   these, for others the counters are set to 1 when written to and
+   instead cleared when read. So we clear them both ways ... */
+static inline void clear_tally_counters(const long ioaddr)
+{
+	writel(0, ioaddr + RxMissed);
+	readw(ioaddr + RxCRCErrs);
+	readw(ioaddr + RxMissed);
+}
+
 /* The big-endian AUTODIN II ethernet CRC calculation.
    N.B. Do not use for bulk data, use a table-based routine instead.
    This is common code and should be moved to net/core/crc.c */
 static unsigned const ethernet_polynomial = 0x04c11db7U;
 static inline u32 ether_crc(int length, unsigned char *data)
 {
-    int crc = -1;
+	int crc = -1;
 
-    while(--length >= 0) {
+	while(--length >= 0) {
 		unsigned char current_octet = *data++;
 		int bit;
 		for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
 			crc = (crc << 1) ^
 				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
 		}
-    }
-    return crc;
+	}
+	return crc;
 }
 
 static void set_rx_mode(struct device *dev)
@@ -1178,6 +1186,8 @@
 	} else if ((dev->mc_count > multicast_filter_limit)
 			   ||  (dev->flags & IFF_ALLMULTI)) {
 		/* Too many to match, or accept all multicasts. */
+		writel(0xffffffff, ioaddr + MulticastFilter0);
+		writel(0xffffffff, ioaddr + MulticastFilter1);
 		rx_mode = 0x0C;
 	} else {
 		struct dev_mc_list *mclist;
@@ -1190,7 +1200,7 @@
 		}
 		writel(mc_filter[0], ioaddr + MulticastFilter0);
 		writel(mc_filter[1], ioaddr + MulticastFilter1);
-		rx_mode = 0x08;
+		rx_mode = 0x0C;
 	}
 	writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig);
 }
@@ -1207,7 +1217,7 @@
 		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
 		return 0;
@@ -1229,32 +1239,39 @@
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
 			   dev->name, readw(ioaddr + ChipCmd));
 
+	del_timer(&np->timer);
+
+	/* Switch to loopback mode to avoid hardware races. */
+	writeb(np->tx_thresh | 0x01, ioaddr + TxConfig);
+
 	/* Disable interrupts by clearing the interrupt mask. */
 	writew(0x0000, ioaddr + IntrEnable);
 
 	/* Stop the chip's Tx and Rx processes. */
 	writew(CmdStop, ioaddr + ChipCmd);
 
-	del_timer(&np->timer);
-
 	free_irq(dev->irq, dev);
 
 	/* Free all the skbuffs in the Rx queue. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		np->rx_ring[i].rx_length = 0;
+		np->rx_ring[i].rx_status = 0;
 		np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
 		if (np->rx_skbuff[i]) {
 #if LINUX_VERSION_CODE < 0x20100
 			np->rx_skbuff[i]->free = 1;
 #endif
-			dev_free_skb(np->rx_skbuff[i]);
+			dev_kfree_skb(np->rx_skbuff[i]);
 		}
 		np->rx_skbuff[i] = 0;
 	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (np->tx_skbuff[i])
-			dev_free_skb(np->tx_skbuff[i]);
+			dev_kfree_skb(np->tx_skbuff[i]);
 		np->tx_skbuff[i] = 0;
+		if (np->tx_buf[i]) {
+			kfree(np->tx_buf[i]);
+			np->tx_buf[i] = 0;
+		}
 	}
 
 	MOD_DEC_USE_COUNT;
@@ -1267,37 +1284,28 @@
 int init_module(void)
 {
 	if (debug)					/* Emit version even if no cards detected. */
-		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
-#ifdef CARDBUS
-	register_driver(&etherdev_ops);
-	return 0;
-#else
+		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
 	return pci_etherdev_probe(NULL, pci_tbl);
-#endif
 }
 
 void cleanup_module(void)
 {
-
-#ifdef CARDBUS
-	unregister_driver(&etherdev_ops);
-#endif
+	struct device *next_dev;
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_net_dev) {
-		struct netdev_private *np =
-			(struct netdev_private *)(root_net_dev->priv);
+		struct netdev_private *np = (void *)(root_net_dev->priv);
 		unregister_netdev(root_net_dev);
-#ifdef VIA_USE_IO
+#ifdef USE_IO_OPS
 		release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size);
 #else
 		iounmap((char *)(root_net_dev->base_addr));
 #endif
+		next_dev = np->next_module;
+		if (np->priv_addr)
+			kfree(np->priv_addr);
 		kfree(root_net_dev);
-		root_net_dev = np->next_module;
-#if 0
-		kfree(np);				/* Assumption: no struct realignment. */
-#endif
+		root_net_dev = next_dev;
 	}
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd.c linux.19p6/drivers/s390/block/dasd.c
--- linux.vanilla/drivers/s390/block/dasd.c	Mon Dec 11 22:12:51 2000
+++ linux.19p6/drivers/s390/block/dasd.c	Thu Dec 21 22:51:31 2000
@@ -2924,9 +2924,7 @@
 int
 init_module ( void )
 {
-        int rc=0;
         return dasd_init(); 
-        return rc;
 }
 
 void 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd.h linux.19p6/drivers/s390/block/dasd.h
--- linux.vanilla/drivers/s390/block/dasd.h	Mon Dec 11 22:12:51 2000
+++ linux.19p6/drivers/s390/block/dasd.h	Thu Dec 21 22:50:30 2000
@@ -7,3 +7,8 @@
 int (*genhd_dasd_name)(char*,int,int,struct gendisk*);
 int dasd_oper_handler ( int irq, devreg_t *devreg );
 
+dasd_era_t dasd_3370_erp_examine (ccw_req_t * cqr, devstat_t * stat);
+dasd_era_t dasd_3990_erp_examine (ccw_req_t * cqr, devstat_t * stat);
+dasd_era_t dasd_9336_erp_examine (ccw_req_t * cqr, devstat_t * stat);
+dasd_era_t dasd_9343_erp_examine (ccw_req_t * cqr, devstat_t * stat);
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_3370_erp.c linux.19p6/drivers/s390/block/dasd_3370_erp.c
--- linux.vanilla/drivers/s390/block/dasd_3370_erp.c	Sat Sep  9 18:42:40 2000
+++ linux.19p6/drivers/s390/block/dasd_3370_erp.c	Thu Dec 21 22:50:30 2000
@@ -5,6 +5,7 @@
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
  */
 
+#include <linux/blkdev.h>
 #include <asm/ccwcache.h>
 #include <asm/dasd.h>
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_3990_erp.c linux.19p6/drivers/s390/block/dasd_3990_erp.c
--- linux.vanilla/drivers/s390/block/dasd_3990_erp.c	Mon Dec 11 22:12:51 2000
+++ linux.19p6/drivers/s390/block/dasd_3990_erp.c	Thu Dec 21 22:50:30 2000
@@ -6,6 +6,7 @@
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
  */
 
+#include <linux/blkdev.h>
 #include <asm/ccwcache.h>
 #include <asm/dasd.h>
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_9336_erp.c linux.19p6/drivers/s390/block/dasd_9336_erp.c
--- linux.vanilla/drivers/s390/block/dasd_9336_erp.c	Sat Sep  9 18:42:40 2000
+++ linux.19p6/drivers/s390/block/dasd_9336_erp.c	Thu Dec 21 22:50:30 2000
@@ -5,6 +5,7 @@
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
  */
 
+#include <linux/blkdev.h>
 #include <asm/ccwcache.h>
 #include <asm/dasd.h>
 
@@ -32,8 +33,6 @@
 dasd_era_t
 dasd_9336_erp_examine (ccw_req_t * cqr, devstat_t * stat)
 {
-	char *sense = stat->ii.sense.data;
-
 	/* check for successful execution first */
 	if (stat->cstat == 0x00 &&
 	    stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_9343_erp.c linux.19p6/drivers/s390/block/dasd_9343_erp.c
--- linux.vanilla/drivers/s390/block/dasd_9343_erp.c	Sat Sep  9 18:42:40 2000
+++ linux.19p6/drivers/s390/block/dasd_9343_erp.c	Thu Dec 21 22:50:30 2000
@@ -5,6 +5,7 @@
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
  */
 
+#include <linux/blkdev.h>
 #include <asm/ccwcache.h>
 #include <asm/dasd.h>
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_eckd.c linux.19p6/drivers/s390/block/dasd_eckd.c
--- linux.vanilla/drivers/s390/block/dasd_eckd.c	Mon Dec 11 22:12:51 2000
+++ linux.19p6/drivers/s390/block/dasd_eckd.c	Thu Dec 21 22:50:30 2000
@@ -74,6 +74,7 @@
         eckd_count_t count_area;
 } dasd_eckd_private_t;
 
+#ifdef CONFIG_DASD_DYNAMIC
 static
 devreg_t dasd_eckd_known_devices[] = {
 	{
@@ -92,6 +93,7 @@
                 oper_func: dasd_oper_handler
         }
 };
+#endif
 
 static inline unsigned int
 round_up_multiple (unsigned int no, unsigned int mult)
@@ -923,7 +925,9 @@
 int
 dasd_eckd_init( void ) {
         int rc = 0;
+#ifdef CONFIG_DASD_DYNAMIC
 	int i;
+#endif
         printk ( KERN_INFO PRINTK_HEADER
                  "%s discipline initializing\n", dasd_eckd_discipline.name);
         ASCEBC(dasd_eckd_discipline.ebcname,4);
@@ -944,8 +948,9 @@
 
 void
 dasd_eckd_cleanup( void ) {
-        int rc = 0;
+#ifdef CONFIG_DASD_DYNAMIC
 	int i;
+#endif
         printk ( KERN_INFO PRINTK_HEADER
                  "%s discipline cleaning up\n", dasd_eckd_discipline.name);
 #ifdef CONFIG_DASD_DYNAMIC
@@ -960,7 +965,7 @@
 	} 
 #endif /* CONFIG_DASD_DYNAMIC */
         dasd_discipline_deq(&dasd_eckd_discipline);
-        return rc;
+        return;
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_eckd.h linux.19p6/drivers/s390/block/dasd_eckd.h
--- linux.vanilla/drivers/s390/block/dasd_eckd.h	Sat Sep  9 18:42:40 2000
+++ linux.19p6/drivers/s390/block/dasd_eckd.h	Thu Dec 21 22:50:30 2000
@@ -282,5 +282,6 @@
 } __attribute__ ((packed)) dasd_eckd_confdata_t;
 
 int dasd_eckd_init( void );
+void dasd_eckd_cleanup( void );
 
 #endif /* DASD_ECKD_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_fba.c linux.19p6/drivers/s390/block/dasd_fba.c
--- linux.vanilla/drivers/s390/block/dasd_fba.c	Mon Dec 11 22:12:51 2000
+++ linux.19p6/drivers/s390/block/dasd_fba.c	Thu Dec 21 22:50:30 2000
@@ -181,9 +181,7 @@
         int rc = 0;
 	unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
 	unsigned long tracks = sectors >> 6;
-	unsigned long trk_rem = sectors & ((1<<6)-1);
 	unsigned long cyls = tracks >> 4;
-	unsigned long cyls_rem = tracks & ((1<<4)-1);
 
         switch(device->sizes.bp_block) {
         case 512:
@@ -371,7 +369,9 @@
 int
 dasd_fba_init( void ) {
         int rc = 0;
+#ifdef	CONFIG_DASD_DYNAMIC
 	int i;
+#endif
         printk ( KERN_INFO PRINTK_HEADER
                  "%s discipline initializing\n", dasd_fba_discipline.name);
         ASCEBC(dasd_fba_discipline.ebcname,4);
@@ -392,8 +392,9 @@
 
 void
 dasd_fba_cleanup( void ) {
-        int rc = 0;
+#ifdef	CONFIG_DASD_DYNAMIC
 	int i;
+#endif
         printk ( KERN_INFO PRINTK_HEADER
                  "%s discipline cleaning up\n", dasd_fba_discipline.name);
 #ifdef CONFIG_DASD_DYNAMIC
@@ -408,7 +409,7 @@
         }
 #endif /* CONFIG_DASD_DYNAMIC */
         dasd_discipline_deq(&dasd_fba_discipline);
-        return rc;
+        return;
 }
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_fba.h linux.19p6/drivers/s390/block/dasd_fba.h
--- linux.vanilla/drivers/s390/block/dasd_fba.h	Sat Sep  9 18:42:40 2000
+++ linux.19p6/drivers/s390/block/dasd_fba.h	Thu Dec 21 22:50:30 2000
@@ -66,5 +66,6 @@
 } __attribute__ ((packed)) dasd_fba_characteristics_t;
 
 int dasd_fba_init( void );
+void dasd_fba_cleanup( void );
 
 #endif /* DASD_FBA_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/mdisk.c linux.19p6/drivers/s390/block/mdisk.c
--- linux.vanilla/drivers/s390/block/mdisk.c	Sun Jun 11 21:44:15 2000
+++ linux.19p6/drivers/s390/block/mdisk.c	Thu Dec 21 22:50:30 2000
@@ -105,6 +105,122 @@
 } mdisk_setup_data;
 
 /*
+ * The 'low level' IO function
+ */
+
+static __inline__ int
+dia250(void* iob,int cmd)
+{
+	int rc;
+	
+	iob = (void*) virt_to_phys(iob);
+
+	asm volatile ("    lr    2,%1\n"
+		      "    lr    3,%2\n"
+		      "    .long 0x83230250\n"
+		      "    lr    %0,3"
+		      : "=d" (rc)
+		      : "d" (iob) , "d" (cmd)
+		      : "2", "3" );
+	return rc;
+}
+
+/*
+ * The device characteristics function
+ */
+
+static __inline__ int
+dia210(void* devchar)
+{
+	int rc;
+
+	devchar = (void*) virt_to_phys(devchar);
+
+	asm volatile ("    lr    2,%1\n"
+		      "    .long 0x83200210\n"
+		      "    ipm   %0\n"
+		      "    srl   %0,28"
+		      : "=d" (rc)
+		      : "d" (devchar)
+		      : "2" );
+	return rc;
+}
+
+
+/*
+ * release of minidisk device
+ */
+
+static __inline__ int
+mdisk_term_io(mdisk_Dev *dev)
+{
+	mdisk_init_io_t *iob = (mdisk_init_io_t*) dev->iob;
+	
+	memset(iob,0,sizeof(mdisk_init_io_t));
+	
+	iob->dev_nr = dev->vdev;
+	
+	return dia250(iob,TERM_BIO);
+}
+
+
+/*
+ * Init of minidisk device
+ */
+
+static __inline__ int
+mdisk_init_io(mdisk_Dev *dev,int blocksize,int offset,int size)
+{
+	mdisk_init_io_t *iob = (mdisk_init_io_t*) dev->iob;
+	int rc;
+
+	memset(iob,0,sizeof(mdisk_init_io_t));
+	
+	iob->dev_nr = dev->vdev;
+	iob->block_size = blocksize;
+	iob->offset     = offset;
+	iob->start_block= 0;
+	iob->end_block  = size;
+	
+	rc = dia250(iob,INIT_BIO);
+	
+	/*
+	 * clear for following io once
+	 */
+	
+	memset(iob,0,sizeof(mdisk_rw_io_t));
+	
+	return rc;
+}
+
+
+/*
+ * setup and start of minidisk io request
+ */
+
+static __inline__ int
+mdisk_rw_io_clustered (mdisk_Dev *dev,
+                       mdisk_bio_t* bio_array,
+                       int length,
+                       int req,
+                       int sync)
+{
+	int rc;
+	mdisk_rw_io_t *iob = dev->iob;
+	
+	iob->dev_nr      = dev->vdev;
+	iob->key         = 0;
+	iob->flags       = sync;
+	
+	iob->block_count = length;
+	iob->interrupt_params = req;
+	iob->bio_list     = virt_to_phys(bio_array);
+	
+	rc = dia250(iob,RW_BIO);
+	return rc;
+}
+
+/*
  * Parameter parsing function, called from init/main.c
  * vdev    : virtual device number
  * size    : size in kbyte
@@ -117,7 +233,7 @@
 {
 	char *cur = str;
 	int vdev, size, offset=0,blksize;
-	static i = 0;
+static	int i = 0;
 	if (!i)
 	  memset(&mdisk_setup_data,0,sizeof(mdisk_setup_data));
 
@@ -280,48 +396,7 @@
 	NULL,          /* mdisk_lock */
 };
 
-/*
- * The 'low level' IO function
- */
-
-
-static __inline__ int
-dia250(void* iob,int cmd)
-{
-	int rc;
-	
-	iob = (void*) virt_to_phys(iob);
-
-	asm volatile ("    lr    2,%1\n"
-		      "    lr    3,%2\n"
-		      "    .long 0x83230250\n"
-		      "    lr    %0,3"
-		      : "=d" (rc)
-		      : "d" (iob) , "d" (cmd)
-		      : "2", "3" );
-	return rc;
-}
-
-/*
- * The device characteristics function
- */
-
-static __inline__ int
-dia210(void* devchar)
-{
-	int rc;
-
-	devchar = (void*) virt_to_phys(devchar);
 
-	asm volatile ("    lr    2,%1\n"
-		      "    .long 0x83200210\n"
-		      "    ipm   %0\n"
-		      "    srl   %0,28"
-		      : "=d" (rc)
-		      : "d" (devchar)
-		      : "2" );
-	return rc;
-}
 /*
  * read the label of a minidisk and extract its characteristics
  */
@@ -396,80 +471,6 @@
 }
 
 
-
-/*
- * Init of minidisk device
- */
-
-static __inline__ int
-mdisk_init_io(mdisk_Dev *dev,int blocksize,int offset,int size)
-{
-	mdisk_init_io_t *iob = (mdisk_init_io_t*) dev->iob;
-	int rc;
-
-	memset(iob,0,sizeof(mdisk_init_io_t));
-	
-	iob->dev_nr = dev->vdev;
-	iob->block_size = blocksize;
-	iob->offset     = offset;
-	iob->start_block= 0;
-	iob->end_block  = size;
-	
-	rc = dia250(iob,INIT_BIO);
-	
-	/*
-	 * clear for following io once
-	 */
-	
-	memset(iob,0,sizeof(mdisk_rw_io_t));
-	
-	return rc;
-}
-
-/*
- * release of minidisk device
- */
-
-static __inline__ int
-mdisk_term_io(mdisk_Dev *dev)
-{
-	mdisk_init_io_t *iob = (mdisk_init_io_t*) dev->iob;
-	
-	memset(iob,0,sizeof(mdisk_init_io_t));
-	
-	iob->dev_nr = dev->vdev;
-	
-	return dia250(iob,TERM_BIO);
-}
-
-/*
- * setup and start of minidisk io request
- */
-
-static __inline__ int
-mdisk_rw_io_clustered (mdisk_Dev *dev,
-                       mdisk_bio_t* bio_array,
-                       int length,
-                       int req,
-                       int sync)
-{
-	int rc;
-	mdisk_rw_io_t *iob = dev->iob;
-	
-	iob->dev_nr      = dev->vdev;
-	iob->key         = 0;
-	iob->flags       = sync;
-	
-	iob->block_count = length;
-	iob->interrupt_params = req;
-	iob->bio_list     = virt_to_phys(bio_array);
-	
-	rc = dia250(iob,RW_BIO);
-	return rc;
-}
-
-
-
 /*
  * this handles a clustered request in success case
  * all buffers are detach and marked uptodate to the kernel
@@ -640,11 +641,8 @@
  */
 void do_mdisk_interrupt(struct pt_regs *regs, __u16 code)
 {
-        u16 code;
 	mdisk_Dev *dev;
 	
-	code = S390_lowcore.cpu_addr;
-
 	if ((code >> 8) != 0x03) {
 	  printk("mnd: wrong sub-interruption code %d",code>>8);
 	  return;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/3w-xxxx.c linux.19p6/drivers/scsi/3w-xxxx.c
--- linux.vanilla/drivers/scsi/3w-xxxx.c	Sat Sep  9 18:42:40 2000
+++ linux.19p6/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.19p6/drivers/scsi/Config.in
--- linux.vanilla/drivers/scsi/Config.in	Mon Dec 11 22:12:53 2000
+++ linux.19p6/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.19p6/drivers/scsi/Makefile
--- linux.vanilla/drivers/scsi/Makefile	Mon Dec 11 22:12:53 2000
+++ linux.19p6/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.19p6/drivers/scsi/README.osst
--- linux.vanilla/drivers/scsi/README.osst	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/scsi/README.osst	Sat Dec 30 19:50:25 2000
@@ -0,0 +1,218 @@
+README file for the osst driver
+===============================
+(w) Kurt Garloff <garloff@suse.de> 11/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 being a copy of st and as such, the osst devices'
+behavior 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
+------------
+osst got integrated into the linux kernel. Select it during kernel
+configuration as module or compile statically into the kernel.
+Compile your kernel and install the modules.
+
+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 something 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.
+
+The device nodes for osst have to be created. Use the Makedevs.sh script
+attached to this file.
+
+
+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 block size of 32kB.
+
+(This limitation is worked on and will be fixed in version 0.8.7 of
+ this driver.)
+
+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 file system) 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 block size 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 completely interoperable with osst tapes.
+
+The ADR-x0 line is fully SCSI-2 compliant and is supported 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 info:
+* 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.
+
+
+Acknowledgments
+----------------
+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.kernel,v 1.1.2.2 2000/12/20 14:08:38 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/README.tmscsim linux.19p6/drivers/scsi/README.tmscsim
--- linux.vanilla/drivers/scsi/README.tmscsim	Sat Sep  9 18:42:40 2000
+++ linux.19p6/drivers/scsi/README.tmscsim	Sat Dec 30 19:46:46 2000
@@ -9,6 +9,7 @@
 6. Potential improvements
 7. Bug reports, debugging and updates
 8. Acknowledgements
+9. Copyright
 
 
 1. Purpose and history
@@ -25,7 +26,7 @@
 Tekram DC390(T) adapter. This is where the name comes from: tm = Tekram
 scsi = SCSI driver, m = AMD (?) as opposed to w for the DC390W/U/F
 (NCR53c8X5, X=2/7) driver. Yes, there was also a driver for the latter,
-tmscsimw, which supported DC390W/U/F adapters. It's not maintained any more,
+tmscsiw, which supported DC390W/U/F adapters. It's not maintained any more,
 as the ncr53c8xx is perfectly supporting these adpaters since some time.
 
 The driver first appeared in April 1996, exclusively supported the DC390 
@@ -56,10 +57,13 @@
 driver. Of course you have to choose to compile SCSI support and DC390(T)
 support into your kernel or as module when configuring your kernel for
 compiling.
+NEW: You may as well compile this module outside your kernel, using the
+supplied Makefile.
 
  If you got an old kernel (pre 2.1.127, pre 2.0.37p1) with an old version of
  this driver: Get dc390-21125-20b.diff.gz or dc390-2036p21-20b1.diff.gz from
- my website and apply the patch. 
+ my web page and apply the patch. Apply further patches to upgrade to the 
+ latest version of the driver.
 
  If you want to do it manually, you should copy the files (dc390.h,
  tmscsim.h, tmscsim.c, scsiiom.c and README.tmscsim) from this directory to
@@ -91,6 +95,8 @@
   tune2fs -e remount-ro /dev/sd??
 * have copies of your SCSI disk's partition tables on some safe location:
   dd if=/dev/sda of=/mnt/floppy/sda bs=512 count=1
+  or just print it with:
+  fdisk -l | lpr
 * make sure you are able to boot Linux (e.g. from floppy disk using InitRD)
   if your SCSI disk gets corrupted. You can use 
   ftp://student.physik.uni-dortmund.de/pub/linux/kernel/bootdisk.gz
@@ -102,7 +108,7 @@
 than the 33.33 MHz being in the PCI spec.
 
 If you want to share the IRQ with another device and the driver refuses to
-do, you might succeed with changing the DC390_IRQ type in tmscsim.c to 
+do so, you might succeed with changing the DC390_IRQ type in tmscsim.c to 
 SA_SHIRQ | SA_INTERRUPT.
 
 
@@ -121,11 +127,12 @@
  * Dynamically configurable by writing to /proc/scsi/tmscsim/?
  * Dynamic allocation of resources
  * SMP support: Locking on io_request lock (Linux 2.1/2.2) or adapter 
-    specific locks (Linux 2.3)
+    specific locks (Linux 2.5?)
  * Uniform source code for Linux-2.x.y
  * Support for dyn. addition/removal of devices via add/remove-single-device
-   (Try: echo "scsi add-single-device H C I L" >/proc/scsi/scsi 
-    H = Host, C = Channel, I = SCSI ID, L = SCSI LUN.) Use with care!
+   (Try: echo "scsi add-single-device C B T U" >/proc/scsi/scsi 
+    C = Controller, B = Bus, T = Target SCSI ID, U = Unit SCSI LUN.) 
+    Use with care!
  * Try to use the partition table for the determination of the mapping
 
 
@@ -139,20 +146,21 @@
 the attached devices and their settings.
 
 Here's an example:
-garloff@kg1:/home/garloff > cat /proc/scsi/tmscsim/0
-Tekram DC390/AM53C974 PCI SCSI Host Adapter, Driver Version 1.20s, 1998/08/20
-SCSI Host Nr 0, AM53C974 Adapter Nr 0
-IOPortBase 0x6200, IRQLevel 0x09
-MaxID 7, MaxLUN 8, AdapterID 7, SelTimeout 250 ms
-TagMaxNum 16, Status 0, ACBFlag 0, GlitchEater 24 ns
-Statistics: Nr of Cmnds 39563, Cmnds not sent directly 0, Out of SRB conds 0
-            Nr of lost arbitrations 17
+garloff@kurt:/home/garloff > cat /proc/scsi/tmscsim/0
+Tekram DC390/AM53C974 PCI SCSI Host Adapter, Driver Version 2.0e7 2000-11-28
+SCSI Host Nr 1, AM53C974 Adapter Nr 0
+IOPortBase 0xb000, IRQ 10
+MaxID 8, MaxLUN 8, AdapterID 6, SelTimeout 250 ms, DelayReset 1 s
+TagMaxNum 16, Status 0x00, ACBFlag 0x00, GlitchEater 24 ns
+Statistics: Cmnds 1470165, Cmnds not sent directly 0, Out of SRB conds 0
+            Lost arbitrations 587,  Sel. connected 0, Connected: No
 Nr of attached devices: 4, Nr of DCBs: 4
-Idx ID LUN Prty Sync DsCn SndS TagQ STOP NegoPeriod SyncSpeed SyncOffs
-00  00  00  Yes  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15
-01  01  00  Yes  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15
-02  03  00  Yes  Yes  Yes  Yes  No   No    100 ns    10.0 M      15
-03  05  00  Yes  No   Yes  Yes  No   No   (200 ns)
+Map of attached LUNs: 01 00 00 03 01 00 00 00
+Idx ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs MaxCmd
+00  00  00  Yes  Yes  Yes  Yes  Yes   100 ns    10.0 M      15      16
+01  03  00  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15      01
+02  03  01  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15      01
+03  04  00  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15      01
 
 Note that the settings MaxID and MaxLUN are not zero- but one-based, which
 means that a setting MaxLUN=4, will result in the support of LUNs 0..3. This
@@ -178,9 +186,8 @@
 The last values are only shown, if Sync is enabled. (NegoPeriod is still
 displayed in brackets to show the values which will be used after enabling
 Sync.)
-The STOP parameter is for testing/debugging purposes only and should bet set
-to No. Please don't fiddle with it, unless you want to get rid of the
-contents of your disk.
+MaxCmd ist the number of commands (=tags) which can be processed at the same
+time by the device.
 
 If you want to change a setting, you can do that by writing to
 /proc/scsi/tmscsim/?. Basically you have to imitate the output of driver.
@@ -196,8 +203,8 @@
      echo "MaxLUN=8 seltimeout 200" >/proc/scsi/tmscsim/0
 
     Note that you can only change MaxID, MaxLUN, AdapterID, SelTimeOut,
-    TagMaxNum, ACBFlag and GlitchEater. Don't change ACBFlag unless you
-    want to see what happens, if the driver hangs.
+    TagMaxNum, ACBFlag, GlitchEater and DelayReset. Don't change ACBFlag
+    unless you want to see what happens, if the driver hangs.
 
 (2) Change device settings: You write a config line to the driver. The Nr
     must match the ID and LUN given. If you give "-" as parameter, it is
@@ -207,8 +214,8 @@
     an INQUIRY on the device if necessary to check if it is capable to operate
     with the given settings (Sync, TagQ).
     Examples:
-     echo "0 0 0 y y y - y - 10" >/proc/scsi/tmscsim/0
-     echo "3 5 0 y n y" >/proc/scsi/tmscsim/0
+     echo "0 0 0 y y y - y - 10 " >/proc/scsi/tmscsim/0
+     echo "3 5 0 y n y " >/proc/scsi/tmscsim/0
 
     To give a short explanation of the first example: 
     The first three numbers, "0 0 0" (Device index 0, SCSI ID 0, SCSI LUN 0),
@@ -223,22 +230,26 @@
     discussed above. The values used in this example will result in maximum
     performance.
 
-(3) Special commands: You can force a SCSI bus reset, an INQUIRY command and
-    the removal of a device's DCB.
+(3) Special commands: You can force a SCSI bus reset, an INQUIRY command, the
+    removal or the addition of a device's DCB and a SCSI register dump.
     This is only used for debugging when you meet problems. The parameter of
-    the INQUIRY and remove command is the device index as shown by the
+    the INQUIRY and REMOVE commands is the device index as shown by the
     output of /proc/scsi/tmscsim/? in the device listing in the first column
-    (Idx).
+    (Idx). ADD takes the SCSI ID and LUN.
     Examples:
      echo "reset" >/proc/scsi/tmscsim/0
      echo "inquiry 1" >/proc/scsi/tmscsim/0
      echo "remove 2" >/proc/scsi/tmscsim/1
+     echo "add 2 3" >/proc/scsi/tmscsim/?
+     echo "dump" >/proc/scsi/tmscsim/0
 
-    Note that you will meet problems when you remove a device's DCB with the
+    Note that you will meet problems when you REMOVE a device's DCB with the
     remove command if it contains partitions which are mounted. Only use it
     after unmounting its partitions, telling the SCSI mid-level code to
     remove it (scsi remove-single-device) and you really need a few bytes of
     memory.
+    The ADD command allows you to configure a device before you tell the
+    mid-level code to try detection.
 
 
 I'd suggest reviewing the output of /proc/scsi/tmscsim/? after changing
@@ -247,18 +258,20 @@
 
 5. Configuration via boot/module parameters
 -------------------------------------------
-With the DC390, the driver reads its EEPROM settings and IGNORES boot / 
-module parameters. If you want to override the EEPROM settings of a DC390, 
-you have to use the /proc/scsi/tmscsim/? interface described in the above
-chapter.
-
-However, if you do have another AM53C974 based adapter you might want to
-adjust some settings before you are able to write to the /proc/scsi/tmscsim/?
-pseudo-file, e.g. if you want to use another adapter ID than 7. (Note that
-the log message "DC390: No EEPROM found!" is normal without a DC390.)
+With the DC390, the driver reads its EEPROM settings and tries to use them.
+But you may want to override the settings prior to being able to change the
+driver configuration via /proc/scsi/tmscsim/?.
+If you do have another AM53C974 based adapter, that's even the only
+possibility to adjust settings before you are able to write to the
+/proc/scsi/tmscsim/? pseudo-file, e.g. if you want to use another 
+adapter ID than 7.  
+(BTW, the log message "DC390: No EEPROM found!" is normal without a DC390.)
 For this purpose, you can pass options to the driver before it is initialised
 by using kernel or module parameters. See lilo(8) or modprobe(1) manual
 pages on how to pass params to the kernel or a module.
+[NOTE: Formerly, it was not possible to override the EEPROM supplied
+ settings of the DC390 with cmd line parameters. This has changed since
+ 2.0e7]
 
 The syntax of the params is much shorter than the syntax of the /proc/...
 interface. This makes it a little bit more difficult to use. However, long
@@ -269,7 +282,7 @@
 DC390 EEPROM, the settings are given in a DC390 BIOS' way.
 
 Here's the syntax:
-tmscsim=AdaptID,SpdIdx,DevMode,AdaptMode,TaggedCmnds
+tmscsim=AdaptID,SpdIdx,DevMode,AdaptMode,TaggedCmnds,DelayReset
 
 Each of the parameters is a number, containing the described information:
 
@@ -278,7 +291,7 @@
 
 * SpdIdx: The index of the maximum speed as in the DC390 BIOS. The values
   0..7 mean 10, 8.0, 6.7, 5.7, 5.0, 4.0, 3.1 and 2 MHz resp. Default is
-  1 (8.0 MHz).
+  0 (10.0 MHz).
 
 * DevMode is a bit mapped value describing the per-device features. It
   applies to all devices. (Sync, Disc and TagQ will only apply, if the
@@ -289,7 +302,7 @@
    *1	 0x02	    2	  Synchronous Negotiation
    *2	 0x04	    4	  Disconnection
    *3	 0x08	    8	  Send Start command on startup. (Not used)
-   *4	 0x10	   16	  Tagged Queueing
+   *4	 0x10	   16	  Tagged Command Queueing
 
   As usual, the desired value is obtained by adding the wanted values. If
   you want to enable all values, e.g., you would use 31(0x1f). Default is 31.
@@ -315,18 +328,23 @@
     *3		16
      4		32
 
+* DelayReset is the time in seconds (minus 0.5s), the adapter waits, after a
+  bus reset. Default is 1 (corresp. to 1.5s).
+
 Example:
  modprobe tmscsim tmscsim=6,2,31
 would set the adapter ID to 6, max. speed to 6.7 MHz, enable all device
-features and leave the adapter features and the number of Tagged Commands 
-to the defaults.
-
-As you can see, you don't need to specify all of the five params.
+features and leave the adapter features, the number of Tagged Commands
+and the Delay after a reset to the defaults.
 
-The defaults (7,1,31,15,3) are aggressive to allow good performance. You can
-use tmscsim=7,0,31,63,4 for maximum performance, if your SCSI chain is
-perfect. If you meet problems, you can use tmscsim=-1 which is a shortcut
-for tmscsim=7,4,9,15,2. 
+As you can see, you don't need to specify all of the six params.
+If you want values to be ignored (i.e. the EEprom settings or the defaults
+will be used), you may pass -2 (not 0!) at the corresponding position.
+
+The defaults (7,0,31,15,3,1) are aggressive to allow good performance. You
+can use tmscsim=7,0,31,63,4,0 for maximum performance, if your SCSI chain
+allows it. If you meet problems, you can use tmscsim=-1 which is a shortcut
+for tmscsim=7,4,9,15,2,10.
 
 
 6. Potential improvements
@@ -334,42 +352,47 @@
 Most of the intended work on the driver has been done. Here are a few ideas
 to further improve its usability:
 
+* Cleanly separate per-Target and per-LUN properties (DCB)
 * More intelligent abort() routine
-* Implement new_eh code (Linux-2.1+)
-* Have the mid-level code (and not the driver) handle more of the various
-  conditions.
-* Rework command queueing in the driver
+* Use new_eh code (Linux-2.1+)
+* Have the mid-level (ML) code (and not the driver) handle more of the
+  various conditions.
+* Command queueing in the driver: Eliminate Query list and use ML instead.
 * More user friendly boot/module param syntax
 
 Further investigation on these problems:
 
 * Driver hangs with sync readcdda (xcdroast) (most probably VIA PCI error)
 
-Known problems:
+Known problems: 
+Please see http://www.garloff.de/kurt/linux/dc390/problems.html
 
-* There was a report that with a certain Scanner, the last SCSI command
-  won't be finished correctly. This might be a command queueing bug or a bug
-  in SCSI implementation of the scanner. Issueing another command to the
-  scanner seems to help. (Try echo "INQUIRY x" >/proc/scsi/tmscsim/?, where
-  x is the index (not the SCSI ID!) of the scanner. See 4.(3).)
+* Changing the parameters of multi-lun by the tmscsim/? interface will
+  cause problems, cause these settings are mostly per Target and not per LUN
+  and should be updated accordingly. To be fixed for 2.0d24.
+* CDRs (eg Yam CRW4416) not recognized, because some buggy devices don't 
+  recover from a SCSI reset in time. Use a higher delay or don't issue
+  a SCSI bus reset on driver initialization. See problems page.
+  For the CRW4416S, this seems to be solved with firmware 1.0g (reported by 
+  Jean-Yves Barbier).
+* TEAC CD-532S not being recognized. (Works with 1.11).
+* Scanners (eg. Astra UMAX 1220S) don't work: Disable Sync Negotiation.
+  If this does not help, try echo "INQUIRY t" >/proc/scsi/tmscsim/? (t
+  replaced by the dev index of your scanner). You may try to reset your SCSI
+  bus afterwards (echo "RESET" >/proc/scsi/tmscsim/?).
+  The problem seems to be solved as of 2.0d18, thanks to Andreas Rick.
 * If there is a valid partition table, the driver will use it for determing
-  the mapping. Other operating systems may not like this mapping, though
+  the mapping. If there's none, a reasonable mapping (Symbios-like) will be
+  assumed. Other operating systems may not like this mapping, though
   it's consistent with the BIOS' behaviour. Old DC390 drivers ignored the
   partition table and used a H/S = 64/32 or 255/63 translation. So if you
   want to be compatible to those, use this old mapping when creating
-  partition tables.
-* In some situations, the driver will get stuck in an abort loop. Please
-  disable DsCn, if you meet this problem. Please contact me for further
-  debugging.
-* 2.1.115+: Linux misses locks in sr_ioctl.c and scsi_ioctl.c
-  There used to be a patch included here, which partially solved the
-  problem. I suggest you contact Chiaki Ishikawa <ishikawa@yk.rim.or.jp>, 
-  Richard Waltham <dormouse@farsrobt.demon.co.uk> or Doug Ledford
-  <dledford@dialnet.net>, if you want to help further debugging it.
-* 2.0.35: CD changers (e.g. NAKAMICHI MBR-7.{0,2}) have problems because
-  the mid-level code doesn't handle BLIST_SINGLELUN correctly. There used
-  to be a patch included here to fix this, but I was told that it is fixed
-  in 2.0.36.
+  partition tables. Even worse, on bootup the DC390 might complain if other
+  mappings are found, so auto rebooting may fail.
+* In some situations, the driver will get stuck in an abort loop. This is a
+  bad interaction between the Mid-Layer of Linux' SCSI code and the driver.
+  Try to disable DsCn, if you meet this problem. Please contact me for
+  further debugging.
 
 
 7. Bug reports, debugging and updates
@@ -394,25 +417,33 @@
 
 The latest version of the driver can be found at:
  http://www.garloff.de/kurt/linux/dc390/
-and
- ftp://student.physik.uni-dortmund.de/pub/linux/kernel/dc390/
-(The latter might shut down some day.)
+ ftp://ftp.suse.com/pub/people/garloff/linux/dc390/
 
 
 8. Acknowledgements
 -------------------
-Thanks to Linus Torvalds, Alan Cox, David Miller, Rik v. Riel, the FSF
-people, the XFree86 team and all the others for the wonderful OS and
-software. 
+Thanks to Linus Torvalds, Alan Cox, the FSF people, the XFree86 team and 
+all the others for the wonderful OS and software.
 Thanks to C.L. Huang and Philip Giang (Tekram) for the initial driver
 release and support.
-Thanks to Doug Ledford, Gerard Roudier for support with SCSI coding.
+Thanks to Doug Ledford, Gérard Roudier for support with SCSI coding.
 Thanks to a lot of people (espec. Chiaki Ishikawa, Andreas Haumer, Hubert 
 Tonneau) for intensively testing the driver (and even risking data loss
-doing this during early revisions). 
+doing this during early revisions).
+Recently, SuSE GmbH, Nuernberg, FRG, has been paying me for the driver
+development and maintenance. Special thanks!
+
 
+9. Copyright
+------------
+ This driver 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; version 2 of the License.
+ If you want to use any later version of the GNU GPL, you will probably
+ be allowed to, but you have to ask me and Tekram <erich@tekram.com.tw>
+ before.
 
 -------------------------------------------------------------------------
 Written by Kurt Garloff <kurt@garloff.de> 1998/06/11
-Last updated 1998/12/25, driver revision 2.0d
-$Id: README.tmscsim,v 2.9 1998/12/25 18:04:20 garloff Exp $
+Last updated 2000/11/28, driver revision 2.0e7
+$Id: README.tmscsim,v 2.25.2.7 2000/12/20 01:07:12 garloff Exp $
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/atp870u.c linux.19p6/drivers/scsi/atp870u.c
--- linux.vanilla/drivers/scsi/atp870u.c	Mon Dec 11 22:12:53 2000
+++ linux.19p6/drivers/scsi/atp870u.c	Thu Dec 21 10:54:32 2000
@@ -10,6 +10,7 @@
  *		   support atp876 chip
  *		   enable 32 bit fifo transfer
  *		   support cdrom & remove device run ultra speed
+ *		   fix disconnect bug  2000/12/21
  */
 
 #include <linux/module.h>
@@ -76,6 +77,7 @@
 		unsigned char dirctu;
 		unsigned char devspu;
 		unsigned char devtypeu;
+		unsigned char devlcmdu;
 		unsigned long prdaddru;
 		unsigned long tran_lenu;
 		unsigned long last_lenu;
@@ -143,14 +145,6 @@
 		tmport -= 0x08;
 
 		i = inb(tmport);
-		if ((j & 0x40) == 0)
-		{
-			if ((dev->last_cmd & 0x40) == 0)
-			{
-				dev->last_cmd = 0xff;
-			}
-		}
-		else dev->last_cmd |= 0x40;
 
 		tmport -= 0x02;
 		target_id = inb(tmport);
@@ -166,8 +160,21 @@
 			target_id &= 0x07;
 		}
 
+		if ((j & 0x40) != 0)
+		{
+		     if (dev->last_cmd == 0xff)
+		     {
+			dev->last_cmd = target_id;
+		     }
+		     dev->last_cmd |= 0x40;
+		}
+
 		if (i == 0x85)
 		{
+			if ((dev->last_cmd & 0xf0) != 0x40)
+			{
+			   dev->last_cmd = 0xff;
+			}
 			/*
 			 *	Flip wide
 			 */
@@ -196,6 +203,10 @@
 		}
 		if (i == 0x21)
 		{
+			if ((dev->last_cmd & 0xf0) != 0x40)
+			{
+			   dev->last_cmd = 0xff;
+			}
 			tmport -= 0x05;
 			adrcntu = 0;
 			((unsigned char *) &adrcntu)[2] = inb(tmport++);
@@ -221,6 +232,10 @@
 				tmport += 0x0d;
 				lun = inb(tmport) & 0x07;
 			} else {
+				if ((dev->last_cmd & 0xf0) != 0x40)
+				{
+				   dev->last_cmd = 0xff;
+				}
 				if (j == 0x41)
 				{
 					tmport += 0x02;
@@ -251,6 +266,10 @@
 					return;
 				}
 			}
+			if (dev->last_cmd != 0xff)
+			{
+			   dev->last_cmd |= 0x40;
+			}
 			tmport = workportu + 0x10;
 			outb(0x45, tmport);
 			tmport += 0x06;
@@ -371,12 +390,20 @@
 		workrequ = dev->id[target_id].curr_req;
 
 		if (i == 0x42) {
+			if ((dev->last_cmd & 0xf0) != 0x40)
+			{
+			   dev->last_cmd = 0xff;
+			}
 			errstus = 0x02;
 			workrequ->result = errstus;
 			goto go_42;
 		}
 		if (i == 0x16)
 		{
+			if ((dev->last_cmd & 0xf0) != 0x40)
+			{
+			   dev->last_cmd = 0xff;
+			}
 			errstus = 0;
 			tmport -= 0x08;
 			errstus = inb(tmport);
@@ -387,13 +414,13 @@
 			 */
 			spin_lock_irqsave(&io_request_lock, flags);
 			(*workrequ->scsi_done) (workrequ);
-			spin_unlock_irqrestore(&io_request_lock, flags);
 
 			/*
 			 *	Clear it off the queue
 			 */
 			dev->id[target_id].curr_req = 0;
 			dev->working--;
+			spin_unlock_irqrestore(&io_request_lock, flags);
 			/*
 			 *	Take it back wide
 			 */
@@ -411,11 +438,15 @@
 			if (((dev->last_cmd != 0xff) || (dev->quhdu != dev->quendu)) &&
 			    (dev->in_snd == 0))
 			{
-				send_s870(h);
+			   send_s870(h);
 			}
 			dev->in_int = 0;
 			return;
 		}
+		if ((dev->last_cmd & 0xf0) != 0x40)
+		{
+		   dev->last_cmd = 0xff;
+		}
 		if (i == 0x4f) {
 			i = 0x89;
 		}
@@ -480,7 +511,7 @@
 
 int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done) (Scsi_Cmnd *))
 {
-	unsigned char i, h;
+	unsigned char h;
 	unsigned long flags;
 	unsigned short int m;
 	unsigned int tmport;
@@ -522,6 +553,8 @@
 	/*
 	 *	Count new command
 	 */
+	save_flags(flags);
+	cli();
 	dev->quendu++;
 	if (dev->quendu >= qcnt) {
 		dev->quendu = 0;
@@ -529,18 +562,17 @@
 	/*
 	 *	Check queue state
 	 */
-wait_que_empty:
 	if (dev->quhdu == dev->quendu) {
-		goto wait_que_empty;
+		if (dev->quendu == 0) {
+			dev->quendu = qcnt;
+		}
+		dev->quendu--;
+		req_p->result = 0x00020000;
+		done(req_p);
+		restore_flags(flags);
+		return 0;
 	}
-	save_flags(flags);
-	cli();
 	dev->querequ[dev->quendu] = req_p;
-	if (dev->quendu == 0) {
-		i = qcnt - 1;
-	} else {
-		i = dev->quendu - 1;
-	}
 	tmport = dev->ioport + 0x1c;
 	restore_flags(flags);
 	if ((inb(tmport) == 0) && (dev->in_int == 0) && (dev->in_snd == 0)) {
@@ -593,6 +625,12 @@
 		   return ;
 		}
 	}
+	if ((dev->last_cmd != 0xff) && (dev->working != 0))
+	{
+	     dev->in_snd = 0;
+	     restore_flags(flags);
+	     return ;
+	}
 	dev->working++;
 	j = dev->quhdu;
 	dev->quhdu++;
@@ -626,6 +664,7 @@
 	restore_flags(flags);
 	return;
 oktosend:
+	dev->id[workrequ->target].devlcmdu=0;
 	memcpy(&dev->ata_cdbu[0], &workrequ->cmnd[0], workrequ->cmd_len);
 	if (dev->ata_cdbu[0] == READ_CAPACITY) {
 		if (workrequ->request_bufflen > 8) {
@@ -635,15 +674,6 @@
 	if (dev->ata_cdbu[0] == 0x00) {
 		workrequ->request_bufflen = 0;
 	}
-	/*
-	 *	Why limit this ????
-	 */
-	if (dev->ata_cdbu[0] == INQUIRY) {
-		if (workrequ->request_bufflen > 0x24) {
-			workrequ->request_bufflen = 0x24;
-			dev->ata_cdbu[4] = 0x24;
-		}
-	}
 
 	tmport = workportu + 0x1b;
 	j = 0;
@@ -735,6 +765,7 @@
 		} else {
 			dev->last_cmd |= 0x40;
 		}
+		dev->id[target_id].devlcmdu=dev->last_cmd;
 		dev->in_snd = 0;
 		restore_flags(flags);
 		return;
@@ -809,6 +840,7 @@
 		} else {
 			dev->last_cmd |= 0x40;
 		}
+		dev->id[target_id].devlcmdu=dev->last_cmd;
 		dev->in_snd = 0;
 		restore_flags(flags);
 		return;
@@ -821,6 +853,7 @@
 	} else {
 		dev->last_cmd |= 0x40;
 	}
+	dev->id[target_id].devlcmdu=dev->last_cmd;
 	dev->in_snd = 0;
 	restore_flags(flags);
 	return;
@@ -1751,7 +1784,7 @@
 		if (chip_ver[h] > 0x07) 	  /* check if atp876 chip   */
 		{				  /* then enable terminator */
 		   tmport = base_io + 0x3e;
-		   outb(0x30, tmport);
+		   outb(0x00, tmport);
 		}
 
 		tmport = base_io + 0x3a;
@@ -1812,7 +1845,8 @@
 
 int atp870u_abort(Scsi_Cmnd * SCpnt)
 {
-	unsigned char h, j;
+	unsigned char h, j, k;
+	Scsi_Cmnd *workrequ;
 	unsigned int tmport;
 	struct atp_unit *dev;
 	for (h = 0; h <= admaxu; h++) {
@@ -1839,12 +1873,27 @@
 	printk(" r22=%2x", inb(tmport));
 	tmport += 0x18;
 	printk(" r3a=%2x \n",inb(tmport));
+	for(j=0;j<16;j++)
+	{
+	   if (dev->id[j].curr_req != NULL)
+	   {
+		workrequ = dev->id[j].curr_req;
+		printk("\n que cdb= ");
+		for (k=0; k < workrequ->cmd_len; k++)
+		{
+		    printk(" %2x ",workrequ->cmnd[k]);
+		}
+		printk(" last_lenu= %x ",dev->id[j].last_lenu);
+		printk(" dev_lcmd= %x ",dev->id[j].devlcmdu);
+	   }
+	}
 	return (SCSI_ABORT_SNOOZE);
 }
 
 int atp870u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
 {
 	unsigned char h;
+	struct atp_unit *dev;
 	/*
 	 * See if a bus reset was suggested.
 	 */
@@ -1855,6 +1904,7 @@
 	}
 	panic("Reset bus host not found !");
 find_host:
+	dev=&atp_unit[h];
 /*	SCpnt->result = 0x00080000;
 	SCpnt->scsi_done(SCpnt);
 	dev->working=0;
@@ -1868,7 +1918,7 @@
 {
 	static char buffer[128];
 
-	strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.1+ac ");
+	strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.3+ac ");
 
 	return buffer;
 }
@@ -1913,7 +1963,7 @@
 	if (offset == 0) {
 		memset(buff, 0, sizeof(buff));
 	}
-	size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.1+ac\n");
+	size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.3+ac\n");
 	len += size;
 	pos = begin + len;
 	size = 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/dc390.h linux.19p6/drivers/scsi/dc390.h
--- linux.vanilla/drivers/scsi/dc390.h	Wed May  3 22:31:20 2000
+++ linux.19p6/drivers/scsi/dc390.h	Wed Jan  3 13:59:11 2001
@@ -4,9 +4,7 @@
  *	Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
  *		     Bus Master Host Adapter			       *
  ***********************************************************************/
-/* $Id: dc390.h,v 2.12 1998/12/25 17:33:27 garloff Exp $ */
-
-#include <linux/version.h>
+/* $Id: dc390.h,v 2.43.2.22 2000/12/20 00:39:36 garloff Exp $ */
 
 /*
  * DC390/AMD 53C974 driver, header file
@@ -15,12 +13,27 @@
 #ifndef DC390_H
 #define DC390_H
 
+#include <linux/version.h>
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
 #define DC390_BANNER "Tekram DC390/AM53C974"
-#define DC390_VERSION "2.0d 1998/12/25"
+#define DC390_VERSION "2.0f 2000-12-20"
 
-#if defined(HOSTS_C) || defined(MODULE)
+/* We don't have eh_abort_handler, eh_device_reset_handler, 
+ * eh_bus_reset_handler, eh_host_reset_handler yet! 
+ * So long: Use old exception handling :-( */
+#define OLD_EH
 
-#include <scsi/scsicam.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION (2,1,70) || defined (OLD_EH)
+# define NEW_EH
+#else
+# define NEW_EH use_new_eh_code: 1,
+# define USE_NEW_EH
+#endif
+
+#if defined(HOSTS_C) || defined(MODULE) || LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99)
 
 extern int DC390_detect(Scsi_Host_Template *psht);
 extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
@@ -34,9 +47,29 @@
 # define DC390_release NULL
 #endif
 
-extern struct proc_dir_entry DC390_proc_scsi_tmscsim;
 extern int DC390_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30)
+#define DC390_T    {					\
+   proc_name:      "tmscsim",                           \
+   proc_info:      DC390_proc_info,			\
+   name:           DC390_BANNER " V" DC390_VERSION,	\
+   detect:         DC390_detect,			\
+   release:        DC390_release,			\
+   queuecommand:   DC390_queue_command,			\
+   abort:          DC390_abort,				\
+   reset:          DC390_reset,				\
+   bios_param:     DC390_bios_param,			\
+   can_queue:      42,					\
+   this_id:        7,					\
+   sg_tablesize:   SG_ALL,				\
+   cmd_per_lun:    16,					\
+   NEW_EH						\
+   unchecked_isa_dma: 0,				\
+   use_clustering: DISABLE_CLUSTERING			\
+   }
+#else
+extern struct proc_dir_entry DC390_proc_scsi_tmscsim;
 #define DC390_T    {					\
    proc_dir:       &DC390_proc_scsi_tmscsim,		\
    proc_info:      DC390_proc_info,			\
@@ -47,13 +80,15 @@
    abort:          DC390_abort,				\
    reset:          DC390_reset,				\
    bios_param:     DC390_bios_param,			\
-   can_queue:      17,					\
+   can_queue:      42,					\
    this_id:        7,					\
    sg_tablesize:   SG_ALL,				\
-   cmd_per_lun:    8,					\
+   cmd_per_lun:    16,					\
+   NEW_EH						\
+   unchecked_isa_dma: 0,				\
    use_clustering: DISABLE_CLUSTERING			\
    }
-
+#endif
 #endif /* defined(HOSTS_C) || defined(MODULE) */
 
 #endif /* DC390_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth.c linux.19p6/drivers/scsi/gdth.c
--- linux.vanilla/drivers/scsi/gdth.c	Mon Dec 11 22:12:54 2000
+++ linux.19p6/drivers/scsi/gdth.c	Thu Nov 16 12:03:24 2000
@@ -20,9 +20,19 @@
  * along with this kernel; if not, write to the Free Software           *
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
  *                                                                      *
- * Tested with Linux 1.2.13, ..., 2.2.16, ..., 2.4.0-test4              *
+ * Tested with Linux 1.2.13, ..., 2.2.16, ..., 2.4.0-test10             *
  *                                                                      *
  * $Log: gdth.c,v $
+ * Revision 1.45  2000/11/16 12:02:24  achim
+ * Changes for kernel 2.4
+ *
+ * Revision 1.44  2000/10/11 08:44:10  achim
+ * Clustering changes: New flag media_changed added
+ *
+ * Revision 1.43  2000/09/20 12:59:01  achim
+ * DPMEM remap functions for all PCI controller types implemented
+ * Small changes for ia64 platform
+ *
  * Revision 1.42  2000/07/20 09:04:50  achim
  * Small changes for kernel 2.4
  *
@@ -180,7 +190,7 @@
  * Initial revision
  *
  ************************************************************************/
-#ident "$Id: gdth.c,v 1.42 2000/07/20 09:04:50 achim Exp $" 
+#ident "$Id: gdth.c,v 1.45 2000/11/16 12:02:24 achim Exp $" 
 
 /* All GDT Disk Array Controllers are fully supported by this driver.
  * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the
@@ -1111,8 +1121,51 @@
             printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
             return 0;
         }
+        /* check and reset interface area */
         dp6_ptr = (gdt6_dpram_str *)ha->brd;
-        /* reset interface area */
+        gdth_writel(DPMEM_MAGIC, &dp6_ptr->u);
+        if (gdth_readl(&dp6_ptr->u) != DPMEM_MAGIC) {
+            printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n", 
+                   pcistr->dpmem);
+            found = FALSE;
+            for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+                gdth_munmap(ha->brd);
+                ha->brd = gdth_mmap(i, sizeof(ushort)); 
+                if (ha->brd == NULL) {
+                    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+                    return 0;
+                }
+                if (gdth_readw(ha->brd) != 0xffff) {
+                    TRACE2(("init_pci_old() address 0x%x busy\n", i));
+                    continue;
+                }
+                gdth_munmap(ha->brd);
+#if LINUX_VERSION_CODE >= 0x2015C
+                pci_write_config_dword(pcistr->pdev, 
+                                       PCI_BASE_ADDRESS_0, i);
+#else
+                pcibios_write_config_dword(pcistr->bus, pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_0, i);
+#endif
+                ha->brd = gdth_mmap(i, sizeof(gdt6_dpram_str)); 
+                if (ha->brd == NULL) {
+                    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+                    return 0;
+                }
+                dp6_ptr = (gdt6_dpram_str *)ha->brd;
+                gdth_writel(DPMEM_MAGIC, &dp6_ptr->u);
+                if (gdth_readl(&dp6_ptr->u) == DPMEM_MAGIC) {
+                    printk("GDT-PCI: Use free address at 0x%x\n", i);
+                    found = TRUE;
+                    break;
+                }
+            }   
+            if (!found) {
+                printk("GDT-PCI: No free address found!\n");
+                gdth_munmap(ha->brd);
+                return 0;
+            }
+        }
         memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
         if (gdth_readl(&dp6_ptr->u) != 0) {
             printk("GDT-PCI: Initialization error (DPMEM write error)\n");
@@ -1181,8 +1234,51 @@
             gdth_munmap(ha->brd);
             return 0;
         }
+        /* check and reset interface area */
         dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
-        /* reset interface area */
+        gdth_writel(DPMEM_MAGIC, &dp6c_ptr->u);
+        if (gdth_readl(&dp6c_ptr->u) != DPMEM_MAGIC) {
+            printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n", 
+                   pcistr->dpmem);
+            found = FALSE;
+            for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+                gdth_munmap(ha->brd);
+                ha->brd = gdth_mmap(i, sizeof(ushort)); 
+                if (ha->brd == NULL) {
+                    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+                    return 0;
+                }
+                if (gdth_readw(ha->brd) != 0xffff) {
+                    TRACE2(("init_pci_plx() address 0x%x busy\n", i));
+                    continue;
+                }
+                gdth_munmap(ha->brd);
+#if LINUX_VERSION_CODE >= 0x2015C
+                pci_write_config_dword(pcistr->pdev, 
+                                       PCI_BASE_ADDRESS_2, i);
+#else
+                pcibios_write_config_dword(pcistr->bus, pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_2, i);
+#endif
+                ha->brd = gdth_mmap(i, sizeof(gdt6c_dpram_str)); 
+                if (ha->brd == NULL) {
+                    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+                    return 0;
+                }
+                dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+                gdth_writel(DPMEM_MAGIC, &dp6c_ptr->u);
+                if (gdth_readl(&dp6c_ptr->u) == DPMEM_MAGIC) {
+                    printk("GDT-PCI: Use free address at 0x%x\n", i);
+                    found = TRUE;
+                    break;
+                }
+            }   
+            if (!found) {
+                printk("GDT-PCI: No free address found!\n");
+                gdth_munmap(ha->brd);
+                return 0;
+            }
+        }
         memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
         if (gdth_readl(&dp6c_ptr->u) != 0) {
             printk("GDT-PCI: Initialization error (DPMEM write error)\n");
@@ -1966,7 +2062,6 @@
     TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
 
     /* set/get features raw service (scatter/gather) */
-    ha->raw_feat = 0;
     if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER,
                           0,0)) {
         TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
@@ -2061,18 +2156,14 @@
         TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
                 hdrive,ha->info));
         ha->hdr[hdrive].cluster_type = (unchar)ha->info;
-    } else {
-        ha->hdr[hdrive].cluster_type = 0;
-    }           
+    }
 
     /* R/W attributes */
     if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,hdrive,0,0)) {
         TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
                 hdrive,ha->info));
         ha->hdr[hdrive].rw_attribs = (unchar)ha->info;
-    } else {
-        ha->hdr[hdrive].rw_attribs = 0;
-    }           
+    }
 
     return 1;
 }
@@ -2264,10 +2355,14 @@
             TRACE2(("Command 0x%x to bus %d id %d lun %d -> IGNORE\n",
                     nscp->cmnd[0], b, t, nscp->lun));
             nscp->result = DID_BAD_TARGET << 16;
-            GDTH_UNLOCK_HA(ha,flags);
-            /* io_request_lock already active ! */      
-            nscp->scsi_done(nscp);
-            GDTH_LOCK_HA(ha,flags);
+	    if (!nscp->SCp.have_data_in)
+		nscp->SCp.have_data_in++;
+	    else {
+		GDTH_UNLOCK_HA(ha,flags);
+		/* io_request_lock already active ! */      
+		nscp->scsi_done(nscp);
+		GDTH_LOCK_HA(ha,flags);
+	    }
         } else {
             switch (nscp->cmnd[0]) {
               case TEST_UNIT_READY:
@@ -2280,7 +2375,24 @@
                 TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
                        nscp->cmnd[4],nscp->cmnd[5]));
-                if (gdth_internal_cache_cmd(hanum,nscp)) {
+                if (ha->hdr[t].media_changed && nscp->cmnd[0] != INQUIRY) {
+                    /* return UNIT_ATTENTION */
+                    TRACE2(("cmd 0x%x target %d: UNIT_ATTENTION\n",
+                             nscp->cmnd[0], t));
+                    ha->hdr[t].media_changed = FALSE;
+                    memset((char*)nscp->sense_buffer,0,16);
+                    nscp->sense_buffer[0] = 0x70;
+                    nscp->sense_buffer[2] = UNIT_ATTENTION;
+                    nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+                    if (!nscp->SCp.have_data_in)
+                        nscp->SCp.have_data_in++;
+                    else {
+                        GDTH_UNLOCK_HA(ha,flags);
+                        /* io_request_lock already active ! */      
+                        nscp->scsi_done(nscp);
+                        GDTH_LOCK_HA(ha,flags);
+                    }
+		} else if (gdth_internal_cache_cmd(hanum,nscp)) {
                     GDTH_UNLOCK_HA(ha,flags);
                     /* io_request_lock already active ! */      
                     nscp->scsi_done(nscp);
@@ -2325,7 +2437,24 @@
               case WRITE_6:
               case READ_10:
               case WRITE_10:
-                if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+                if (ha->hdr[t].media_changed) {
+                    /* return UNIT_ATTENTION */
+                    TRACE2(("cmd 0x%x target %d: UNIT_ATTENTION\n",
+                             nscp->cmnd[0], t));
+                    ha->hdr[t].media_changed = FALSE;
+                    memset((char*)nscp->sense_buffer,0,16);
+                    nscp->sense_buffer[0] = 0x70;
+                    nscp->sense_buffer[2] = UNIT_ATTENTION;
+                    nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+                    if (!nscp->SCp.have_data_in)
+                        nscp->SCp.have_data_in++;
+                    else {
+                        GDTH_UNLOCK_HA(ha,flags);
+                        /* io_request_lock already active ! */      
+                        nscp->scsi_done(nscp);
+                        GDTH_LOCK_HA(ha,flags);
+                    }
+		} else if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
                     this_cmd = FALSE;
                 break;
 
@@ -2471,6 +2600,7 @@
         TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
         break;
     }
+
     scp->result = DID_OK << 16;
     scp->sense_buffer[0] = 0;
 
@@ -2487,7 +2617,8 @@
     register gdth_ha_str *ha;
     register gdth_cmd_str *cmdp;
     struct scatterlist *sl;
-    ushort i;
+    ushort i, cnt;
+    ulong32 no;
     int cmd_index, read_write;
 
     ha = HADATA(gdth_ctr_tab[hanum]);
@@ -2543,11 +2674,13 @@
 
     if (read_write) {
         if (scp->cmd_len != 6) {
-            cmdp->u.cache.BlockNo = ntohl(*(ulong32*)&scp->cmnd[2]);
-            cmdp->u.cache.BlockCnt= (ulong32)ntohs(*(ushort*)&scp->cmnd[7]);
+            memcpy(&no, &scp->cmnd[2], sizeof(ulong32));
+            cmdp->u.cache.BlockNo = ntohl(no);
+            memcpy(&cnt, &scp->cmnd[7], sizeof(ushort));
+            cmdp->u.cache.BlockCnt = (ulong32)ntohs(cnt);
         } else {
-            cmdp->u.cache.BlockNo = 
-                ntohl(*(ulong32*)&scp->cmnd[0]) & 0x001fffffUL;
+            memcpy(&no, &scp->cmnd[0], sizeof(ulong32));
+            cmdp->u.cache.BlockNo = ntohl(no) & 0x001fffffUL;
             cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
         }
 
@@ -2660,7 +2793,7 @@
         cmdp->u.raw.lun        = l;
         cmdp->u.raw.bus        = b;
         cmdp->u.raw.priority   = 0;
-        cmdp->u.raw.link_p     = NULL;
+        cmdp->u.raw.link_p     = 0;
         cmdp->u.raw.sdlen      = scp->request_bufflen;
         cmdp->u.raw.sense_len  = 16;
         cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer);
@@ -2825,6 +2958,7 @@
         e->first_stamp = e->last_stamp = tv.tv_sec;
         e->same_count = 1;
         e->event_data = *evt;
+        e->application = 0;
     }
     return e;
 }
@@ -3195,36 +3329,28 @@
                     if (!(ha->hdr[scp->target].cluster_type & 
                         CLUSTER_MOUNTED)) {
                         /* NOT MOUNTED -> MOUNT */
-                        if (!(ha->hdr[scp->target].cluster_type & 
-                            CLUSTER_RESERVED)) {
-                            /* cluster drive NOT RESERVED */
-                            scp->SCp.Status = GDT_MOUNT;
-                        } else {
+                        scp->SCp.Status = GDT_MOUNT;
+                        if (ha->hdr[scp->target].cluster_type & 
+                            CLUSTER_RESERVED) {
                             /* cluster drive RESERVED (on the other node) */
-                            scp->SCp.Status = GDT_MOUNT;
                             scp->SCp.phase = -2;      /* reservation conflict */
                         }
                     } else {
                         scp->SCp.Status = -1;
                     }
-                    /* retry */
-                    scp->SCp.this_residual = HIGH_PRI;
-                    return 2;
-                } else if (scp->SCp.Status == GDT_MOUNT) {
-                    ha->hdr[scp->target].cluster_type |= CLUSTER_MOUNTED;
-                    scp->SCp.Status = -1;
-                    /* return UNIT_ATTENTION */
-                    memset((char*)scp->sense_buffer,0,16);
-                    scp->sense_buffer[0] = 0x70;
-                    scp->sense_buffer[2] = UNIT_ATTENTION;
-                    scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
-                    scp->SCp.Message = (int)(ha->info<<16|S_BSY);
-                } else {
+		} else {
+                    if (scp->SCp.Status == GDT_MOUNT) {
+                        ha->hdr[scp->target].cluster_type |= CLUSTER_MOUNTED;
+                        ha->hdr[scp->target].media_changed = TRUE;
+                    } else if (scp->SCp.Status == GDT_UNMOUNT) {
+                        ha->hdr[scp->target].cluster_type &= ~CLUSTER_MOUNTED;
+                        ha->hdr[scp->target].media_changed = TRUE;
+                    } 
                     scp->SCp.Status = -1;
-                    /* retry */
-                    scp->SCp.this_residual = HIGH_PRI;
-                    return 2;
                 }
+                /* retry */
+                scp->SCp.this_residual = HIGH_PRI;
+                return 2;
             } else {
                 /* RESERVE/RELEASE ? */
                 if (scp->cmnd[0] == RESERVE) {
@@ -3259,12 +3385,6 @@
                 scp->sense_buffer[0] = 0x70;
                 scp->sense_buffer[2] = NOT_READY;
                 scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
-            } else if (scp->cmnd[0] == RESERVE ||
-                       scp->cmnd[0] == RELEASE) {
-                memset((char*)scp->sense_buffer,0,16);
-                scp->sense_buffer[0] = 0x70;
-                scp->sense_buffer[2] = NOT_READY;
-                scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
             } else if (service == CACHESERVICE) {
                 if (ha->status == S_CACHE_UNKNOWN &&
                     (ha->hdr[scp->target].cluster_type & 
@@ -3293,7 +3413,7 @@
                 }
             } else {
                 /* sense buffer filled from controller firmware (DMA) */
-                if (ha->status!=S_RAW_SCSI || ha->info>=0x100) {
+                if (ha->status != S_RAW_SCSI || ha->info >= 0x100) {
                     scp->result = DID_BAD_TARGET << 16;
                 } else {
                     scp->result = (DID_OK << 16) | ha->info;
@@ -4467,8 +4587,12 @@
     }
 }
 
-
+#if LINUX_VERSION_CODE < 0x020400
 #ifdef MODULE
+Scsi_Host_Template driver_template = GDTH;
+#include "scsi_module.c"
+#endif
+#else
 Scsi_Host_Template driver_template = GDTH;
 #include "scsi_module.c"
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth.h linux.19p6/drivers/scsi/gdth.h
--- linux.vanilla/drivers/scsi/gdth.h	Mon Dec 11 22:12:54 2000
+++ linux.19p6/drivers/scsi/gdth.h	Wed Jan  3 13:59:11 2001
@@ -10,7 +10,7 @@
  *
  * <achim@vortex.de>
  *
- * $Id: gdth.h,v 1.33 2000/07/24 09:29:25 achim Exp $
+ * $Id: gdth.h,v 1.35 2000/10/11 08:42:38 achim Exp $
  */
 
 #include <linux/version.h>
@@ -29,9 +29,9 @@
 /* defines, macros */
 
 /* driver version */
-#define GDTH_VERSION_STR        "1.23"
+#define GDTH_VERSION_STR        "1.25"
 #define GDTH_VERSION            1
-#define GDTH_SUBVERSION         23
+#define GDTH_SUBVERSION         25
 
 /* protocol version */
 #define PROTOCOL_VERSION        1
@@ -625,7 +625,7 @@
             unchar      priority;               /* only 0 used */
             ulong32     sense_len;              /* sense data length */
             ulong32     sense_data;             /* sense data addr. */
-            struct raw  *link_p;                /* linked cmds (not supp.) */
+            ulong32     link_p;                 /* linked cmds (not supp.) */
             ulong32     sg_ranz;                /* s/g element count */
             gdth_sg_str sg_lst[GDTH_MAXSG];     /* s/g list */
         } PACKED raw;                           /* raw service cmd. struct. */
@@ -877,7 +877,7 @@
     Scsi_Cmnd           *req_first;             /* top of request queue */
     struct {
         unchar          present;                /* Flag: host drive present? */
-        unchar          is_logdrv;              /* Flag: logical drive (master)? */
+        unchar          is_logdrv;              /* Flag: log. drive (master)? */
         unchar          is_arraydrv;            /* Flag: array drive? */
         unchar          is_master;              /* Flag: array drive master? */
         unchar          is_parity;              /* Flag: parity drive? */
@@ -891,7 +891,7 @@
         unchar          ldr_no;                 /* log. drive no. */
         unchar          rw_attribs;             /* r/w attributes */
         unchar          cluster_type;           /* cluster properties */
-        unchar          reserved;
+        unchar          media_changed;          /* Flag:MOUNT/UNMOUNT occured */
         ulong32         start_sec;              /* start sector */
     } hdr[MAX_LDRIVES];                         /* host drives */
     struct {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth_ioctl.h linux.19p6/drivers/scsi/gdth_ioctl.h
--- linux.vanilla/drivers/scsi/gdth_ioctl.h	Mon Dec 11 22:12:54 2000
+++ linux.19p6/drivers/scsi/gdth_ioctl.h	Thu Nov 16 12:03:24 2000
@@ -2,7 +2,7 @@
 #define _GDTH_IOCTL_H
 
 /* gdth_ioctl.h
- * $Id: gdth_ioctl.h,v 1.6 2000/07/24 09:29:43 achim Exp $
+ * $Id: gdth_ioctl.h,v 1.7 2000/10/11 08:43:35 achim Exp $
  */
 
 /* IOCTLs */
@@ -19,6 +19,7 @@
 #define GDTIOCTL_SCSI       (GDTIOCTL_MASK | 9) /* SCSI command */
 #define GDTIOCTL_RESET_BUS  (GDTIOCTL_MASK |10) /* reset SCSI bus */
 #define GDTIOCTL_RESCAN     (GDTIOCTL_MASK |11) /* rescan host drives */
+#define GDTIOCTL_RESET_DRV  (GDTIOCTL_MASK |12) /* reset (remote) drv. res. */
 
 #define GDTIOCTL_MAGIC      0xaffe0003UL
 #define EVENT_SIZE          294 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth_proc.c linux.19p6/drivers/scsi/gdth_proc.c
--- linux.vanilla/drivers/scsi/gdth_proc.c	Mon Dec 11 22:12:54 2000
+++ linux.19p6/drivers/scsi/gdth_proc.c	Thu Nov 16 12:03:24 2000
@@ -1,5 +1,5 @@
 /* gdth_proc.c 
- * $Id: gdth_proc.c,v 1.22 2000/07/24 09:30:28 achim Exp $
+ * $Id: gdth_proc.c,v 1.23 2000/10/11 08:43:11 achim Exp $
  */
 
 #include "gdth_ioctl.h"
@@ -524,6 +524,30 @@
             gdth_analyse_hdrive(hanum, k);
         gdth_polling = FALSE;
         GDTH_UNLOCK_HA(ha, flags);
+        break;
+
+      case GDTIOCTL_RESET_DRV:
+        if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ))
+            return(-EBUSY);
+        piord = (gdth_iord_str *)ha->pscratch;
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        i = piowr->iu.scsi.target;
+        if (ha->hdr[i].present) {
+            gdtcmd.BoardNode = LOCALBOARD;
+            gdtcmd.Service = CACHESERVICE;
+            gdtcmd.OpCode = GDT_CLUST_RESET;
+            gdtcmd.u.cache.DeviceNo = i;
+            gdtcmd.u.cache.BlockNo = 0;
+            gdtcmd.u.cache.sg_canz = 0;
+#if LINUX_VERSION_CODE >= 0x020322
+            gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+            piord->status = (ulong32)scp->SCp.Message;
+#else
+            gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+            piord->status = (ulong32)scp.SCp.Message;
+#endif
+        }
         break;
 
       default:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/hosts.c linux.19p6/drivers/scsi/hosts.c
--- linux.vanilla/drivers/scsi/hosts.c	Mon Dec 11 22:12:54 2000
+++ linux.19p6/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.19p6/drivers/scsi/hosts.h
--- linux.vanilla/drivers/scsi/hosts.h	Sun Jun 11 21:44:15 2000
+++ linux.19p6/drivers/scsi/hosts.h	Wed Jan  3 13:59:08 2001
@@ -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.19p6/drivers/scsi/i60uscsi.c
--- linux.vanilla/drivers/scsi/i60uscsi.c	Fri Apr 21 12:46:28 2000
+++ linux.19p6/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.19p6/drivers/scsi/osst.c
--- linux.vanilla/drivers/scsi/osst.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/scsi/osst.c	Sat Dec 30 19:50:25 2000
@@ -0,0 +1,5184 @@
+/*
+  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.12.2.1 2000/12/15 20:36:12 garloff 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.12.2.1 2000/12/15 20:36:12 garloff Exp $";
+const char * osst_version = "0.8.6.1";
+
+/* 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;
+//static int inject = 0; /* FIXME - take out inject occasional read errors */
+//static int repeat = 0;
+  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);
+//if ((STp->buffer)->last_result_fatal == 0 &&
+//    cmd[0] == READ_6 && cmd[4] && ( /* (++ inject % 83) == 29  || */
+//     (STp->first_frame_position == 240 /* or STp->read_error_frame to fail again on the block calculated above */ && ++repeat < 3))) {
+//	printk(OSST_DEB_MSG "osst%d: injecting read error\n", TAPE_NR(STp->devt));
+//	STp->buffer->last_result_fatal = 1; /* FIXME - take out inject occasional read errors */
+//}
+  }
+
+  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 && (i > STp->filemark_cnt ||
+		    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 (i >= STp->filemark_cnt)
+				 STp->filemark_cnt = i+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);
+}
+/*
+static int osst_buffer_stats[64];
+
+static void osst_build_stats(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt)
+{
+	unsigned int i;
+
+	osst_get_frame_position (STp, aSCpnt);
+	i = STp->cur_frames > 63 ? 63 : STp->cur_frames;
+	osst_buffer_stats[i]++;
+}
+
+static void osst_report_stats()
+{
+	unsigned int i;
+
+	for (i=0; i<64; i+=8) {
+		printk(OSST_DEB_MSG "osst buffer stats %2d: %5d %5d %5d %5d %5d %5d %5d %5d\n", i,
+			osst_buffer_stats[i  ], osst_buffer_stats[i+1],
+		       	osst_buffer_stats[i+2], osst_buffer_stats[i+3], 
+			osst_buffer_stats[i+4], osst_buffer_stats[i+5],
+		       	osst_buffer_stats[i+6], osst_buffer_stats[i+7]);
+	}
+
+}
+*/
+#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 (minlast >= 0 && 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)
+			if ((result = osst_write_error_recovery(STp, aSCpnt, 0)) == 0)
+				return 0;	/* successfull recovery leaves drive ready for frame */
+		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 (STp->read_error_frame == 0) {
+		STp->read_error_frame = STp->first_frame_position;
+		printk(OSST_DEB_MSG "osst: recording read error at %d\n", STp->read_error_frame);/*FIXME*/
+	    }
+#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,
+		      bad   = 0,
+		      past  = 0,
+		      x,
+		      position;
+
+        /*
+         * Search and wait for the next logical tape block
+         */
+        while (1) {
+                if (cnt++ > 400) {
+                        printk(KERN_WARNING "osst%d: Couldn't find logical block %d, aborting\n",
+					    dev, logical_blk_num);
+			if (STp->read_error_frame) {
+				osst_set_frame_position(STp, aSCpnt, STp->read_error_frame, 0);
+#if 1 //DEBUG
+                        	printk(OSST_DEB_MSG "osst%d: Repositioning tape to bad block %d\n",
+						 dev, STp->read_error_frame);
+#endif
+				STp->read_error_frame = 0;
+			}
+                        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 if (position > STp->eod_frame_ppos || ++bad == 10) {
+printk(OSST_DEB_MSG "osst%d: start again from pos %d, eod %d, bad %d\n", dev, position, STp->eod_frame_ppos, bad); /*FIXME*/
+				position = STp->read_error_frame - 1;
+			}
+			else {
+				position += 39;
+                        	cnt += 20;
+			}
+#if 1 //DEBUG
+                        printk(OSST_DEB_MSG "osst%d: Bad 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;
+				STp->read_error_frame = 0;
+				return (-EIO);
+			}
+                        if (x > logical_blk_num) {
+				if (++past > 3) {
+					/* positioning backwards did not bring us to the desired block */
+					position = STp->read_error_frame - 1;
+				}
+				else
+			        	position = osst_get_frame_position(STp, aSCpnt)
+					         + logical_blk_num - x - 1;
+#if 1 //DEBUG
+                                printk(OSST_DEB_MSG
+				       "osst%d: Found logical block %d while looking for %d: back up %d\n",
+						dev, x, logical_blk_num,
+					       	STp->first_frame_position - position);
+#endif
+                        	osst_set_frame_position(STp, aSCpnt, position, 0);
+                        	cnt += 10;
+                        }
+			else
+				past = 0;
+                }
+                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;
+	STp->read_error_frame = 0;
+        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; /* FIXME test for eod */
+	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 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 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 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 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 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 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 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 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
+			if (STp->first_frame_position > STp->eod_frame_ppos+1) {
+#if DEBUG
+				printk(OSST_DEB_MSG "osst%i: EOD position corrected (%d=>%d)\n",
+					       	dev, STp->eod_frame_ppos, STp->first_frame_position-1);
+#endif
+				STp->eod_frame_ppos = STp->first_frame_position-1;
+			}
+			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);
+			}
+			if (ntohl(STp->buffer->aux->filemark_cnt) != cnt + mt_count) {
+				printk(KERN_INFO "osst%i: Expected to find marker %d at block %d, not %d\n",
+						 dev, cnt+mt_count, next_mark_ppos,
+						 ntohl(STp->buffer->aux->filemark_cnt));
+				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);
+//printk(OSST_DEB_MSG "osst%d: Finished writing file\n",TAPE_NR(STp->devt));
+	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 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 */
+	unsigned char mysense[16];
+	memcpy (mysense, SCpnt->sense_buffer, 16);
+	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);
+	if (!STp->buffer->last_result_fatal)
+	   memcpy (SCpnt->sense_buffer, mysense, 16);
+      }
+      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);
+//osst_build_stats(STp, aSCpnt);
+    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) {
+      /* Are we totally rewriting this tape? */
+      if (!STp->header_ok || STp->first_frame_position == STp->first_data_ppos ||
+		            (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;
+      }
+      /* Do we know where we'll be writing on the tape? */
+      else {
+	if ((STp->fast_open && osst_verify_position(STp, &SCpnt)) ||
+		STps->drv_file < 0 || STps->drv_block < 0) {
+	  if (STp->first_frame_position == STp->eod_frame_ppos) {
+	    STps->drv_file = STp->filemark_cnt;
+	    STps->drv_block = 0;
+	  }
+	  else {
+	    /* We have no idea where the tape is positioned - give up */
+#if DEBUG
+	    printk(OSST_DEB_MSG "osst%d: Cannot write at indeterminate position.\n", dev);
+#endif
+	    if (SCpnt) scsi_release_command(SCpnt);
+	    return (-EIO);
+	  }
+	}	  
+	if (STps->drv_file > 0 && STps->drv_file < STp->filemark_cnt) {
+	  STp->filemark_cnt = STps->drv_file;
+	  STp->last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt-1]);
+	  printk(KERN_WARNING "osst%d: Overwriting file %d with old write pass counter %d\n",
+		      dev, STps->drv_file, STp->wrt_pass_cntr);
+	  printk(KERN_WARNING "osst%d: may lead to stale data being accepted on reading back!\n",
+		      dev);
+#if DEBUG
+	  printk(OSST_DEB_MSG "osst%d: resetting filemark count to %d and last mark ppos to %d\n",
+		dev, STp->filemark_cnt, STp->last_mark_ppos);
+#endif
+	}
+      }
+      STp->fast_open = FALSE;
+//printk(OSST_DEB_MSG "osst%d: Starting write next file\n",dev);
+    }
+    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 ! */
+//osst_build_stats(STp, &SCpnt);	
+    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 = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD: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;
+	    if (STps->drv_block > 0 && STps->drv_file >= 0)
+		STps->drv_file++;
+	    STps->drv_block = 0;
+	}
+	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, 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) {
+	   ioctl_result = -EIO;
+	   goto os_bypass;
+	}
+	if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_EOD) {
+#if DEBUG
+	   printk(OSST_DEB_MSG "osst%d: No EOD frame found where expected.\n", dev);
+#endif
+     	   ioctl_result = -EIO;
+	   goto os_bypass;
+	}
+	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 = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD: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 (cmd_in == MTBSF || cmd_in == MTBSFM ) {
+      if (osst_position_tape_and_confirm(STp, &SCpnt, STp->first_data_ppos) < 0)
+	  STps->drv_file = STps->drv_block = -1;
+      else
+	  STps->drv_file = STps->drv_block = 0;
+      STps->eof = ST_NOEOF;
+   } else if (cmd_in == MTFSF || cmd_in == MTFSFM) {
+      if (osst_position_tape_and_confirm(STp, &SCpnt, STp->eod_frame_ppos) < 0)
+	  STps->drv_file = STps->drv_block = -1;
+      else {
+	  STps->drv_file  = STp->filemark_cnt;
+	  STps->drv_block = 0;
+      }
+      STps->eof = ST_EOD;
+   } else if (cmd_in == MTBSR || cmd_in == MTFSR || cmd_in == MTWEOF || cmd_in == MTEOM) {
+      STps->drv_file = STps->drv_block = (-1);
+      STps->eof = ST_NOEOF;
+      STp->header_ok = 0;
+   } else if (cmd_in == MTERASE) {
+      STp->header_ok = 0;
+   } else if (SCpnt) {  /* SCSI command was not completely successful. */
+      if (SCpnt->sense_buffer[2] & 0x40) {
+	 STps->eof = ST_EOM_OK;
+	 STps->drv_block = 0;
+      }
+      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;
+//osst_report_stats();
+      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.19p6/drivers/scsi/osst.h
--- linux.vanilla/drivers/scsi/osst.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/scsi/osst.h	Wed Jan  3 14:11:59 2001
@@ -0,0 +1,514 @@
+/*
+ *	$Header: /home/cvsroot/Driver/osst.h,v 1.5.2.2 2000/10/08 03:07:33 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 */
+  int      read_error_frame;			/* used in read 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.19p6/drivers/scsi/osst_detect.h
--- linux.vanilla/drivers/scsi/osst_detect.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/drivers/scsi/osst_options.h
--- linux.vanilla/drivers/scsi/osst_options.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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/ppa.c linux.19p6/drivers/scsi/ppa.c
--- linux.vanilla/drivers/scsi/ppa.c	Mon Dec 11 22:12:54 2000
+++ linux.19p6/drivers/scsi/ppa.c	Wed Jan  3 00:57:16 2001
@@ -31,6 +31,7 @@
     Scsi_Cmnd *cur_cmd;		/* Current queued command       */
     struct tq_struct ppa_tq;	/* Polling interupt stuff       */
     unsigned long jstart;	/* Jiffies at start             */
+    unsigned long recon_tmo;    /* How many usecs to wait for reconnection (6th bit) */
     unsigned int failed:1;	/* Failure flag                 */
     unsigned int p_busy:1;	/* Parport sharing busy flag    */
 } ppa_struct;
@@ -43,6 +44,7 @@
 	cur_cmd:	NULL,		\
 	ppa_tq:		{0, 0, ppa_interrupt, NULL},	\
 	jstart:		0,		\
+	recon_tmo:      PPA_RECON_TMO,	\
 	failed:		0,		\
 	p_busy:		0		\
 }
@@ -236,6 +238,12 @@
 	ppa_hosts[hostno].mode = x;
 	return length;
     }
+    if ((length > 10) && (strncmp(buffer, "recon_tmo=", 10) == 0)) {
+	x = simple_strtoul(buffer + 10, NULL, 0);
+	ppa_hosts[hostno].recon_tmo = x;
+        printk("ppa: recon_tmo set to %ld\n", x);
+	return length;
+    }
     printk("ppa /proc: invalid variable\n");
     return (-EINVAL);
 }
@@ -256,6 +264,9 @@
     len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
     len += sprintf(buffer + len, "Parport : %s\n", ppa_hosts[i].dev->port->name);
     len += sprintf(buffer + len, "Mode    : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);
+#if PPA_DEBUG > 0
+    len += sprintf(buffer + len, "recon_tmo : %lu\n", ppa_hosts[i].recon_tmo);
+#endif
 
     /* Request for beyond end of buffer */
     if (offset > length)
@@ -299,12 +310,11 @@
     unsigned char r;
 
     k = PPA_SPIN_TMO;
-    do {
-	r = r_str(ppb);
-	k--;
-	udelay(1);
+    /* Wait for bit 6 and 7 - PJC */
+    for (r = r_str (ppb); ((r & 0xc0)!=0xc0) && (k); k--) {
+	    udelay (1);
+	    r = r_str (ppb);
     }
-    while (!(r & 0x80) && (k));
 
     /*
      * return some status information.
@@ -545,6 +555,7 @@
     k = PPA_SELECT_TMO;
     do {
 	k--;
+	udelay(1);
     } while ((r_str(ppb) & 0x40) && (k));
     if (!k)
 	return 0;
@@ -558,6 +569,7 @@
     k = PPA_SELECT_TMO;
     do {
 	k--;
+	udelay(1);
     }
     while (!(r_str(ppb) & 0x40) && (k));
     if (!k)
@@ -667,12 +679,36 @@
 	if (time_after(jiffies, start_jiffies + 1))
 	    return 0;
 
-	if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) {
+	if ((cmd->SCp.this_residual <= 0)) {
 	    ppa_fail(host_no, DID_ERROR);
 	    return -1;		/* ERROR_RETURN */
 	}
-	/* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
-	    ? PPA_BURST_SIZE : 1;
+
+	/* On some hardware we have SCSI disconnected (6th bit low)
+	 * for about 100usecs. It is too expensive to wait a 
+	 * tick on every loop so we busy wait for no more than
+	 * 500usecs to give the drive a chance first. We do not 
+	 * change things for "normal" hardware since generally 
+	 * the 6th bit is always high.
+	 * This makes the CPU load higher on some hardware 
+	 * but otherwise we can not get more then 50K/secs 
+	 * on this problem hardware.
+	 */
+	if ((r & 0xc0) != 0xc0) {
+	   /* Wait for reconnection should be no more than 
+	    * jiffy/2 = 5ms = 5000 loops
+	    */
+	   unsigned long k = ppa_hosts[host_no].recon_tmo; 
+	   for (; k && ((r = (r_str(ppb) & 0xf0)) & 0xc0) != 0xc0; k--)
+	     udelay(1);
+
+	   if(!k) 
+	     return 0;
+	}	   
+
+	/* determine if we should use burst I/O */ 
+	fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE)) 
+	     ? PPA_BURST_SIZE : 1;
 
 	if (r == (unsigned char) 0xc0)
 	    status = ppa_out(host_no, cmd->SCp.ptr, fast);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ppa.h linux.19p6/drivers/scsi/ppa.h
--- linux.vanilla/drivers/scsi/ppa.h	Sat Sep  9 18:42:41 2000
+++ linux.19p6/drivers/scsi/ppa.h	Wed Jan  3 13:59:11 2001
@@ -10,7 +10,7 @@
 #ifndef _PPA_H
 #define _PPA_H
 
-#define   PPA_VERSION   "2.03 (for Linux 2.2.x)"
+#define   PPA_VERSION   "2.04a (for Linux 2.2.x)"
 
 /* 
  * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
@@ -51,6 +51,15 @@
  *    CONFIG_SCSI_PPA_HAVE_PEDANTIC => CONFIG_SCSI_IZIP_EPP16
  *    added CONFIG_SCSI_IZIP_SLOW_CTR option
  *                                                      [2.03]
+ *
+ * Use ppa_wait() to check for ready AND connected status bits
+ * Busy wait for connected status bit in ppa_completion()
+ *  in order to cope with some hardware that has this bit low
+ *  for short periods of time.
+ * Add udelay() to ppa_select()
+ *  by Dr. Peter Cherriman and
+ *     Oleg Makarenko <omakarenko@cyberplat.ru>
+ *                                                      [2.04a]
  */
 /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
 
@@ -106,6 +115,7 @@
 #define PPA_BURST_SIZE	512	/* data burst size */
 #define PPA_SELECT_TMO  5000	/* how long to wait for target ? */
 #define PPA_SPIN_TMO    50000	/* ppa_wait loop limiter */
+#define PPA_RECON_TMO   500	/* scsi reconnection loop limiter */
 #define PPA_DEBUG	0	/* debuging option */
 #define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.h linux.19p6/drivers/scsi/scsi.h
--- linux.vanilla/drivers/scsi/scsi.h	Fri Apr 21 23:20:05 2000
+++ linux.19p6/drivers/scsi/scsi.h	Wed Jan  3 13:59:08 2001
@@ -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/scsiiom.c linux.19p6/drivers/scsi/scsiiom.c
--- linux.vanilla/drivers/scsi/scsiiom.c	Fri Apr 21 12:46:31 2000
+++ linux.19p6/drivers/scsi/scsiiom.c	Sat Dec 30 19:46:46 2000
@@ -4,16 +4,40 @@
  *	Description: Device Driver for Tekram DC-390 (T) PCI SCSI      *
  *		     Bus Master Host Adapter			       *
  ***********************************************************************/
-/* $Id: scsiiom.c,v 2.15 1998/12/25 17:33:27 garloff Exp $ */
+/* $Id: scsiiom.c,v 2.55.2.17 2000/12/20 00:39:37 garloff Exp $ */
+
+static void __inline__
+dc390_freetag (PDCB pDCB, PSRB pSRB)
+{
+	if (pSRB->TagNumber < 255) {
+		pDCB->TagMask &= ~(1 << pSRB->TagNumber);   /* free tag mask */
+		pSRB->TagNumber = 255;
+	}
+};
+
 
 UCHAR
 dc390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
 {
-    USHORT wlval;
-    UCHAR  bval, bval1;
+    UCHAR cmd; UCHAR  disc_allowed, try_sync_nego;
+
+    pSRB->ScsiPhase = SCSI_NOP0;
 
-    pSRB->TagNumber = 31;
-    DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID);
+    if (pACB->Connected)
+    {
+	// Should not happen normally
+	printk (KERN_WARNING "DC390: Can't select when connected! (%08x,%02x)\n",
+		pSRB->SRBState, pSRB->SRBFlag);
+	pSRB->SRBState = SRB_READY;
+	pACB->SelConn++;
+	return 1;
+    }
+    if (time_before (jiffies, pACB->pScsiHost->last_reset))
+    {
+	DEBUG0(printk ("DC390: We were just reset and don't accept commands yet!\n");)
+	return 1;
+    }
+    DC390_write8 (Scsi_Dest_ID, pDCB->TargetID);
     DC390_write8 (Sync_Period, pDCB->SyncPeriod);
     DC390_write8 (Sync_Offset, pDCB->SyncOffset);
     DC390_write8 (CtrlReg1, pDCB->CtrlR1);
@@ -21,151 +45,105 @@
     DC390_write8 (CtrlReg4, pDCB->CtrlR4);
     DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);		/* Flush FIFO */
     DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\
-	    pSRB->CmdBlock[0], pDCB->SyncMode);)
-    pSRB->ScsiPhase = SCSI_NOP0;
-    //pSRB->MsgOutBuf[0] = MSG_NOP;
-    //pSRB->MsgCnt = 0;
-    bval = pDCB->IdentifyMsg;
-    if( !(pDCB->SyncMode & EN_ATN_STOP) )	/* Don't always try send Extended messages on arbitration */
-    {
-	if( (pSRB->CmdBlock[0] == INQUIRY) ||
-	    (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
-	    (pSRB->SRBFlag & AUTO_REQSENSE) )
-	{
-	    bval &= 0xBF;	/* No DisConn */
-	    DC390_write8 (ScsiFifo, bval);
-	    bval1 = SEL_W_ATN;
-	    pSRB->SRBState = SRB_START_;
-	    DEBUG1(printk (KERN_DEBUG "DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);)
-	    if( pDCB->SyncMode & SYNC_ENABLE )
-		{
-		if( !(pDCB->IdentifyMsg & 7) ||		/* LUN == 0 || Cmd != INQUIRY */
-		    (pSRB->CmdBlock[0] != INQUIRY) )
-		{
-		    bval1 = SEL_W_ATN_STOP;	/* Try to establish SYNC nego */
-		    pSRB->SRBState = SRB_MSGOUT;
-		}
-	    }
-	}
-	else	/* TagQ ? */
-	{
-	    DC390_write8 (ScsiFifo, bval);
-	    if(pDCB->SyncMode & EN_TAG_QUEUEING)
-	    {
-	        DC390_write8 (ScsiFifo, MSG_SIMPLE_QTAG);
-		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN3, pDCB->TagMask);)
-	        bval = 0; wlval = 1;
-	        while (wlval & pDCB->TagMask)
-		  { bval++; wlval <<= 1; };
-		pDCB->TagMask |= wlval;
-	        DC390_write8 (ScsiFifo, bval);
-		pSRB->TagNumber = bval;
-		DEBUG1(printk (KERN_DEBUG "DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);)
-		bval1 = SEL_W_ATN3;
-		pSRB->SRBState = SRB_START_;
-	    }
-	    else	/* No TagQ */
-	    {
-		bval1 = SEL_W_ATN;
-		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);)
-		pSRB->SRBState = SRB_START_;
-	    }
-	}
+	    pSRB->pcmd->cmnd[0], pDCB->SyncMode);)
+    disc_allowed = pDCB->DevMode & EN_DISCONNECT_; try_sync_nego = 0;
+    /* Don't disconnect on AUTO_REQSENSE, cause it might be an
+     * Contingent Allegiance Condition (6.6), where no tags should be used.
+     * All other have to be allowed to disconnect to prevent Incorrect 
+     * Initiator Connection (6.8.2/6.5.2) */
+    /* Changed KG, 99/06/06 */
+    if( /*(((pSRB->pcmd->cmnd[0] == INQUIRY) || (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) ||
+	 * (pSRB->pcmd->cmnd[0] == TEST_UNIT_READY)) && pACB->scan_devices)
+		||*/ (pSRB->SRBFlag & AUTO_REQSENSE) ) 
+      disc_allowed = 0;
+    if ( (pDCB->SyncMode & SYNC_ENABLE) && (pDCB->TargetLUN == 0) && (pDCB->Inquiry7 & 0x10) &&
+	( ( ( (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE) )
+	  && !(pDCB->SyncMode & SYNC_NEGO_DONE) ) || (pSRB->pcmd->cmnd[0] == INQUIRY) ) )
+      try_sync_nego = 1;
+
+    pSRB->MsgCnt = 0; cmd = SEL_W_ATN;
+    DC390_write8 (ScsiFifo, IDENTIFY(disc_allowed, pDCB->TargetLUN));
+    /* Change 99/05/31: Don't use tags when not disconnecting (BUSY) */
+    if ((pDCB->SyncMode & EN_TAG_QUEUEING) && disc_allowed)
+      {
+	UCHAR tag_no = 0;
+	while ((1 << tag_no) & pDCB->TagMask) tag_no++;
+	if (tag_no >= sizeof (pDCB->TagMask)*8 || tag_no >= pDCB->MaxCommand) { 
+		printk (KERN_WARNING "DC390: Out of tags for Dev. %02x %02x\n", pDCB->TargetID, pDCB->TargetLUN); 
+		return 1;
+		//goto no_tag;
+	};
+	DC390_write8 (ScsiFifo, SIMPLE_QUEUE_TAG);
+	pDCB->TagMask |= (1 << tag_no); pSRB->TagNumber = tag_no;
+	DC390_write8 (ScsiFifo, tag_no);
+	DEBUG1(printk (KERN_DEBUG "DC390: Select w/DisCn for Cmd %li (SRB %p), Using Tag %02x\n", pSRB->pcmd->pid, pSRB, tag_no);)
+	cmd = SEL_W_ATN3;
+      }
+    else	/* No TagQ */
+      {
+//      no_tag:
+	DEBUG1(printk (KERN_DEBUG "DC390: Select w%s/DisCn for Cmd %li (SRB %p), No TagQ\n", (disc_allowed?"":"o"), pSRB->pcmd->pid, pSRB);) 
+      };
 
-    }
-    else	/* ATN_STOP: Always try to establish Sync nego */
-    {
-	if( (pSRB->CmdBlock[0] == INQUIRY) ||
-	    (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
-	    (pSRB->SRBFlag & AUTO_REQSENSE) )
-	{
-	    bval &= 0xBF;	/* No DisConn */
-	    DC390_write8 (ScsiFifo, bval);
-	    bval1 = SEL_W_ATN;
-	    DEBUG1(printk (KERN_DEBUG "DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);)
-	    pSRB->SRBState = SRB_START_;
-	      /* ??? */
-	    if( pDCB->SyncMode & SYNC_ENABLE )
-	    {
-		if( !(pDCB->IdentifyMsg & 7) ||		/* LUN == 0 || Cmd != INQUIRY */
-		    (pSRB->CmdBlock[0] != INQUIRY) )
-		{
-		    bval1 = SEL_W_ATN_STOP;	/* Try to establish Sync nego */
-		    pSRB->SRBState = SRB_MSGOUT;
-		}
-	    }
-	}
-	else	/* TagQ ? */
-	{
-	    DC390_write8 (ScsiFifo, bval);
-	    if(pDCB->SyncMode & EN_TAG_QUEUEING)
-	    {
-		pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
-		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN_STOP, pDCB->TagMask);)
-		bval = 0; wlval = 1;
-	        while (wlval & pDCB->TagMask)
-		  { bval++; wlval <<= 1; };
-		pDCB->TagMask |= wlval;
-		pSRB->TagNumber = bval;
-		DEBUG1(printk (KERN_DEBUG "DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);)
-		pSRB->MsgOutBuf[1] = bval;
-		pSRB->MsgCnt = 2;
-		bval1 = SEL_W_ATN_STOP;
-		pSRB->SRBState = SRB_START_;	/* ?? */
-	    }
-	    else	/* No TagQ */
-	    {
-		pSRB->MsgOutBuf[0] = MSG_NOP;
-		pSRB->MsgCnt = 1;
-		pSRB->SRBState = SRB_START_;
-		bval1 = SEL_W_ATN_STOP;
-		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);)
-	    };
-	}
-    }
-    if (bval1 != SEL_W_ATN_STOP)
-      {	/* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */
+    pSRB->SRBState = SRB_START_;
+
+    if (try_sync_nego)
+      { 
+	UCHAR Sync_Off = pDCB->SyncOffset;
+        DEBUG0(printk (KERN_INFO "DC390: NEW Sync Nego code triggered (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN);)
+	pSRB->MsgOutBuf[0] = EXTENDED_MESSAGE;
+	pSRB->MsgOutBuf[1] = 3;
+	pSRB->MsgOutBuf[2] = EXTENDED_SDTR;
+	pSRB->MsgOutBuf[3] = pDCB->NegoPeriod;
+	if (!(Sync_Off & 0x0f)) Sync_Off = SYNC_NEGO_OFFSET;
+	pSRB->MsgOutBuf[4] = Sync_Off;
+	pSRB->MsgCnt = 5;
+	//pSRB->SRBState = SRB_MSGOUT_;
+	pSRB->SRBState |= DO_SYNC_NEGO;
+	cmd = SEL_W_ATN_STOP;
+      };
+
+    /* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */
+    if (cmd != SEL_W_ATN_STOP)
+      {
 	if( pSRB->SRBFlag & AUTO_REQSENSE )
 	  {
-	    bval = 0;
 	    DC390_write8 (ScsiFifo, REQUEST_SENSE);
-	    DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5);
-	    DC390_write8 (ScsiFifo, bval);
-	    DC390_write8 (ScsiFifo, bval);
+	    DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5);
+	    DC390_write8 (ScsiFifo, 0);
+	    DC390_write8 (ScsiFifo, 0);
 	    DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer));
-	    DC390_write8 (ScsiFifo, bval);
+	    DC390_write8 (ScsiFifo, 0);
 	    DEBUG1(printk (KERN_DEBUG "DC390: AutoReqSense !\n");)
 	  }
 	else	/* write cmnd to bus */ 
 	  {
 	    PUCHAR ptr; UCHAR i;
-	    ptr = (PUCHAR) pSRB->CmdBlock;
-	    for (i=0; i<pSRB->ScsiCmdLen; i++)
+	    ptr = (PUCHAR) pSRB->pcmd->cmnd;
+	    for (i=0; i<pSRB->pcmd->cmd_len; i++)
 	      DC390_write8 (ScsiFifo, *(ptr++));
 	  };
       }
-
-    /* Check if we can't win arbitration */
+    DEBUG0(if (pACB->pActiveDCB)	\
+	   printk (KERN_WARNING "DC390: ActiveDCB != 0\n");)
+    DEBUG0(if (pDCB->pActiveSRB)	\
+	   printk (KERN_WARNING "DC390: ActiveSRB != 0\n");)
+    //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
     if (DC390_read8 (Scsi_Status) & INTERRUPT)
     {
+	dc390_freetag (pDCB, pSRB);
+	DEBUG0(printk ("DC390: Interrupt during Start SCSI (pid %li, target %02i-%02i)\n",
+		pSRB->pcmd->pid, pSRB->pcmd->target, pSRB->pcmd->lun);)
 	pSRB->SRBState = SRB_READY;
-	pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
-	DEBUG0(printk (KERN_WARNING "DC390: Interrupt during StartSCSI!\n");)
+	//DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+	pACB->SelLost++;
 	return 1;
-    }
-    else
-    {
-	pSRB->ScsiPhase = SCSI_NOP1;
-	DEBUG0(if (pACB->pActiveDCB)	\
-		    printk (KERN_WARNING "DC390: ActiveDCB != 0\n");)
-	DEBUG0(if (pDCB->pActiveSRB)	\
-		    printk (KERN_WARNING "DC390: ActiveSRB != 0\n");)
-	pACB->pActiveDCB = pDCB;
-	pDCB->pActiveSRB = pSRB;
-	//DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
-	DC390_write8 (ScsiCmd, bval1);
-	return 0;
-    }
+    };
+    DC390_write8 (ScsiCmd, cmd);
+    pACB->pActiveDCB = pDCB; pDCB->pActiveSRB = pSRB;
+    pACB->Connected = 1;
+    pSRB->ScsiPhase = SCSI_NOP1;
+    return 0;
 }
 
 //#define DMA_INT EN_DMA_INT /*| EN_PAGE_INT*/
@@ -198,7 +176,7 @@
     };
   if (dstate & DMA_XFER_DONE)
     {
-	ULONG residual, xferCnt; int ctr = 5000000;
+	UINT residual, xferCnt; int ctr = 6000000;
 	if (! (DC390_read8 (DMA_Cmd) & READ_DIRECTION))
 	  {
 	    do
@@ -236,47 +214,34 @@
 void __inline__
 DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
 {
-    PACB   pACB;
+    PACB   pACB, pACB2;
     PDCB   pDCB;
     PSRB   pSRB;
     UCHAR  sstatus=0;
-    UCHAR  phase, i;
+    UCHAR  phase;
     void   (*stateV)( PACB, PSRB, PUCHAR );
     UCHAR  istate, istatus;
 #if DMA_INT
     UCHAR  dstatus;
 #endif
-    DC390_AFLAGS DC390_IFLAGS DC390_DFLAGS
-
-    pACB = dc390_pACB_start;
+    DC390_AFLAGS DC390_IFLAGS //DC390_DFLAGS
 
-    if (pACB == 0)
+    pACB = (PACB)dev_id;
+    for (pACB2 = dc390_pACB_start; (pACB2 && pACB2 != pACB); pACB2 = pACB2->pNextACB);
+    if (!pACB2)
     {
-	printk(KERN_WARNING "DC390: Interrupt on uninitialized adapter!\n");
+	printk ("DC390: IRQ called with foreign dev_id %p!\n", pACB);
 	return;
     }
-    DC390_LOCK_DRV;
+    
+    //DC390_LOCK_DRV;
 
-    for( i=0; i < dc390_adapterCnt; i++ )
-    {
-	if( pACB->IRQLevel == (UCHAR) irq )
-	{
-	     sstatus = DC390_read8 (Scsi_Status);
-	     if( sstatus & INTERRUPT )
-		 break;
-	     else
-		pACB = pACB->pNextACB;
-	}
-	else
-	{
-	    pACB = pACB->pNextACB;
-	}
-    }
+    sstatus = DC390_read8 (Scsi_Status);
+    if( !(sstatus & INTERRUPT) )
+	{ /*DC390_UNLOCK_DRV;*/ return; };
 
     DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus);)
 
-    if( !pACB ) { DC390_UNLOCK_DRV; return; };
-
 #if DMA_INT
     DC390_LOCK_IO;
     DC390_LOCK_ACB;
@@ -288,7 +253,7 @@
     if (! (dstatus & SCSI_INTERRUPT))
       {
 	DEBUG0(printk (KERN_WARNING "DC390 Int w/o SCSI actions (only DMA?)\n");)
-	DC390_UNLOCK_DRV;
+	//DC390_UNLOCK_DRV;
 	return;
       };
 #else
@@ -299,7 +264,7 @@
 
     DC390_LOCK_IO;
     DC390_LOCK_ACB;
-    DC390_UNLOCK_DRV_NI; /* Allow _other_ CPUs to process IRQ (useful for shared IRQs) */
+    //DC390_UNLOCK_DRV_NI; /* Allow _other_ CPUs to process IRQ (useful for shared IRQs) */
 
     istate = DC390_read8 (Intern_State);
     istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */
@@ -310,23 +275,42 @@
 
     if (sstatus & ILLEGAL_OP_ERR)
     {
-	printk ("DC390: Illegal Operation detected (%08lx)!\n", dc390_laststatus);
+	printk ("DC390: Illegal Operation detected (%08x)!\n", dc390_laststatus);
 	dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB);
-    };
+    }
 	
-    if(istatus &  DISCONNECTED)
+    else if (istatus &  INVALID_CMD)
+    {
+	printk ("DC390: Invalid Command detected (%08x)!\n", dc390_laststatus);
+	dc390_InvalidCmd( pACB );
+	goto unlock;
+    }
+
+    if (istatus &  SCSI_RESET)
+    {
+	dc390_ScsiRstDetect( pACB );
+	goto unlock;
+    }
+
+    if (istatus &  DISCONNECTED)
     {
 	dc390_Disconnect( pACB );
 	goto unlock;
     }
 
-    if(istatus &  RESELECTED)
+    if (istatus &  RESELECTED)
     {
 	dc390_Reselect( pACB );
 	goto unlock;
     }
 
-    if( istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) )
+    else if (istatus & (SELECTED | SEL_ATTENTION))
+    {
+	printk (KERN_ERR "DC390: Target mode not supported!\n");
+	goto unlock;
+    }
+
+    if (istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) )
     {
 	pDCB = pACB->pActiveDCB;
 	if (!pDCB)
@@ -351,23 +335,11 @@
 	goto unlock;
     }
 
-    if(istatus &  INVALID_CMD)
-    {
-	dc390_InvalidCmd( pACB );
-	goto unlock;
-    }
-
-    if(istatus &  SCSI_RESET)
-    {
-	dc390_ScsiRstDetect( pACB );
-	goto unlock;
-    }
-
  unlock:
-    DC390_LOCK_DRV_NI;
+    //DC390_LOCK_DRV_NI;
     DC390_UNLOCK_ACB;
     DC390_UNLOCK_IO;
-    DC390_UNLOCK_DRV; /* Restore initial flags */
+    //DC390_UNLOCK_DRV; /* Restore initial flags */
 }
 
 void
@@ -384,7 +356,7 @@
 {
     UCHAR   sstatus;
     PSGL    psgl;
-    ULONG   ResidCnt, xferCnt;
+    UINT    ResidCnt, xferCnt;
     UCHAR   dstate = 0;
 
     sstatus = *psstatus;
@@ -396,7 +368,7 @@
 
 	if( sstatus & COUNT_2_ZERO )
 	{
-	    int ctr = 5000000; /* only try for about a tenth of a second */
+	    int ctr = 6000000; /* only try for about a second */
 	    while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen );
 	    if (!ctr) printk (KERN_CRIT "DC390: Deadlock in DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr));
 	    dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24;
@@ -415,10 +387,10 @@
 	}
 	else
 	{
-	    ResidCnt = (ULONG) DC390_read8 (Current_Fifo) & 0x1f;
-	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_High) << 16;
-	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid) << 8; 
-	    ResidCnt += (ULONG) DC390_read8 (CtcReg_Low);
+	    ResidCnt  = (UINT) DC390_read8 (Current_Fifo) & 0x1f;
+	    ResidCnt |= (UINT) DC390_read8 (CtcReg_High) << 16;
+	    ResidCnt |= (UINT) DC390_read8 (CtcReg_Mid) << 8; 
+	    ResidCnt += (UINT) DC390_read8 (CtcReg_Low);
 
 	    xferCnt = pSRB->SGToBeXferLen - ResidCnt;
 	    pSRB->SGBusAddr += xferCnt;
@@ -426,7 +398,11 @@
 	    pSRB->SGToBeXferLen = ResidCnt;
 	}
     }
-    DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+    if ((*psstatus & 7) != SCSI_DATA_OUT)
+    {
+	    DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+	    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+    }	    
 }
 
 void
@@ -434,7 +410,8 @@
 {
     UCHAR   sstatus, residual, bval;
     PSGL    psgl;
-    ULONG   ResidCnt, xferCnt, i;
+    UINT    ResidCnt, i;
+    ULONG   xferCnt;
     PUCHAR  ptr;
 
     sstatus = *psstatus;
@@ -446,7 +423,7 @@
 
 	if( sstatus & COUNT_2_ZERO )
 	{
-	    int ctr = 5000000; /* only try for about a tenth of a second */
+	    int ctr = 6000000; /* only try for about a second */
 	    int dstate = 0;
 	    while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen );
 	    if (!ctr) printk (KERN_CRIT "DC390: Deadlock in DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr));
@@ -455,7 +432,7 @@
 	    DEBUG1(ResidCnt = ((ULONG) DC390_read8 (CtcReg_High) << 16)	\
 		+ ((ULONG) DC390_read8 (CtcReg_Mid) << 8)		\
 		+ ((ULONG) DC390_read8 (CtcReg_Low));)
-	    DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%li,ToBeXfer=%li),", ResidCnt, pSRB->SGToBeXferLen);)
+	    DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%i,ToBeXfer=%li),", ResidCnt, pSRB->SGToBeXferLen);)
 
 	    DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
 
@@ -509,12 +486,12 @@
 	    //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
 	    dc390_laststatus &= ~0xff000000; dc390_laststatus |= bval << 24;
 
-	    DEBUG1(printk (KERN_DEBUG "Blast: Read %li times DMA_Status %02x", 0xa000-i, bval);)
-	    ResidCnt = (ULONG) DC390_read8 (CtcReg_High);
+	    DEBUG1(printk (KERN_DEBUG "Blast: Read %i times DMA_Status %02x", 0xa000-i, bval);)
+	    ResidCnt = (UINT) DC390_read8 (CtcReg_High);
 	    ResidCnt <<= 8;
-	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid);
+	    ResidCnt |= (UINT) DC390_read8 (CtcReg_Mid);
 	    ResidCnt <<= 8;
-	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_Low);
+	    ResidCnt |= (UINT) DC390_read8 (CtcReg_Low);
 
 	    xferCnt = pSRB->SGToBeXferLen - ResidCnt;
 	    pSRB->SGBusAddr += xferCnt;
@@ -535,6 +512,11 @@
 
 	}
     }
+    if ((*psstatus & 7) != SCSI_DATA_IN)
+    {
+	    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+	    DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+    }	    
 }
 
 static void
@@ -593,7 +575,7 @@
 static void __inline__
 dc390_MsgIn_reject (PACB pACB, PSRB pSRB)
 {
-  pSRB->MsgOutBuf[0] = MSG_REJECT_;
+  pSRB->MsgOutBuf[0] = MESSAGE_REJECT;
   pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
   DEBUG0 (printk (KERN_INFO "DC390: Reject message\n");)
 }
@@ -602,7 +584,7 @@
 static void __inline__
 dc390_EnableMsgOut_Abort ( PACB pACB, PSRB pSRB )
 {
-    pSRB->MsgOutBuf[0] = MSG_ABORT; 
+    pSRB->MsgOutBuf[0] = ABORT; 
     pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
     pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_;
 }
@@ -640,7 +622,7 @@
       pSRB = pACB->pTmpSRB;
       pSRB->SRBState = SRB_UNEXPECT_RESEL;
       pDCB->pActiveSRB = pSRB;
-      pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
+      pSRB->MsgOutBuf[0] = ABORT_TAG;
       pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
     }
   return pSRB;
@@ -653,7 +635,7 @@
 {
   PDCB pDCB = pSRB->pSRBDCB;
   if (!(pSRB->SRBState & DO_SYNC_NEGO)) 
-    printk ("DC390: Target %i initiates Non-Sync?\n", pDCB->UnitSCSIID);
+    printk (KERN_INFO "DC390: Target %i initiates Non-Sync?\n", pDCB->TargetID);
   pSRB->SRBState &= ~DO_SYNC_NEGO;
   pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
   pDCB->SyncPeriod = 0;
@@ -677,8 +659,8 @@
   
   if (!(pSRB->SRBState & DO_SYNC_NEGO))
     {
-      printk ("DC390: Target %i initiates Sync: %ins %i ... answer ...\n", 
-	      pDCB->UnitSCSIID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]);
+      printk (KERN_INFO "DC390: Target %i initiates Sync: %ins %i ... answer ...\n", 
+	      pDCB->TargetID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]);
 
       /* reject */
       //dc390_MsgIn_reject (pACB, pSRB);
@@ -687,12 +669,12 @@
       /* Reply with corrected SDTR Message */
       if (pSRB->MsgInBuf[4] > 15)
 	{ 
-	  printk ("DC390: Lower Sync Offset to 15\n");
+	  printk (KERN_INFO "DC390: Lower Sync Offset to 15\n");
 	  pSRB->MsgInBuf[4] = 15;
 	}
       if (pSRB->MsgInBuf[3] < pDCB->NegoPeriod)
 	{
-	  printk ("DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2);
+	  printk (KERN_INFO "DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2);
 	  pSRB->MsgInBuf[3] = pDCB->NegoPeriod;
 	};
       memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5);
@@ -728,10 +710,10 @@
   pDCB->CtrlR3 = bval;
   pDCB->SyncPeriod = (UCHAR)wval1;
   
-  if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->UnitSCSILUN == 0)
+  if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->TargetLUN == 0)
     {
       if (! (bval & FAST_SCSI)) wval1++;
-      printk ("DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->UnitSCSIID, 
+      printk (KERN_INFO "DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->TargetID, 
 	      40/wval1, ((40%wval1)*10+wval1/2)/wval1, pDCB->SyncOffset & 0x0f);
     }
   
@@ -739,6 +721,56 @@
 };
 
 
+/* handle RESTORE_PTR */
+static void 
+dc390_restore_ptr (PACB pACB, PSRB pSRB)
+{
+  PSGL psgl;
+  pSRB->TotalXferredLen = 0;
+  pSRB->SGIndex = 0;
+  if( pSRB->pcmd->use_sg )
+    {
+      pSRB->SGcount = (UCHAR) pSRB->pcmd->use_sg;
+      pSRB->pSegmentList = (PSGL) pSRB->pcmd->request_buffer;
+      psgl = pSRB->pSegmentList;
+      while (pSRB->TotalXferredLen + (ULONG) psgl->length < pSRB->Saved_Ptr)
+	{
+	  pSRB->TotalXferredLen += (ULONG) psgl->length;
+	  pSRB->SGIndex++;
+	  if( pSRB->SGIndex < pSRB->SGcount )
+	    {
+	      pSRB->pSegmentList++;
+	      psgl = pSRB->pSegmentList;
+	      
+	      pSRB->SGBusAddr = virt_to_bus( psgl->address );
+	      pSRB->SGToBeXferLen = (ULONG) psgl->length;
+	    }
+	  else
+	    pSRB->SGToBeXferLen = 0;
+	}
+      pSRB->SGToBeXferLen -= (pSRB->Saved_Ptr - pSRB->TotalXferredLen);
+      pSRB->SGBusAddr += (pSRB->Saved_Ptr - pSRB->TotalXferredLen);
+      printk (KERN_INFO "DC390: Pointer restored. Segment %i, Total %li, Bus %08lx\n", pSRB->SGIndex, pSRB->Saved_Ptr, pSRB->SGBusAddr);
+    }
+    else if( pSRB->pcmd->request_buffer )
+    {
+	pSRB->SGcount = 1;
+	pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+	pSRB->Segmentx.address = (PUCHAR) pSRB->pcmd->request_buffer + pSRB->Saved_Ptr;
+	pSRB->Segmentx.length = pSRB->pcmd->request_bufflen - pSRB->Saved_Ptr;
+	printk (KERN_INFO "DC390: Pointer restored. Total %li, Bus %p\n",
+		pSRB->Saved_Ptr, pSRB->Segmentx.address);
+    }
+     else
+       {
+	 pSRB->SGcount = 0;
+	 printk (KERN_INFO "DC390: RESTORE_PTR message for Transfer without Scatter-Gather ??\n");
+       };
+
+  pSRB->TotalXferredLen = pSRB->Saved_Ptr;
+};
+
+
 /* According to the docs, the AM53C974 reads the message and 
  * generates a Succesful Operation IRQ before asserting ACK for
  * the last byte (how does it know whether it's the last ?) */
@@ -749,15 +781,15 @@
 
 /* Check if the message is complete */
 static UCHAR __inline__
-dc390_MsgIn_complete (UCHAR *msgbuf, ULONG len)
+dc390_MsgIn_complete (UCHAR *msgbuf, UINT len)
 { 
-  if (*msgbuf == MSG_EXTENDED)
-    {
-      if (len < 2) return 0;
-      if (len < msgbuf[1] + 2) return 0;
-    }
+  if (*msgbuf == EXTENDED_MESSAGE)
+  {
+	if (len < 2) return 0;
+	if (len < msgbuf[1] + 2) return 0;
+  }
   else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages
-    if (len < 2) return 0;
+	if (len < 2) return 0;
   return 1;
 }
 
@@ -781,23 +813,23 @@
 	/* Now eval the msg */
 	switch (pSRB->MsgInBuf[0]) 
 	  {
-	  case MSG_DISCONNECT: 
+	  case DISCONNECT: 
 	    pSRB->SRBState = SRB_DISCONNECT; break;
 	    
-	  case MSG_SIMPLE_QTAG:
-	  case MSG_HEAD_QTAG:
-	  case MSG_ORDER_QTAG:
+	  case SIMPLE_QUEUE_TAG:
+	  case HEAD_OF_QUEUE_TAG:
+	  case ORDERED_QUEUE_TAG:
 	    pSRB = dc390_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]);
 	    break;
 	    
-	  case MSG_REJECT_: 
+	  case MESSAGE_REJECT: 
 	    DC390_write8 (ScsiCmd, RESET_ATN_CMD);
 	    pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
 	    if( pSRB->SRBState & DO_SYNC_NEGO)
 	      dc390_MsgIn_set_async (pACB, pSRB);
 	    break;
 	    
-	  case MSG_EXTENDED:
+	  case EXTENDED_MESSAGE:
 	    /* reject every extended msg but SDTR */
 	    if (pSRB->MsgInBuf[1] != 3 || pSRB->MsgInBuf[2] != EXTENDED_SDTR)
 	      dc390_MsgIn_reject (pACB, pSRB);
@@ -810,15 +842,18 @@
 	      };
 	    
 	    // nothing has to be done
-	  case MSG_COMPLETE: break;
+	  case COMMAND_COMPLETE: break;
 	    
-	    // SAVE POINTER my be ignored as we have the PSRB associated with the
+	    // SAVE POINTER may be ignored as we have the PSRB associated with the
 	    // scsi command. Thanks, Gerard, for pointing it out.
-	  case MSG_SAVE_PTR: break;
+	  case SAVE_POINTERS: 
+	    pSRB->Saved_Ptr = pSRB->TotalXferredLen;
+	    break;
 	    // The device might want to restart transfer with a RESTORE
-	  case MSG_RESTORE_PTR: 
-	    printk ("DC390: RESTORE POINTER message received ... reject\n");
-	    // fall through
+	  case RESTORE_POINTERS:
+	    DEBUG0(printk ("DC390: RESTORE POINTER message received ... try to handle\n");)
+	    dc390_restore_ptr (pACB, pSRB);
+	    break;
 
 	    // reject unknown messages
 	  default: dc390_MsgIn_reject (pACB, pSRB);
@@ -840,6 +875,17 @@
 {
     PSGL   psgl;
     ULONG  lval;
+    PDCB   pDCB = pACB->pActiveDCB;
+
+    if (pSRB == pACB->pTmpSRB)
+    {
+	if (pDCB) printk (KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (%02i-%i)\n",
+			  pDCB->TargetID, pDCB->TargetLUN);
+	else printk (KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (DCB 0!)\n");
+	dc390_EnableMsgOut_Abort (pACB, pSRB);
+	if (pDCB) pDCB->DCBFlag |= ABORT_DEV;
+	return;
+    }
 
     if( pSRB->SGIndex < pSRB->SGcount )
     {
@@ -852,7 +898,7 @@
 	    DEBUG1(printk (KERN_DEBUG " DC390: Next SG segment.");)
 	}
 	lval = pSRB->SGToBeXferLen;
-	DEBUG1(printk (KERN_DEBUG " DC390: Transfer %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr);)
+	DEBUG1(printk (KERN_DEBUG " DC390: Start transfer: %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr);)
 	DC390_write8 (CtcReg_Low, (UCHAR) lval);
 	lval >>= 8;
 	DC390_write8 (CtcReg_Mid, (UCHAR) lval);
@@ -918,8 +964,8 @@
     DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
     {
-	cnt = (UCHAR) pSRB->ScsiCmdLen;
-	ptr = (PUCHAR) pSRB->CmdBlock;
+	cnt = (UCHAR) pSRB->pcmd->cmd_len;
+	ptr = (PUCHAR) pSRB->pcmd->cmnd;
 	for(i=0; i < cnt; i++)
 	    DC390_write8 (ScsiFifo, *(ptr++));
     }
@@ -928,11 +974,12 @@
 	UCHAR bval = 0;
 	DC390_write8 (ScsiFifo, REQUEST_SENSE);
 	pDCB = pACB->pActiveDCB;
-	DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5);
+	DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5);
 	DC390_write8 (ScsiFifo, bval);
 	DC390_write8 (ScsiFifo, bval);
 	DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer));
 	DC390_write8 (ScsiFifo, bval);
+	DEBUG0(printk(KERN_DEBUG "DC390: AutoReqSense (CmndPhase)!\n");)
     }
     pSRB->SRBState = SRB_COMMAND;
     DC390_write8 (ScsiCmd, INFO_XFER_CMD);
@@ -966,14 +1013,14 @@
 		DC390_write8 (ScsiFifo, *(ptr++));
 	    pSRB->MsgCnt = 0;
 	    if( (pDCB->DCBFlag & ABORT_DEV_) &&
-		(pSRB->MsgOutBuf[0] == MSG_ABORT) )
+		(pSRB->MsgOutBuf[0] == ABORT) )
 		pSRB->SRBState = SRB_ABORT_SENT;
 	}
 	else
 	{
-	    bval = MSG_ABORT;	/* ??? MSG_NOP */
-	    if( (pSRB->CmdBlock[0] == INQUIRY ) ||
-		(pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+	    bval = ABORT;	/* ??? MSG_NOP */
+	    if( (pSRB->pcmd->cmnd[0] == INQUIRY ) ||
+		(pSRB->pcmd->cmnd[0] == REQUEST_SENSE) ||
 		(pSRB->SRBFlag & AUTO_REQSENSE) )
 	    {
 		if( pDCB->SyncMode & SYNC_ENABLE )
@@ -986,8 +1033,8 @@
     else
     {
 mop1:
-	//printk ("DC390: Send SDTR message to %i %i ... \n", pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
-	DC390_write8 (ScsiFifo, MSG_EXTENDED);
+        printk (KERN_ERR "DC390: OLD Sync Nego code triggered! (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN);
+	DC390_write8 (ScsiFifo, EXTENDED_MESSAGE);
 	DC390_write8 (ScsiFifo, 3);	/*    ;length of extended msg */
 	DC390_write8 (ScsiFifo, EXTENDED_SDTR);	/*    ; sync nego */
 	DC390_write8 (ScsiFifo, pDCB->NegoPeriod);
@@ -1030,20 +1077,16 @@
     UCHAR  bval, i, cnt;
     PDCB   ptr;
 
-    if( !(pDCB->IdentifyMsg & 0x07) )
+    if( !(pDCB->TargetLUN) )
     {
-	if( pACB->scan_devices )
-	{
-	    dc390_CurrSyncOffset = pDCB->SyncOffset;
-	}
-	else
+	if( !pACB->scan_devices )
 	{
 	    ptr = pACB->pLinkDCB;
 	    cnt = pACB->DCBCnt;
-	    bval = pDCB->UnitSCSIID;
+	    bval = pDCB->TargetID;
 	    for(i=0; i<cnt; i++)
 	    {
-		if( ptr->UnitSCSIID == bval )
+		if( ptr->TargetID == bval )
 		{
 		    ptr->SyncPeriod = pDCB->SyncPeriod;
 		    ptr->SyncOffset = pDCB->SyncOffset;
@@ -1068,25 +1111,27 @@
 
     DEBUG0(printk(KERN_INFO "DISC,");)
 
+    if (!pACB->Connected) printk(KERN_ERR "DC390: Disconnect not-connected bus?\n");
+    pACB->Connected = 0;
     pDCB = pACB->pActiveDCB;
     if (!pDCB)
      {
 	int j = 400;
-	DEBUG0(printk(KERN_WARNING "ACB:%08lx->ActiveDCB:%08lx IOPort:%04x IRQ:%02x !\n",\
-	       (ULONG)pACB,(ULONG)pDCB,pACB->IOPortBase,pACB->IRQLevel);)
+	DEBUG0(printk(KERN_ERR "ACB:%p->ActiveDCB:%p IOPort:%04x IRQ:%02x !\n",\
+	       pACB, pDCB, pACB->IOPortBase, pACB->IRQLevel);)
 	while (--j) udelay (1000);
 	DC390_read8 (INT_Status);	/* Reset Pending INT */
 	DC390_write8 (ScsiCmd, EN_SEL_RESEL);
 	return;
      }
+    DC390_write8 (ScsiCmd, EN_SEL_RESEL);
     pSRB = pDCB->pActiveSRB;
     pACB->pActiveDCB = 0;
     pSRB->ScsiPhase = SCSI_NOP0;
-    DC390_write8 (ScsiCmd, EN_SEL_RESEL);
     if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
     {
 	pSRB->SRBState = 0;
-	dc390_DoWaitingSRB( pACB );
+	dc390_Waiting_process ( pACB );
     }
     else if( pSRB->SRBState & SRB_ABORT_SENT )
     {
@@ -1098,22 +1143,24 @@
 	for( i=0; i < cnt; i++)
 	{
 	    psrb = pSRB->pNextSRB;
-	    pSRB->pNextSRB = pACB->pFreeSRB;
-	    pACB->pFreeSRB = pSRB;
+	    dc390_Free_insert (pACB, pSRB);
 	    pSRB = psrb;
 	}
 	pDCB->pGoingSRB = 0;
-	dc390_DoWaitingSRB( pACB );
+	dc390_Query_to_Waiting (pACB);
+	dc390_Waiting_process (pACB);
     }
     else
     {
 	if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
 	   !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
 	{	/* Selection time out */
-	    if( !(pACB->scan_devices) )
+	    if( !(1/*pACB->scan_devices*/) )
 	    {
 		pSRB->SRBState = SRB_READY;
-		dc390_RewaitSRB( pDCB, pSRB);
+		dc390_freetag (pDCB, pSRB);
+		dc390_Going_to_Waiting (pDCB, pSRB);
+		dc390_waiting_timer (pACB, HZ/5);
 	    }
 	    else
 	    {
@@ -1123,15 +1170,12 @@
 	}
 	else if( pSRB->SRBState & SRB_DISCONNECT )
 	{
-	    dc390_DoWaitingSRB( pACB );
+	    dc390_Waiting_process ( pACB );
 	}
 	else if( pSRB->SRBState & SRB_COMPLETED )
 	{
 disc1:
-	    if(pDCB->MaxCommand > 1)
-	    {
-	       pDCB->TagMask &= (~(1 << pSRB->TagNumber));   /* free tag mask */
-	    }
+	    dc390_freetag (pDCB, pSRB);
 	    pDCB->pActiveSRB = 0;
 	    pSRB->SRBState = SRB_FREE;
 	    dc390_SRBdone( pACB, pDCB, pSRB);
@@ -1146,43 +1190,48 @@
 {
     PDCB   pDCB;
     PSRB   pSRB;
-    USHORT wval;
-    UCHAR  bval;
+    UCHAR  id, lun;
 
     DEBUG0(printk(KERN_INFO "RSEL,");)
+    pACB->Connected = 1;
     pDCB = pACB->pActiveDCB;
     if( pDCB )
     {	/* Arbitration lost but Reselection won */
-	DEBUG0(printk ("(ActiveDCB != 0)");)
+	DEBUG0(printk ("DC390: (ActiveDCB != 0: Arb. lost but resel. won)!\n");)
 	pSRB = pDCB->pActiveSRB;
 	if( !( pACB->scan_devices ) )
 	{
 	    pSRB->SRBState = SRB_READY;
-	    dc390_RewaitSRB( pDCB, pSRB);
+	    dc390_freetag (pDCB, pSRB);
+	    dc390_Going_to_Waiting ( pDCB, pSRB);
+	    dc390_waiting_timer (pACB, HZ/5);
 	}
     }
-    bval = DC390_read8 (ScsiFifo);	/* get ID */
-    DEBUG0(printk ("Dev %02x,", bval);)
-    bval ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */
-    wval = 0;
-    while (bval >>= 1) wval++;
-    wval |=  ( (USHORT) DC390_read8 (ScsiFifo) & 7) << 8;  /* get LUN */
-    DEBUG0(printk ("(ID %02x, LUN %02x),", wval & 0xff, (wval & 0xff00) >> 8);)
-    pDCB = pACB->pLinkDCB;
-    while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
+    /* Get ID */
+    lun = DC390_read8 (ScsiFifo);
+    DEBUG0(printk ("Dev %02x,", lun);)
+    if (!(lun & (1 << pACB->pScsiHost->this_id)))
+      printk (KERN_ERR "DC390: Reselection must select host adapter: %02x!\n", lun);
+    else
+      lun ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */
+    id = 0; while (lun >>= 1) id++;
+    /* Get LUN */
+    lun = DC390_read8 (ScsiFifo);
+    if (!(lun & IDENTIFY_BASE)) printk (KERN_ERR "DC390: Resel: Expect identify message!\n");
+    lun &= 7;
+    DEBUG0(printk ("(%02i-%i),", id, lun);)
+    pDCB = dc390_findDCB (pACB, id, lun);
+    if (!pDCB)
     {
-	pDCB = pDCB->pNextDCB;
-	if( pDCB == pACB->pLinkDCB )
-	 {
-	    printk (KERN_ERR "DC390: Reselect from non existing device (ID %02x, LUN %02x)\n",
-		    wval & 0xff, (wval & 0xff00) >> 8);
-	    return;
-	 }
+	printk (KERN_ERR "DC390: Reselect from non existing device (%02i-%i)\n",
+		    id, lun);
+	return;
     }
     pACB->pActiveDCB = pDCB;
+    /* TagQ: We expect a message soon, so never mind the exact SRB */
     if( pDCB->SyncMode & EN_TAG_QUEUEING )
     {
-	pSRB = pACB->pTmpSRB; /* ?? */
+	pSRB = pACB->pTmpSRB;
 	pDCB->pActiveSRB = pSRB;
     }
     else
@@ -1192,8 +1241,8 @@
 	{
 	    pSRB= pACB->pTmpSRB;
 	    pSRB->SRBState = SRB_UNEXPECT_RESEL;
-	    printk (KERN_ERR "DC390: Reselect without outstanding cmnd (ID %02x, LUN %02x)\n",
-		    wval & 0xff, (wval & 0xff00) >> 8);
+	    printk (KERN_ERR "DC390: Reselect without outstanding cmnd (%02i-%i)\n",
+		    id, lun);
 	    pDCB->pActiveSRB = pSRB;
 	    dc390_EnableMsgOut_Abort ( pACB, pSRB );
 	}
@@ -1202,8 +1251,8 @@
 	    if( pDCB->DCBFlag & ABORT_DEV_ )
 	    {
 		pSRB->SRBState = SRB_ABORT_SENT;
-		printk (KERN_INFO "DC390: Reselect: Abort (ID %02x, LUN %02x)\n",
-			wval & 0xff, (wval & 0xff00) >> 8);
+		printk (KERN_INFO "DC390: Reselect: Abort (%02i-%i)\n",
+			id, lun);
 		dc390_EnableMsgOut_Abort( pACB, pSRB );
 	    }
 	    else
@@ -1213,7 +1262,7 @@
 
     DEBUG1(printk (KERN_DEBUG "Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber);)
     pSRB->ScsiPhase = SCSI_NOP0;
-    DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID);
+    DC390_write8 (Scsi_Dest_ID, pDCB->TargetID);
     DC390_write8 (Sync_Period, pDCB->SyncPeriod);
     DC390_write8 (Sync_Offset, pDCB->SyncOffset);
     DC390_write8 (CtrlReg1, pDCB->CtrlR1);
@@ -1228,30 +1277,36 @@
 {
    PDCB pPrevDCB = pACB->pLinkDCB;
 
-   pACB->DCBmap[pDCB->UnitSCSIID] &= ~(1 << pDCB->UnitSCSILUN);
    if (pDCB->GoingSRBCnt > 1)
      {
 	DCBDEBUG(printk (KERN_INFO "DC390: Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n",\
-		pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB, pDCB->GoingSRBCnt);)
+		pDCB->TargetID, pDCB->TargetLUN, (int)pDCB, pDCB->GoingSRBCnt);)
 	return;
      };
+   pACB->DCBmap[pDCB->TargetID] &= ~(1 << pDCB->TargetLUN);
    
+   // The first one
    if (pDCB == pACB->pLinkDCB) 
-     {
-	if (pDCB->pNextDCB == pDCB) pDCB->pNextDCB = 0;
+   {
+	// The last one
+	if (pACB->pLastDCB == pDCB) {
+		pDCB->pNextDCB = 0; pACB->pLastDCB = 0;
+	}
 	pACB->pLinkDCB = pDCB->pNextDCB;
-	pACB->pLastDCB->pNextDCB = pDCB->pNextDCB;
-     }
+   }
    else
-     {
+   {
 	while (pPrevDCB->pNextDCB != pDCB) pPrevDCB = pPrevDCB->pNextDCB;
 	pPrevDCB->pNextDCB = pDCB->pNextDCB;
 	if (pDCB == pACB->pLastDCB) pACB->pLastDCB = pPrevDCB;
-     }
+   }
 
-   DCBDEBUG(printk (KERN_INFO "DC390: Driver about to free DCB (ID %i, LUN %i): 0x%08x\n",\
-	   pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);)
-   kfree (pDCB); if (pDCB == pACB->pActiveDCB) pACB->pActiveDCB = 0;
+   DCBDEBUG(printk (KERN_INFO "DC390: Driver about to free DCB (ID %i, LUN %i): %p\n",\
+	   pDCB->TargetID, pDCB->TargetLUN, pDCB);)
+   if (pDCB == pACB->pActiveDCB) pACB->pActiveDCB = 0;
+   if (pDCB == pACB->pLinkDCB) pACB->pLinkDCB = pDCB->pNextDCB;
+   if (pDCB == pACB->pDCBRunRobin) pACB->pDCBRunRobin = pDCB->pNextDCB;
+   kfree (pDCB); 
    pACB->DCBCnt--;
    /* pACB->DeviceCnt--; */
 };
@@ -1280,21 +1335,12 @@
 		|| (pDCB->DevType == TYPE_MOD)) &&*/
 	    !dc390_tagq_blacklist (((char*)ptr)+8) )
 	  {
-	     pDCB->MaxCommand = pDCB->pDCBACB->TagMaxNum;
+	     if (pDCB->MaxCommand ==1) pDCB->MaxCommand = pDCB->pDCBACB->TagMaxNum;
 	     pDCB->SyncMode |= EN_TAG_QUEUEING /* | EN_ATN_STOP */;
-	     pDCB->TagMask = 0;
+	     //pDCB->TagMask = 0;
 	  }
 	else
-	  {
-	     /* Do we really need to check for DevType here ? */
-	     if ( 0 /*(pDCB->DevMode & EN_DISCONNECT_)*/
-		 /* && ((pDCB->DevType == TYPE_DISK) 
-		     || (pDCB->DevType == TYPE_MOD))*/ )
-		pDCB->SyncMode |= EN_ATN_STOP;
-	     else
-	       //pDCB->SyncMode &= ~EN_ATN_STOP;
-	       pDCB->SyncMode &= ~0;
-	  }
+	     pDCB->MaxCommand = 1;
      }
 };
 
@@ -1312,60 +1358,62 @@
 void
 dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
 {
-    PSRB   psrb;
-    UCHAR  bval, status, i;
+    UCHAR  bval, status, i, DCB_removed;
     PSCSICMD pcmd;
     PSCSI_INQDATA  ptr;
     PSGL   ptr2;
     ULONG  swlval;
 
-    pcmd = pSRB->pcmd;
+    pcmd = pSRB->pcmd; DCB_removed = 0;
     status = pSRB->TargetStatus;
+    ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
+    if( pcmd->use_sg )
+	ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
+	
     DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p, pid %li\n", status, pcmd->result,\
 		pSRB, pcmd->pid);)
     if(pSRB->SRBFlag & AUTO_REQSENSE)
     {	/* Last command was a Request Sense */
 	pSRB->SRBFlag &= ~AUTO_REQSENSE;
 	pSRB->AdaptStatus = 0;
-	pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+	pSRB->TargetStatus = CHECK_CONDITION << 1;
 #ifdef DC390_REMOVABLEDEBUG
 	switch (pcmd->sense_buffer[2] & 0x0f)
 	{	    
 	 case NOT_READY: printk (KERN_INFO "DC390: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
-				 pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				 pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN,
 				 status, pACB->scan_devices); break;
 	 case UNIT_ATTENTION: printk (KERN_INFO "DC390: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
-				      pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				      pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN,
 				      status, pACB->scan_devices); break;
 	 case ILLEGAL_REQUEST: printk (KERN_INFO "DC390: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
-				       pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				       pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN,
 				       status, pACB->scan_devices); break;
 	 case MEDIUM_ERROR: printk (KERN_INFO "DC390: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
-				    pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				    pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN,
 				    status, pACB->scan_devices); break;
 	 case HARDWARE_ERROR: printk (KERN_INFO "DC390: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
-				      pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				      pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN,
 				      status, pACB->scan_devices); break;
 	}
 #endif
-	//pcmd->result = DRIVER_SENSE << 24 | DID_OK << 16 | status;
-	if(status == SCSI_STAT_CHECKCOND)
+	//pcmd->result = MK_RES(DRIVER_SENSE,DID_OK,0,status);
+	if (status == (CHECK_CONDITION << 1))
 	{
-	    pcmd->result = DID_BAD_TARGET << 16;
+	    pcmd->result = MK_RES_LNX(0,DID_BAD_TARGET,0,/*CHECK_CONDITION*/0);
 	    goto ckc_e;
 	}
 	if(pSRB->RetryCnt == 0)
 	{
-	    (ULONG)(pSRB->CmdBlock[0]) = pSRB->Segment0[0];
-	    pSRB->TotalXferredLen = pSRB->Segment1[1];
+	    //(UINT)(pSRB->pcmd->cmnd[0]) = pSRB->Segment0[0];
+	    pSRB->TotalXferredLen = pSRB->SavedTotXLen;
 	    if( (pSRB->TotalXferredLen) &&
 		(pSRB->TotalXferredLen >= pcmd->underflow) )
-		pcmd->result |= (DID_OK << 16);
+		  SET_RES_DID(pcmd->result,DID_OK)
 	    else
-		pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
-				SCSI_STAT_CHECKCOND;
-	    REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x,Result=%08x,XferL=%08x\n",pSRB->CmdBlock[0],\
-		(UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);)
+		  pcmd->result = MK_RES_LNX(DRIVER_SENSE,DID_OK,0,CHECK_CONDITION);
+		  REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x,Result=%08x,XferL=%08x\n",pSRB->pcmd->cmnd[0],\
+			(UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);)
 	    goto ckc_e;
 	}
 	else /* Retry */
@@ -1373,20 +1421,20 @@
 	    pSRB->RetryCnt--;
 	    pSRB->AdaptStatus = 0;
 	    pSRB->TargetStatus = 0;
-	    *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
-	    *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
+	    //*((PUINT) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+	    //*((PUINT) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
 	    /* Don't retry on TEST_UNIT_READY */
-	    if( pSRB->CmdBlock[0] == TEST_UNIT_READY /* || pSRB->CmdBlock[0] == START_STOP */)
+	    if( pSRB->pcmd->cmnd[0] == TEST_UNIT_READY /* || pSRB->pcmd->cmnd[0] == START_STOP */)
 	    {
-		pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) 
-			    | SCSI_STAT_CHECKCOND;
-		REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->CmdBlock[0],\
+		pcmd->result = MK_RES_LNX(DRIVER_SENSE,DID_OK,0,CHECK_CONDITION);
+		REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->pcmd->cmnd[0],\
 		       (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);)
 		goto ckc_e;
 	    }
-	    pcmd->result |= (DRIVER_SENSE << 24);
-	    pSRB->SGcount	 = (UCHAR) pSRB->Segment1[0];
-	    pSRB->ScsiCmdLen	 = (UCHAR) (pSRB->Segment1[0] >> 8);
+	    SET_RES_DRV(pcmd->result,DRIVER_SENSE);
+	    pSRB->SGcount	 = (UCHAR) pSRB->SavedSGCount;
+	    //pSRB->ScsiCmdLen	 = (UCHAR) (pSRB->Segment1[0] >> 8);
+	    DEBUG0 (printk ("DC390: RETRY pid %li (%02x), target %02i-%02i\n", pcmd->pid, pcmd->cmnd[0], pcmd->target, pcmd->lun);)
 	    pSRB->SGIndex = 0;
 	    pSRB->TotalXferredLen = 0;
 	    pSRB->SGToBeXferLen = 0;
@@ -1398,17 +1446,19 @@
 		pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
 		pSRB->Segmentx.length = pcmd->request_bufflen;
 	    }
-	    if( dc390_StartSCSI( pACB, pDCB, pSRB ) )
-		dc390_RewaitSRB( pDCB, pSRB );
+	    if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) {
+		dc390_Going_to_Waiting ( pDCB, pSRB );
+		dc390_waiting_timer (pACB, HZ/5);
+	    }
 	    return;
 	}
     }
     if( status )
     {
-	if( status == SCSI_STAT_CHECKCOND)
+	if( status_byte(status) == CHECK_CONDITION )
 	{
-	    REMOVABLEDEBUG(printk (KERN_INFO "DC390: Scsi_Stat_CheckCond (Cmd %02x, Id %02x, LUN %02x)\n",\
-		    pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);)
+	    REMOVABLEDEBUG(printk (KERN_INFO "DC390: Check_Condition (Cmd %02x, Id %02x, LUN %02x)\n",\
+		    pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN);)
 	    if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
 	    {
 		bval = pSRB->SGcount;
@@ -1425,12 +1475,14 @@
 	    dc390_RequestSense( pACB, pDCB, pSRB );
 	    return;
 	}
-	else if( status == SCSI_STAT_QUEUEFULL )
+	else if( status_byte(status) == QUEUE_FULL )
 	{
 	    bval = (UCHAR) pDCB->GoingSRBCnt;
 	    bval--;
 	    pDCB->MaxCommand = bval;
-	    dc390_RewaitSRB( pDCB, pSRB );
+	    dc390_freetag (pDCB, pSRB);
+	    dc390_Going_to_Waiting ( pDCB, pSRB );
+	    dc390_waiting_timer (pACB, HZ/5);
 	    pSRB->AdaptStatus = 0;
 	    pSRB->TargetStatus = 0;
 	    return;
@@ -1439,23 +1491,23 @@
 	{
 	    pSRB->AdaptStatus = H_SEL_TIMEOUT;
 	    pSRB->TargetStatus = 0;
-	    pcmd->result = DID_BAD_TARGET << 16;
+	    pcmd->result = MK_RES(0,DID_NO_CONNECT,0,0);
 	    /* Devices are removed below ... */
 	}
-	else if (status == SCSI_STAT_BUSY && 
-		 (pSRB->CmdBlock[0] == TEST_UNIT_READY || pSRB->CmdBlock[0] == INQUIRY) &&
+	else if (status_byte(status) == BUSY && 
+		 (pcmd->cmnd[0] == TEST_UNIT_READY || pcmd->cmnd[0] == INQUIRY) &&
 		 pACB->scan_devices)
 	{
 	    pSRB->AdaptStatus = 0;
 	    pSRB->TargetStatus = status;
-	    pcmd->result = (ULONG) (pSRB->EndMessage << 8)
-				/* | (ULONG) status */;
+	    pcmd->result = MK_RES(0,0,pSRB->EndMessage,/*status*/0);
 	}
 	else
 	{   /* Another error */
 	    pSRB->AdaptStatus = 0;
 	    if( pSRB->RetryCnt )
 	    {	/* Retry */
+		//printk ("DC390: retry\n");
 		pSRB->RetryCnt--;
 		pSRB->TargetStatus = 0;
 		pSRB->SGIndex = 0;
@@ -1469,14 +1521,18 @@
 		    pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
 		    pSRB->Segmentx.length = pcmd->request_bufflen;
 		}
-		if( dc390_StartSCSI( pACB, pDCB, pSRB ) )
-		    dc390_RewaitSRB( pDCB, pSRB );
-		return;
+		if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) {
+		    dc390_Going_to_Waiting ( pDCB, pSRB );
+		    dc390_waiting_timer (pACB, HZ/5);
+		}
+      		return;
 	    }
 	    else
 	    {	/* Report error */
-		pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) |
-			       (ULONG) status;
+	      //pcmd->result = MK_RES(0, DID_ERROR, pSRB->EndMessage, status);
+	      SET_RES_DID(pcmd->result,DID_ERROR);
+	      SET_RES_MSG(pcmd->result,pSRB->EndMessage);
+	      SET_RES_TARGET(pcmd->result,status);
 	    }
 	}
     }
@@ -1486,38 +1542,52 @@
 	if(status & H_OVER_UNDER_RUN)
 	{
 	    pSRB->TargetStatus = 0;
-	    pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8);
+	    SET_RES_DID(pcmd->result,DID_OK);
+	    SET_RES_MSG(pcmd->result,pSRB->EndMessage);
 	}
 	else if( pSRB->SRBStatus & PARITY_ERROR)
 	{
-	    pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 8);
+	    //pcmd->result = MK_RES(0,DID_PARITY,pSRB->EndMessage,0);
+	    SET_RES_DID(pcmd->result,DID_PARITY);
+	    SET_RES_MSG(pcmd->result,pSRB->EndMessage);
 	}
 	else		       /* No error */
 	{
 	    pSRB->AdaptStatus = 0;
 	    pSRB->TargetStatus = 0;
-	    pcmd->result |= (DID_OK << 16);
+	    SET_RES_DID(pcmd->result,DID_OK);
 	}
     }
+    if ((pcmd->result & RES_DID) == 0 &&
+	pcmd->cmnd[0] == INQUIRY && 
+	pcmd->cmnd[2] == 0 &&
+	pcmd->request_bufflen >= 8 &&
+	ptr &&
+	(ptr->Vers & 0x07) >= 2)
+	    pDCB->Inquiry7 = ptr->Flags;
 
 ckc_e:
     if( pACB->scan_devices )
     {
-	if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+	if( pcmd->cmnd[0] == TEST_UNIT_READY ||
+	    pcmd->cmnd[0] == INQUIRY)
 	{
 #ifdef DC390_DEBUG0
-	    printk (KERN_INFO "DC390: Test_Unit_Ready: result: %08x", pcmd->result);
-	    if (pcmd->result & DRIVER_SENSE << 24) printk (" (sense: %02x %02x %02x %02x)\n",
+	    printk (KERN_INFO "DC390: %s: result: %08x", 
+		    (pcmd->cmnd[0] == INQUIRY? "INQUIRY": "TEST_UNIT_READY"),
+		    pcmd->result);
+	    if (pcmd->result & (DRIVER_SENSE << 24)) printk (" (sense: %02x %02x %02x %02x)\n",
 				   pcmd->sense_buffer[0], pcmd->sense_buffer[1],
 				   pcmd->sense_buffer[2], pcmd->sense_buffer[3]);
 	    else printk ("\n");
 #endif
-	    if((pcmd->result != (DID_OK << 16) && !(pcmd->result & SCSI_STAT_CHECKCOND) && !(pcmd->result & SCSI_STAT_BUSY)) ||
-	       ((pcmd->result & DRIVER_SENSE << 24) && (pcmd->sense_buffer[0] & 0x70) == 0x70 &&
-		(pcmd->sense_buffer[2] & 0xf) == ILLEGAL_REQUEST) || pcmd->result & DID_ERROR << 16)
+	    if( (host_byte(pcmd->result) != DID_OK && !(status_byte(pcmd->result) & CHECK_CONDITION) && !(status_byte(pcmd->result) & BUSY)) ||
+	       ((driver_byte(pcmd->result) & DRIVER_SENSE) && (pcmd->sense_buffer[0] & 0x70) == 0x70 &&
+		(pcmd->sense_buffer[2] & 0xf) == ILLEGAL_REQUEST) || host_byte(pcmd->result) & DID_ERROR )
 	    {
 	       /* device not present: remove */ 
-	       dc390_remove_dev (pACB, pDCB);
+	       //dc390_Going_remove (pDCB, pSRB);
+	       dc390_remove_dev (pACB, pDCB); DCB_removed = 1;
 	       
 	       if( (pcmd->target == pACB->pScsiHost->max_id - 1) &&
 		  ((pcmd->lun == 0) || (pcmd->lun == pACB->pScsiHost->max_lun - 1)) )
@@ -1534,16 +1604,17 @@
 	}
     }
    
-   if( pSRB->CmdBlock[0] == INQUIRY && 
-      (pcmd->result == DID_OK << 16 || pcmd->result & SCSI_STAT_CHECKCOND) )
+    //if( pSRB->pcmd->cmnd[0] == INQUIRY && 
+    //  (host_byte(pcmd->result) == DID_OK || status_byte(pcmd->result) & CHECK_CONDITION) )
+    if( pcmd->cmnd[0] == INQUIRY && 
+	(pcmd->result == (DID_OK << 16) || status_byte(pcmd->result) & CHECK_CONDITION) )
      {
-	ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
-	if( pcmd->use_sg )
-	  ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
-	if ((ptr->DevType & SCSI_DEVTYPE) == TYPE_NODEV)
+	if ((ptr->DevType & SCSI_DEVTYPE) == TYPE_NODEV && !DCB_removed)
 	  {
+	     //printk ("DC390: Type = nodev! (%02i-%i)\n", pcmd->target, pcmd->lun);
 	     /* device not present: remove */
-	     dc390_remove_dev (pACB, pDCB);
+	     //dc390_Going_remove (pDCB, pSRB);
+	     dc390_remove_dev (pACB, pDCB); DCB_removed = 1;
 	  }
 	else
 	  {
@@ -1555,40 +1626,29 @@
 	    (pcmd->lun == pACB->pScsiHost->max_lun - 1) )
 	  pACB->scan_devices = 0;
      };
-/*  dc390_ReleaseSRB( pDCB, pSRB ); */
 
-    if(pSRB == pDCB->pGoingSRB )
-    {
-	pDCB->pGoingSRB = pSRB->pNextSRB;
-    }
-    else
-    {
-	psrb = pDCB->pGoingSRB;
-	while( psrb->pNextSRB != pSRB )
-	    psrb = psrb->pNextSRB;
-	psrb->pNextSRB = pSRB->pNextSRB;
-	if( pSRB == pDCB->pGoingLast )
-	    pDCB->pGoingLast = psrb;
-    }
-    pSRB->pNextSRB = pACB->pFreeSRB;
-    pACB->pFreeSRB = pSRB;
-    pDCB->GoingSRBCnt--;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,30)
+    pcmd->resid = pcmd->request_bufflen - pSRB->TotalXferredLen;
+#endif
 
-    dc390_DoWaitingSRB( pACB );
+    if (!DCB_removed) dc390_Going_remove (pDCB, pSRB);
+    /* Add to free list */
+    dc390_Free_insert (pACB, pSRB);
 
+    DEBUG0(printk (KERN_DEBUG "DC390: SRBdone: done pid %li\n", pcmd->pid);)
     DC390_UNLOCK_ACB_NI;
-    pcmd->scsi_done( pcmd );
+    pcmd->scsi_done (pcmd);
     DC390_LOCK_ACB_NI;
 
-    if( pDCB->QIORBCnt )
-	dc390_DoNextCmd( pACB, pDCB );
+    dc390_Query_to_Waiting (pACB);
+    dc390_Waiting_process (pACB);
     return;
 }
 
 
-/* Remove all SRBs and tell midlevel code DID_RESET */
+/* Remove all SRBs from Going list and inform midlevel */
 void
-dc390_DoingSRB_Done( PACB pACB )
+dc390_DoingSRB_Done( PACB pACB, PSCSICMD cmd )
 {
     PDCB   pDCB, pdcb;
     PSRB   psrb, psrb2;
@@ -1605,16 +1665,21 @@
 	{
 	    psrb2 = psrb->pNextSRB;
 	    pcmd = psrb->pcmd;
-	    pcmd->result = DID_RESET << 16;
+	    dc390_Free_insert (pACB, psrb);
+#ifndef USE_NEW_EH
+	    /* New EH will crash on being given timed out cmnds */
+	    if (pcmd == cmd)
+		pcmd->result = MK_RES(0,DID_ABORT,0,0);
+	    else
+		pcmd->result = MK_RES(0,DID_RESET,0,0);
 
 /*	    ReleaseSRB( pDCB, pSRB ); */
 
-	    psrb->pNextSRB = pACB->pFreeSRB;
-	    pACB->pFreeSRB = psrb;
-
+	    DEBUG0(printk (KERN_DEBUG "DC390: DoingSRB_Done: done pid %li\n", pcmd->pid);)
 	    DC390_UNLOCK_ACB_NI;
 	    pcmd->scsi_done( pcmd );
 	    DC390_LOCK_ACB_NI;
+#endif	
 	    psrb  = psrb2;
 	}
 	pdcb->GoingSRBCnt = 0;;
@@ -1622,21 +1687,21 @@
 	pdcb->TagMask = 0;
 	pdcb = pdcb->pNextDCB;
     } while( pdcb != pDCB );
+    dc390_Query_to_Waiting (pACB);
 }
 
 
 static void
 dc390_ResetSCSIBus( PACB pACB )
 {
-    pACB->ACBFlag |= RESET_DEV;
-
-    DC390_write8 (ScsiCmd, RST_DEVICE_CMD);
-    udelay (250);
-    DC390_write8 (ScsiCmd, NOP_CMD);
+    //DC390_write8 (ScsiCmd, RST_DEVICE_CMD);
+    //udelay (250);
+    //DC390_write8 (ScsiCmd, NOP_CMD);
 
     DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
     DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD);
+    pACB->Connected = 0;
 
     return;
 }
@@ -1644,27 +1709,31 @@
 static void
 dc390_ScsiRstDetect( PACB pACB )
 {
-    printk ("DC390: Rst_Detect: laststat = %08lx\n", dc390_laststatus);
+    printk ("DC390: Rst_Detect: laststat = %08x\n", dc390_laststatus);
     //DEBUG0(printk(KERN_INFO "RST_DETECT,");)
 
+    if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer);
     DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
     /* Unlock before ? */
-    /* delay a second */
-    { unsigned int msec = 1*1000; while (--msec) udelay(1000); }
+    /* delay half a second */
+    udelay (1000);
     DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+    pACB->pScsiHost->last_reset = jiffies + 5*HZ/2
+		    + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
+    pACB->Connected = 0;
 
     if( pACB->ACBFlag & RESET_DEV )
 	pACB->ACBFlag |= RESET_DONE;
     else
-    {
+    {   /* Reset was issued by sb else */
 	pACB->ACBFlag |= RESET_DETECT;
 
 	dc390_ResetDevParam( pACB );
-/*	dc390_DoingSRB_Done( pACB ); ???? */
-	dc390_RecoverSRB( pACB );
+	dc390_DoingSRB_Done( pACB, 0 );
+	//dc390_RecoverSRB( pACB );
 	pACB->pActiveDCB = NULL;
 	pACB->ACBFlag = 0;
-	dc390_DoWaitingSRB( pACB );
+	dc390_Waiting_process( pACB );
     }
     return;
 }
@@ -1676,15 +1745,17 @@
     PSCSICMD  pcmd;
 
     REMOVABLEDEBUG(printk (KERN_INFO "DC390: RequestSense (Cmd %02x, Id %02x, LUN %02x)\n",\
-	    pSRB->CmdBlock[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);)
+	    pSRB->pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN);)
 
     pSRB->SRBFlag |= AUTO_REQSENSE;
-    pSRB->Segment0[0] = (ULONG) pSRB->CmdBlock[0];
-    pSRB->Segment0[1] = (ULONG) pSRB->CmdBlock[4];
-    pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
-    pSRB->Segment1[1] = pSRB->TotalXferredLen;
+    //pSRB->Segment0[0] = (UINT) pSRB->CmdBlock[0];
+    //pSRB->Segment0[1] = (UINT) pSRB->CmdBlock[4];
+    //pSRB->Segment1[0] = ((UINT)(pSRB->pcmd->cmd_len) << 8) + pSRB->SGcount;
+    //pSRB->Segment1[1] = pSRB->TotalXferredLen;
+    pSRB->SavedSGCount = pSRB->SGcount;
+    pSRB->SavedTotXLen = pSRB->TotalXferredLen;
     pSRB->AdaptStatus = 0;
-    pSRB->TargetStatus = 0; /* SCSI_STAT_CHECKCOND; */
+    pSRB->TargetStatus = 0; /* CHECK_CONDITION<<1; */
 
     pcmd = pSRB->pcmd;
 
@@ -1694,16 +1765,18 @@
     pSRB->SGcount = 1;
     pSRB->SGIndex = 0;
 
-    pSRB->CmdBlock[0] = REQUEST_SENSE;
-    pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
-    (USHORT) pSRB->CmdBlock[2] = 0;
-    (USHORT) pSRB->CmdBlock[4] = sizeof(pcmd->sense_buffer);
-    pSRB->ScsiCmdLen = 6;
+    //pSRB->CmdBlock[0] = REQUEST_SENSE;
+    //pSRB->CmdBlock[1] = pDCB->TargetLUN << 5;
+    //(USHORT) pSRB->CmdBlock[2] = 0;
+    //(USHORT) pSRB->CmdBlock[4] = sizeof(pcmd->sense_buffer);
+    //pSRB->ScsiCmdLen = 6;
 
     pSRB->TotalXferredLen = 0;
     pSRB->SGToBeXferLen = 0;
-    if( dc390_StartSCSI( pACB, pDCB, pSRB ) )
-	dc390_RewaitSRB( pDCB, pSRB );
+    if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) {
+	dc390_Going_to_Waiting ( pDCB, pSRB );
+	dc390_waiting_timer (pACB, HZ/5);
+    }
 }
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr.c linux.19p6/drivers/scsi/sr.c
--- linux.vanilla/drivers/scsi/sr.c	Mon Dec 11 22:12:55 2000
+++ linux.19p6/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.19p6/drivers/scsi/st.c
--- linux.vanilla/drivers/scsi/st.c	Mon Dec 11 22:12:55 2000
+++ linux.19p6/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/scsi/tmscsim.c linux.19p6/drivers/scsi/tmscsim.c
--- linux.vanilla/drivers/scsi/tmscsim.c	Sat Sep  9 18:42:42 2000
+++ linux.19p6/drivers/scsi/tmscsim.c	Sat Dec 30 19:46:47 2000
@@ -5,11 +5,11 @@
  *		     Bus Master Host Adapter			       *
  * (C)Copyright 1995-1996 Tekram Technology Co., Ltd.		       *
  ***********************************************************************/
-/* (C) Copyright: put under GNU GPL in 10/96				*
+/* (C) Copyright: put under GNU GPL in 10/96  (see README.tmscsim)	*
 *************************************************************************/
-/* $Id: tmscsim.c,v 2.16 1998/12/25 17:54:44 garloff Exp $		*/
+/* $Id: tmscsim.c,v 2.60.2.30 2000/12/20 01:07:12 garloff Exp $		*/
 /*	Enhancements and bugfixes by					*
- *	Kurt Garloff <kurt@garloff.de>					*
+ *	Kurt Garloff <kurt@garloff.de>	<garloff@suse.de>		*
  ***********************************************************************/
 /*	HISTORY:							*
  *									*
@@ -95,8 +95,75 @@
  *	2.0c  98/11/19	KG	Cleaned up detect/init for SMP boxes, 	*
  *				Write Erase DMA (1.20t) caused problems	*
  *	2.0d  98/12/25	KG	Christmas release ;-) Message handling  *
- *				competely reworked. Handle target ini-	*
+ *				completely reworked. Handle target ini-	*
  *				tiated SDTR correctly.			*
+ *	2.0d1 99/01/25	KG	Try to handle RESTORE_PTR		*
+ *	2.0d2 99/02/08	KG	Check for failure of kmalloc, correct 	*
+ *				inclusion of scsicam.h, DelayReset	*
+ *	2.0d3 99/05/31	KG	DRIVER_OK -> DID_OK, DID_NO_CONNECT,	*
+ *				detect Target mode and warn.		*
+ *				pcmd->result handling cleaned up.	*
+ *	2.0d4 99/06/01	KG	Cleaned selection process. Found bug	*
+ *				which prevented more than 16 tags. Now:	*
+ *				24. SDTR cleanup. Cleaner multi-LUN	*
+ *				handling. Don't modify ControlRegs/FIFO	*
+ *				when connected.				*
+ *	2.0d5 99/06/01	KG	Clear DevID, Fix INQUIRY after cfg chg.	*
+ *	2.0d6 99/06/02	KG	Added ADD special command to allow cfg.	*
+ *				before detection. Reset SYNC_NEGO_DONE	*
+ *				after a bus reset.			*
+ *	2.0d7 99/06/03	KG	Fixed bugs wrt add,remove commands	*
+ *	2.0d8 99/06/04	KG	Removed copying of cmnd into CmdBlock.	*
+ *				Fixed Oops in _release().		*
+ *	2.0d9 99/06/06	KG	Also tag queue INQUIRY, T_U_R, ...	*
+ *				Allow arb. no. of Tagged Cmnds. Max 32	*
+ *	2.0d1099/06/20	KG	TagMaxNo changes now honoured! Queueing *
+ *				clearified (renamed ..) TagMask handling*
+ *				cleaned.				*
+ *	2.0d1199/06/28	KG	cmd->result now identical to 2.0d2	*
+ *	2.0d1299/07/04	KG	Changed order of processing in IRQ	*
+ *	2.0d1399/07/05	KG	Don't update DCB fields if removed	*
+ *	2.0d1499/07/05	KG	remove_dev: Move kfree() to the end	*
+ *	2.0d1599/07/12	KG	use_new_eh_code: 0, ULONG -> UINT where	*
+ *				appropriate				*
+ *	2.0d1699/07/13	KG	Reenable StartSCSI interrupt, Retry msg	*
+ *	2.0d1799/07/15	KG	Remove debug msg. Disable recfg. when	*
+ *				there are queued cmnds			*
+ *	2.0d1899/07/18	KG	Selection timeout: Don't requeue	*
+ *	2.0d1999/07/18	KG	Abort: Only call scsi_done if dequeued	*
+ *	2.0d2099/07/19	KG	Rst_Detect: DoingSRB_Done		*
+ *	2.0d2199/08/15	KG	dev_id for request/free_irq, cmnd[0] for*
+ *				RETRY, SRBdone does DID_ABORT for the 	*
+ *				cmd passed by DC390_reset()		*
+ *	2.0d2299/08/25	KG	dev_id fixed. can_queue: 42		*
+ *	2.0d2399/08/25	KG	Removed some debugging code. dev_id 	*
+ *				now is set to pACB. Use u8,u16,u32. 	*
+ *	2.0d2499/11/14	KG	Unreg. I/O if failed IRQ alloc. Call	*
+ * 				done () w/ DID_BAD_TARGET in case of	*
+ *				missing DCB. We	are old EH!!		*
+ *	2.0d2500/01/15	KG	2.3.3x compat from Andreas Schultz	*
+ *				set unique_id. Disable RETRY message.	*
+ *	2.0d2600/01/29	KG	Go to new EH.				*
+ *	2.0d2700/01/31	KG	... but maintain 2.0 compat.		*
+ *				and fix DCB freeing			*
+ *	2.0d2800/02/14	KG	Queue statistics fixed, dump special cmd*
+ *				Waiting_Timer for failed StartSCSI	*
+ *				New EH: Don't return cmnds to ML on RST *
+ *				Use old EH (don't have new EH fns yet)	*
+ * 				Reset: Unlock, but refuse to queue	*
+ * 				2.3 __setup function			*
+ *	2.0e  00/05/22	KG	Return residual for 2.3			*
+ *	2.0e1 00/05/25	KG	Compile fixes for 2.3.99		*
+ *	2.0e2 00/05/27	KG	Jeff Garzik's pci_enable_device()	*
+ *	2.0e3 00/09/29	KG	Some 2.4 changes. Don't try Sync Nego	*
+ *				before INQUIRY has reported ability. 	*
+ *				Recognise INQUIRY as scanning command.	*
+ *	2.0e4 00/10/13	KG	Allow compilation into 2.4 kernel	*
+ *	2.0e5 00/11/17	KG	Store Inq.flags in DCB			*
+ *	2.0e6 00/11/22  KG	2.4 init function (Thx to O.Schumann)	*
+ * 				2.4 PCI device table (Thx to A.Richter)	*
+ *	2.0e7 00/11/28	KG	Allow overriding of BIOS settings	*
+ *	2.0f  00/12/20	KG	Handle failed INQUIRYs during scan	*
  ***********************************************************************/
 
 /* Uncomment SA_INTERRUPT, if the driver refuses to share its IRQ with other devices */
@@ -108,6 +175,7 @@
 //#define DC390_DCBDEBUG
 //#define DC390_PARSEDEBUG
 //#define DC390_REMOVABLEDEBUG
+//#define DC390_LOCKDEBUG
 
 /* Debug definitions */
 #ifdef DC390_DEBUG0
@@ -159,6 +227,7 @@
 #include <linux/config.h>
 #include <linux/version.h>
 #include <linux/blk.h>
+#include <linux/timer.h>
 
 #include "scsi.h"
 #include "hosts.h"
@@ -175,8 +244,7 @@
 
 /* Note: Starting from 2.1.9x, the mid-level scsi code issues a 
  * spinlock_irqsave (&io_request_lock) before calling the driver's 
- * routines, so we don't need to lock.
- * TODO: Verify, if we are locked in every case!
+ * routines, so we don't need to lock, except in the IRQ handler.
  * The policy 3, let the midlevel scsi code do the io_request_locks
  * and us locking on a driver specific lock, shouldn't hurt anybody; it
  * just causes a minor performance degradation for setting the locks.
@@ -189,31 +257,46 @@
  * undef  : traditional save_flags; cli; restore_flags;
  */
 
-//#define DEBUG_SPINLOCKS 2	/* Set to 0, 1 or 2 in include/asm/spinlock.h */
+//#define DEBUG_SPINLOCKS 2	/* Set to 0, 1 or 2 in include/linux/spinlock.h */
 
-#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,30)
 # include <linux/init.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30)
+# include <linux/spinlock.h>
+#else
 # include <asm/spinlock.h>
 #endif
+#endif
 
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93) 
 # define USE_SPINLOCKS 1
 # define NEW_PCI 1
 #else
 # undef NEW_PCI
-# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,30)
 #  define USE_SPINLOCKS 2
 # endif
 #endif
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,99)
+static struct pci_device_id tmscsim_pci_tbl[] __initdata = {
+	{
+		vendor: PCI_VENDOR_ID_AMD,
+		device: PCI_DEVICE_ID_AMD53C974,
+		subvendor: PCI_ANY_ID,
+		subdevice: PCI_ANY_ID,
+	},
+	{ }		/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, tmscsim_pci_tbl);
+#endif
+	
 #ifdef USE_SPINLOCKS
 
 # if USE_SPINLOCKS == 3 /* both */
 
-#  if defined (__SMP__) || DEBUG_SPINLOCKS > 0
+#  if defined (CONFIG_SMP) || DEBUG_SPINLOCKS > 0
 #   define DC390_LOCKA_INIT { spinlock_t __unlocked = SPIN_LOCK_UNLOCKED; pACB->lock = __unlocked; };
 #  else
 #   define DC390_LOCKA_INIT
@@ -320,7 +403,11 @@
 # define PCI_PRESENT pci_present ()
 # define PCI_SET_MASTER pci_set_master (pdev)
 # define PCI_FIND_DEVICE(vend, id) (pdev = pci_find_device (vend, id, pdev))
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,10)
+# define PCI_GET_IO_AND_IRQ io_port = pci_resource_start (pdev, 0); irq = pdev->irq
+#else
 # define PCI_GET_IO_AND_IRQ io_port = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; irq = pdev->irq
+#endif
 #else
 # include <linux/bios32.h>
 # define PDEV pbus, pdevfn
@@ -369,7 +456,7 @@
 void dc390_Disconnect( PACB pACB );
 void dc390_Reselect( PACB pACB );
 void dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
-void dc390_DoingSRB_Done( PACB pACB );
+void dc390_DoingSRB_Done( PACB pACB, PSCSICMD cmd );
 static void dc390_ScsiRstDetect( PACB pACB );
 static void dc390_ResetSCSIBus( PACB pACB );
 static void __inline__ dc390_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
@@ -379,7 +466,7 @@
 void do_DC390_Interrupt( int, void *, struct pt_regs *);
 
 int    dc390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, UCHAR index );
-void   dc390_initDCB( PACB pACB, PDCB *ppDCB, PSCSICMD cmd );
+void   dc390_initDCB( PACB pACB, PDCB *ppDCB, UCHAR id, UCHAR lun);
 void   dc390_updateDCB (PACB pACB, PDCB pDCB);
 
 #ifdef MODULE
@@ -393,31 +480,19 @@
 //static PSH	dc390_pSH_current = NULL;
 static PACB	dc390_pACB_start= NULL;
 static PACB	dc390_pACB_current = NULL;
-static UCHAR	dc390_adapterCnt = 0;
-static UCHAR	dc390_CurrSyncOffset = 0;
 static ULONG	dc390_lastabortedpid = 0;
-static ULONG	dc390_laststatus = 0;
+static UINT	dc390_laststatus = 0;
+static UCHAR	dc390_adapterCnt = 0;
 
-#ifndef CONFIG_SCSI_DC390T_NOGENSUPP
 /* Startup values, to be overriden on the commandline */
-int tmscsim[] = {7, 1 /* 8MHz */, 
-		PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_
-		| SYNC_NEGO_ | TAG_QUEUEING_,
-		MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION
-		/* | NO_SEEK */
-# ifdef CONFIG_SCSI_MULTI_LUN
-		| LUN_CHECK
-# endif
-		, 3 /* 16 Tags per LUN */};
+int tmscsim[] = {-2, -2, -2, -2, -2, -2};
 
-# if defined(MODULE) && LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
-MODULE_PARM(tmscsim, "1-5i");
-MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1)");
+# if defined(MODULE) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,30)
+MODULE_PARM(tmscsim, "1-6i");
+MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)");
 # endif
 
-#endif /* CONFIG_SCSI_DC390T_NOGENSUPP */
-
-#if defined(MODULE) && LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+#if defined(MODULE) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,30)
 MODULE_AUTHOR("C.L. Huang / Kurt Garloff");
 MODULE_DESCRIPTION("SCSI host adapter driver for Tekram DC390 and other AMD53C974A based PCI SCSI adapters");
 MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
@@ -482,12 +557,14 @@
 static char*  dc390_adapname = "DC390";
 UCHAR  dc390_eepromBuf[MAX_ADAPTER_NUM][EE_LEN];
 UCHAR  dc390_clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+UCHAR  dc390_clock_speed[] = {100,80,67,57,50, 40, 31, 20};
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,30)
 struct proc_dir_entry	DC390_proc_scsi_tmscsim ={
        PROC_SCSI_DC390T, 7 ,"tmscsim",
        S_IFDIR | S_IRUGO | S_IXUGO, 2
        };
-
+#endif
 
 /***********************************************************************
  * Functions for access to DC390 EEPROM
@@ -511,59 +588,107 @@
     udelay(160);
 }
 
-#ifndef CONFIG_SCSI_DC390T_NOGENSUPP
-static void __init dc390_EEpromDefaults (UCHAR index)
+
+/* Override EEprom values with explicitly set values */
+static void __init dc390_EEprom_Override (UCHAR index)
 {
     PUCHAR ptr;
     UCHAR  id;
     ptr = (PUCHAR) dc390_eepromBuf[index];
     
     /* Adapter Settings */
-    ptr[EE_ADAPT_SCSI_ID] = (UCHAR)tmscsim[0]; /* Adapter ID */
-    ptr[EE_MODE2] = (UCHAR)tmscsim[3];
-    ptr[EE_DELAY] = 0; /* ?? */
-    ptr[EE_TAG_CMD_NUM] = (UCHAR)tmscsim[4]; /* Tagged Comds */
+    if (tmscsim[0] != -2)
+	ptr[EE_ADAPT_SCSI_ID] = (UCHAR)tmscsim[0];	/* Adapter ID */
+    if (tmscsim[3] != -2)
+	ptr[EE_MODE2] = (UCHAR)tmscsim[3];
+    if (tmscsim[5] != -2)
+	ptr[EE_DELAY] = tmscsim[5];			/* Reset delay */
+    if (tmscsim[4] != -2)
+	ptr[EE_TAG_CMD_NUM] = (UCHAR)tmscsim[4];	/* Tagged Cmds */
     
     /* Device Settings */
     for (id = 0; id < MAX_SCSI_ID; id++)
     {
-      ptr[id<<2] = (UCHAR)tmscsim[2]; /* EE_MODE1 */
-      ptr[(id<<2) + 1] = (UCHAR)tmscsim[1]; /* EE_Speed */
+	if (tmscsim[2] != -2)
+		ptr[id<<2] = (UCHAR)tmscsim[2];		/* EE_MODE1 */
+	if (tmscsim[1] != -2)
+		ptr[(id<<2) + 1] = (UCHAR)tmscsim[1];	/* EE_Speed */
     };
-    dc390_adapname = "AM53C974";
 }
 
-static void __init dc390_checkparams (void)
+/* Handle "-1" case */
+static void __init dc390_check_for_safe_settings (void)
 {
-	PARSEDEBUG(printk(KERN_INFO "DC390: setup %08x %08x %08x %08x %08x\n", tmscsim[0],\
-		      tmscsim[1], tmscsim[2], tmscsim[3], tmscsim[4]);)
-	if (tmscsim[0] < 0 || tmscsim[0] > 7) /* modules-2.0.0 passes -1 as string */
+	if (tmscsim[0] == -1 || tmscsim[0] > 15) /* modules-2.0.0 passes -1 as string */
 	{
-		tmscsim[0] = 7; tmscsim[1] = 4; 
-		tmscsim[2] = 9; tmscsim[3] = 15;
-		tmscsim[4] = 2;
+		tmscsim[0] = 7; tmscsim[1] = 4;
+		tmscsim[2] = 0x09; tmscsim[3] = 0x0f;
+		tmscsim[4] = 2; tmscsim[5] = 10;
 		printk (KERN_INFO "DC390: Using safe settings.\n");
 	}
-	else
+}
+
+
+#ifndef CONFIG_SCSI_DC390T_NOGENSUPP
+int __initdata tmscsim_def[] = {7, 0 /* 10MHz */,
+		PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_
+		| SYNC_NEGO_ | TAG_QUEUEING_,
+		MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION
+		/* | NO_SEEK */
+# ifdef CONFIG_SCSI_MULTI_LUN
+		| LUN_CHECK
+# endif
+		, 3 /* 16 Tags per LUN */, 1 /* s delay after Reset */ };
+
+/* Copy defaults over set values where missing */
+static void __init dc390_fill_with_defaults (void)
+{
+	int i;
+	PARSEDEBUG(printk(KERN_INFO "DC390: setup %08x %08x %08x %08x %08x %08x\n", tmscsim[0],\
+		      tmscsim[1], tmscsim[2], tmscsim[3], tmscsim[4], tmscsim[5]);)
+	for (i = 0; i < 6; i++)
 	{
-		/* if (tmscsim[0] < 0 || tmscsim[0] > 7) tmscsim[0] = 7; */
-		if (tmscsim[1] < 0 || tmscsim[1] > 7) tmscsim[1] = 4;
-		if (tmscsim[4] < 0 || tmscsim[4] > 5) tmscsim[4] = 4;
-	};
+		if (tmscsim[i] < 0 || tmscsim[i] > 255)
+			tmscsim[i] = tmscsim_def[i];
+	}
+	/* Sanity checks */
+	if (tmscsim[0] >   7) tmscsim[0] =   7;
+	if (tmscsim[1] >   7) tmscsim[1] =   4;
+	if (tmscsim[4] >   5) tmscsim[4] =   4;
+	if (tmscsim[5] > 180) tmscsim[5] = 180;
 };
+#endif
+
 /* Override defaults on cmdline:
  * tmscsim: AdaptID, MaxSpeed (Index), DevMode (Bitmapped), AdaptMode (Bitmapped)
  */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,13)
+void __init dc390_setup (char *str)
+{	
+	int ints[8];
+	int i, im;
+	(void)get_options (str, ARRAY_SIZE(ints), ints);
+#else
 void __init dc390_setup (char *str, int *ints)
 {
-	int i;
-	for (i = 0; i < ints[0]; i++)
-		tmscsim[i] = ints[i+1];
-	if (ints[0] > 5)
+	int i, im;
+#endif
+	im = ints[0];
+	if (im > 6)
+	{
 		printk (KERN_NOTICE "DC390: ignore extra params!\n");
+		im = 6;
+	};
+	for (i = 0; i < im; i++)
+		tmscsim[i] = ints[i+1];
 	/* dc390_checkparams (); */
 };
-#endif /* CONFIG_SCSI_DC390T_NOGENSUPP */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)
+#ifndef MODULE
+__setup("tmscsim=", dc390_setup);
+#endif
+#endif
 
 
 static void __init dc390_EEpromOutDI( PDEVDECL, PUCHAR regval, UCHAR Carry )
@@ -652,6 +777,12 @@
 }
 
 
+static void __init dc390_interpret_delay (UCHAR index)
+{
+    char interpd [] = {1,3,5,10,16,30,60,120};
+    dc390_eepromBuf[index][EE_DELAY] = interpd [dc390_eepromBuf[index][EE_DELAY]];
+};
+
 static UCHAR __init dc390_CheckEEpromCheckSum( PDEVDECL, UCHAR index )
 {
     UCHAR  i;
@@ -662,6 +793,8 @@
     memcpy (dc390_eepromBuf[index], EEbuf, EE_ADAPT_SCSI_ID);
     memcpy (&dc390_eepromBuf[index][EE_ADAPT_SCSI_ID], 
 	    &EEbuf[REAL_EE_ADAPT_SCSI_ID], EE_LEN - EE_ADAPT_SCSI_ID);
+    dc390_interpret_delay (index);
+    
     wval = 0;
     for(i=0; i<0x40; i++, ptr++)
 	wval += *ptr;
@@ -674,65 +807,94 @@
  * (DCBs, SRBs, Queueing)
  *
  **********************************************************************/
-static PDCB __inline__ dc390_findDCB ( PACB pACB, Scsi_Cmnd *cmd)
+static PDCB __inline__ dc390_findDCB ( PACB pACB, UCHAR id, UCHAR lun)
 {
    PDCB pDCB = pACB->pLinkDCB; if (!pDCB) return 0;
-   while (pDCB->UnitSCSIID != cmd->target || pDCB->UnitSCSILUN != cmd->lun)
+   while (pDCB->TargetID != id || pDCB->TargetLUN != lun)
      {
 	pDCB = pDCB->pNextDCB;
 	if (pDCB == pACB->pLinkDCB)
 	  {
-	     printk (KERN_WARNING "DC390: DCB not found (DCB=%08x, DCBmap[%2x]=%2x)\n",
-		     (int)pDCB, cmd->target, pACB->DCBmap[cmd->target]);
+	     DCBDEBUG(printk (KERN_WARNING "DC390: DCB not found (DCB=%p, DCBmap[%2x]=%2x)\n",
+		     pDCB, id, pACB->DCBmap[id]);)
 	     return 0;
 	  }
      };
-   DCBDEBUG1( printk (KERN_DEBUG "DCB %08x (%02x,%02x) found.\n",	\
-	   (int)pDCB, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);)
+   DCBDEBUG1( printk (KERN_DEBUG "DCB %p (%02x,%02x) found.\n",	\
+		      pDCB, pDCB->TargetID, pDCB->TargetLUN);)
    return pDCB;
 };
 
-static void dc390_QLinkcmd( PSCSICMD cmd, PDCB pDCB )
-{
-    PSCSICMD  pcmd;
+/* Queueing philosphy:
+ * There are a couple of lists:
+ * - Query: Contains the Scsi Commands not yet turned into SRBs (per ACB)
+ *   (Note: For new EH, it is unecessary!)
+ * - Waiting: Contains a list of SRBs not yet sent (per DCB)
+ * - Free: List of free SRB slots
+ * 
+ * If there are no waiting commands for the DCB, the new one is sent to the bus
+ * otherwise the oldest one is taken from the Waiting list and the new one is 
+ * queued to the Waiting List
+ * 
+ * Lists are managed using two pointers and eventually a counter
+ */
+
 
-    if( !pDCB->QIORBCnt )
+#if 0
+/* Look for a SCSI cmd in a SRB queue */
+static PSRB dc390_find_cmd_in_SRBq (PSCSICMD cmd, PSRB queue)
+{
+    PSRB q = queue;
+    while (q)
     {
-	pDCB->pQIORBhead = cmd;
-	pDCB->pQIORBtail = cmd;
-	pDCB->QIORBCnt++;
-	cmd->next = NULL;
+	if (q->pcmd == cmd) return q;
+	q = q->pNextSRB;
+	if (q == queue) return 0;
     }
+    return q;
+};
+#endif
+    
+
+/* Append to Query List */
+static void dc390_Query_append( PSCSICMD cmd, PACB pACB )
+{
+    DEBUG0(printk ("DC390: Append cmd %li to Query\n", cmd->pid);)
+    if( !pACB->QueryCnt )
+	pACB->pQueryHead = cmd;
     else
-    {
-	pcmd = pDCB->pQIORBtail;
-	pcmd->next = cmd;
-	pDCB->pQIORBtail = cmd;
-	pDCB->QIORBCnt++;
-	cmd->next = NULL;
-    }
+	pACB->pQueryTail->next = cmd;
 
+    pACB->pQueryTail = cmd;
+    pACB->QueryCnt++;
+    pACB->CmdOutOfSRB++;
+    cmd->next = NULL;
 }
 
 
-static __inline__ PSCSICMD dc390_Getcmd( PDCB pDCB )
+/* Return next cmd from Query list */
+static PSCSICMD dc390_Query_get ( PACB pACB )
 {
     PSCSICMD  pcmd;
 
-    pcmd = pDCB->pQIORBhead;
-    pDCB->pQIORBhead = pcmd->next;
+    pcmd = pACB->pQueryHead;
+    if (!pcmd) return pcmd;
+    DEBUG0(printk ("DC390: Get cmd %li from Query\n", pcmd->pid);)
+    pACB->pQueryHead = pcmd->next;
     pcmd->next = NULL;
-    pDCB->QIORBCnt--;
-
+    if (!pACB->pQueryHead) pACB->pQueryTail = NULL;
+    pACB->QueryCnt--;
     return( pcmd );
 }
 
 
-static __inline__ PSRB dc390_GetSRB( PACB pACB )
+/* Return next free SRB */
+static __inline__ PSRB dc390_Free_get ( PACB pACB )
 {
     PSRB   pSRB;
 
     pSRB = pACB->pFreeSRB;
+    DEBUG0(printk ("DC390: Get Free SRB %p\n", pSRB);)
     if( pSRB )
     {
 	pACB->pFreeSRB = pSRB->pNextSRB;
@@ -742,130 +904,174 @@
     return( pSRB );
 }
 
-
-static __inline__ void dc390_RewaitSRB0( PDCB pDCB, PSRB pSRB )
+/* Insert SRB oin top of free list */
+static __inline__ void dc390_Free_insert (PACB pACB, PSRB pSRB)
 {
-    PSRB   psrb1;
+    DEBUG0(printk ("DC390: Free SRB %p\n", pSRB);)
+    pSRB->pNextSRB = pACB->pFreeSRB;
+    pACB->pFreeSRB = pSRB;
+}
 
-    if( (psrb1 = pDCB->pWaitingSRB) )
-    {
-	pSRB->pNextSRB = psrb1;
-    }
-    else
-    {
-	pSRB->pNextSRB = NULL;
+
+/* Inserts a SRB to the top of the Waiting list */
+static __inline__ void dc390_Waiting_insert ( PDCB pDCB, PSRB pSRB )
+{
+    DEBUG0(printk ("DC390: Insert pSRB %p cmd %li to Waiting\n", pSRB, pSRB->pcmd->pid);)
+    pSRB->pNextSRB = pDCB->pWaitingSRB;
+    if (!pDCB->pWaitingSRB)
 	pDCB->pWaitLast = pSRB;
-    }
     pDCB->pWaitingSRB = pSRB;
+    pDCB->WaitSRBCnt++;
 }
 
 
-static void dc390_RewaitSRB( PDCB pDCB, PSRB pSRB )
+/* Queue SRB to waiting list */
+static __inline__ void dc390_Waiting_append ( PDCB pDCB, PSRB pSRB)
 {
-    PSRB   psrb1;
-    UCHAR  bval;
-
-    pDCB->GoingSRBCnt--; pDCB->pDCBACB->SelLost++;
-    DEBUG0(printk(KERN_INFO "DC390: RewaitSRB (%p, %p) pid = %li\n", pDCB, pSRB, pSRB->pcmd->pid);)
-    psrb1 = pDCB->pGoingSRB;
-    if( pSRB == psrb1 )
-    {
-	pDCB->pGoingSRB = psrb1->pNextSRB;
-    }
+    DEBUG0(printk ("DC390: Append pSRB %p cmd %li to Waiting\n", pSRB, pSRB->pcmd->pid);)
+    if( pDCB->pWaitingSRB )
+	pDCB->pWaitLast->pNextSRB = pSRB;
     else
-    {
-	while( pSRB != psrb1->pNextSRB )
-	    psrb1 = psrb1->pNextSRB;
-	psrb1->pNextSRB = pSRB->pNextSRB;
-	if( pSRB == pDCB->pGoingLast )
-	    pDCB->pGoingLast = psrb1;
-    }
-    if( (psrb1 = pDCB->pWaitingSRB) )
-    {
-	pSRB->pNextSRB = psrb1;
 	pDCB->pWaitingSRB = pSRB;
-    }
+
+    pDCB->pWaitLast = pSRB;
+    pSRB->pNextSRB = NULL;
+    pDCB->WaitSRBCnt++;
+    pDCB->pDCBACB->CmdInQ++;
+}
+
+static __inline__ void dc390_Going_append (PDCB pDCB, PSRB pSRB)
+{
+    pDCB->GoingSRBCnt++;
+    DEBUG0(printk("DC390: Append SRB %p to Going\n", pSRB);)
+    /* Append to the list of Going commands */
+    if( pDCB->pGoingSRB )
+	pDCB->pGoingLast->pNextSRB = pSRB;
     else
-    {
-	pSRB->pNextSRB = NULL;
-	pDCB->pWaitingSRB = pSRB;
-	pDCB->pWaitLast = pSRB;
-    }
+	pDCB->pGoingSRB = pSRB;
+
+    pDCB->pGoingLast = pSRB;
+    /* No next one in sent list */
+    pSRB->pNextSRB = NULL;
+};
+
+static __inline__ void dc390_Going_remove (PDCB pDCB, PSRB pSRB)
+{
+   DEBUG0(printk("DC390: Remove SRB %p from Going\n", pSRB);)
+   if (pSRB == pDCB->pGoingSRB)
+	pDCB->pGoingSRB = pSRB->pNextSRB;
+   else
+     {
+	PSRB psrb = pDCB->pGoingSRB;
+	while (psrb && psrb->pNextSRB != pSRB)
+	  psrb = psrb->pNextSRB;
+	if (!psrb) 
+	  { printk (KERN_ERR "DC390: Remove non-ex. SRB %p from Going!\n", pSRB); return; }
+	psrb->pNextSRB = pSRB->pNextSRB;
+	if (pSRB == pDCB->pGoingLast)
+	  pDCB->pGoingLast = psrb;
+     }
+   pDCB->GoingSRBCnt--;
+};
+
+/* Moves SRB from Going list to the top of Waiting list */
+static void dc390_Going_to_Waiting ( PDCB pDCB, PSRB pSRB )
+{
+    DEBUG0(printk(KERN_INFO "DC390: Going_to_Waiting (SRB %p) pid = %li\n", pSRB, pSRB->pcmd->pid);)
+    /* Remove SRB from Going */
+    dc390_Going_remove (pDCB, pSRB);
+    /* Insert on top of Waiting */
+    dc390_Waiting_insert (pDCB, pSRB);
+    /* Tag Mask must be freed elsewhere ! (KG, 99/06/18) */
+}
+
+/* Moves first SRB from Waiting list to Going list */
+static __inline__ void dc390_Waiting_to_Going ( PDCB pDCB, PSRB pSRB )
+{	
+	/* Remove from waiting list */
+	DEBUG0(printk("DC390: Remove SRB %p from head of Waiting\n", pSRB);)
+	pDCB->pWaitingSRB = pSRB->pNextSRB;
+	if( !pDCB->pWaitingSRB ) pDCB->pWaitLast = NULL;
+	pDCB->WaitSRBCnt--;
+	dc390_Going_append (pDCB, pSRB);
+}
+
+/* 2.0 timer compatibility */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,30)
+ static inline int timer_pending(struct timer_list * timer)
+ {
+	return timer->prev != NULL;
+ }
+ #define time_after(a,b)         ((long)(b) - (long)(a) < 0)
+ #define time_before(a,b)        time_after(b,a)
+#endif
 
-    bval = pSRB->TagNumber;
-    pDCB->TagMask &= (~(1 << bval));	  /* Free TAG number */
+void DC390_waiting_timed_out (unsigned long ptr);
+/* Sets the timer to wake us up */
+static void dc390_waiting_timer (PACB pACB, unsigned long to)
+{
+	if (timer_pending (&pACB->Waiting_Timer)) return;
+	init_timer (&pACB->Waiting_Timer);
+	pACB->Waiting_Timer.function = DC390_waiting_timed_out;
+	pACB->Waiting_Timer.data = (unsigned long)pACB;
+	if (time_before (jiffies + to, pACB->pScsiHost->last_reset))
+		pACB->Waiting_Timer.expires = pACB->pScsiHost->last_reset + 1;
+	else
+		pACB->Waiting_Timer.expires = jiffies + to + 1;
+	add_timer (&pACB->Waiting_Timer);
 }
 
 
-static void dc390_DoWaitingSRB( PACB pACB )
+/* Send the next command from the waiting list to the bus */
+static void dc390_Waiting_process ( PACB pACB )
 {
     PDCB   ptr, ptr1;
     PSRB   pSRB;
 
-    if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
-    {
-	ptr = pACB->pDCBRunRobin;
-	if( !ptr )
-	{
-	    ptr = pACB->pLinkDCB;
-	    pACB->pDCBRunRobin = ptr;
-	}
-	ptr1 = ptr;
-	for( ;ptr1; )
-	{
-	    pACB->pDCBRunRobin = ptr1->pNextDCB;
-	    if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) ||
-		!( pSRB = ptr1->pWaitingSRB ) )
-	    {
-		if(pACB->pDCBRunRobin == ptr)
-		    break;
-		ptr1 = ptr1->pNextDCB;
-	    }
+    if( (pACB->pActiveDCB) || (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
+	return;
+    if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer);
+    ptr = pACB->pDCBRunRobin;
+    if( !ptr )
+      {
+	ptr = pACB->pLinkDCB;
+	pACB->pDCBRunRobin = ptr;
+      }
+    ptr1 = ptr;
+    if (!ptr1) return;
+    do 
+      {
+	pACB->pDCBRunRobin = ptr1->pNextDCB;
+	if( !( pSRB = ptr1->pWaitingSRB ) ||
+	    ( ptr1->MaxCommand <= ptr1->GoingSRBCnt ))
+	  ptr1 = ptr1->pNextDCB;
+	else
+	  {
+	    /* Try to send to the bus */
+	    if( !dc390_StartSCSI(pACB, ptr1, pSRB) )
+	      dc390_Waiting_to_Going (ptr1, pSRB);
 	    else
-	    {
-		if( !dc390_StartSCSI(pACB, ptr1, pSRB) )
-		{
-		    ptr1->GoingSRBCnt++;
-		    if( ptr1->pWaitLast == pSRB )
-		    {
-			ptr1->pWaitingSRB = NULL;
-			ptr1->pWaitLast = NULL;
-		    }
-		    else
-		    {
-			ptr1->pWaitingSRB = pSRB->pNextSRB;
-		    }
-		    pSRB->pNextSRB = NULL;
-
-		    if( ptr1->pGoingSRB )
-			ptr1->pGoingLast->pNextSRB = pSRB;
-		    else
-			ptr1->pGoingSRB = pSRB;
-		    ptr1->pGoingLast = pSRB;
-		}
-		break;
-	    }
-	}
-    }
+	      dc390_waiting_timer (pACB, HZ/5);
+	    break;
+	  }
+      } while (ptr1 != ptr);
     return;
 }
 
-
-static __inline__ void dc390_SRBwaiting( PDCB pDCB, PSRB pSRB)
+/* Wake up waiting queue */
+void DC390_waiting_timed_out (unsigned long ptr)
 {
-    if( pDCB->pWaitingSRB )
-    {
-	pDCB->pWaitLast->pNextSRB = pSRB;
-	pSRB->pNextSRB = NULL;
-    }
-    else
-    {
-	pDCB->pWaitingSRB = pSRB;
-    }
-    pDCB->pWaitLast = pSRB;
+	PACB pACB = (PACB)ptr;
+	DC390_IFLAGS
+	DC390_AFLAGS
+	DEBUG0(printk ("DC390: Debug: Waiting queue woken up by timer!\n");)
+	DC390_LOCK_IO;
+	DC390_LOCK_ACB;
+	dc390_Waiting_process (pACB);
+	DC390_UNLOCK_ACB;
+	DC390_UNLOCK_IO;
 }
 
-
 /***********************************************************************
  * Function: static void dc390_SendSRB (PACB pACB, PSRB pSRB)
  *
@@ -878,41 +1084,33 @@
     PDCB   pDCB;
 
     pDCB = pSRB->pSRBDCB;
-    if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
+    if( (pDCB->MaxCommand <= pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
 	(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) )
     {
-	dc390_SRBwaiting(pDCB, pSRB);
-	goto SND_EXIT;
+	dc390_Waiting_append (pDCB, pSRB);
+	dc390_Waiting_process (pACB);
+	return;
     }
 
+#if 0
     if( pDCB->pWaitingSRB )
     {
-	dc390_SRBwaiting(pDCB, pSRB);
+	dc390_Waiting_append (pDCB, pSRB);
 /*	pSRB = GetWaitingSRB(pDCB); */	/* non-existent */
 	pSRB = pDCB->pWaitingSRB;
+	/* Remove from waiting list */
 	pDCB->pWaitingSRB = pSRB->pNextSRB;
 	pSRB->pNextSRB = NULL;
+	if (!pDCB->pWaitingSRB) pDCB->pWaitLast = NULL;
     }
-
-    if( !dc390_StartSCSI(pACB, pDCB, pSRB) )
-    {
-	pDCB->GoingSRBCnt++;
-	if( pDCB->pGoingSRB )
-	{
-	    pDCB->pGoingLast->pNextSRB = pSRB;
-	    pDCB->pGoingLast = pSRB;
-	}
-	else
-	{
-	    pDCB->pGoingSRB = pSRB;
-	    pDCB->pGoingLast = pSRB;
-	}
-    }
-    else
-	dc390_RewaitSRB0( pDCB, pSRB );
-
-SND_EXIT:
-    return;
+#endif
+	
+    if (!dc390_StartSCSI(pACB, pDCB, pSRB))
+	dc390_Going_append (pDCB, pSRB);
+    else {
+	dc390_Waiting_insert (pDCB, pSRB);
+	dc390_waiting_timer (pACB, HZ/5);
+    };
 }
 
 /***********************************************************************
@@ -927,8 +1125,8 @@
 {
     pSRB->pSRBDCB = pDCB;
     pSRB->pcmd = pcmd;
-    pSRB->ScsiCmdLen = pcmd->cmd_len;
-    memcpy (pSRB->CmdBlock, pcmd->cmnd, pcmd->cmd_len);
+    //pSRB->ScsiCmdLen = pcmd->cmd_len;
+    //memcpy (pSRB->CmdBlock, pcmd->cmnd, pcmd->cmd_len);
     
     if( pcmd->use_sg )
     {
@@ -961,8 +1159,39 @@
     pSRB->SGToBeXferLen = 0;
     pSRB->ScsiPhase = 0;
     pSRB->EndMessage = 0;
+    pSRB->TagNumber = 255;
 };
 
+/* Put cmnd from Query to Waiting list and send next Waiting cmnd */
+static void dc390_Query_to_Waiting (PACB pACB)
+{
+    Scsi_Cmnd *pcmd;
+    PSRB   pSRB;
+    PDCB   pDCB;
+
+    if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
+	return;
+
+    while (pACB->QueryCnt)
+    {
+	pSRB = dc390_Free_get ( pACB );
+	if (!pSRB) return;
+	pcmd = dc390_Query_get ( pACB );
+	if (!pcmd) { dc390_Free_insert (pACB, pSRB); return; }; /* should not happen */
+	pDCB = dc390_findDCB (pACB, pcmd->target, pcmd->lun);
+	if (!pDCB) 
+	{ 
+		dc390_Free_insert (pACB, pSRB);
+		printk (KERN_ERR "DC390: Command in queue to non-existing device!\n");
+		pcmd->result = MK_RES(DRIVER_ERROR,DID_ERROR,0,0);
+		DC390_UNLOCK_ACB_NI;
+		pcmd->done (pcmd);
+		DC390_LOCK_ACB_NI;
+	};
+	dc390_BuildSRB (pcmd, pDCB, pSRB);
+	dc390_Waiting_append ( pDCB, pSRB );
+    }
+}
 
 /***********************************************************************
  * Function : static int DC390_queue_command (Scsi_Cmnd *cmd,
@@ -985,7 +1214,6 @@
 
 int DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 {
-    Scsi_Cmnd *pcmd;
     PDCB   pDCB;
     PSRB   pSRB;
     DC390_AFLAGS
@@ -993,7 +1221,7 @@
 
 
     DEBUG0(/*  if(pACB->scan_devices) */	\
-	printk(KERN_INFO "DC390: Queue Cmd=%02x,ID=%d,LUN=%d (pid=%li)\n",\
+	printk(KERN_INFO "DC390: Queue Cmd=%02x,Tgt=%d,LUN=%d (pid=%li)\n",\
 		cmd->cmnd[0],cmd->target,cmd->lun,cmd->pid);)
 
     DC390_LOCK_ACB;
@@ -1014,25 +1242,32 @@
     if ( ( cmd->target >= pACB->pScsiHost->max_id ) || 
 	 (cmd->lun >= pACB->pScsiHost->max_lun) )
     {
-/*	printk("DC390: Ignore target %d lun %d\n",
+/*	printk ("DC390: Ignore target %d lun %d\n",
 		cmd->target, cmd->lun); */
 	DC390_UNLOCK_ACB;
-	done(cmd);
-	return( 0 );
+	//return (1);
+	done (cmd);
+	return (0);
     }
 
-    if( (pACB->scan_devices || cmd->cmnd[0] == TEST_UNIT_READY) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+    if( (pACB->scan_devices || cmd->cmnd[0] == TEST_UNIT_READY || cmd->cmnd[0] == INQUIRY) && 
+       !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
     {
-	pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
         pACB->scan_devices = 1;
 
-	dc390_initDCB( pACB, &pDCB, cmd );
+	dc390_initDCB( pACB, &pDCB, cmd->target, cmd->lun );
 	if (!pDCB)
 	  {
-	    printk (KERN_ERR "DC390: kmalloc for DCB failed, ID=%2x\n", cmd->target);
+	    printk (KERN_ERR "DC390: kmalloc for DCB failed, target %02x lun %02x\n", 
+		    cmd->target, cmd->lun);
 	    DC390_UNLOCK_ACB;
-	    done(cmd);
-	    return(0);
+	    printk ("DC390: No DCB in queue_command!\n");
+#ifdef USE_NEW_EH
+	    return (1);
+#else
+	    done (cmd);
+	    return (0);
+#endif
 	  };
             
     }
@@ -1041,71 +1276,73 @@
 	printk(KERN_INFO "DC390: Ignore target %02x lun %02x\n",
 		cmd->target, cmd->lun); 
 	DC390_UNLOCK_ACB;
-	done(cmd);
-	return(0);
+	//return (1);
+	done (cmd);
+	return (0);
     }
     else
     {
-	pDCB = dc390_findDCB (pACB, cmd);
+	pDCB = dc390_findDCB (pACB, cmd->target, cmd->lun);
 	if (!pDCB)
 	 {  /* should never happen */
+	    printk (KERN_ERR "DC390: no DCB failed, target %02x lun %02x\n", 
+		    cmd->target, cmd->lun);
 	    DC390_UNLOCK_ACB;
-	    done(cmd);
-	    return(0);
+	    printk ("DC390: No DCB in queuecommand (2)!\n");
+#ifdef USE_NEW_EH
+	    return (1);
+#else
+	    done (cmd);
+	    return (0);
+#endif
 	 };
     }
 
     pACB->Cmds++;
     cmd->scsi_done = done;
     cmd->result = 0;
+	
+    dc390_Query_to_Waiting (pACB);
 
-    if( pDCB->QIORBCnt ) /* Unsent commands ? */
+    if( pACB->QueryCnt ) /* Unsent commands ? */
     {
-	dc390_QLinkcmd( cmd, pDCB );
-	pcmd = dc390_Getcmd( pDCB ); /* Get first command */
-        pACB->CmdInQ++;
+	DEBUG0(printk ("DC390: QueryCnt != 0\n");)
+	dc390_Query_append ( cmd, pACB );
+	dc390_Waiting_process (pACB);
     }
-    else
-	pcmd = cmd;
-
-    pSRB = dc390_GetSRB( pACB );
-
-    if( !pSRB )
+    else if (pDCB->pWaitingSRB)
     {
-        dc390_QLinkcmd( pcmd, pDCB ); /* Queue command at the end */
-	pACB->CmdOutOfSRB++;
-	DC390_UNLOCK_ACB;
-	return(0);
+ 	pSRB = dc390_Free_get ( pACB );
+	DEBUG0(if (!pSRB) printk ("DC390: No free SRB but Waiting\n"); else printk ("DC390: Free SRB w/ Waiting\n");)
+	if (!pSRB) dc390_Query_append (cmd, pACB);
+	else 
+	  {
+	    dc390_BuildSRB (cmd, pDCB, pSRB);
+	    dc390_Waiting_append (pDCB, pSRB);
+	  }
+	dc390_Waiting_process (pACB);
     }
-
-    dc390_BuildSRB (pcmd, pDCB, pSRB);
-    dc390_SendSRB( pACB, pSRB );
+    else
+    {
+ 	pSRB = dc390_Free_get ( pACB );
+	DEBUG0(if (!pSRB) printk ("DC390: No free SRB w/o Waiting\n"); else printk ("DC390: Free SRB w/o Waiting\n");)
+	if (!pSRB)
+	{
+	    dc390_Query_append (cmd, pACB);
+	    dc390_Waiting_process (pACB);
+	}
+	else 
+	{
+	    dc390_BuildSRB (cmd, pDCB, pSRB);
+	    dc390_SendSRB (pACB, pSRB);
+	};
+    };
 
     DC390_UNLOCK_ACB;
-    DEBUG1(printk (KERN_DEBUG " ... command (%02x) queued successfully.\n", pcmd->cmnd[0]);)
+    DEBUG1(printk (KERN_DEBUG " ... command (pid %li) queued successfully.\n", cmd->pid);)
     return(0);
 }
 
-
-static void dc390_DoNextCmd( PACB pACB, PDCB pDCB )
-{
-    Scsi_Cmnd *pcmd;
-    PSRB   pSRB;
-
-    if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
-	return;
-
-    pcmd = dc390_Getcmd( pDCB );
-    pSRB = dc390_GetSRB( pACB );
-    if( !pSRB )
-	dc390_QLinkcmd( pcmd, pDCB );
-    else
-      {
-	dc390_BuildSRB (pcmd, pDCB, pSRB);
-	dc390_SendSRB( pACB, pSRB );
-      };
-}
-
 /* We ignore mapping problems, as we expect everybody to respect 
  * valid partition tables. Waiting for complaints ;-) */
 
@@ -1242,15 +1479,17 @@
 void dc390_dumpinfo (PACB pACB, PDCB pDCB, PSRB pSRB)
 {
     USHORT pstat; PDEVDECL1;
+    if (!pDCB) pDCB = pACB->pActiveDCB;
+    if (!pSRB && pDCB) pSRB = pDCB->pActiveSRB;
 
     if (pSRB) 
     {
-	printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08lx, Phase %02x\n",
+	printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08x, Phase %02x\n",
 		pSRB->TotalXferredLen, pSRB->SGToBeXferLen, pSRB->SRBState,
 		pSRB->ScsiPhase);
 	printk ("DC390: AdpaterStatus: %02x, SRB Status %02x\n", pSRB->AdaptStatus, pSRB->SRBStatus);
     };
-    printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08lx\n", dc390_laststatus);
+    printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08x\n", dc390_laststatus);
     printk ("DC390: Register dump: SCSI block:\n");
     printk ("DC390: XferCnt  Cmd Stat IntS IRQS FFIS Ctl1 Ctl2 Ctl3 Ctl4\n");
     printk ("DC390:  %06x   %02x   %02x   %02x",
@@ -1260,6 +1499,12 @@
 	    DC390_read8(INT_Status), DC390_read8(Current_Fifo), DC390_read8(CtrlReg1),
 	    DC390_read8(CtrlReg2), DC390_read8(CtrlReg3), DC390_read8(CtrlReg4));
     DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
+    if (DC390_read8(Current_Fifo) & 0x1f)
+      {
+	printk ("DC390: FIFO:");
+	while (DC390_read8(Current_Fifo) & 0x1f) printk (" %02x", DC390_read8(ScsiFifo));
+	printk ("\n");
+      };
     printk ("DC390: Register dump: DMA engine:\n");
     printk ("DC390: Cmd   STrCnt    SBusA    WrkBC    WrkAC Stat SBusCtrl\n");
     printk ("DC390:  %02x %08x %08x %08x %08x   %02x %08x\n",
@@ -1289,44 +1534,40 @@
 {
     PDCB  pDCB;
     PSRB  pSRB, psrb;
-    ULONG count, i;
-    PSCSICMD  pcmd, pcmd1;
+    UINT  count, i;
+    PSCSICMD  pcmd;
     int   status;
-    ULONG sbac;
+    //ULONG sbac;
     DC390_AFLAGS
     PACB  pACB = (PACB) cmd->host->hostdata;
 
     DC390_LOCK_ACB;
 
-    pDCB = dc390_findDCB (pACB, cmd);
-    /* abort() is too buggy at the moment. If it's called we are in trouble anyway.
-     * so let's dump some info into the syslog at least. (KG, 98/08/20) */
-    if (pDCB) pSRB = pDCB->pActiveSRB; else pSRB = 0;
-    printk ("DC390: Abort command (pid %li, DCB %p, SRB %p)\n",
-	    cmd->pid, pDCB, pSRB);
-    dc390_dumpinfo (pACB, pDCB, pSRB);
-
-    if( !pDCB ) goto  NOT_RUN;
+    printk ("DC390: Abort command (pid %li, Device %02i-%02i)\n",
+	    cmd->pid, cmd->target, cmd->lun);
 
-    if( pDCB->QIORBCnt )
+    /* First scan Query list */
+    if( pACB->QueryCnt )
     {
-	pcmd = pDCB->pQIORBhead;
+	pcmd = pACB->pQueryHead;
 	if( pcmd == cmd )
 	{
-	    pDCB->pQIORBhead = pcmd->next;
+	    /* Found: Dequeue */
+	    pACB->pQueryHead = pcmd->next;
 	    pcmd->next = NULL;
-	    pDCB->QIORBCnt--;
+	    if (cmd == pACB->pQueryTail) pACB->pQueryTail = NULL;
+	    pACB->QueryCnt--;
 	    status = SCSI_ABORT_SUCCESS;
 	    goto  ABO_X;
 	}
-	for( count = pDCB->QIORBCnt, i=0; i<count-1; i++)
+	for( count = pACB->QueryCnt, i=0; i<count-1; i++)
 	{
 	    if( pcmd->next == cmd )
 	    {
-		pcmd1 = pcmd->next;
-		pcmd->next = pcmd1->next;
-		pcmd1->next = NULL;
-		pDCB->QIORBCnt--;
+		pcmd->next = cmd->next;
+		cmd->next = NULL;
+		if (cmd == pACB->pQueryTail) pACB->pQueryTail = NULL;
+		pACB->QueryCnt--;
 		status = SCSI_ABORT_SUCCESS;
 		goto  ABO_X;
 	    }
@@ -1337,14 +1578,21 @@
 	}
     }
 	
+    pDCB = dc390_findDCB (pACB, cmd->target, cmd->lun);
+    if( !pDCB ) goto  NOT_RUN;
+
     /* Added 98/07/02 KG */
+    /*
     pSRB = pDCB->pActiveSRB;
     if (pSRB && pSRB->pcmd == cmd )
 	goto ON_GOING;
-	
+     */
+    
     pSRB = pDCB->pWaitingSRB;
     if( !pSRB )
 	goto  ON_GOING;
+
+    /* Now scan Waiting queue */
     if( pSRB->pcmd == cmd )
     {
 	pDCB->pWaitingSRB = pSRB->pNextSRB;
@@ -1364,16 +1612,21 @@
 	pSRB = psrb->pNextSRB;
 	psrb->pNextSRB = pSRB->pNextSRB;
 	if( pSRB == pDCB->pWaitLast )
-	    pDCB->pWaitLast = psrb; /* No check for psrb == NULL ? */
+	    pDCB->pWaitLast = psrb;
 IN_WAIT:
-	pSRB->pNextSRB = pACB->pFreeSRB;
-	pACB->pFreeSRB = pSRB;
+	dc390_Free_insert (pACB, pSRB);
+	pDCB->WaitSRBCnt--;
 	cmd->next = NULL;
 	status = SCSI_ABORT_SUCCESS;
 	goto  ABO_X;
     }
 
+    /* SRB has already been sent ! */
 ON_GOING:
+    /* abort() is too stupid for already sent commands at the moment. 
+     * If it's called we are in trouble anyway, so let's dump some info 
+     * into the syslog at least. (KG, 98/08/20,99/06/20) */
+    dc390_dumpinfo (pACB, pDCB, pSRB);
     pSRB = pDCB->pGoingSRB;
     pDCB->DCBFlag |= ABORT_DEV_;
     /* Now for the hard part: The command is currently processed */
@@ -1404,12 +1657,13 @@
 ABO_X:
     cmd->result = DID_ABORT << 16;
     printk(KERN_INFO "DC390: Aborted pid %li with status %i\n", cmd->pid, status);
+#if 0
     if (cmd->pid == dc390_lastabortedpid) /* repeated failure ? */
 	{
 		/* Let's do something to help the bus getting clean again */
 		DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
 		DC390_write8 (ScsiCmd, DMA_COMMAND);
-			//DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+		//DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
 		//DC390_write8 (ScsiCmd, RESET_ATN_CMD);
 		DC390_write8 (ScsiCmd, NOP_CMD);
 		//udelay (10000);
@@ -1434,10 +1688,13 @@
 	sbac = DC390_read32 (DMA_ScsiBusCtrl);
 	printk ("%08lx\n", sbac);
     };
+#endif
     dc390_lastabortedpid = cmd->pid;
     DC390_UNLOCK_ACB;
     //do_DC390_Interrupt (pACB->IRQLevel, 0, 0);
-    cmd->scsi_done(cmd);
+#ifndef USE_NEW_EH	
+    if (status == SCSI_ABORT_SUCCESS) cmd->scsi_done(cmd);
+#endif	
     return( status );
 }
 
@@ -1454,20 +1711,24 @@
 	pDCB->SyncMode &= ~SYNC_NEGO_DONE;
 	pDCB->SyncPeriod = 0;
 	pDCB->SyncOffset = 0;
+	pDCB->TagMask = 0;
 	pDCB->CtrlR3 = FAST_CLK;
 	pDCB->CtrlR4 &= NEGATE_REQACKDATA | CTRL4_RESERVED | NEGATE_REQACK;
 	pDCB->CtrlR4 |= pACB->glitch_cfg;
 	pDCB = pDCB->pNextDCB;
     }
     while( pdcb != pDCB );
-}
+    pACB->ACBFlag &= ~(RESET_DEV | RESET_DONE | RESET_DETECT);
 
+}
 
+#if 0
+/* Moves all SRBs from Going to Waiting for all DCBs */
 static void dc390_RecoverSRB( PACB pACB )
 {
     PDCB   pDCB, pdcb;
     PSRB   psrb, psrb2;
-    ULONG  cnt, i;
+    UINT   cnt, i;
 
     pDCB = pACB->pLinkDCB;
     if( !pDCB ) return;
@@ -1499,7 +1760,7 @@
 	pdcb = pdcb->pNextDCB;
     } while( pdcb != pDCB );
 }
-
+#endif
 
 /***********************************************************************
  * Function : int DC390_reset (Scsi_Cmnd *cmd, ...)
@@ -1507,34 +1768,37 @@
  * Purpose : perform a hard reset on the SCSI bus
  *
  * Inputs : cmd - command which caused the SCSI RESET
+ *	    resetFlags - how hard to try
  *
  * Returns : 0 on success.
  ***********************************************************************/
 
-int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
+int DC390_reset (Scsi_Cmnd *cmd, unsigned int resetFlags)
 {
     UCHAR   bval;
-    ULONG   i;
     DC390_AFLAGS
     PACB    pACB = (PACB) cmd->host->hostdata;
 
     printk(KERN_INFO "DC390: RESET ... ");
 
     DC390_LOCK_ACB;
+    if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer);
     bval = DC390_read8 (CtrlReg1);
     bval |= DIS_INT_ON_SCSI_RST;
-    DC390_write8 (CtrlReg1, bval);	/* disable interrupt */
+    DC390_write8 (CtrlReg1, bval);	/* disable IRQ on bus reset */
 
+    pACB->ACBFlag |= RESET_DEV;
     dc390_ResetSCSIBus( pACB );
-    /* Unlock ? */
-    for( i=0; i<600; i++ )
-	udelay(1000);
 
+    dc390_ResetDevParam( pACB );
+    udelay (1000);
+    pACB->pScsiHost->last_reset = jiffies + 3*HZ/2 
+		+ HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
+    
     DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     DC390_read8 (INT_Status);		/* Reset Pending INT */
 
-    dc390_ResetDevParam( pACB );
-    dc390_DoingSRB_Done( pACB );
+    dc390_DoingSRB_Done( pACB, cmd );
     /* dc390_RecoverSRB (pACB); */
     pACB->pActiveDCB = NULL;
 
@@ -1543,10 +1807,10 @@
     bval &= ~DIS_INT_ON_SCSI_RST;
     DC390_write8 (CtrlReg1, bval);	/* re-enable interrupt */
 
-    dc390_DoWaitingSRB( pACB );
+    dc390_Waiting_process( pACB );
 
-    DC390_UNLOCK_ACB;
     printk("done\n");
+    DC390_UNLOCK_ACB;
     return( SCSI_RESET_SUCCESS );
 }
 
@@ -1556,22 +1820,22 @@
 /***********************************************************************
  * Function : static void dc390_initDCB()
  *
- * Purpose :  initialize the internal structures for a given DCB
+ * Purpose :  initialize the internal structures for a DCB (to be malloced)
  *
- * Inputs : cmd - pointer to this scsi cmd request block structure
+ * Inputs : SCSI id and lun
  ***********************************************************************/
 
-void dc390_initDCB( PACB pACB, PDCB *ppDCB, PSCSICMD cmd )
+void dc390_initDCB( PACB pACB, PDCB *ppDCB, UCHAR id, UCHAR lun )
 {
     PEEprom	prom;
     UCHAR	index;
-    PDCB pDCB;
+    PDCB pDCB, pDCB2;
 
     pDCB = kmalloc (sizeof(DC390_DCB), GFP_ATOMIC);
-    DCBDEBUG(printk (KERN_INFO "DC390: alloc mem for DCB (ID %i, LUN %i): 0x%08x\n",	\
-	    cmd->target, cmd->lun, (int)pDCB);)
+    DCBDEBUG(printk (KERN_INFO "DC390: alloc mem for DCB (ID %i, LUN %i): %p\n"	\
+	    id, lun, pDCB);)
  
-    *ppDCB = pDCB;
+    *ppDCB = pDCB; pDCB2 = 0;
     if (!pDCB) return;
     if( pACB->DCBCnt == 0 )
     {
@@ -1585,37 +1849,58 @@
    
     pACB->DCBCnt++;
    
-    pACB->pLastDCB = pDCB;
     pDCB->pNextDCB = pACB->pLinkDCB;
+    pACB->pLastDCB = pDCB;
 
     pDCB->pDCBACB = pACB;
-    pDCB->QIORBCnt = 0;
-    pDCB->UnitSCSIID = cmd->target;
-    pDCB->UnitSCSILUN = cmd->lun;
+    pDCB->TargetID = id;
+    pDCB->TargetLUN = lun;
     pDCB->pWaitingSRB = NULL;
     pDCB->pGoingSRB = NULL;
     pDCB->GoingSRBCnt = 0;
+    pDCB->WaitSRBCnt = 0;
     pDCB->pActiveSRB = NULL;
     pDCB->TagMask = 0;
     pDCB->MaxCommand = 1;
     index = pACB->AdapterIndex;
     pDCB->DCBFlag = 0;
 
-    prom = (PEEprom) &dc390_eepromBuf[index][cmd->target << 2];
-    pDCB->DevMode = prom->EE_MODE1;
+    /* Is there a corresp. LUN==0 device ? */
+    if (lun != 0)
+	pDCB2 = dc390_findDCB (pACB, id, 0);
+    prom = (PEEprom) &dc390_eepromBuf[index][id << 2];
+    /* Some values are for all LUNs: Copy them */
+    /* In a clean way: We would have an own structure for a SCSI-ID */
+    if (pDCB2)
+    {
+      pDCB->DevMode = pDCB2->DevMode;
+      pDCB->SyncMode = pDCB2->SyncMode;
+      pDCB->SyncPeriod = pDCB2->SyncPeriod;
+      pDCB->SyncOffset = pDCB2->SyncOffset;
+      pDCB->NegoPeriod = pDCB2->NegoPeriod;
+      
+      pDCB->CtrlR3 = pDCB2->CtrlR3;
+      pDCB->CtrlR4 = pDCB2->CtrlR4;
+      pDCB->Inquiry7 = pDCB2->Inquiry7;
+    }
+    else
+    {		
+      pDCB->DevMode = prom->EE_MODE1;
+      pDCB->SyncMode = 0;
+      pDCB->SyncPeriod = 0;
+      pDCB->SyncOffset = 0;
+      pDCB->NegoPeriod = (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2;
+            
+      pDCB->CtrlR3 = FAST_CLK;
+      
+      pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED;
+      if( dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION)
+	pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK;
+      pDCB->Inquiry7 = 0;
+    }
 
-    pDCB->SyncMode = 0;
+    pACB->DCBmap[id] |= (1 << lun);
     dc390_updateDCB(pACB, pDCB);
-
-    pDCB->SyncPeriod = 0;
-    pDCB->SyncOffset = 0;
-    pDCB->NegoPeriod = (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2;
-
-    pDCB->CtrlR3 = FAST_CLK;
-
-    pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED;
-    if( dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION)
-	pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK;
 }
 
 /***********************************************************************
@@ -1626,24 +1911,26 @@
 
 void dc390_updateDCB (PACB pACB, PDCB pDCB)
 {
-  pDCB->IdentifyMsg = IDENTIFY (pDCB->DevMode & EN_DISCONNECT_, pDCB->UnitSCSILUN);
-  
-  if (pDCB->DevMode & TAG_QUEUEING_) pDCB->SyncMode &= EN_TAG_QUEUEING | SYNC_NEGO_DONE | EN_ATN_STOP;
-  else pDCB->SyncMode &= SYNC_NEGO_DONE | EN_ATN_STOP;
-  
-  if( pDCB->DevMode & SYNC_NEGO_ && (!(pDCB->UnitSCSILUN) || dc390_CurrSyncOffset) )
-     pDCB->SyncMode |= SYNC_ENABLE;
-  else
-     {
+  pDCB->SyncMode &= EN_TAG_QUEUEING | SYNC_NEGO_DONE /*| EN_ATN_STOP*/;
+  if (pDCB->DevMode & TAG_QUEUEING_) {
+	//if (pDCB->SyncMode & EN_TAG_QUEUEING) pDCB->MaxCommand = pACB->TagMaxNum;
+  } else {
+	pDCB->SyncMode &= ~EN_TAG_QUEUEING;
+	pDCB->MaxCommand = 1;
+  };
+
+  if( pDCB->DevMode & SYNC_NEGO_ )
+	pDCB->SyncMode |= SYNC_ENABLE;
+  else {
 	pDCB->SyncMode &= ~(SYNC_NEGO_DONE | SYNC_ENABLE);
 	pDCB->SyncOffset &= ~0x0f;
-     };
-   
-  if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; 
-  
+  };
+
+  //if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; 
+
   pDCB->CtrlR1 = pACB->pScsiHost->this_id;
   if( pDCB->DevMode & PARITY_CHK_ )
-    pDCB->CtrlR1 |= PARITY_ERR_REPO;
+	pDCB->CtrlR1 |= PARITY_ERR_REPO;
 };  
 
 
@@ -1657,7 +1944,7 @@
 {
   int i;
   PDCB pDCB = pACB->pLinkDCB;
-  for (i = 0; i < pACB->DeviceCnt; i++)
+  for (i = 0; i < pACB->DCBCnt; i++)
     {
       dc390_updateDCB (pACB, pDCB);
       pDCB = pDCB->pNextDCB;
@@ -1681,12 +1968,12 @@
 
 void dc390_linkSRB( PACB pACB )
 {
-    ULONG   count, i;
+    UINT   count, i;
 
     count = pACB->SRBCount;
-    for( i=0; i< count; i++)
+    for( i=0; i<count; i++)
     {
-	if( i != count - 1)
+	if( i != count-1 )
 	    pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
 	else
 	    pACB->SRB_array[i].pNextSRB = NULL;
@@ -1716,7 +2003,15 @@
     psh->io_port = io_port;
     psh->n_io_port = 0x80;
     psh->irq = Irq;
-
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,50)
+    psh->base = io_port;
+#else
+    psh->base = (char*)io_port;
+#endif	
+    psh->unique_id = io_port;
+    psh->dma_channel = -1;
+    psh->last_reset = jiffies;
+	
     pACB = (PACB) psh->hostdata;
     DC390_LOCKA_INIT;
     DC390_LOCK_ACB;
@@ -1741,6 +2036,8 @@
     pACB->pActiveDCB = NULL;
     pACB->pFreeSRB = pACB->SRB_array;
     pACB->SRBCount = MAX_SRB_CNT;
+    pACB->QueryCnt = 0;
+    pACB->pQueryHead = NULL;
     pACB->AdapterIndex = index;
     pACB->status = 0;
     psh->this_id = dc390_eepromBuf[index][EE_ADAPT_SCSI_ID];
@@ -1759,7 +2056,9 @@
 	pACB->DCBmap[i] = 0;
     pACB->sel_timeout = SEL_TIMEOUT;
     pACB->glitch_cfg = EATER_25NS;
-    pACB->Cmds = pACB->CmdInQ = pACB->CmdOutOfSRB = pACB->SelLost = 0;
+    pACB->Cmds = pACB->CmdInQ = pACB->CmdOutOfSRB = 0;
+    pACB->SelLost = pACB->SelConn = 0;
+    init_timer (&pACB->Waiting_Timer);
 }
 
 
@@ -1777,22 +2076,11 @@
 int __init dc390_initAdapter (PSH psh, ULONG io_port, UCHAR Irq, UCHAR index)
 {
     PACB   pACB, pACB2;
-    UCHAR  used_irq = 0, dstate;
+    UCHAR  dstate;
     int    i;
     
     pACB = (PACB) psh->hostdata;
     
-    for ( pACB2 = dc390_pACB_start; pACB2 ; )
-      {
-	if( pACB2->IRQLevel == Irq )
-	  {
-	    used_irq = 1;
-	    break;
-	  }
-	else
-	  pACB2 = pACB2->pNextACB;
-      }
-
     if (check_region (io_port, psh->n_io_port))
 	{
 	    printk(KERN_ERR "DC390: register IO ports error!\n");
@@ -1803,14 +2091,12 @@
 
     DC390_read8_ (INT_Status, io_port);		/* Reset Pending INT */
 
-    if( !used_irq )
-    {
-	if( (i = request_irq(Irq, do_DC390_Interrupt, DC390_IRQ, "tmscsim", NULL) ))
-	    {
-		    printk(KERN_ERR "DC390: register IRQ error!\n");
-		    return( -1 );
-	    }
-    }
+    if( (i = request_irq(Irq, do_DC390_Interrupt, DC390_IRQ, "tmscsim", pACB) ))
+      {
+	printk(KERN_ERR "DC390: register IRQ error!\n");
+	release_region (io_port, psh->n_io_port);
+	return( -1 );
+      }
 
     if( !dc390_pACB_start )
       {
@@ -1832,9 +2118,13 @@
     if (pACB->Gmode2 & RST_SCSI_BUS)
     {
 	dc390_ResetSCSIBus( pACB );
-	/* Unlock before ? */
-	for( i=0; i<600; i++ )
+	udelay (1000);
+	pACB->pScsiHost->last_reset = jiffies + HZ/2
+		    + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
+	/*
+	for( i=0; i<(500 + 1000*dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]); i++ )
 		udelay(1000);
+	 */
     };
     pACB->ACBFlag = 0;
     DC390_read8 (INT_Status);				/* Reset Pending INT */
@@ -1846,6 +2136,7 @@
     DC390_write8 (CtrlReg3, FAST_CLK);			/* fast clock */
     DC390_write8 (CtrlReg4, pACB->glitch_cfg |			/* glitch eater */
 		(dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) ? NEGATE_REQACKDATA : 0);	/* Negation */
+    DC390_write8 (CtcReg_High, 0);			/* Clear Transfer Count High: ID */
     DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
     DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
@@ -1885,18 +2176,24 @@
 	printk (KERN_ERR "DC390_init: No EEPROM found!\n");
 	return( -1 );
 #else
-	int period;
-	printk (KERN_INFO "DC390_init: No EEPROM found!\n");
-	printk (KERN_INFO "DC390_init: Trying default EEPROM settings:\n");
-	dc390_checkparams ();
-	period = dc390_clock_period1[tmscsim[1]];
+	int speed;
+	dc390_adapname = "AM53C974";
+	printk (KERN_INFO "DC390_init: No EEPROM found! Trying default settings ...\n");
+	dc390_check_for_safe_settings ();
+	dc390_fill_with_defaults ();
+	dc390_EEprom_Override (index);
+	speed = dc390_clock_speed[tmscsim[1]];
 	printk (KERN_INFO "DC390: Used defaults: AdaptID=%i, SpeedIdx=%i (%i.%i MHz),"
-		" DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i)\n", tmscsim[0], tmscsim[1],
-		40 / period, ((40%period)*10 + period/2) / period,
-		(UCHAR)tmscsim[2], (UCHAR)tmscsim[3], tmscsim[4], 2 << (tmscsim[4]));
-	dc390_EEpromDefaults (index);
+		" DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i), DelayReset=%is\n", 
+		tmscsim[0], tmscsim[1], speed/10, speed%10,
+		(UCHAR)tmscsim[2], (UCHAR)tmscsim[3], tmscsim[4], 2 << (tmscsim[4]), tmscsim[5]);
 #endif
-    };
+    }
+    else
+    {
+	dc390_check_for_safe_settings ();
+	dc390_EEprom_Override (index);
+    }
    
     psh = scsi_register( psht, sizeof(DC390_ACB) );
     if( !psh ) return( -1 );
@@ -2000,7 +2297,8 @@
     PDEVDECL0;
     UCHAR   irq;
     UINT    io_port;
-    DC390_IFLAGS DC390_DFLAGS
+    //DC390_IFLAGS
+    DC390_DFLAGS
 
     DC390_LOCK_DRV;
     //dc390_pSHT_start = psht;
@@ -2009,7 +2307,11 @@
     if ( PCI_PRESENT )
 	while (PCI_FIND_DEVICE (PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974))
 	{
-	    DC390_LOCK_IO;		/* Remove this when going to new eh */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,30)
+	    if (pci_enable_device (pdev))
+		continue;
+#endif
+	    //DC390_LOCK_IO;		/* Remove this when going to new eh */
 	    PCI_GET_IO_AND_IRQ;
 	    DEBUG0(printk(KERN_INFO "DC390(%i): IO_PORT=%04x,IRQ=%x\n", dc390_adapterCnt, (UINT) io_port, irq);)
 
@@ -2019,14 +2321,17 @@
 		dc390_set_pci_cfg (PDEV);
 		dc390_adapterCnt++;
 	    };
-	    DC390_UNLOCK_IO;		/* Remove when going to new eh */
+	    //DC390_UNLOCK_IO;		/* Remove when going to new eh */
 	}
     else
 	printk (KERN_ERR "DC390: No PCI BIOS found!\n");
    
     if (dc390_adapterCnt)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30)
+	psht->proc_name = "tmscsim";
+#else
 	psht->proc_dir = &DC390_proc_scsi_tmscsim;
-
+#endif
     printk(KERN_INFO "DC390: %i adapters found\n", dc390_adapterCnt);
     DC390_UNLOCK_DRV;
     return( dc390_adapterCnt );
@@ -2048,7 +2353,7 @@
    if (cmd->result)
    {
 	PACB pACB = (PACB)cmd->host->hostdata;
-	PDCB pDCB = dc390_findDCB (pACB, cmd);
+	PDCB pDCB = dc390_findDCB (pACB, cmd->target, cmd->lun);
 	printk ("DC390: Unsetting DsCn, Sync and TagQ!\n");
 	if (pDCB)
 	{
@@ -2056,7 +2361,6 @@
 		dc390_updateDCB (pACB, pDCB);
 	};
    };
-   kfree (cmd->buffer);
    kfree (cmd);
 };
 
@@ -2064,20 +2368,21 @@
 {
    char* buffer;
    Scsi_Cmnd* cmd;
-   buffer = kmalloc (256, GFP_ATOMIC);
-   cmd = kmalloc (sizeof (Scsi_Cmnd), GFP_ATOMIC);
+   cmd = kmalloc (sizeof(Scsi_Cmnd) + 256, GFP_ATOMIC);
+   if (!cmd) { printk ("DC390: kmalloc failed in inquiry!\n"); return; };
+   buffer = (char*)cmd + sizeof(Scsi_Cmnd);
 
-   memset (buffer, 0, 256);
-   memset (cmd, 0, sizeof(Scsi_Cmnd));
+   memset (cmd, 0, sizeof(Scsi_Cmnd) + 256);
    cmd->cmnd[0] = INQUIRY;
-   cmd->cmnd[1] = (pDCB->UnitSCSILUN << 5) & 0xe0;
+   cmd->cmnd[1] = (pDCB->TargetLUN << 5) & 0xe0;
    cmd->cmnd[4] = 0xff;
    
    cmd->cmd_len = 6; cmd->old_cmd_len = 6;
    cmd->host = pACB->pScsiHost;
-   cmd->target = pDCB->UnitSCSIID;
-   cmd->lun = pDCB->UnitSCSILUN; 
+   cmd->target = pDCB->TargetID;
+   cmd->lun = pDCB->TargetLUN; 
    cmd->serial_number = 1;
+   cmd->pid = 390;
    cmd->bufflen = 128;
    cmd->buffer = buffer;
    cmd->request_bufflen = 128;
@@ -2088,11 +2393,62 @@
 
    cmd->request.rq_status = RQ_SCSI_BUSY;
 
+   pDCB->SyncMode &= ~SYNC_NEGO_DONE;
    printk (KERN_INFO "DC390: Queue INQUIRY command to dev ID %02x LUN %02x\n",
-	   pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+	   pDCB->TargetID, pDCB->TargetLUN);
    DC390_queue_command (cmd, dc390_inquiry_done);
 };
 
+/***********************************************************************
+ * Functions: dc390_sendstart(), dc390_sendstart_done()
+ *
+ * Purpose: When changing speed etc., we have to issue an INQUIRY
+ *	    command to make sure, we agree upon the nego parameters
+ *	    with the device
+ ***********************************************************************/
+
+static void dc390_sendstart_done (Scsi_Cmnd* cmd)
+{
+   printk (KERN_INFO "DC390: SENDSTART (ID %02x LUN %02x) returned %08x\n",
+	   cmd->target, cmd->lun, cmd->result);
+   kfree (cmd);
+};
+
+void dc390_sendstart (PACB pACB, PDCB pDCB)
+{
+   char* buffer;
+   Scsi_Cmnd* cmd;
+   cmd = kmalloc (sizeof(Scsi_Cmnd) + 256, GFP_ATOMIC);
+   if (!cmd) { printk ("DC390: kmalloc failed in sendstart!\n"); return; };
+   buffer = (char*)cmd + sizeof(Scsi_Cmnd);
+
+   memset (cmd, 0, sizeof(Scsi_Cmnd) + 256);
+   cmd->cmnd[0] = 0x1b; /* START_STOP_UNIT */
+   cmd->cmnd[1] = (pDCB->TargetLUN << 5) & 0xe0;
+   cmd->cmnd[4] = 0x01; /* START */
+   
+   cmd->cmd_len = 6; cmd->old_cmd_len = 6;
+   cmd->host = pACB->pScsiHost;
+   cmd->target = pDCB->TargetID;
+   cmd->lun = pDCB->TargetLUN; 
+   cmd->serial_number = 1;
+   cmd->pid = 310;
+   cmd->bufflen = 128;
+   cmd->buffer = buffer;
+   cmd->request_bufflen = 128;
+   cmd->request_buffer = &buffer[128];
+   cmd->done = dc390_sendstart_done;
+   cmd->scsi_done = dc390_sendstart_done;
+   cmd->timeout_per_command = 5*HZ;
+
+   cmd->request.rq_status = RQ_SCSI_BUSY;
+
+   pDCB->SyncMode &= ~SYNC_NEGO_DONE;
+   printk (KERN_INFO "DC390: Queue SEND_START command to dev ID %02x LUN %02x\n",
+	   pDCB->TargetID, pDCB->TargetLUN);
+   DC390_queue_command (cmd, dc390_sendstart_done);
+};
+
 /********************************************************************
  * Function: dc390_set_info()
  *
@@ -2210,6 +2566,9 @@
   if (!memcmp (pos, "RESET", 5)) goto reset;
   else if (!memcmp (pos, "INQUIRY", 7)) goto inquiry;
   else if (!memcmp (pos, "REMOVE", 6)) goto remove;
+  else if (!memcmp (pos, "ADD", 3)) goto add;
+  else if (!memcmp (pos, "START", 5)) goto start;
+  else if (!memcmp (pos, "DUMP", 4)) goto dump;
   
   if (isdigit (*pos))
     {
@@ -2226,13 +2585,20 @@
       pDCB = pACB->pLinkDCB;
       for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB;
       /* Sanity Check */
-      if (pDCB->UnitSCSIID != id || pDCB->UnitSCSILUN != lun) 
+      if (pDCB->TargetID != id || pDCB->TargetLUN != lun) 
 	 {
 	    printk (KERN_ERR "DC390: no such device: Idx=%02i ID=%02i LUN=%02i\n",
 		    dev, id, lun);
 	    goto einv2;
 	 };
 
+      if (pDCB->pWaitingSRB || pDCB->pGoingSRB)
+      {
+	  printk ("DC390: Cannot change dev (%i-%i) cfg: Pending requests\n",
+		  pDCB->TargetID, pDCB->TargetLUN);
+	  goto einv;
+      };
+	  
       olddevmode = pDCB->DevMode;
       YESNO (pos, pDCB->DevMode, PARITY_CHK_);
       needs_inquiry++;
@@ -2245,8 +2611,7 @@
       needs_inquiry++;
       YESNO (pos, pDCB->DevMode, TAG_QUEUEING_);
       if ((olddevmode & TAG_QUEUEING_) == (pDCB->DevMode & TAG_QUEUEING_)) needs_inquiry--;
-      YESNO (pos, pDCB->SyncMode, EN_ATN_STOP);
-      
+
       dc390_updateDCB (pACB, pDCB);
       if (!pos) goto ok;
        
@@ -2266,7 +2631,7 @@
       else pos = strtok (0, " \t\n:=,;.");
       if (!pos) goto ok;
       
-      /* Speed: NegoPeriod */
+      /* Sync Speed in MHz */
       if (*pos != '-')
 	{
 	  SCANF (pos, p0, dum, 1, 13); 
@@ -2281,11 +2646,11 @@
 		for (; p0-pos > 1; p0--) dum /= 10;
 		pDCB->NegoPeriod = (100000/(100*dumold + dum)) >> 2;
 		if (pDCB->NegoPeriod < 19) pDCB->NegoPeriod = 19;
-		if (pDCB->NegoPeriod != olddevmode) needs_inquiry++;
 		pos = strtok (0, " \t\n:=,;");
 		if (!pos) goto ok;
 	     };
 	  if (*pos == 'M') pos = strtok (0, " \t\n:=,;");
+	  if (pDCB->NegoPeriod != olddevmode) needs_inquiry++;
 	}
       else pos = strtok (0, " \t\n:=,;");
       /* dc390_updateDCB (pACB, pDCB); */
@@ -2300,28 +2665,49 @@
 	  if (pDCB->SyncOffset > olddevmode) needs_inquiry++;
 	}
       else pos = strtok (0, " \t\n:=,;");
+      if (!pos) goto ok;
       dc390_updateDCB (pACB, pDCB);
+
+      //olddevmode = pDCB->MaxCommand;
+      /* MaxCommand (Tags) */
+      if (*pos != '-')
+	{
+	  SCANF (pos, p0, dum, 1, 32 /*pACB->TagMaxNum*/);
+	  if (pDCB->SyncMode & EN_TAG_QUEUEING)
+		pDCB->MaxCommand = dum;
+	  else printk (KERN_INFO "DC390: Can't set MaxCmd larger than one without Tag Queueing!\n");
+	}
+      else pos = strtok (0, " \t\n:=,;");
+
     }
   else
     {
-      char* p1 = pos; UCHAR dum;
+      char* p1 = pos; UCHAR dum, newadaptid;
       PARSEDEBUG(printk (KERN_INFO "DC390: chg adapt cfg \"%s\"\n", prstr (pos, &buffer[length]));)
       dum = GLITCH_TO_NS (pACB->glitch_cfg);
       /* Adapter setting */
       SEARCH (pos, p0, pACB->pScsiHost->max_id, "MAXID", 8); 
       SEARCH (pos, p0, pACB->pScsiHost->max_lun, "MAXLUN", 8); 
-      SEARCH (pos, p0, pACB->pScsiHost->this_id, "ADAPTERID", 7); 
+      SEARCH (pos, p0, newadaptid, "ADAPTERID", 7);
       SEARCH (pos, p0, pACB->TagMaxNum, "TAGMAXNUM", 32);
       SEARCH (pos, p0, pACB->ACBFlag, "ACBFLAG", 255);
       SEARCH3 (pos, p0, dum, "GLITCHEATER", 40, 1000, "NS");
       SEARCH3 (pos, p0, pACB->sel_timeout, "SELTIMEOUT", 400, 163, "MS");
+      SEARCH3 (pos, p0, dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY], "DELAYRESET", 180, 100, "S");
     ok2:
       pACB->glitch_cfg = NS_TO_GLITCH (dum);
       if (pACB->sel_timeout < 60) pACB->sel_timeout = 60;
-      dum = 0; while (1 << dum <= pACB->TagMaxNum) dum ++;
-      pACB->TagMaxNum &= (1 << --dum);
-      if (pos == p1) goto einv;
+      DC390_write8 (Scsi_TimeOut, pACB->sel_timeout);
+      if (newadaptid != pACB->pScsiHost->this_id)
+      {
+	pACB->pScsiHost->this_id = newadaptid;
+	dc390_ResetDevParam (pACB);
+      }	    
+      //dum = 0; while (1 << dum <= pACB->TagMaxNum) dum ++;
+      //pACB->TagMaxNum &= (1 << --dum);
       dc390_updateDCBs (pACB);
+      // All devs should be INQUIRED now
+      if (pos == p1) goto einv;
     }
   if (pos) goto next;
       
@@ -2351,7 +2737,15 @@
 	DC390_UNLOCK_IO;
      };
   return (length);
-  
+
+ dump:
+     {
+	dc390_dumpinfo (pACB, 0, 0);
+	DC390_UNLOCK_ACB;
+	DC390_UNLOCK_IO;       
+     }
+  return (length);
+	
  inquiry:
      {
 	pos = strtok (0, " \t\n.:;="); if (!pos) goto einv;
@@ -2359,7 +2753,7 @@
 	if (dev >= pACB->DCBCnt) goto einv_dev;
 	for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB;
 	printk (KERN_NOTICE " DC390: Issue INQUIRY command to Dev(Idx) %i SCSI ID %i LUN %i\n",
-		dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+		dev, pDCB->TargetID, pDCB->TargetLUN);
 	DC390_UNLOCK_ACB;
 	dc390_inquiry (pACB, pDCB);
 	DC390_UNLOCK_IO;
@@ -2373,13 +2767,45 @@
 	if (dev >= pACB->DCBCnt) goto einv_dev;
 	for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB;
 	printk (KERN_NOTICE " DC390: Remove DCB for Dev(Idx) %i SCSI ID %i LUN %i\n",
-		dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+		dev, pDCB->TargetID, pDCB->TargetLUN);
+	/* TO DO: We should make sure no pending commands are left */
 	dc390_remove_dev (pACB, pDCB);
 	DC390_UNLOCK_ACB;
 	DC390_UNLOCK_IO;
      };
    return (length);
 
+ add:
+     {
+	int id, lun;
+	pos = strtok (0, " \t\n.:;=");
+	if (pos) { SCANF (pos, p0, id, 0, 7); } else goto einv;
+	if (pos) { SCANF (pos, p0, lun, 0, 7); } else goto einv;
+	pDCB = dc390_findDCB (pACB, id, lun);
+	if (pDCB) { printk ("DC390: ADD: Device already existing\n"); goto einv; };
+	dc390_initDCB (pACB, &pDCB, id, lun);
+	DC390_UNLOCK_ACB;
+	dc390_inquiry (pACB, pDCB);
+	DC390_UNLOCK_IO;
+     };
+   return (length);
+
+ start:
+     {
+	int id, lun;
+	pos = strtok (0, " \t\n.:;=");
+	if (pos) { SCANF (pos, p0, id, 0, 7); } else goto einv;
+	if (pos) { SCANF (pos, p0, lun, 0, 7); } else goto einv;
+	pDCB = dc390_findDCB (pACB, id, lun);
+	if (pDCB) printk ("DC390: SendStart: Device already existing ...\n");
+	else dc390_initDCB (pACB, &pDCB, id, lun);
+	DC390_UNLOCK_ACB;
+	dc390_sendstart (pACB, pDCB);
+	dc390_inquiry (pACB, pDCB);
+	DC390_UNLOCK_IO;
+     };
+   return (length);
+
  einv_dev:
    printk (KERN_WARNING "DC390: Ignore cmnd to illegal Dev(Idx) %i. Valid range: 0 - %i.\n", 
 	   dev, pACB->DCBCnt - 1);
@@ -2429,6 +2855,7 @@
   PSH shpnt;
   PACB pACB;
   PDCB pDCB;
+  PSCSICMD pcmd;
   DC390_AFLAGS
 
   pACB = dc390_pACB_start;
@@ -2454,46 +2881,87 @@
   SPRINTF("SCSI Host Nr %i, ", shpnt->host_no);
   SPRINTF("%s Adapter Nr %i\n", dc390_adapname, pACB->AdapterIndex);
   SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase);
-  SPRINTF("IRQLevel 0x%02x\n", pACB->IRQLevel);
+  SPRINTF("IRQ %02i\n", pACB->IRQLevel);
 
   SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun);
-  SPRINTF("AdapterID %i, SelTimeout %i ms\n", 
-	  shpnt->this_id, (pACB->sel_timeout*164)/100);
+  SPRINTF("AdapterID %i, SelTimeout %i ms, DelayReset %i s\n", 
+	  shpnt->this_id, (pACB->sel_timeout*164)/100,
+	  dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]);
 
-  SPRINTF("TagMaxNum %i, Status %i, ACBFlag %i, GlitchEater %i ns\n",
+  SPRINTF("TagMaxNum %i, Status 0x%02x, ACBFlag 0x%02x, GlitchEater %i ns\n",
 	  pACB->TagMaxNum, pACB->status, pACB->ACBFlag, GLITCH_TO_NS(pACB->glitch_cfg)*12);
 
-  SPRINTF("Statistics: Cmnds %li, Cmnds not sent directly %li, Out of SRB conds %li\n",
+  SPRINTF("Statistics: Cmnds %li, Cmnds not sent directly %i, Out of SRB conds %i\n",
 	  pACB->Cmds, pACB->CmdInQ, pACB->CmdOutOfSRB);
-  SPRINTF("            Lost arbitrations %li\n", pACB->SelLost);
+  SPRINTF("            Lost arbitrations %i, Sel. connected %i, Connected: %s\n", 
+	  pACB->SelLost, pACB->SelConn, pACB->Connected? "Yes": "No");
    
   SPRINTF("Nr of attached devices: %i, Nr of DCBs: %i\n", pACB->DeviceCnt, pACB->DCBCnt);
+  SPRINTF("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	  pACB->DCBmap[0], pACB->DCBmap[1], pACB->DCBmap[2], pACB->DCBmap[3], 
+	  pACB->DCBmap[4], pACB->DCBmap[5], pACB->DCBmap[6], pACB->DCBmap[7]);
 
-  SPRINTF("Idx ID LUN Prty Sync DsCn SndS TagQ STOP NegoPeriod SyncSpeed SyncOffs\n");
+  SPRINTF("Idx ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs MaxCmd\n");
 
   pDCB = pACB->pLinkDCB;
   for (dev = 0; dev < pACB->DCBCnt; dev++)
      {
-      SPRINTF("%02i  %02i  %02i ", dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+      SPRINTF("%02i  %02i  %02i ", dev, pDCB->TargetID, pDCB->TargetLUN);
       YESNO(pDCB->DevMode & PARITY_CHK_);
       YESNO(pDCB->SyncMode & SYNC_NEGO_DONE);
       YESNO(pDCB->DevMode & EN_DISCONNECT_);
-      //YESNO(pDCB->SyncMode & EN_ATN_STOP);
       YESNO(pDCB->DevMode & SEND_START_);
       YESNO(pDCB->SyncMode & EN_TAG_QUEUEING);
-      YESNO(pDCB->SyncMode & EN_ATN_STOP);
       if (pDCB->SyncOffset & 0x0f)
       {
 	 int sp = pDCB->SyncPeriod; if (! (pDCB->CtrlR3 & FAST_SCSI)) sp++;
 	 SPRINTF("  %03i ns ", (pDCB->NegoPeriod) << 2);
 	 spd = 40/(sp); spd1 = 40%(sp);
 	 spd1 = (spd1 * 10 + sp/2) / (sp);
-	 SPRINTF("   %2i.%1i M      %02i\n", spd, spd1, (pDCB->SyncOffset & 0x0f));
+	 SPRINTF("   %2i.%1i M      %02i", spd, spd1, (pDCB->SyncOffset & 0x0f));
       }
-      else SPRINTF(" (%03i ns)\n", (pDCB->NegoPeriod) << 2);
+      else SPRINTF(" (%03i ns)                 ", (pDCB->NegoPeriod) << 2);
       /* Add more info ...*/
+      SPRINTF ("      %02i\n", pDCB->MaxCommand);
       pDCB = pDCB->pNextDCB;
      }
+    SPRINTF ("Commands in Queues: Query: %li:", pACB->QueryCnt);
+    for (pcmd = pACB->pQueryHead; pcmd; pcmd = pcmd->next)
+	SPRINTF (" %li", pcmd->pid);
+    if (timer_pending(&pACB->Waiting_Timer)) SPRINTF ("Waiting queue timer running\n");
+    else SPRINTF ("\n");
+    pDCB = pACB->pLinkDCB;
+	
+    for (dev = 0; dev < pACB->DCBCnt; dev++)
+    {
+	PSRB pSRB;
+	if (pDCB->WaitSRBCnt) 
+		    SPRINTF ("DCB (%02i-%i): Waiting: %i:", pDCB->TargetID, pDCB->TargetLUN,
+			     pDCB->WaitSRBCnt);
+	for (pSRB = pDCB->pWaitingSRB; pSRB; pSRB = pSRB->pNextSRB)
+		SPRINTF(" %li", pSRB->pcmd->pid);
+	if (pDCB->GoingSRBCnt) 
+		    SPRINTF ("\nDCB (%02i-%i): Going  : %i:", pDCB->TargetID, pDCB->TargetLUN,
+			     pDCB->GoingSRBCnt);
+	for (pSRB = pDCB->pGoingSRB; pSRB; pSRB = pSRB->pNextSRB)
+#if 0 //def DC390_DEBUGTRACE
+		SPRINTF(" %s\n  ", pSRB->debugtrace);
+#else
+		SPRINTF(" %li", pSRB->pcmd->pid);
+#endif
+	if (pDCB->WaitSRBCnt || pDCB->GoingSRBCnt) SPRINTF ("\n");
+	pDCB = pDCB->pNextDCB;
+    }
+	
+#ifdef DC390_DEBUGDCB
+    SPRINTF ("DCB list for ACB %p:\n", pACB);
+    pDCB = pACB->pLinkDCB;
+    SPRINTF ("%p", pDCB);
+    for (dev = 0; dev < pACB->DCBCnt; dev++, pDCB=pDCB->pNextDCB)
+	SPRINTF ("->%p", pDCB->pNextDCB);
+    SPRINTF("\n");
+#endif
+  
 
   DC390_UNLOCK_ACB;
   *start = buffer + offset;
@@ -2528,13 +2996,14 @@
 
     printk(KERN_INFO "DC390: shutdown\n");
 
-    pACB->ACBFlag = RESET_DONE;
+    pACB->ACBFlag = RESET_DEV;
     bval = DC390_read8 (CtrlReg1);
     bval |= DIS_INT_ON_SCSI_RST;
     DC390_write8 (CtrlReg1, bval);	/* disable interrupt */
     if (pACB->Gmode2 & RST_SCSI_BUS)
 		dc390_ResetSCSIBus (pACB);
 
+    if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer);
     return( 0 );
 }
 
@@ -2548,41 +3017,30 @@
     do
     {
 	nDCB = pDCB->pNextDCB;
-	DCBDEBUG(printk (KERN_INFO "DC390: Free DCB (ID %i, LUN %i): 0x%08x\n",\
-		pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);)
-	kfree (pDCB);
+	DCBDEBUG(printk (KERN_INFO "DC390: Free DCB (ID %i, LUN %i): %p\n",\
+		pDCB->TargetID, pDCB->TargetLUN, pDCB);)
+	//kfree (pDCB);
+	dc390_remove_dev (pACB, pDCB);
 	pDCB = nDCB;
-    } while (pDCB && pDCB != pACB->pLinkDCB);
+    } while (pDCB && pACB->pLinkDCB);
 
 };
 
-int DC390_release(struct Scsi_Host *host)
+int DC390_release (struct Scsi_Host *host)
 {
-    int irq_count; 
-    PACB pACB;
     DC390_AFLAGS DC390_IFLAGS
-#if USE_SPINLOCKS > 1
     PACB pACB = (PACB)(host->hostdata);
-#endif
 
     DC390_LOCK_IO;
     DC390_LOCK_ACB;
 
+    /* TO DO: We should check for outstanding commands first. */
     dc390_shutdown (host);
 
     if (host->irq != IRQ_NONE)
     {
-	for (irq_count = 0, pACB = dc390_pACB_start; 
-	     pACB; pACB = pACB->pNextACB)
-	{
-	    if ( pACB->IRQLevel == host->irq )
-		++irq_count;
-	}
-	if (irq_count == 1)
-	 {
-	    DEBUG0(printk(KERN_INFO "DC390: Free IRQ %i\n",host->irq);)
-	    free_irq(host->irq,NULL);
-	 }
+	DEBUG0(printk(KERN_INFO "DC390: Free IRQ %i\n",host->irq);)
+	free_irq (host->irq, pACB);
     }
 
     release_region(host->io_port,host->n_io_port);
@@ -2591,7 +3049,12 @@
     DC390_UNLOCK_IO;
     return( 1 );
 }
+#endif /* def MODULE */
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99)
+static Scsi_Host_Template driver_template = DC390_T;
+#include "scsi_module.c"
+#elif defined(MODULE)
 Scsi_Host_Template driver_template = DC390_T;
 #include "scsi_module.c"
-#endif /* def MODULE */
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/tmscsim.h linux.19p6/drivers/scsi/tmscsim.h
--- linux.vanilla/drivers/scsi/tmscsim.h	Fri Apr 21 23:21:08 2000
+++ linux.19p6/drivers/scsi/tmscsim.h	Sat Dec 30 19:46:47 2000
@@ -3,33 +3,47 @@
 ;*		    TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter  *
 ;*		    Device Driver				       *
 ;***********************************************************************/
-/* $Id: tmscsim.h,v 2.4 1998/12/25 17:33:27 garloff Exp $ */
+/* $Id: tmscsim.h,v 2.15.2.3 2000/11/17 20:52:27 garloff Exp $ */
 
 #ifndef _TMSCSIM_H
 #define _TMSCSIM_H
 
+#include <linux/types.h>
+#include <linux/config.h>
+/* 2.0 compat */
+#if defined(__SMP__) && !defined(CONFIG_SMP)
+# if LINUX_VERSION_CODE < KERNEL_VERSION (2,2,0)
+#  define CONFIG_SMP
+# else
+#  error __SMP__ defined but not CONFIG_SMP
+# endif
+#endif
+
+
 #define IRQ_NONE 255
 
 #define MAX_ADAPTER_NUM 	4
-#define MAX_SG_LIST_BUF 	16
-#define MAX_CMD_PER_LUN 	8
-#define MAX_CMD_QUEUE		2*MAX_CMD_PER_LUN+1	
+#define MAX_SG_LIST_BUF 	16	/* Not used */
+#define MAX_CMD_PER_LUN 	32
+#define MAX_CMD_QUEUE		MAX_CMD_PER_LUN+MAX_CMD_PER_LUN/2+1	
 #define MAX_SCSI_ID		8
 #define MAX_SRB_CNT		MAX_CMD_QUEUE+1	/* Max number of started commands */
-#define END_SCAN		2
 
 #define SEL_TIMEOUT		153	/* 250 ms selection timeout (@ 40 MHz) */
 
-typedef unsigned char	UCHAR;
-typedef unsigned short	USHORT;
-typedef unsigned long	ULONG;
-typedef unsigned int	UINT;
+#define END_SCAN		2
+
+typedef u8		UCHAR;	/*  8 bits */
+typedef u16		USHORT;	/* 16 bits */
+typedef u32		UINT;	/* 32 bits */
+typedef unsigned long	ULONG;	/* 32/64 bits */
 
 typedef UCHAR		*PUCHAR;
 typedef USHORT		*PUSHORT;
+typedef UINT		*PUINT;
 typedef ULONG		*PULONG;
-typedef Scsi_Host_Template  *PSHT;
-typedef struct Scsi_Host    *PSH;
+typedef Scsi_Host_Template	*PSHT;
+typedef struct Scsi_Host	*PSH;
 typedef Scsi_Device	*PSCSIDEV;
 typedef Scsi_Cmnd	*PSCSICMD;
 typedef void		*PVOID;
@@ -72,48 +86,49 @@
 */
 struct	_SRB
 {
-UCHAR		CmdBlock[12];
+//UCHAR		CmdBlock[12];
 
 struct _SRB	*pNextSRB;
 struct _DCB	*pSRBDCB;
 PSCSICMD	pcmd;
 PSGL		pSegmentList;
 
-ULONG		Segment0[2];
-ULONG		Segment1[2];
+/* 0x10: */
+SGL		Segmentx;	/* make a one entry of S/G list table */
 
-/* 0x2c:*/
-ULONG		TotalXferredLen;
+/* 0x1c: */
 ULONG		SGBusAddr;	/*;a segment starting address as seen by AM53C974A*/
 ULONG		SGToBeXferLen;	/*; to be xfer length */
-ULONG		SRBState;
-
-/* 0x3c: */
-UCHAR		MsgInBuf[6];
-UCHAR		MsgOutBuf[6];
-
-/* 0x48: */
-SGL		Segmentx;	/* make a one entry of S/G list table */
-
-UCHAR		ScsiCmdLen;
-UCHAR		ScsiPhase;
+ULONG		TotalXferredLen;
+ULONG		SavedTotXLen;
+UINT		SRBState;
 
+/* 0x30: */
+UCHAR		SRBStatus;
+UCHAR		SRBFlag;	/*; b0-AutoReqSense,b6-Read,b7-write */
+				/*; b4-settimeout,b5-Residual valid */
 UCHAR		AdaptStatus;
 UCHAR		TargetStatus;
 
-/* 0x58: */
+UCHAR		ScsiPhase;
+UCHAR		TagNumber;
+UCHAR		SGIndex;
+UCHAR		SGcount;
+
+/* 0x38: */
 UCHAR		MsgCnt;
 UCHAR		EndMessage;
 UCHAR		RetryCnt;
-UCHAR		SRBFlag;	/*; b0-AutoReqSense,b6-Read,b7-write */
-				/*; b4-settimeout,b5-Residual valid */
-UCHAR		TagNumber;
-UCHAR		SGcount;
-UCHAR		SGIndex;
-UCHAR		SRBStatus;
-  //UCHAR		IORBFlag;	/*;81h-Reset, 2-retry */
+UCHAR		SavedSGCount;			
+
+ULONG		Saved_Ptr;
+
+/* 0x40: */
+UCHAR		MsgInBuf[6];
+UCHAR		MsgOutBuf[6];
 
-/* 0x60: */
+//UCHAR		IORBFlag;	/*;81h-Reset, 2-retry */
+/* 0x4c: */
 };
 
 
@@ -129,48 +144,47 @@
 struct _DCB	*pNextDCB;
 struct _ACB	*pDCBACB;
 
-PSCSICMD	pQIORBhead;
-PSCSICMD	pQIORBtail;
-PSCSICMD	AboIORBhead;
-PSCSICMD	AboIORBtail;
-ULONG		QIORBCnt;
-ULONG		AboIORBcnt;
+/* Aborted Commands */
+//PSCSICMD	AboIORBhead;
+//PSCSICMD	AboIORBtail;
+//ULONG		AboIORBcnt;
 
-/* 0x20: */
+/* 0x08: */
+/* Queued SRBs */
 PSRB		pWaitingSRB;
 PSRB		pWaitLast;
 PSRB		pGoingSRB;
 PSRB		pGoingLast;
 PSRB		pActiveSRB;
+UCHAR		WaitSRBCnt;	/* Not used */
 UCHAR		GoingSRBCnt;
-UCHAR		WaitSRBCnt;	/* ??? */
+
 UCHAR		DevType;
 UCHAR		MaxCommand;
 
-/* 0x38: */
-ULONG		TagMask;
+/* 0x20: */
+UINT		TagMask;
 
-UCHAR		UnitSCSIID;	/*; SCSI Target ID  (SCSI Only) */
-UCHAR		UnitSCSILUN;	/*; SCSI Log.  Unit (SCSI Only) */
+UCHAR		TargetID;	/*; SCSI Target ID  (SCSI Only) */
+UCHAR		TargetLUN;	/*; SCSI Log.  Unit (SCSI Only) */
 UCHAR		DevMode;
-UCHAR		IdentifyMsg;
+UCHAR		DCBFlag;
 
 UCHAR		CtrlR1;
 UCHAR		CtrlR3;
 UCHAR		CtrlR4;
+UCHAR		Inquiry7;
 
-UCHAR		DCBFlag;
-
-/* 0x44: */
+/* 0x2c: */
 UCHAR		SyncMode;	/*; 0:async mode */
 UCHAR		NegoPeriod;	/*;for nego. */
 UCHAR		SyncPeriod;	/*;for reg. */
 UCHAR		SyncOffset;	/*;for reg. and nego.(low nibble) */
 
-/* 0x48:*/
+/* 0x30:*/
 //UCHAR		InqDataBuf[8];
 //UCHAR		CapacityBuf[8];
-/* 0x58: */
+///* 0x40: */
 };
 
 typedef  struct  _DCB	 DC390_DCB, *PDCB;
@@ -201,16 +215,24 @@
 PDCB		pLinkDCB;
 PDCB		pLastDCB;
 PDCB		pDCBRunRobin;
+
 PDCB		pActiveDCB;
 PSRB		pFreeSRB;
 PSRB		pTmpSRB;
 
 /* 0x2c: */
+ULONG		QueryCnt;
+PSCSICMD	pQueryHead;
+PSCSICMD	pQueryTail;
 
+/* 0x38: */
 UCHAR		msgin123[4];
 UCHAR		DCBmap[MAX_SCSI_ID];
+UCHAR		Connected;
+UCHAR		pad;
 
-#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(__SMP__) || DEBUG_SPINLOCKS > 0)
+/* 0x3c: */
+#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(CONFIG_SMP) || DEBUG_SPINLOCKS > 0)
 spinlock_t	lock;
 #endif
 UCHAR		sel_timeout;
@@ -220,18 +242,20 @@
 UCHAR		Ignore_IRQ;	/* Not used */
 
 PDEVDECL1;			/* Pointer to PCI cfg. space */
-/* 0x40/0x3c: */
+/* 0x4c/0x48: */
 ULONG		Cmds;
-ULONG		CmdInQ;
-ULONG		CmdOutOfSRB;
-ULONG		SelLost;
-
+UINT		SelLost;
+UINT		SelConn;
+UINT		CmdInQ;
+UINT		CmdOutOfSRB;
 	
-/* 0x50/0x4c: */	
+/* 0x60/0x5c: */
+struct timer_list	Waiting_Timer;
+/* 0x74/0x70: */
 DC390_SRB	TmpSRB;
-/* 0xb4/0xb0: */
-DC390_SRB	SRB_array[MAX_SRB_CNT]; 	/* 18 SRBs */
-/* 0x7bc/0x7b8: */
+/* 0xd8/0xd4: */
+DC390_SRB	SRB_array[MAX_SRB_CNT]; 	/* 50 SRBs */
+/* 0xfb0/0xfac: */
 };
 
 typedef  struct  _ACB	 DC390_ACB, *PACB;
@@ -339,21 +363,29 @@
 #define H_BAD_CCB_OR_SG  0x1A
 #define H_ABORT 	 0x0FF
 
-/*; SCSI Status byte codes*/ /* Twice the values defined in scsi/scsi.h */
-#define SCSI_STAT_GOOD		0x0	/*;  Good status */
-#define SCSI_STAT_CHECKCOND	0x02	/*;  SCSI Check Condition */
-#define SCSI_STAT_CONDMET	0x04	/*;  Condition Met */
-#define SCSI_STAT_BUSY		0x08	/*;  Target busy status */
-#define SCSI_STAT_INTER 	0x10	/*;  Intermediate status */
-#define SCSI_STAT_INTERCONDMET	0x14	/*;  Intermediate condition met */
-#define SCSI_STAT_RESCONFLICT	0x18	/*;  Reservation conflict */
-#define SCSI_STAT_CMDTERM	0x22	/*;  Command Terminated */
-#define SCSI_STAT_QUEUEFULL	0x28	/*;  Queue Full */
+/*; SCSI Status byte codes*/ 
+/* The values defined in include/scsi/scsi.h, to be shifted << 1 */
 
 #define SCSI_STAT_UNEXP_BUS_F	0xFD	/*;  Unexpect Bus Free */
 #define SCSI_STAT_BUS_RST_DETECT 0xFE	/*;  Scsi Bus Reset detected */
 #define SCSI_STAT_SEL_TIMEOUT	0xFF	/*;  Selection Time out */
 
+/* cmd->result */
+#define RES_TARGET		0x000000FF	/* Target State */
+#define RES_TARGET_LNX		STATUS_MASK	/* Only official ... */
+#define RES_ENDMSG		0x0000FF00	/* End Message */
+#define RES_DID			0x00FF0000	/* DID_ codes */
+#define RES_DRV			0xFF000000	/* DRIVER_ codes */
+
+#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt))
+#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1)
+
+#define SET_RES_TARGET(who,tgt) { who &= ~RES_TARGET; who |= (int)(tgt); }
+#define SET_RES_TARGET_LNX(who,tgt) { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; }
+#define SET_RES_MSG(who,msg) { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; }
+#define SET_RES_DID(who,did) { who &= ~RES_DID; who |= (int)(did) << 16; }
+#define SET_RES_DRV(who,drv) { who &= ~RES_DRV; who |= (int)(drv) << 24; }
+
 /*;---Sync_Mode */
 #define SYNC_DISABLE	0
 #define SYNC_ENABLE	BIT0
@@ -375,31 +407,8 @@
 #define SCSI_MSG_OUT	6
 #define SCSI_MSG_IN	7
 
-/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */
-#define MSG_COMPLETE		0x00
-#define MSG_EXTENDED		0x01
-#define MSG_SAVE_PTR		0x02
-#define MSG_RESTORE_PTR 	0x03
-#define MSG_DISCONNECT		0x04
-#define MSG_INITIATOR_ERROR	0x05
-#define MSG_ABORT		0x06
-#define MSG_REJECT_		0x07
-#define MSG_NOP 		0x08
-#define MSG_PARITY_ERROR	0x09
-#define MSG_LINK_CMD_COMPL	0x0A
-#define MSG_LINK_CMD_COMPL_FLG	0x0B
-#define MSG_BUS_RESET		0x0C
-#define MSG_ABORT_TAG		0x0D
-#define MSG_SIMPLE_QTAG 	0x20
-#define MSG_HEAD_QTAG		0x21
-#define MSG_ORDER_QTAG		0x22
-#define MSG_IDENTIFY		0x80
-#define MSG_HOST_ID		0x0C0
-
-/* cmd->result */
-#define STATUS_MASK_		0xFF
-#define MSG_MASK		0xFF00
-#define RETURN_MASK		0xFF0000
+/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */ /* One is missing ! */
+#define ABORT_TAG	0x0d
 
 /*
 **  Inquiry Data format
@@ -439,8 +448,12 @@
 /*  Peripheral Device Type definitions */
 /*  see include/scsi/scsi.h for the rest */
 
-#define TYPE_PRINTER		 0x02	   /* Printer device		   */
-#define TYPE_COMM		 0x09	   /* Communications device	   */
+#ifndef TYPE_PRINTER
+# define TYPE_PRINTER		 0x02	   /* Printer device		   */
+#endif
+#ifndef TYPE_COMM
+# define TYPE_COMM		 0x09	   /* Communications device	   */
+#endif
 
 /*
 ** Inquiry flag definitions (Inq data byte 7)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ultrastor.c linux.19p6/drivers/scsi/ultrastor.c
--- linux.vanilla/drivers/scsi/ultrastor.c	Fri Apr 21 12:46:33 2000
+++ linux.19p6/drivers/scsi/ultrastor.c	Thu Dec 21 22:57:30 2000
@@ -882,9 +882,8 @@
 	(inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
       {
 	int flags;
-	save_flags(flags);
 	printk("Ux4F: abort while completed command pending\n");
-	restore_flags(flags);
+	save_flags(flags);
 	cli();
 	ultrastor_interrupt(0, NULL, NULL);
 	restore_flags(flags);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/Config.in linux.19p6/drivers/sound/Config.in
--- linux.vanilla/drivers/sound/Config.in	Mon Dec 11 22:12:55 2000
+++ linux.19p6/drivers/sound/Config.in	Wed Jan  3 13:26:18 2001
@@ -35,6 +35,7 @@
 fi
 
 dep_tristate 'ESS Maestro' CONFIG_SOUND_MAESTRO $CONFIG_SOUND
+dep_tristate 'ESS Maestro3' CONFIG_SOUND_MAESTRO3 $CONFIG_SOUND
 dep_tristate 'ESS Solo1' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND
 
 dep_tristate 'Intel ICH (810, 820, 440MX...)' CONFIG_SOUND_ICH $CONFIG_SOUND
@@ -311,9 +312,9 @@
   fi
   
   dep_tristate 'NM256AV/NM256ZX audio support' CONFIG_SOUND_NM256 $CONFIG_SOUND_OSS
-  dep_tristate 'Yamaha PCI legacy mode support' CONFIG_SOUND_YMPCI $CONFIG_SOUND_OSS
-  if [ "$CONFIG_SOUND_YMPCI" = "n" ]; then
-    dep_tristate 'Yamaha PCI native mode support (EXPERIMENTAL)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS
+  dep_tristate 'Yamaha PCI native mode support' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_YMFPCI" != "n" ]; then
+    bool '  Yamaha PCI legacy ports support' CONFIG_SOUND_YMFPCI_LEGACY
   fi
 
   # Additional low level drivers.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/Makefile linux.19p6/drivers/sound/Makefile
--- linux.vanilla/drivers/sound/Makefile	Mon Dec 11 22:12:55 2000
+++ linux.19p6/drivers/sound/Makefile	Wed Jan  3 13:26:18 2001
@@ -94,10 +94,14 @@
 obj-$(CONFIG_SOUND_FUSION)	+= cs46xx.o ac97_codec.o
 obj-$(CONFIG_SOUND_ICH)		+= i810_audio.o ac97_codec.o
 obj-$(CONFIG_SOUND_MAESTRO)	+= maestro.o
+obj-$(CONFIG_SOUND_MAESTRO3)	+= maestro3.o
 obj-$(CONFIG_SOUND_SONICVIBES)	+= sonicvibes.o
 obj-$(CONFIG_SOUND_TRIDENT)     += trident.o ac97_codec.o
 obj-$(CONFIG_SOUND_VIA82CXXX)	+= via82cxxx_audio.o ac97_codec.o
 obj-$(CONFIG_SOUND_YMFPCI)	+= ymfpci.o ac97_codec.o
+ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
+  obj-$(CONFIG_SOUND_YMFPCI)	+= opl3.o uart401.o
+endif
 
 ifeq ($(CONFIG_SOUND_EMU10K1),y)
   obj-y				+= emu10k1/emu10k1.o
@@ -126,7 +130,6 @@
 wavefront-objs  := wavfront.o wf_midi.o yss225.o
 nm256-objs	:= nm256_audio.o ac97.o
 via82cxxx-objs  := via82cxxx_audio.o ac97.o
-ymfsb-objs	:= ymf_sb.o ac97.o
 
 
 # Extract lists of the multi-part drivers.
@@ -217,9 +220,6 @@
 
 via82cxxx.o: $(via82cxxx-objs)
 	$(LD) -r -o $@ $(via82cxxx-objs)
-
-ymfsb.o: $(ymfsb-objs)
-	$(LD) -r -o $@ $(ymfsb-objs)
 
 # Firmware files that need translation
 #
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cs46xx.c linux.19p6/drivers/sound/cs46xx.c
--- linux.vanilla/drivers/sound/cs46xx.c	Mon Dec 11 22:12:55 2000
+++ linux.19p6/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/sound/emu10k1/main.c linux.19p6/drivers/sound/emu10k1/main.c
--- linux.vanilla/drivers/sound/emu10k1/main.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/emu10k1/main.c	Thu Dec 21 23:00:02 2000
@@ -784,10 +784,3 @@
 
 module_init(emu10k1_init_module);
 module_exit(emu10k1_cleanup_module);
-
-#ifndef MODULE
-int __init init_emu10k1(void)
-{
-        return emu10k1_init_module();
-}
-#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/es1370.c linux.19p6/drivers/sound/es1370.c
--- linux.vanilla/drivers/sound/es1370.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/es1370.c	Tue Dec 19 02:13:08 2000
@@ -114,6 +114,11 @@
  *    03.09.1999   0.30  change read semantics for MIDI to match
  *                       OSS more closely; remove possible wakeup race
  *    28.10.1999   0.31  More waitqueue races fixed
+ *    08.01.2000   0.32  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *    21.11.2000   0.34  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.35  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -858,7 +863,8 @@
 	VALIDATE_STATE(s);
 	if (cmd == SOUND_MIXER_PRIVATE1) {
 		/* enable/disable/query mixer preamp */
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != -1) {
 			s->mix.micpreamp = !!val;
 			wrcodec(s, 0x19, s->mix.micpreamp);
@@ -867,7 +873,8 @@
 	}
 	if (cmd == SOUND_MIXER_PRIVATE2) {
 		/* enable/disable/query use of linein as second lineout */
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != -1) {
 			spin_lock_irqsave(&s->lock, flags);
 			if (val)
@@ -881,7 +888,8 @@
 	}
 	if (cmd == SOUND_MIXER_PRIVATE3) {
 		/* enable/disable/query microphone impedance setting */
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != -1) {
 			spin_lock_irqsave(&s->lock, flags);
 			if (val)
@@ -957,12 +965,14 @@
 	switch (_IOC_NR(cmd)) {
 
 	case SOUND_MIXER_IMIX:
-		get_user_ret(s->mix.imix, (int *)arg, -EFAULT);
+		if (get_user(s->mix.imix, (int *)arg))
+			return -EFAULT;
 		set_recsrc(s, s->mix.recsrc);
 		return 0;
 
 	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		set_recsrc(s, val);
 		return 0;
 
@@ -970,7 +980,8 @@
 		i = _IOC_NR(cmd);
 		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
 			return -EINVAL;
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		l = val & 0xff;
 		if (l > 100)
 			l = 100;
@@ -1354,6 +1365,7 @@
 	unsigned long flags;
         audio_buf_info abinfo;
         count_info cinfo;
+	int count;
 	int val, mapped, ret;
 
 	VALIDATE_STATE(s);
@@ -1388,7 +1400,8 @@
 		return 0;
 
         case SNDCTL_DSP_SPEED:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val >= 0) {
 			if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE))
 				return -EINVAL;
@@ -1407,7 +1420,8 @@
 		return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg);
 		
         case SNDCTL_DSP_STEREO:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			stop_adc(s);
 			s->dma_adc.ready = 0;
@@ -1433,7 +1447,8 @@
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 0) {
 			if (file->f_mode & FMODE_READ) {
 				stop_adc(s);
@@ -1464,7 +1479,8 @@
                 return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
 		
 	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != AFMT_QUERY) {
 			if (file->f_mode & FMODE_READ) {
 				stop_adc(s);
@@ -1504,7 +1520,8 @@
 		return put_user(val, (int *)arg);
 		
 	case SNDCTL_DSP_SETTRIGGER:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			if (val & PCM_ENABLE_INPUT) {
 				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
@@ -1526,12 +1543,15 @@
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
-		if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0)
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
 		abinfo.fragsize = s->dma_dac2.fragsize;
-                abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac2.dmasize - count;
                 abinfo.fragstotal = s->dma_dac2.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -1540,12 +1560,15 @@
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
-		if (!(s->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0)
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
 		abinfo.fragsize = s->dma_adc.fragsize;
-                abinfo.bytes = s->dma_adc.count;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = count;
                 abinfo.fragstotal = s->dma_adc.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -1558,19 +1581,28 @@
         case SNDCTL_DSP_GETODELAY:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
-                val = s->dma_dac2.count;
+                count = s->dma_dac2.count;
 		spin_unlock_irqrestore(&s->lock, flags);
-		return put_user(val, (int *)arg);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
 
         case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -1580,10 +1612,15 @@
         case SNDCTL_DSP_GETOPTR:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
                 cinfo.bytes = s->dma_dac2.total_bytes;
-                cinfo.blocks = s->dma_dac2.count >> s->dma_dac2.fragshift;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac2.fragshift;
                 cinfo.ptr = s->dma_dac2.hwptr;
 		if (s->dma_dac2.mapped)
 			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
@@ -1601,7 +1638,8 @@
 		return put_user(s->dma_adc.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			s->dma_adc.ossfragshift = val & 0xffff;
 			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
@@ -1628,7 +1666,8 @@
 		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
 		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
 			return -EINVAL;
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		if (file->f_mode & FMODE_READ)
@@ -1878,6 +1917,7 @@
 	unsigned long flags;
         audio_buf_info abinfo;
         count_info cinfo;
+	int count;
 	unsigned ctrl;
 	int val, ret;
 
@@ -1902,7 +1942,8 @@
 		return 0;
 
         case SNDCTL_DSP_SPEED:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val >= 0) {
 			stop_dac1(s);
 			s->dma_dac1.ready = 0;
@@ -1917,7 +1958,8 @@
 		return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg);
 		
         case SNDCTL_DSP_STEREO:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		stop_dac1(s);
 		s->dma_dac1.ready = 0;
 		spin_lock_irqsave(&s->lock, flags);
@@ -1930,7 +1972,8 @@
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 0) {
 			if (s->dma_dac1.mapped)
 				return -EINVAL;
@@ -1950,7 +1993,8 @@
                 return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
 		
         case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != AFMT_QUERY) {
 			stop_dac1(s);
 			s->dma_dac1.ready = 0;
@@ -1971,7 +2015,8 @@
 		return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg);
 						
 	case SNDCTL_DSP_SETTRIGGER:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val & PCM_ENABLE_OUTPUT) {
 			if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
 				return ret;
@@ -1981,12 +2026,15 @@
 		return 0;
 
 	case SNDCTL_DSP_GETOSPACE:
-		if (!(s->ctrl & CTRL_DAC1_EN) && (val = prog_dmabuf_dac1(s)) != 0)
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
 		abinfo.fragsize = s->dma_dac1.fragsize;
-                abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac1.dmasize - count;
                 abinfo.fragstotal = s->dma_dac1.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -1997,19 +2045,26 @@
                 return 0;
 
         case SNDCTL_DSP_GETODELAY:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
-                val = s->dma_dac1.count;
+                count = s->dma_dac1.count;
 		spin_unlock_irqrestore(&s->lock, flags);
-		return put_user(val, (int *)arg);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
 
         case SNDCTL_DSP_GETOPTR:
-		if (!(file->f_mode & FMODE_WRITE))
-			return -EINVAL;
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
                 cinfo.bytes = s->dma_dac1.total_bytes;
-                cinfo.blocks = s->dma_dac1.count >> s->dma_dac1.fragshift;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac1.fragshift;
                 cinfo.ptr = s->dma_dac1.hwptr;
 		if (s->dma_dac1.mapped)
 			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
@@ -2022,7 +2077,8 @@
                 return put_user(s->dma_dac1.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		s->dma_dac1.ossfragshift = val & 0xffff;
 		s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
 		if (s->dma_dac1.ossfragshift < 4)
@@ -2036,7 +2092,8 @@
         case SNDCTL_DSP_SUBDIVIDE:
 		if (s->dma_dac1.subdivision)
 			return -EINVAL;
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		s->dma_dac1.subdivision = val;
@@ -2463,7 +2520,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.31 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.35 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/es1371.c linux.19p6/drivers/sound/es1371.c
--- linux.vanilla/drivers/sound/es1371.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/es1371.c	Tue Dec 19 02:13:08 2000
@@ -96,6 +96,12 @@
  *                       detect ES137x chip and derivatives.
  *    05.01.2000   0.22  Should now work with rev7 boards; patch by
  *                       Eric Lemar, elemar@cs.washington.edu
+ *    08.01.2000   0.23  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *    01.03.2000   0.26  SPDIF patch by Mikael Bouillot <mikael.bouillot@bigfoot.com>
+ *    21.11.2000   0.27  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.28  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
  */
 
 /*****************************************************************************/
@@ -1937,6 +1943,7 @@
 	unsigned long flags;
         audio_buf_info abinfo;
         count_info cinfo;
+	int count;
 	int val, mapped, ret;
 
 	VALIDATE_STATE(s);
@@ -1971,7 +1978,8 @@
 		return 0;
 
         case SNDCTL_DSP_SPEED:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val >= 0) {
 			if (file->f_mode & FMODE_READ) {
 				stop_adc(s);
@@ -1987,7 +1995,8 @@
 		return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg);
 
         case SNDCTL_DSP_STEREO:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			stop_adc(s);
 			s->dma_adc.ready = 0;
@@ -2013,7 +2022,8 @@
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 0) {
 			if (file->f_mode & FMODE_READ) {
 				stop_adc(s);
@@ -2044,7 +2054,8 @@
                 return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
 		
 	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != AFMT_QUERY) {
 			if (file->f_mode & FMODE_READ) {
 				stop_adc(s);
@@ -2084,7 +2095,8 @@
 		return put_user(val, (int *)arg);
 		
 	case SNDCTL_DSP_SETTRIGGER:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			if (val & PCM_ENABLE_INPUT) {
 				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
@@ -2106,12 +2118,15 @@
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
-		if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0)
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
 		abinfo.fragsize = s->dma_dac2.fragsize;
-                abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac2.dmasize - count;
                 abinfo.fragstotal = s->dma_dac2.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -2120,12 +2135,15 @@
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
-		if (!(s->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0)
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
 		abinfo.fragsize = s->dma_adc.fragsize;
-                abinfo.bytes = s->dma_adc.count;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = count;
                 abinfo.fragstotal = s->dma_adc.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -2138,19 +2156,28 @@
         case SNDCTL_DSP_GETODELAY:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
-                val = s->dma_dac2.count;
+                count = s->dma_dac2.count;
 		spin_unlock_irqrestore(&s->lock, flags);
-		return put_user(val, (int *)arg);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
 
         case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -2160,10 +2187,15 @@
         case SNDCTL_DSP_GETOPTR:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
                 cinfo.bytes = s->dma_dac2.total_bytes;
-                cinfo.blocks = s->dma_dac2.count >> s->dma_dac2.fragshift;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac2.fragshift;
                 cinfo.ptr = s->dma_dac2.hwptr;
 		if (s->dma_dac2.mapped)
 			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
@@ -2181,7 +2213,8 @@
 		return put_user(s->dma_adc.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			s->dma_adc.ossfragshift = val & 0xffff;
 			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
@@ -2208,7 +2241,8 @@
 		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
 		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
 			return -EINVAL;
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		if (file->f_mode & FMODE_READ)
@@ -2458,6 +2492,7 @@
 	unsigned long flags;
         audio_buf_info abinfo;
         count_info cinfo;
+	int count;
 	int val, ret;
 
 	VALIDATE_STATE(s);
@@ -2481,7 +2516,8 @@
 		return 0;
 
         case SNDCTL_DSP_SPEED:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val >= 0) {
 			stop_dac1(s);
 			s->dma_dac1.ready = 0;
@@ -2490,7 +2526,8 @@
 		return put_user(s->dac1rate, (int *)arg);
 
         case SNDCTL_DSP_STEREO:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		stop_dac1(s);
 		s->dma_dac1.ready = 0;
 		spin_lock_irqsave(&s->lock, flags);
@@ -2503,7 +2540,8 @@
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 0) {
 			stop_dac1(s);
 			s->dma_dac1.ready = 0;
@@ -2521,7 +2559,8 @@
                 return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
 		
         case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != AFMT_QUERY) {
 			stop_dac1(s);
 			s->dma_dac1.ready = 0;
@@ -2542,7 +2581,8 @@
 		return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg);
 						
 	case SNDCTL_DSP_SETTRIGGER:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val & PCM_ENABLE_OUTPUT) {
 			if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
 				return ret;
@@ -2552,12 +2592,15 @@
 		return 0;
 
 	case SNDCTL_DSP_GETOSPACE:
-		if (!(s->ctrl & CTRL_DAC1_EN) && (val = prog_dmabuf_dac1(s)) != 0)
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
 		abinfo.fragsize = s->dma_dac1.fragsize;
-                abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac1.dmasize - count;
                 abinfo.fragstotal = s->dma_dac1.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -2568,19 +2611,26 @@
                 return 0;
 
         case SNDCTL_DSP_GETODELAY:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
-                val = s->dma_dac1.count;
+                count = s->dma_dac1.count;
 		spin_unlock_irqrestore(&s->lock, flags);
-		return put_user(val, (int *)arg);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
 
         case SNDCTL_DSP_GETOPTR:
-		if (!(file->f_mode & FMODE_WRITE))
-			return -EINVAL;
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
                 cinfo.bytes = s->dma_dac1.total_bytes;
-                cinfo.blocks = s->dma_dac1.count >> s->dma_dac1.fragshift;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac1.fragshift;
                 cinfo.ptr = s->dma_dac1.hwptr;
 		if (s->dma_dac1.mapped)
 			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
@@ -2593,7 +2643,8 @@
                 return put_user(s->dma_dac1.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		s->dma_dac1.ossfragshift = val & 0xffff;
 		s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
 		if (s->dma_dac1.ossfragshift < 4)
@@ -2607,7 +2658,8 @@
         case SNDCTL_DSP_SUBDIVIDE:
 		if (s->dma_dac1.subdivision)
 			return -EINVAL;
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		s->dma_dac1.subdivision = val;
@@ -3261,7 +3313,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1371: version v0.22 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1371: version v0.28 time " __TIME__ " " __DATE__ "\n");
 	for (pcidev = pci_devices; pcidev && index < NR_DEVICE; pcidev = pcidev->next) {
 		if (pcidev->vendor == PCI_VENDOR_ID_ENSONIQ) {
 			if (pcidev->device != PCI_DEVICE_ID_ENSONIQ_ES1371 &&
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/esssolo1.c linux.19p6/drivers/sound/esssolo1.c
--- linux.vanilla/drivers/sound/esssolo1.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/esssolo1.c	Tue Dec 19 02:13:08 2000
@@ -63,7 +63,12 @@
  *    28.10.1999   0.10  More waitqueue races fixed
  *    09.12.1999   0.11  Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M)
  *                       Disabling recording on Alpha
- *
+ *    12.01.2000   0.12  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *                       Integrated (aka redid 8-)) APM support patch by Zach Brown
+ *    21.11.2000   0.16  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.17  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
  */
 
 /*****************************************************************************/
@@ -1276,7 +1281,8 @@
 		return 0;
 
         case SNDCTL_DSP_SPEED:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val >= 0) {
 			stop_adc(s);
 			stop_dac(s);
@@ -1303,7 +1309,8 @@
 		return put_user(s->rate, (int *)arg);
 		
         case SNDCTL_DSP_STEREO:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		stop_adc(s);
 		stop_dac(s);
 		s->dma_adc.ready = s->dma_dac.ready = 0;
@@ -1313,7 +1320,8 @@
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 0) {
 			stop_adc(s);
 			stop_dac(s);
@@ -1328,7 +1336,8 @@
                 return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
 
 	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != AFMT_QUERY) {
 			stop_adc(s);
 			stop_dac(s);
@@ -1354,7 +1363,8 @@
 		return put_user(val, (int *)arg);
 
 	case SNDCTL_DSP_SETTRIGGER:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			if (val & PCM_ENABLE_INPUT) {
 				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
@@ -1378,7 +1388,7 @@
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
-		if (!(s->ena & FMODE_WRITE) && (val = prog_dmabuf_dac(s)) != 0)
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		solo1_update_ptr(s);
@@ -1395,7 +1405,7 @@
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
-		if (!(s->ena & FMODE_READ) && (val = prog_dmabuf_adc(s)) != 0)
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		solo1_update_ptr(s);
@@ -1413,6 +1423,8 @@
         case SNDCTL_DSP_GETODELAY:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		solo1_update_ptr(s);
                 count = s->dma_dac.count;
@@ -1424,13 +1436,12 @@
         case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		solo1_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-		count = s->dma_dac.count;
-		if (count < 0)
-			count = 0;
-		cinfo.blocks = count >> s->dma_dac.fragshift;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -1440,10 +1451,15 @@
         case SNDCTL_DSP_GETOPTR:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		solo1_update_ptr(s);
                 cinfo.bytes = s->dma_dac.total_bytes;
-                cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac.fragshift;
                 cinfo.ptr = s->dma_dac.hwptr;
 		if (s->dma_dac.mapped)
 			s->dma_dac.count &= s->dma_dac.fragsize-1;
@@ -1467,7 +1483,8 @@
 		return put_user(s->dma_adc.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			s->dma_adc.ossfragshift = val & 0xffff;
 			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
@@ -1494,7 +1511,8 @@
 		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
 		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
 			return -EINVAL;
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		if (file->f_mode & FMODE_READ)
@@ -2170,7 +2188,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "solo1: version v0.11 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "solo1: version v0.17 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) {
 		if (pcidev->base_address[0] == 0 ||
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/maestro3.c linux.19p6/drivers/sound/maestro3.c
--- linux.vanilla/drivers/sound/maestro3.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/sound/maestro3.c	Wed Jan  3 01:01:13 2001
@@ -0,0 +1,3033 @@
+/*****************************************************************************
+ *
+ *      ESS Maestro3/Allegro driver for Linux 2.2.x
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    (c) Copyright 2000 Zach Brown <zab@zabbo.net>
+ *
+ * I need to thank many people for helping make this driver happen.  
+ * As always, Eric Brombaugh was a hacking machine and killed many bugs
+ * that I was too dumb to notice.  Howard Kim at ESS provided reference boards 
+ * and as much docs as he could.  Todd and Mick at Dell tested snapshots on 
+ * an army of laptops.  msw and deviant at Red Hat also humoured me by hanging
+ * their laptops every few hours in the name of science.
+ * 
+ * Shouts go out to Mike "DJ XPCom" Ang.
+ *
+ * History
+ *  v0.51 - Dec 31 2000 - Zach Brown <zab@zabbo.net>
+ *   fix up incredibly broken open/release resource management
+ *   duh.  fix record format setting.
+ *   add SMP locking and cleanup formatting here and there
+ *  v0.50 - Dec 16 2000 - Zach Brown <zab@zabbo.net>
+ *   use native ac97_codec
+ *   pull out most SILLY_ stuff
+ *   align instance allocation so record works
+ *   fix up PCI IDs..
+ *  v0.02 - Nov 04 2000 - Zach Brown <zab@zabbo.net>
+ *   changed clocking setup for m3, slowdown fixed.
+ *   codec reset is hopefully reliable now
+ *   rudimentary apm/power management makes suspend/resume work
+ *  v0.01 - Oct 31 2000 - Zach Brown <zab@zabbo.net>
+ *   first release
+ *  v0.00 - Sep 09 2000 - Zach Brown <zab@zabbo.net>
+ *   first pass derivation from maestro.c
+ *
+ * TODO
+ *  no beep on init (mute)
+ *  resetup msrc data memory if freq changes?
+ *  clean up driver in general, 2.2/2.3 junk, etc.
+ *
+ *  --- 
+ *
+ *  Allow me to ramble a bit about the m3 architecture.  The core of the
+ *  chip is the 'assp', the custom ESS dsp that runs the show.  It has
+ *  a small amount of code and data ram.  ESS drops binary dsp code images
+ *  on our heads, but we don't get to see specs on the dsp.  
+ *
+ *  The constant piece of code on the dsp is the 'kernel'.  It also has a 
+ *  chunk of the dsp memory that is statically set aside for its control
+ *  info.  This is the KDATA defines in maestro3.h.  Part of its core
+ *  data is a list of code addresses that point to the pieces of DSP code
+ *  that it should walk through in its loop.  These other pieces of code
+ *  do the real work.  The kernel presumably jumps into each of them in turn.
+ *  These code images tend to have their own data area, and one can have
+ *  multiple data areas representing different states for each of the 'client
+ *  instance' code portions.  There is generaly a list in the kernel data
+ *  that points to the data instances for a given piece of code.
+ *
+ *  We've only been given the binary image for the 'minisrc', mini sample 
+ *  rate converter.  This is rather annoying because it limits the work
+ *  we can do on the dsp, but it also greatly simplifies the job of managing
+ *  dsp data memory for the code and data for our playing streams :).  We
+ *  statically allocate the minisrc code into a region we 'know' to be free
+ *  based on the map of the binary kernel image we're loading.  We also 
+ *  statically allocate the data areas for the maximum number of pcm streams
+ *  we can be dealing with.  This max is set by the length of the static list
+ *  in the kernel data that records the number of minisrc data regions we
+ *  can have.  Thats right, all software dsp mixing with static code list
+ *  limits.  Rock.
+ *
+ *  How sound goes in and out is still a relative mystery.  It appears
+ *  that the dsp has the ability to get input and output through various
+ *  'connections'.  To do IO from or to a connection, you put the address
+ *  of the minisrc client area in the static kernel data lists for that 
+ *  input or output.  so for pcm -> dsp -> mixer, we put the minisrc data
+ *  instance in the DMA list and also in the list for the mixer.  I guess
+ *  it Just Knows which is in/out, and we give some dma control info that
+ *  helps.  There are all sorts of cool inputs/outputs that it seems we can't
+ *  use without dsp code images that know how to use them.
+ *
+ *  So at init time we preload all the memory allocation stuff and set some
+ *  system wide parameters.  When we really get a sound to play we build
+ *  up its minisrc header (stream parameters, buffer addresses, input/output
+ *  settings).  Then we throw its header on the various lists.  We also
+ *  tickle some KDATA settings that ask the assp to raise clock interrupts
+ *  and do some amount of software mixing before handing data to the ac97.
+ *
+ *  Sorry for the vague details.  Feel free to ask Eric or myself if you
+ *  happen to be trying to use this driver elsewhere.  Please accept my
+ *  apologies for the quality of the OSS support code, its passed through
+ *  too many hands now and desperately wants to be rethought.
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/spinlock.h>
+
+ /* 
+  * apologies for old intra-2.2 and 
+  * 2.2->2.4 compat cruft.. it will
+  * die someday.
+  */
+#ifndef wait_queue_head_t 
+    #define wait_queue_head_t struct wait_queue *
+#endif
+#ifndef DECLARE_WAITQUEUE
+    #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL}
+#endif
+#ifndef init_waitqueue_head
+  #define init_waitqueue_head init_waitqueue
+#endif
+
+#define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK)
+#define SILLY_INIT_SEM(SEM) SEM=MUTEX;
+#define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC)
+#define SILLY_OFFSET(VMA) ((VMA)->vm_offset)
+
+#include <linux/ac97_codec.h>
+
+#include "maestro3.h"
+
+#define M_DEBUG 1
+
+#define DRIVER_VERSION      "0.51"
+#define PFX                 "maestro3: "
+
+#ifndef PCI_VENDOR_ESS
+#define PCI_VENDOR_ESS      0x125D
+#endif
+
+#define M3_STATE_MAGIC      0x734d724d
+#define M3_CARD_MAGIC       0x646e6f50
+
+#define ESS_FMT_STEREO      0x01
+#define ESS_FMT_16BIT       0x02
+#define ESS_FMT_MASK        0x03
+#define ESS_DAC_SHIFT       0   
+#define ESS_ADC_SHIFT       4
+
+#define DAC_RUNNING         1
+#define ADC_RUNNING         2
+
+#define SND_DEV_DSP16       5 
+
+   
+#ifdef M_DEBUG
+static int debug=0;
+static int global_dsp_speed = 49;
+#define DPMOD   1   /* per module load */
+#define DPSTR   2   /* per 'stream' */
+#define DPSYS   3   /* per syscall */
+#define DPCRAP  4   /* stuff the user shouldn't see unless they're really debuggin */
+#define DPINT   5   /* per interrupt, LOTS */
+#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);}
+#else
+#define DPRINTK(x)
+#endif
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int m3_apm_callback(apm_event_t ae);
+static int in_suspend=0;
+wait_queue_head_t suspend_queue;
+static void check_suspend(void);
+#else
+#define check_suspend(args...)
+#define in_suspend 0
+#endif
+
+struct m3_list {
+    int curlen;
+    int mem_addr;
+    int max;
+};
+
+int external_amp = 1;
+static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf);
+
+struct notifier_block maestro_nb = {maestro_notifier, NULL, 0};
+
+struct ess_state {
+    unsigned int magic;
+    struct ess_card *card;
+    unsigned char fmt, enable;
+
+    spinlock_t lock;
+
+    int index;
+
+    struct semaphore open_sem;
+    wait_queue_head_t open_wait;
+    mode_t open_mode;
+
+    int dev_audio;
+
+    struct assp_instance {
+        u16 code, data;
+    } dac_inst, adc_inst;
+
+    /* should be in dmabuf */
+    unsigned int rateadc, ratedac;
+
+    struct dmabuf {
+        void *rawbuf;
+        unsigned buforder;
+        unsigned numfrag;
+        unsigned fragshift;
+        unsigned hwptr, swptr;
+        unsigned total_bytes;
+        int count;
+        unsigned error; /* over/underrun */
+        wait_queue_head_t wait;
+        /* redundant, but makes calculations easier */
+        unsigned fragsize;
+        unsigned dmasize;
+        unsigned fragsamples;
+        /* OSS stuff */
+        unsigned mapped:1;
+        unsigned ready:1;    
+        unsigned endcleared:1;
+        unsigned ossfragshift;
+        int ossmaxfrags;
+        unsigned subdivision;
+        /* new in m3 */
+        int mixer_index, dma_index, msrc_index, adc1_index;
+        int in_lists;
+
+    } dma_dac, dma_adc;
+};
+    
+struct ess_card {
+    unsigned int magic;
+
+    struct ess_card *next;
+
+    struct ac97_codec *ac97;
+    spinlock_t ac97_lock;
+
+    int card_type;
+
+#define NR_DSPS 1
+#define MAX_DSPS NR_DSPS
+    struct ess_state channels[MAX_DSPS];
+
+    spinlock_t lock;
+
+    /* hardware resources */
+    struct pci_dev *pcidev;
+    u32 iobase;
+    u32 irq;
+
+    int dacs_active;
+
+    int timer_users;
+
+    struct m3_list  msrc_list,
+                    mixer_list,
+                    adc1_list,
+                    dma_list;
+
+    /* for storing reset state..*/
+    u8 reset_state;
+
+#ifdef CONFIG_APM
+    u16 *suspend_dsp_mem;
+#endif
+};
+
+/*
+ * an arbitrary volume we set the internal
+ * volume settings to so that the ac97 volume
+ * range is a little less insane.  0x7fff is 
+ * max.
+ */
+#define ARB_VOLUME ( 0x6800 )
+
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+enum {
+    ESS_ALLEGRO,
+    ESS_MAESTRO3,
+    /* hardware strapping */
+    ESS_MAESTRO3HW
+};
+
+static struct card_type {
+    int pci_id;
+    int type;
+    char *name;
+} m3_card_types[] = {
+    {0x1988, ESS_ALLEGRO, "Allegro"},
+    {0x1998, ESS_MAESTRO3, "Maestro3(i)"},
+    {0x199a, ESS_MAESTRO3, "Maestro3(i)hw"},
+};
+#define NUM_CARD_TYPES ( sizeof(m3_card_types) / sizeof(m3_card_types[0]) )
+
+static unsigned 
+ld2(unsigned int x)
+{
+    unsigned r = 0;
+    
+    if (x >= 0x10000) {
+        x >>= 16;
+        r += 16;
+    }
+    if (x >= 0x100) {
+        x >>= 8;
+        r += 8;
+    }
+    if (x >= 0x10) {
+        x >>= 4;
+        r += 4;
+    }
+    if (x >= 4) {
+        x >>= 2;
+        r += 2;
+    }
+    if (x >= 2)
+        r++;
+    return r;
+}
+
+static struct ess_card *devs = NULL;
+
+static void m3_outw(struct ess_card *card,
+        u16 value, unsigned long reg)
+{
+    check_suspend();
+    outw(value, card->iobase + reg);
+}
+
+static u16 m3_inw(struct ess_card *card, unsigned long reg)
+{
+    check_suspend();
+    return inw(card->iobase + reg);
+}
+static void m3_outb(struct ess_card *card, 
+        u8 value, unsigned long reg)
+{
+    check_suspend();
+    outb(value, card->iobase + reg);
+}
+static u8 m3_inb(struct ess_card *card, unsigned long reg)
+{
+    check_suspend();
+    return inb(card->iobase + reg);
+}
+
+/*
+ * access 16bit words to the code or data regions of the dsp's memory.
+ * index addresses 16bit words.
+ */
+static u16 __m3_assp_read(struct ess_card *card, u16 region, u16 index)
+{
+    m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+    m3_outw(card, index, DSP_PORT_MEMORY_INDEX);
+    return m3_inw(card, DSP_PORT_MEMORY_DATA);
+}
+static u16 m3_assp_read(struct ess_card *card, u16 region, u16 index)
+{
+    unsigned long flags;
+    u16 ret;
+
+    spin_lock_irqsave(&(card->lock), flags);
+    ret = __m3_assp_read(card, region, index);
+    spin_unlock_irqrestore(&(card->lock), flags);
+
+    return ret;
+}
+static void __m3_assp_write(struct ess_card *card, 
+        u16 region, u16 index, u16 data)
+{
+    m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+    m3_outw(card, index, DSP_PORT_MEMORY_INDEX);
+    m3_outw(card, data, DSP_PORT_MEMORY_DATA);
+}
+
+static void m3_assp_write(struct ess_card *card, 
+        u16 region, u16 index, u16 data)
+{
+    unsigned long flags;
+    spin_lock_irqsave(&(card->lock), flags);
+    __m3_assp_write(card, region, index, data);
+    spin_unlock_irqrestore(&(card->lock), flags);
+}
+
+static void m3_assp_halt(struct ess_card *card)
+{
+    card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
+    mdelay(10);
+    m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+static void m3_assp_continue(struct ess_card *card)
+{
+    m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+/*
+ * This makes me sad. the maestro3 has lists
+ * internally that must be packed.. 0 terminates,
+ * apparently, or maybe all unused entries have
+ * to be 0, the lists have static lengths set
+ * by the binary code images.
+ */
+
+static int m3_add_list(struct ess_card *card,
+        struct m3_list *list, u16 val)
+{
+    DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n",
+            val, list, list->curlen);
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+            list->mem_addr + list->curlen,
+            val);
+
+    return list->curlen++;
+
+}
+
+static void m3_remove_list(struct ess_card *card,
+        struct m3_list *list, int index)
+{
+    u16  val;
+    int lastindex = list->curlen - 1;
+
+    DPRINTK(DPSTR, "removing ind %d from list 0x%p\n",
+            index, list);
+
+    if(index != lastindex) {
+        val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                list->mem_addr + lastindex);
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+                list->mem_addr + index,
+                val);
+    }
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+            list->mem_addr + lastindex,
+            0);
+
+    list->curlen--;
+}
+
+static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data)
+{
+    int tmp;
+
+    s->fmt = (s->fmt & mask) | data;
+
+    tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
+
+    /* write to 'mono' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, 
+            (tmp & ESS_FMT_STEREO) ? 0 : 1);
+    /* write to '8bit' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, 
+            (tmp & ESS_FMT_16BIT) ? 0 : 1);
+
+    tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK;
+
+    /* write to 'mono' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, 
+            (tmp & ESS_FMT_STEREO) ? 0 : 1);
+    /* write to '8bit' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, 
+            (tmp & ESS_FMT_16BIT) ? 0 : 1);
+}
+
+static void set_dac_rate(struct ess_state *s, unsigned int rate)
+{
+    u32 freq;
+
+    if (rate > 48000)
+        rate = 48000;
+    if (rate < 8000)
+        rate = 8000;
+
+    s->ratedac = rate;
+
+    freq = ((rate << 15) + 24000 ) / 48000;
+    if(freq) 
+        freq--;
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + CDATA_FREQUENCY,
+            freq);
+}
+
+static void set_adc_rate(struct ess_state *s, unsigned int rate)
+{
+    u32 freq;
+
+    if (rate > 48000)
+        rate = 48000;
+    if (rate < 8000)
+        rate = 8000;
+
+    s->rateadc = rate;
+
+    freq = ((rate << 15) + 24000 ) / 48000;
+    if(freq) 
+        freq--;
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + CDATA_FREQUENCY,
+            freq);
+}
+
+static void inc_timer_users(struct ess_card *card)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&card->lock, flags);
+    card->timer_users++;
+    DPRINTK(DPSYS, "inc timer users now %d\n",
+            card->timer_users);
+    if(card->timer_users != 1) 
+        goto out;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_RELOAD,
+         240 ) ;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_CURRENT,
+         240 ) ;
+
+    m3_outw(card,  
+            m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE,
+            HOST_INT_CTRL);
+
+out:
+    spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void dec_timer_users(struct ess_card *card)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&card->lock, flags);
+
+    card->timer_users--;
+    DPRINTK(DPSYS, "dec timer users now %d\n",
+            card->timer_users);
+    if(card->timer_users > 0 ) 
+        goto out;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_RELOAD,
+         0 ) ;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_CURRENT,
+         0 ) ;
+
+    m3_outw(card,  m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE,
+            HOST_INT_CTRL);
+out:
+    spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/*
+ * {start,stop}_{adc,dac} should be called
+ * while holding the 'state' lock and they
+ * will try to grab the 'card' lock..
+ */
+static void stop_adc(struct ess_state *s)
+{
+    if (! (s->enable & ADC_RUNNING)) 
+        return;
+
+    s->enable &= ~ADC_RUNNING;
+    dec_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + CDATA_INSTANCE_READY, 0);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_ADC1_REQUEST, 0);
+}    
+
+static void stop_dac(struct ess_state *s)
+{
+    if (! (s->enable & DAC_RUNNING)) 
+        return;
+
+    DPRINTK(DPSYS, "stop_dac()\n");
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + CDATA_INSTANCE_READY, 0);
+
+    s->enable &= ~DAC_RUNNING;
+    s->card->dacs_active--;
+    dec_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_MIXER_TASK_NUMBER, 
+            s->card->dacs_active ) ;
+}    
+
+static void start_dac(struct ess_state *s)
+{
+    if( (!s->dma_dac.mapped && s->dma_dac.count < 1) ||
+            !s->dma_dac.ready ||
+            (s->enable & DAC_RUNNING)) 
+        return;
+
+    DPRINTK(DPSYS, "start_dac()\n");
+
+    s->enable |= DAC_RUNNING;
+    s->card->dacs_active++;
+    inc_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + CDATA_INSTANCE_READY, 1);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_MIXER_TASK_NUMBER, 
+            s->card->dacs_active ) ;
+}    
+
+static void start_adc(struct ess_state *s)
+{
+    if ((! s->dma_adc.mapped &&
+                s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
+        || !s->dma_adc.ready 
+        || (s->enable & ADC_RUNNING) ) 
+            return;
+
+    DPRINTK(DPSYS, "start_adc()\n");
+
+    s->enable |= ADC_RUNNING;
+    inc_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_ADC1_REQUEST, 1);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + CDATA_INSTANCE_READY, 1);
+}    
+
+static struct play_vals {
+    u16 addr, val;
+} pv[] = {
+    {CDATA_LEFT_VOLUME, ARB_VOLUME},
+    {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+    {SRC3_DIRECTION_OFFSET, 0} ,
+    /* +1, +2 are stereo/16 bit */
+    {SRC3_DIRECTION_OFFSET + 3, 0X0000}, /* fraction? */
+    {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+    {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+    {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+    {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+    {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+    {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+    {SRC3_DIRECTION_OFFSET + 10, 0X8000}, /* round */
+    {SRC3_DIRECTION_OFFSET + 11, 0XFF00}, /* higher bute mark */
+    {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+    {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+    {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+    {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */
+    {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */
+    {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */
+    {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+    {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */
+};
+
+static void 
+ess_play_setup(struct ess_state *s, int mode, u32 rate, void *buffer, int size)
+{
+    int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2);
+    int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2);
+    int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+    int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+    struct dmabuf *db = &s->dma_dac;
+    int i;
+
+    DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n",
+        mode, rate, buffer, size);
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+    /* host dma buffer pointers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_ADDRL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_ADDRH,
+        HI(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+        LO(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+        HI(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_CURRENTL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_CURRENTH,
+        HI(virt_to_bus(buffer)));
+#undef LO
+#undef HI
+
+    /* dsp buffers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_BEGIN,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1,
+        dsp_in_buffer + (dsp_in_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_HEAD,
+        dsp_in_buffer);
+    
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_TAIL,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_BEGIN,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1,
+        dsp_out_buffer + (dsp_out_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_HEAD,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_TAIL,
+        dsp_out_buffer);
+
+    /*
+     * some per client initializers
+     */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12,
+        s->dac_inst.data + 40 + 8);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19,
+        s->dac_inst.code + MINISRC_COEF_LOC);
+
+    /* enable or disable low pass filter? */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22,
+        s->ratedac > 45000 ? 0xff : 0 );
+    
+    /* tell it which way dma is going? */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_DMA_CONTROL,
+        DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+    /*
+     * set an armload of static initializers
+     */
+    for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) 
+        m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + pv[i].addr, pv[i].val);
+
+    /* 
+     * put us in the lists if we're not already there
+     */
+
+    if(db->in_lists == 0) {
+
+        db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, 
+                s->dac_inst.data >> DP_SHIFT_COUNT);
+
+        db->dma_index = m3_add_list(s->card, &s->card->dma_list, 
+                s->dac_inst.data >> DP_SHIFT_COUNT);
+
+        db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, 
+                s->dac_inst.data >> DP_SHIFT_COUNT);
+
+        db->in_lists = 1;
+    }
+
+    set_dac_rate(s,rate);
+    start_dac(s);
+}
+
+/*
+ *    Native record driver 
+ */
+static struct rec_vals {
+    u16 addr, val;
+} rv[] = {
+    {CDATA_LEFT_VOLUME, ARB_VOLUME},
+    {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+    {SRC3_DIRECTION_OFFSET, 1} ,
+    /* +1, +2 are stereo/16 bit */
+    {SRC3_DIRECTION_OFFSET + 3, 0X0000}, /* fraction? */
+    {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+    {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+    {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+    {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+    {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+    {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+    {SRC3_DIRECTION_OFFSET + 10, 0X8000}, /* round */
+    {SRC3_DIRECTION_OFFSET + 11, 0XFF00}, /* higher bute mark */
+    {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+    {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+    {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+    {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */
+    {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */
+    {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */
+    {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */
+    {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+    {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */
+    {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */
+};
+
+/*
+ * the buffer passed here must be 32bit aligned 
+ */
+static void 
+ess_rec_setup(struct ess_state *s, int mode, u32 rate, void *buffer, int size)
+{
+    int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2);
+    int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2);
+    int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+    int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+    struct dmabuf *db = &s->dma_adc;
+    int i;
+
+    DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n",
+        mode, rate, buffer, size);
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+    /* host dma buffer pointers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_ADDRL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_ADDRH,
+        HI(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+        LO(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+        HI(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_CURRENTL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_CURRENTH,
+        HI(virt_to_bus(buffer)));
+#undef LO
+#undef HI
+
+    /* dsp buffers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_BEGIN,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1,
+        dsp_in_buffer + (dsp_in_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_HEAD,
+        dsp_in_buffer);
+    
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_TAIL,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_BEGIN,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1,
+        dsp_out_buffer + (dsp_out_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_HEAD,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_TAIL,
+        dsp_out_buffer);
+
+    /*
+     * some per client initializers
+     */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12,
+        s->adc_inst.data + 40 + 8);
+
+    /* tell it which way dma is going? */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_DMA_CONTROL,
+        DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + 
+        DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+    /*
+     * set an armload of static initializers
+     */
+    for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) 
+        m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + rv[i].addr, rv[i].val);
+
+    /* 
+     * put us in the lists if we're not already there
+     */
+
+    if(db->in_lists == 0) {
+
+        db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, 
+                s->adc_inst.data >> DP_SHIFT_COUNT);
+
+        db->dma_index = m3_add_list(s->card, &s->card->dma_list, 
+                s->adc_inst.data >> DP_SHIFT_COUNT);
+
+        db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, 
+                s->adc_inst.data >> DP_SHIFT_COUNT);
+
+        db->in_lists = 1;
+    }
+
+    set_fmt(s, ~0, 0);
+    set_adc_rate(s,rate);
+    start_adc(s);
+}
+/* --------------------------------------------------------------------- */
+
+static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count)
+{
+    DPRINTK(DPINT,"set_dmaa??\n");
+}
+
+static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count)
+{
+    DPRINTK(DPINT,"set_dmac??\n");
+}
+
+u32 get_dma_pos(struct ess_card *card,
+        int instance_addr)
+{
+    u16 hi = 0, lo = 0;
+    int retry = 10;
+
+    /*
+     * try and get a valid answer
+     */
+    while(retry--) {
+        hi =  m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                instance_addr + CDATA_HOST_SRC_CURRENTH);
+
+        lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                instance_addr + CDATA_HOST_SRC_CURRENTL);
+
+        if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                instance_addr + CDATA_HOST_SRC_CURRENTH))
+            break;
+    }
+    return lo | (hi<<16);
+}
+
+u32 get_dmaa(struct ess_state *s)
+{
+    u32 offset;
+
+    offset = get_dma_pos(s->card, s->dac_inst.data) - 
+        virt_to_bus(s->dma_dac.rawbuf);
+
+    DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset);
+
+    return offset;
+}
+
+u32 get_dmac(struct ess_state *s)
+{
+    u32 offset;
+
+    offset = get_dma_pos(s->card, s->adc_inst.data) -
+        virt_to_bus(s->dma_adc.rawbuf);
+
+    DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset);
+
+    return offset;
+
+}
+
+static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int 
+prog_dmabuf(struct ess_state *s, unsigned rec)
+{
+    struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+    unsigned rate = rec ? s->rateadc : s->ratedac;
+    unsigned bytepersec;
+    unsigned bufs;
+    unsigned char fmt;
+    unsigned long flags;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    fmt = s->fmt;
+    if (rec) {
+        stop_adc(s);
+        fmt >>= ESS_ADC_SHIFT;
+    } else {
+        stop_dac(s);
+        fmt >>= ESS_DAC_SHIFT;
+    }
+    fmt &= ESS_FMT_MASK;
+
+    db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+
+    bytepersec = rate << sample_shift[fmt];
+    bufs = PAGE_SIZE << db->buforder;
+    if (db->ossfragshift) {
+        if ((1000 << db->ossfragshift) < bytepersec)
+            db->fragshift = ld2(bytepersec/1000);
+        else
+            db->fragshift = db->ossfragshift;
+    } else {
+        db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+        if (db->fragshift < 3)
+            db->fragshift = 3; 
+    }
+    db->numfrag = bufs >> db->fragshift;
+    while (db->numfrag < 4 && db->fragshift > 3) {
+        db->fragshift--;
+        db->numfrag = bufs >> db->fragshift;
+    }
+    db->fragsize = 1 << db->fragshift;
+    if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+        db->numfrag = db->ossmaxfrags;
+    db->fragsamples = db->fragsize >> sample_shift[fmt];
+    db->dmasize = db->numfrag << db->fragshift;
+
+    DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize);
+
+    memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize);
+
+    if (rec) 
+        ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize);
+    else 
+        ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize);
+
+    db->ready = 1;
+
+    spin_unlock_irqrestore(&s->lock, flags);
+
+    return 0;
+}
+
+static void clear_advance(struct ess_state *s)
+{
+    unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80;
+    
+    unsigned char *buf = s->dma_dac.rawbuf;
+    unsigned bsize = s->dma_dac.dmasize;
+    unsigned bptr = s->dma_dac.swptr;
+    unsigned len = s->dma_dac.fragsize;
+    
+    if (bptr + len > bsize) {
+        unsigned x = bsize - bptr;
+        memset(buf + bptr, c, x);
+        /* account for wrapping? */
+        bptr = 0;
+        len -= x;
+    }
+    memset(buf + bptr, c, len);
+}
+
+static void ess_update_ptr(struct ess_state *s)
+{
+    unsigned hwptr;
+    int diff;
+
+    /* update ADC pointer */
+    if (s->dma_adc.ready) {
+        hwptr = get_dmac(s) % s->dma_adc.dmasize;
+        diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+        s->dma_adc.hwptr = hwptr;
+        s->dma_adc.total_bytes += diff;
+        s->dma_adc.count += diff;
+        if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+            wake_up(&s->dma_adc.wait);
+        if (!s->dma_adc.mapped) {
+            if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+                stop_adc(s); 
+                /* brute force everyone back in sync, sigh */
+                s->dma_adc.count = 0;
+                s->dma_adc.swptr = 0;
+                s->dma_adc.hwptr = 0;
+                s->dma_adc.error++;
+            }
+        }
+    }
+    /* update DAC pointer */
+    if (s->dma_dac.ready) {
+        hwptr = get_dmaa(s) % s->dma_dac.dmasize; 
+        diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+
+        DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n",
+                hwptr,diff,s->dma_dac.count);
+
+        s->dma_dac.hwptr = hwptr;
+        s->dma_dac.total_bytes += diff;
+
+        if (s->dma_dac.mapped) {
+            
+            s->dma_dac.count += diff;
+            if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) {
+                wake_up(&s->dma_dac.wait);
+            }
+        } else {
+
+            s->dma_dac.count -= diff;
+            
+            if (s->dma_dac.count <= 0) {
+                DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", 
+                        diff, diff, 
+                        s->dma_dac.count, 
+                        s->dma_dac.count, 
+                    hwptr, hwptr,
+                    s->dma_dac.swptr,
+                    s->dma_dac.swptr);
+                stop_dac(s);
+                /* brute force everyone back in sync, sigh */
+                s->dma_dac.count = 0; 
+                s->dma_dac.swptr = hwptr; 
+                s->dma_dac.error++;
+            } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+                clear_advance(s);
+                s->dma_dac.endcleared = 1;
+            }
+            if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) {
+                wake_up(&s->dma_dac.wait);
+                DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n",
+                        s->dma_dac.count, s->dma_dac.swptr, hwptr);
+            }
+        }
+    }
+}
+
+static void 
+ess_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct ess_card *c = (struct ess_card *)dev_id;
+    struct ess_state *s = &c->channels[0];
+    u8 status;
+
+    status = inb(c->iobase + 0x1A);
+
+    if(status == 0xff) 
+        return;
+   
+    /* presumably acking the ints? */
+    outw(status, c->iobase + 0x1A); 
+
+    if(in_suspend)
+        return;
+
+    /*
+     * ack an assp int if its running
+     * and has an int pending
+     */
+    if( status & ASSP_INT_PENDING) {
+        u8 ctl = inb(c->iobase + ASSP_CONTROL_B);
+        if( !(ctl & STOP_ASSP_CLOCK)) {
+            ctl = inb(c->iobase + ASSP_HOST_INT_STATUS );
+            if(ctl & DSP2HOST_REQ_TIMER) {
+                outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS);
+
+                /* update adc/dac info if it was a timer int */
+                spin_lock(&s->lock);
+                ess_update_ptr(s);
+                spin_unlock(&s->lock);
+            }
+        }
+    }
+
+    /* XXX is this needed? */
+    if(status & 0x40) 
+        outb(0x40, c->iobase+0x1A);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n";
+
+#define VALIDATE_MAGIC(FOO,MAG)                         \
+({                                                \
+    if (!(FOO) || (FOO)->magic != MAG) { \
+        printk(invalid_magic,__FUNCTION__);            \
+        return -ENXIO;                    \
+    }                                         \
+})
+
+#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC)
+#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC)
+
+/* --------------------------------------------------------------------- */
+
+static loff_t m3_llseek(struct file *file, loff_t offset, int origin)
+{
+    return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct ess_state *s, int nonblock)
+{
+    DECLARE_WAITQUEUE(wait,current);
+    unsigned long flags;
+    int count;
+    signed long tmo;
+
+    if (s->dma_dac.mapped || !s->dma_dac.ready)
+        return 0;
+    current->state = TASK_INTERRUPTIBLE;
+    add_wait_queue(&s->dma_dac.wait, &wait);
+    for (;;) {
+        spin_lock_irqsave(&s->lock, flags);
+        count = s->dma_dac.count;
+        spin_unlock_irqrestore(&s->lock, flags);
+        if (count <= 0)
+            break;
+        if (signal_pending(current))
+            break;
+        if (nonblock) {
+            remove_wait_queue(&s->dma_dac.wait, &wait);
+            current->state = TASK_RUNNING;
+            return -EBUSY;
+        }
+        tmo = (count * HZ) / s->ratedac;
+        tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK];
+        /* XXX this is just broken.  someone is waking us up alot, or schedule_timeout is broken.
+            or something.  who cares. - zach */
+        if (!schedule_timeout(tmo ? tmo : 1) && tmo)
+            DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies);
+    }
+    remove_wait_queue(&s->dma_dac.wait, &wait);
+    current->state = TASK_RUNNING;
+    if (signal_pending(current))
+            return -ERESTARTSYS;
+    return 0;
+}
+
+static ssize_t 
+ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+    struct ess_state *s = (struct ess_state *)file->private_data;
+    ssize_t ret;
+    unsigned long flags;
+    unsigned swptr;
+    int cnt;
+    
+    VALIDATE_STATE(s);
+    if (ppos != &file->f_pos)
+        return -ESPIPE;
+    if (s->dma_adc.mapped)
+        return -ENXIO;
+    if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+        return ret;
+    if (!access_ok(VERIFY_WRITE, buffer, count))
+        return -EFAULT;
+    ret = 0;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    while (count > 0) {
+        int timed_out;
+
+        swptr = s->dma_adc.swptr;
+        cnt = s->dma_adc.dmasize-swptr;
+        if (s->dma_adc.count < cnt)
+            cnt = s->dma_adc.count;
+
+        if (cnt > count)
+            cnt = count;
+
+        if (cnt <= 0) {
+            start_adc(s);
+            if (file->f_flags & O_NONBLOCK) {
+                ret = ret ? ret : -EAGAIN;
+                goto out;
+            }
+
+            spin_unlock_irqrestore(&s->lock, flags);
+
+            timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0;
+
+            spin_lock_irqsave(&s->lock, flags);
+                
+            if(timed_out) {
+                printk("read: chip lockup? dmasz %d fragsz %d count %d hwptr %d swptr %d\n",
+                       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, 
+                       s->dma_adc.hwptr, s->dma_adc.swptr);
+                stop_adc(s);
+                set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
+                s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+            }
+
+            if (signal_pending(current)) {
+                ret = ret ? ret : -ERESTARTSYS;
+                goto out;
+            }
+            continue;
+        }
+    
+        spin_unlock_irqrestore(&s->lock, flags);
+        if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+            ret = ret ? ret : -EFAULT;
+            return ret;
+        }
+        spin_lock_irqsave(&s->lock, flags);
+
+        swptr = (swptr + cnt) % s->dma_adc.dmasize;
+        s->dma_adc.swptr = swptr;
+        s->dma_adc.count -= cnt;
+        count -= cnt;
+        buffer += cnt;
+        ret += cnt;
+        start_adc(s);
+    }
+
+out:
+    spin_unlock_irqrestore(&s->lock, flags);
+    return ret;
+}
+
+static ssize_t 
+ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+    struct ess_state *s = (struct ess_state *)file->private_data;
+    ssize_t ret;
+    unsigned long flags;
+    unsigned swptr;
+    int cnt;
+    
+    VALIDATE_STATE(s);
+    if (ppos != &file->f_pos)
+        return -ESPIPE;
+    if (s->dma_dac.mapped)
+        return -ENXIO;
+    if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+        return ret;
+    if (!access_ok(VERIFY_READ, buffer, count))
+        return -EFAULT;
+    ret = 0;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    while (count > 0) {
+        int timed_out;
+
+        if (s->dma_dac.count < 0) {
+            s->dma_dac.count = 0;
+            s->dma_dac.swptr = s->dma_dac.hwptr;
+        }
+        swptr = s->dma_dac.swptr;
+
+        cnt = s->dma_dac.dmasize-swptr;
+
+        if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+            cnt = s->dma_dac.dmasize - s->dma_dac.count;
+
+        if (cnt > count)
+            cnt = count;
+
+        if (cnt <= 0) {
+            start_dac(s);
+            if (file->f_flags & O_NONBLOCK) {
+                if(!ret) ret = -EAGAIN;
+                goto out;
+            }
+
+            spin_unlock_irqrestore(&s->lock, flags);
+            timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0;
+            spin_lock_irqsave(&s->lock, flags);
+
+            if(timed_out) {
+                DPRINTK(DPCRAP,"write: chip lockup? dmasz %d fragsz %d count %d hwptr %d swptr %d\n",
+                       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, 
+                       s->dma_dac.hwptr, s->dma_dac.swptr);
+                stop_dac(s);
+                set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
+                s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+            }
+            if (signal_pending(current)) {
+                if (!ret) ret = -ERESTARTSYS;
+                goto out;
+            }
+            continue;
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+            if (!ret) ret = -EFAULT;
+            return ret;
+        }
+        spin_lock_irqsave(&s->lock, flags);
+        DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n",
+                cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);
+        
+        swptr = (swptr + cnt) % s->dma_dac.dmasize;
+
+        s->dma_dac.swptr = swptr;
+        s->dma_dac.count += cnt;
+        s->dma_dac.endcleared = 0;
+        count -= cnt;
+        buffer += cnt;
+        ret += cnt;
+        start_dac(s);
+    }
+out:
+    spin_unlock_irqrestore(&s->lock, flags);
+    return ret;
+}
+
+static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait)
+{
+    struct ess_state *s = (struct ess_state *)file->private_data;
+    unsigned long flags;
+    unsigned int mask = 0;
+
+    VALIDATE_STATE(s);
+    if (file->f_mode & FMODE_WRITE)
+        poll_wait(file, &s->dma_dac.wait, wait);
+    if (file->f_mode & FMODE_READ)
+        poll_wait(file, &s->dma_adc.wait, wait);
+
+    spin_lock_irqsave(&s->lock, flags);
+    ess_update_ptr(s);
+
+    if (file->f_mode & FMODE_READ) {
+        if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+            mask |= POLLIN | POLLRDNORM;
+    }
+    if (file->f_mode & FMODE_WRITE) {
+        if (s->dma_dac.mapped) {
+            if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+                mask |= POLLOUT | POLLWRNORM;
+        } else {
+            if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+                mask |= POLLOUT | POLLWRNORM;
+        }
+    }
+
+    spin_unlock_irqrestore(&s->lock, flags);
+    return mask;
+}
+
+static int ess_mmap(struct file *file, struct vm_area_struct *vma)
+{
+    struct ess_state *s = (struct ess_state *)file->private_data;
+    struct dmabuf *db;
+    int ret;
+    unsigned long size;
+
+    VALIDATE_STATE(s);
+    if (vma->vm_flags & VM_WRITE) {
+        if ((ret = prog_dmabuf(s, 1)) != 0)
+            return ret;
+        db = &s->dma_dac;
+    } else 
+    if (vma->vm_flags & VM_READ) {
+        if ((ret = prog_dmabuf(s, 0)) != 0)
+            return ret;
+        db = &s->dma_adc;
+    } else  
+        return -EINVAL;
+    if (SILLY_OFFSET(vma) != 0)
+        return -EINVAL;
+    size = vma->vm_end - vma->vm_start;
+    if (size > (PAGE_SIZE << db->buforder))
+        return -EINVAL;
+    if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+        return -EAGAIN;
+    db->mapped = 1;
+    return 0;
+}
+
+/*
+ * god, what an absolute mess..
+ * not all the paths through here are
+ * properly locked.  
+ *   *sob*
+ */
+static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+    struct ess_state *s = (struct ess_state *)file->private_data;
+    unsigned long flags;
+    audio_buf_info abinfo;
+    count_info cinfo;
+    int val, mapped, ret;
+    unsigned char fmtm, fmtd;
+
+    VALIDATE_STATE(s);
+
+    mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+        ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+
+    DPRINTK(DPSYS,"ess_ioctl: cmd %d\n", cmd);
+
+    switch (cmd) {
+    case OSS_GETVERSION:
+        return put_user(SOUND_VERSION, (int *)arg);
+
+    case SNDCTL_DSP_SYNC:
+        if (file->f_mode & FMODE_WRITE)
+            return drain_dac(s, file->f_flags & O_NONBLOCK);
+        return 0;
+        
+    case SNDCTL_DSP_SETDUPLEX:
+        /* XXX fix */
+        return 0;
+
+    case SNDCTL_DSP_GETCAPS:
+        return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+        
+    case SNDCTL_DSP_RESET:
+        spin_lock_irqsave(&s->lock, flags);
+        if (file->f_mode & FMODE_WRITE) {
+            stop_dac(s);
+            synchronize_irq();
+            s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+        }
+        if (file->f_mode & FMODE_READ) {
+            stop_adc(s);
+            synchronize_irq();
+            s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return 0;
+
+    case SNDCTL_DSP_SPEED:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (val >= 0) {
+            if (file->f_mode & FMODE_READ) {
+                stop_adc(s);
+                s->dma_adc.ready = 0;
+                set_adc_rate(s, val);
+            }
+            if (file->f_mode & FMODE_WRITE) {
+                stop_dac(s);
+                s->dma_dac.ready = 0;
+                set_dac_rate(s, val);
+            }
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+        
+    case SNDCTL_DSP_STEREO:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        fmtd = 0;
+        fmtm = ~0;
+        if (file->f_mode & FMODE_READ) {
+            stop_adc(s);
+            s->dma_adc.ready = 0;
+            if (val)
+                fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+            else
+                fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+        }
+        if (file->f_mode & FMODE_WRITE) {
+            stop_dac(s);
+            s->dma_dac.ready = 0;
+            if (val)
+                fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+            else
+                fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+        }
+        set_fmt(s, fmtm, fmtd);
+        spin_unlock_irqrestore(&s->lock, flags);
+        return 0;
+
+    case SNDCTL_DSP_CHANNELS:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (val != 0) {
+            fmtd = 0;
+            fmtm = ~0;
+            if (file->f_mode & FMODE_READ) {
+                stop_adc(s);
+                s->dma_adc.ready = 0;
+                if (val >= 2)
+                    fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+            }
+            if (file->f_mode & FMODE_WRITE) {
+                stop_dac(s);
+                s->dma_dac.ready = 0;
+                if (val >= 2)
+                    fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+            }
+            set_fmt(s, fmtm, fmtd);
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+                       : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+        
+    case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+        return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg);
+        
+    case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (val != AFMT_QUERY) {
+            fmtd = 0;
+            fmtm = ~0;
+            if (file->f_mode & FMODE_READ) {
+                stop_adc(s);
+                s->dma_adc.ready = 0;
+                if (val == AFMT_S16_LE)
+                    fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT);
+            }
+            if (file->f_mode & FMODE_WRITE) {
+                stop_dac(s);
+                s->dma_dac.ready = 0;
+                if (val == AFMT_S16_LE)
+                    fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT);
+            }
+            set_fmt(s, fmtm, fmtd);
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? 
+            (ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+            : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 
+                AFMT_S16_LE : 
+                AFMT_U8, 
+            (int *)arg);
+        
+    case SNDCTL_DSP_POST:
+        return 0;
+
+    case SNDCTL_DSP_GETTRIGGER:
+        val = 0;
+        if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING))
+            val |= PCM_ENABLE_INPUT;
+        if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) 
+            val |= PCM_ENABLE_OUTPUT;
+        return put_user(val, (int *)arg);
+        
+    case SNDCTL_DSP_SETTRIGGER:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        if (file->f_mode & FMODE_READ) {
+            if (val & PCM_ENABLE_INPUT) {
+                if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+                    return ret;
+                start_adc(s);
+            } else
+                stop_adc(s);
+        }
+        if (file->f_mode & FMODE_WRITE) {
+            if (val & PCM_ENABLE_OUTPUT) {
+                if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+                    return ret;
+                start_dac(s);
+            } else
+                stop_dac(s);
+        }
+        return 0;
+
+    case SNDCTL_DSP_GETOSPACE:
+        if (!(file->f_mode & FMODE_WRITE))
+            return -EINVAL;
+        if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0)
+            return val;
+        spin_lock_irqsave(&s->lock, flags);
+        ess_update_ptr(s);
+        abinfo.fragsize = s->dma_dac.fragsize;
+        abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+        abinfo.fragstotal = s->dma_dac.numfrag;
+        abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+    case SNDCTL_DSP_GETISPACE:
+        if (!(file->f_mode & FMODE_READ))
+            return -EINVAL;
+        if (!(s->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0)
+            return val;
+        spin_lock_irqsave(&s->lock, flags);
+        ess_update_ptr(s);
+        abinfo.fragsize = s->dma_adc.fragsize;
+        abinfo.bytes = s->dma_adc.count;
+        abinfo.fragstotal = s->dma_adc.numfrag;
+        abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+        
+    case SNDCTL_DSP_NONBLOCK:
+        file->f_flags |= O_NONBLOCK;
+        return 0;
+
+    case SNDCTL_DSP_GETODELAY:
+        if (!(file->f_mode & FMODE_WRITE))
+            return -EINVAL;
+        spin_lock_irqsave(&s->lock, flags);
+        ess_update_ptr(s);
+        val = s->dma_dac.count;
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user(val, (int *)arg);
+
+    case SNDCTL_DSP_GETIPTR:
+        if (!(file->f_mode & FMODE_READ))
+            return -EINVAL;
+        spin_lock_irqsave(&s->lock, flags);
+        ess_update_ptr(s);
+        cinfo.bytes = s->dma_adc.total_bytes;
+        cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+        cinfo.ptr = s->dma_adc.hwptr;
+        if (s->dma_adc.mapped)
+            s->dma_adc.count &= s->dma_adc.fragsize-1;
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+    case SNDCTL_DSP_GETOPTR:
+        if (!(file->f_mode & FMODE_WRITE))
+            return -EINVAL;
+        spin_lock_irqsave(&s->lock, flags);
+        ess_update_ptr(s);
+        cinfo.bytes = s->dma_dac.total_bytes;
+        cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+        cinfo.ptr = s->dma_dac.hwptr;
+        if (s->dma_dac.mapped)
+            s->dma_dac.count &= s->dma_dac.fragsize-1;
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+    case SNDCTL_DSP_GETBLKSIZE:
+        if (file->f_mode & FMODE_WRITE) {
+            if ((val = prog_dmabuf(s, 0)))
+                return val;
+            return put_user(s->dma_dac.fragsize, (int *)arg);
+        }
+        if ((val = prog_dmabuf(s, 1)))
+            return val;
+        return put_user(s->dma_adc.fragsize, (int *)arg);
+
+    case SNDCTL_DSP_SETFRAGMENT:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (file->f_mode & FMODE_READ) {
+            s->dma_adc.ossfragshift = val & 0xffff;
+            s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+            if (s->dma_adc.ossfragshift < 4)
+                s->dma_adc.ossfragshift = 4;
+            if (s->dma_adc.ossfragshift > 15)
+                s->dma_adc.ossfragshift = 15;
+            if (s->dma_adc.ossmaxfrags < 4)
+                s->dma_adc.ossmaxfrags = 4;
+        }
+        if (file->f_mode & FMODE_WRITE) {
+            s->dma_dac.ossfragshift = val & 0xffff;
+            s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+            if (s->dma_dac.ossfragshift < 4)
+                s->dma_dac.ossfragshift = 4;
+            if (s->dma_dac.ossfragshift > 15)
+                s->dma_dac.ossfragshift = 15;
+            if (s->dma_dac.ossmaxfrags < 4)
+                s->dma_dac.ossmaxfrags = 4;
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return 0;
+
+    case SNDCTL_DSP_SUBDIVIDE:
+        if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+            (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+            return -EINVAL;
+
+        get_user_ret(val, (int *)arg, -EFAULT);
+
+        if (val != 1 && val != 2 && val != 4)
+            return -EINVAL;
+        if (file->f_mode & FMODE_READ)
+            s->dma_adc.subdivision = val;
+        if (file->f_mode & FMODE_WRITE)
+            s->dma_dac.subdivision = val;
+        return 0;
+
+    case SOUND_PCM_READ_RATE:
+        return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+    case SOUND_PCM_READ_CHANNELS:
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+                       : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+
+    case SOUND_PCM_READ_BITS:
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+                       : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg);
+
+    case SOUND_PCM_WRITE_FILTER:
+    case SNDCTL_DSP_SETSYNCRO:
+    case SOUND_PCM_READ_FILTER:
+        return -EINVAL;
+    }
+    return -EINVAL;
+}
+
+static int
+allocate_dmabuf(struct dmabuf *db)
+{
+    int order;
+    unsigned long mapend,map;
+    void *rawbuf = NULL;
+
+    DPRINTK(DPSTR,"allocating for dmabuf %p\n", db);
+
+    /* 
+     * alloc as big a chunk as we can, start with 
+     * 64k 'cause we're insane.
+     */
+    for (order = 16-PAGE_SHIFT; order >= 1; order--)
+        /* XXX might be able to get rid of gfp_dma */
+        if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
+            break;
+
+    if (!rawbuf)
+        return 1;
+
+    /*
+     * can't cross a 64k boundry..
+     */
+    if( ((virt_to_bus(rawbuf) & 0xffff) + ((PAGE_SIZE << order) - 1)) > ~0xffff) {
+        printk(KERN_ERR PFX "DMA buffer crosses 64k: busaddr 0x%lx  size %ld\n",
+            virt_to_bus(rawbuf), PAGE_SIZE << order);
+        kfree(rawbuf);
+        free_pages((unsigned long)db->rawbuf,db->buforder);
+        return 1;
+    }
+
+    DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n",
+            PAGE_SIZE<<order, order, rawbuf);
+
+    /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+    mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1);
+    for (map = MAP_NR(rawbuf); map <= mapend; map++) {
+        set_bit(PG_reserved, &mem_map[map].flags);
+    }
+
+    db->rawbuf = rawbuf;
+    db->buforder = order;
+    db->ready = 0;
+    db->mapped = 0;
+
+    return 0;
+}
+
+static void
+nuke_lists(struct ess_card *card, struct dmabuf *db)
+{
+    m3_remove_list(card, &(card->dma_list), db->dma_index);
+    m3_remove_list(card, &(card->msrc_list), db->msrc_index);
+    db->in_lists = 0;
+}
+
+static void
+free_dmabuf(struct dmabuf *db)
+{
+    unsigned long map, mapend;
+
+    DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db);
+
+    mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+    for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+        clear_bit(PG_reserved, &mem_map[map].flags);    
+
+    free_pages((unsigned long)db->rawbuf,db->buforder);
+
+    db->rawbuf = NULL;
+    db->buforder = 0;
+    db->mapped = 0;
+    db->ready = 0;
+}
+
+static int 
+ess_open(struct inode *inode, struct file *file)
+{
+    int minor = MINOR(inode->i_rdev);
+    struct ess_card *c;
+    struct ess_state *s = NULL;
+    int i;
+    int ret = 0;
+    unsigned char fmtm = ~0, fmts = 0;
+    unsigned long flags;
+
+    /*
+     *    Scan the cards and find the channel. We only
+     *    do this at open time so it is ok
+     */
+    for(c = devs ; c != NULL ; c = c->next) {
+
+        for(i=0;i<NR_DSPS;i++) {
+
+            if(c->channels[i].dev_audio < 0)
+                continue;
+            if((c->channels[i].dev_audio ^ minor) & ~0xf)
+                continue;
+
+            s = &c->channels[i];
+            break;
+        }
+    }
+        
+    if (!s)
+        return -ENODEV;
+        
+    VALIDATE_STATE(s);
+
+    file->private_data = s;
+
+    /* wait for device to become free */
+    down(&s->open_sem);
+    while (s->open_mode & file->f_mode) {
+        if (file->f_flags & O_NONBLOCK) {
+            up(&s->open_sem);
+            return -EWOULDBLOCK;
+        }
+        up(&s->open_sem);
+        interruptible_sleep_on(&s->open_wait);
+        if (signal_pending(current))
+            return -ERESTARTSYS;
+        down(&s->open_sem);
+    }
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    if (file->f_mode & FMODE_READ) {
+        if(allocate_dmabuf(&(s->dma_adc)))  {
+            ret = -ENOMEM;
+            goto out;
+        }
+        fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
+        if ((minor & 0xf) == SND_DEV_DSP16)
+            fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; 
+
+        s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+        set_adc_rate(s, 8000);
+    }
+    if (file->f_mode & FMODE_WRITE) {
+        if(allocate_dmabuf(&(s->dma_dac)))  {
+            ret = -ENOMEM;
+            goto out;
+        }
+
+        fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT);
+        if ((minor & 0xf) == SND_DEV_DSP16)
+            fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+
+        s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+        set_dac_rate(s, 8000);
+    }
+    set_fmt(s, fmtm, fmts);
+    s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+
+    MOD_INC_USE_COUNT;
+out:
+    spin_unlock_irqrestore(&s->lock, flags);
+    up(&s->open_sem);
+    return ret;
+}
+
+static int 
+ess_release(struct inode *inode, struct file *file)
+{
+    struct ess_state *s = (struct ess_state *)file->private_data;
+    unsigned long flags;
+
+    VALIDATE_STATE(s);
+    if (file->f_mode & FMODE_WRITE)
+        drain_dac(s, file->f_flags & O_NONBLOCK);
+
+    down(&s->open_sem);
+    spin_lock_irqsave(&s->lock, flags);
+
+    if (file->f_mode & FMODE_WRITE) {
+        stop_dac(s);
+        if(s->dma_dac.in_lists) {
+            m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index);
+            nuke_lists(s->card, &(s->dma_dac));
+        }
+        free_dmabuf(&(s->dma_dac));
+    }
+    if (file->f_mode & FMODE_READ) {
+        stop_adc(s);
+        if(s->dma_adc.in_lists) {
+            m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index);
+            nuke_lists(s->card, &(s->dma_adc));
+        }
+        free_dmabuf(&(s->dma_adc));
+    }
+        
+    s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+
+    spin_unlock_irqrestore(&s->lock, flags);
+    up(&s->open_sem);
+    wake_up(&s->open_wait);
+
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+/*
+ * Wait for the ac97 serial bus to be free.
+ * return nonzero if the bus is still busy.
+ */
+static int m3_ac97_wait(struct ess_card *card)
+{
+    int i = 10000;
+
+    while( (m3_inb(card, 0x30) & 1) && i--) ;
+
+    return i == 0;
+}
+
+u16 m3_ac97_read(struct ac97_codec *codec, u8 reg)
+{
+    u16 ret = 0;
+    struct ess_card *card = codec->private_data;
+
+    spin_lock(&card->ac97_lock);
+
+    if(m3_ac97_wait(card)) {
+        printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg);
+        goto out;
+    }
+
+    m3_outb(card, 0x80 | (reg & 0x7f), 0x30);
+
+    if(m3_ac97_wait(card)) {
+        printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg);
+        goto out;
+    }
+
+    ret =  m3_inw(card, 0x32);
+    DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg);
+
+out:
+    spin_unlock(&card->ac97_lock);
+    return ret;
+}
+
+void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
+{
+    struct ess_card *card = codec->private_data;
+
+    spin_lock(&card->ac97_lock);
+
+    if(m3_ac97_wait(card)) {
+        printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg);
+        goto out;
+    }
+    DPRINTK(DPCRAP,"writing 0x%04x  to  0x%02x\n", val, reg);
+
+    m3_outw(card, val, 0x32);
+    m3_outb(card, reg & 0x7f, 0x30);
+out:
+    spin_unlock(&card->ac97_lock);
+}
+/* OSS /dev/mixer file operation methods */
+static int m3_open_mixdev(struct inode *inode, struct file *file)
+{
+    int minor = MINOR(inode->i_rdev);
+    struct ess_card *card = devs;
+
+    MOD_INC_USE_COUNT;
+    for (card = devs; card != NULL; card = card->next) {
+        if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor))
+                break;
+    }
+
+    if (!card) {
+        MOD_DEC_USE_COUNT;
+        return -ENODEV;
+    }
+
+    file->private_data = card->ac97;
+
+    return 0;
+}
+
+static int m3_release_mixdev(struct inode *inode, struct file *file)
+{
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int m3_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+                                    unsigned long arg)
+{
+    struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+    return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations m3_mixer_fops = {
+llseek:         m3_llseek,
+ioctl:          m3_ioctl_mixdev,
+open:           m3_open_mixdev,
+release:        m3_release_mixdev,
+};
+
+void remote_codec_config(int io, int isremote)
+{
+    isremote = isremote ? 1 : 0;
+
+    outw(  (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
+            io + RING_BUS_CTRL_B);
+    outw(  (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
+            io + SDO_OUT_DEST_CTRL);
+    outw(  (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
+            io + SDO_IN_DEST_CTRL);
+}
+
+/* 
+ * hack, returns non zero on err 
+ */
+static int try_read_vendor(struct ess_card *card)
+{
+    u16 ret;
+
+    if(m3_ac97_wait(card)) 
+        return 1;
+
+    m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30);
+
+    if(m3_ac97_wait(card)) 
+        return 1;
+
+    ret =  m3_inw(card, 0x32);
+
+    return (ret == 0) || (ret == 0xffff);
+}
+
+static void m3_codec_reset(struct ess_card *card, int busywait)
+{
+    u16 dir;
+    int delay1 = 0, delay2 = 0, i;
+    int io = card->iobase;
+
+    switch (card->card_type) {
+        /*
+         * the onboard codec on the allegro seems 
+         * to want to wait a very long time before
+         * coming back to life 
+         */
+        case ESS_ALLEGRO:
+            delay1 = 50;
+            delay2 = 800;
+        break;
+        case ESS_MAESTRO3:
+            delay1 = 20;
+            delay2 = 500;
+        break;
+    }
+
+    for(i = 0; i < 5; i ++) {
+        dir = inw(io + GPIO_DIRECTION);
+        dir |= 0x10; /* assuming pci bus master? */
+
+        remote_codec_config(io, 0);
+
+        outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
+        udelay(20);
+
+        outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION);
+        outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK);
+        outw(0, io + GPIO_DATA);
+        outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION);
+
+        if(busywait)  {
+            mdelay(delay1);
+        } else {
+            current->state = TASK_UNINTERRUPTIBLE;
+            schedule_timeout((delay1 * HZ) / 1000);
+        }
+
+        outw(GPO_PRIMARY_AC97, io + GPIO_DATA);
+        udelay(5);
+        /* ok, bring back the ac-link */
+        outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A);
+        outw(~0, io + GPIO_MASK);
+
+        if(busywait) {
+            mdelay(delay2);
+        } else {
+            current->state = TASK_UNINTERRUPTIBLE;
+            schedule_timeout((delay2 * HZ) / 1000);
+        }
+        if(! try_read_vendor(card))
+            break;
+
+        delay1 += 10;
+        delay2 += 100;
+
+        DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n",
+                delay1, delay2);
+    }
+
+#if 0
+    /* more gung-ho reset that doesn't
+     * seem to work anywhere :)
+     */
+    tmp = inw(io + RING_BUS_CTRL_A);
+    outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A);
+    mdelay(20);
+    outw(tmp, io + RING_BUS_CTRL_A);
+    mdelay(50);
+#endif
+}
+
+static int __init m3_codec_install(struct ess_card *card)
+{
+    struct ac97_codec *codec;
+
+    if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+        return -ENOMEM;
+    memset(codec, 0, sizeof(struct ac97_codec));
+
+    codec->private_data = card;
+    codec->codec_read = m3_ac97_read;
+    codec->codec_write = m3_ac97_write;
+
+    if (ac97_probe_codec(codec) == 0) {
+        printk(KERN_ERR PFX "codec probe failed\n");
+        kfree(codec);
+        return -1;
+    }
+
+    if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) {
+        printk(KERN_ERR PFX "couldn't register mixer!\n");
+        kfree(codec);
+        return -1;
+    }
+
+    card->ac97 = codec;
+
+    return 0;
+}
+
+
+#define MINISRC_LPF_LEN 10
+static u16 minisrc_lpf[MINISRC_LPF_LEN] = {
+    0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
+    0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
+};
+static void m3_assp_init(struct ess_card *card)
+{
+    int i;
+
+    /* zero kernel data */
+    for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+                KDATA_BASE_ADDR + i, 0);
+
+    /* zero mixer data? */
+    for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+                KDATA_BASE_ADDR2 + i, 0);
+
+    /* init dma pointer */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+            KDATA_CURRENT_DMA, 
+            KDATA_DMA_XFER0);
+
+    /* write kernel into code memory.. */
+    for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) {
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE, 
+                REV_B_CODE_MEMORY_BEGIN + i, 
+                assp_kernel_image[i]);
+    }
+
+    /*
+     * We only have this one client and we know that 0x400
+     * is free in our kernel's mem map, so lets just
+     * drop it there.  It seems that the minisrc doesn't
+     * need vectors, so we won't bother with them..
+     */
+    for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) {
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE, 
+                0x400 + i, 
+                assp_minisrc_image[i]);
+    }
+
+    /*
+     * write the coefficients for the low pass filter?
+     */
+    for(i = 0; i < MINISRC_LPF_LEN ; i++) {
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE,
+            0x400 + MINISRC_COEF_LOC + i,
+            minisrc_lpf[i]);
+    }
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_CODE,
+        0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN,
+        0x8000);
+
+    /*
+     * the minisrc is the only thing on
+     * our task list..
+     */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+            KDATA_TASK0, 
+            0x400);
+
+    /*
+     * init the mixer number..
+     */
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+            KDATA_MIXER_TASK_NUMBER,0);
+
+    /*
+     * EXTREME KERNEL MASTER VOLUME
+     */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_DAC_LEFT_VOLUME, ARB_VOLUME);
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME);
+
+    card->mixer_list.mem_addr = KDATA_MIXER_XFER0;
+    card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS;
+    card->adc1_list.mem_addr = KDATA_ADC1_XFER0;
+    card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS;
+    card->dma_list.mem_addr = KDATA_DMA_XFER0;
+    card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS;
+    card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC;
+    card->msrc_list.max = MAX_INSTANCE_MINISRC;
+}
+
+static int setup_msrc(struct ess_card *card,
+        struct assp_instance *inst, int index)
+{
+    int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + 
+            MINISRC_IN_BUFFER_SIZE / 2 +
+            1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 );
+    int address, i;
+
+    /*
+     * the revb memory map has 0x1100 through 0x1c00
+     * free.  
+     */
+
+    /*
+     * align instance mem so that the shifted list
+     * addresses are aligned.
+     */
+    data_bytes = (data_bytes + 255) & ~255;
+    address = 0x1100 + ((data_bytes/2) * index);
+
+    if((address + (data_bytes/2)) >= 0x1c00) {
+        printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n",
+                data_bytes, index, address);
+        return -1;
+    }
+
+    for(i = 0; i < data_bytes/2 ; i++) 
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+                address + i, 0);
+
+    inst->code = 0x400;
+    inst->data = address;
+
+    return 0;
+}
+
+static int m3_assp_client_init(struct ess_state *s)
+{
+    setup_msrc(s->card, &(s->dac_inst), s->index * 2);
+    setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1);
+
+    return 0;
+}
+
+static void
+m3_amp_enable(struct ess_card *card, int enable)
+{
+    /* 
+     * this works for the reference board, have to find
+     * out about others
+     *
+     * this needs more magic for 4 speaker, but..
+     */
+    int io = card->iobase;
+    u16 gpo, polarity_port, polarity;
+
+    if(!external_amp)
+        return;
+
+    switch (card->card_type) {
+        case ESS_ALLEGRO:
+            polarity_port = 0x1800;
+            break;
+        default:
+            /* presumably this is for all 'maestro3's.. */
+            polarity_port = 0x1100;
+            break;
+    }
+
+    gpo = (polarity_port >> 8) & 0x0F;
+    polarity = polarity_port >> 12;
+    if ( enable )
+        polarity = !polarity;
+    polarity = polarity << gpo;
+    gpo = 1 << gpo;
+
+    outw(~gpo , io + GPIO_MASK);
+
+    outw( inw(io + GPIO_DIRECTION) | gpo ,
+            io + GPIO_DIRECTION);
+
+    outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) ,
+            io + GPIO_DATA);
+
+    outw(0xffff , io + GPIO_MASK);
+}
+
+static int
+maestro_config(struct ess_card *card) 
+{
+    struct pci_dev *pcidev = card->pcidev;
+    u32 n;
+    u8  t; /* makes as much sense as 'n', no? */
+
+    pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+    n &= REDUCED_DEBOUNCE;
+    n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
+    pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+    outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B);
+    pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+    n &= ~INT_CLK_SELECT;
+    if(card->card_type == ESS_MAESTRO3)  {
+        n &= ~INT_CLK_MULT_ENABLE; 
+        n |= INT_CLK_SRC_NOT_PCI;
+    }
+    n &=  ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 );
+    pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+    if(card->card_type <= ESS_ALLEGRO) {
+        pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n);
+        n |= IN_CLK_12MHZ_SELECT;
+        pci_write_config_dword(pcidev, PCI_USER_CONFIG, n);
+    }
+
+    t = inb(card->iobase + ASSP_CONTROL_A);
+    t &= ~( DSP_CLK_36MHZ_SELECT  | ASSP_CLK_49MHZ_SELECT);
+    switch(global_dsp_speed) {
+        case 33:
+            break;
+        case 36:
+            t |= DSP_CLK_36MHZ_SELECT;
+            break;
+        case 49:
+            t |= ASSP_CLK_49MHZ_SELECT;
+            break;
+    }
+    t |= ASSP_0_WS_ENABLE; 
+    outb(t, card->iobase + ASSP_CONTROL_A);
+
+    outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); 
+
+    return 0;
+} 
+
+static void
+m3_enable_ints(struct ess_card *card)
+{
+    unsigned long io = card->iobase;
+
+    outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL);
+    outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
+            io + ASSP_CONTROL_C);
+}
+
+static struct file_operations ess_audio_fops = {
+    &m3_llseek,
+    &ess_read,
+    &ess_write,
+    NULL,  /* readdir */
+    &ess_poll,
+    &ess_ioctl,
+    &ess_mmap,
+    &ess_open,
+    NULL,    /* flush */
+    &ess_release,
+    NULL,  /* fsync */
+    NULL,  /* fasync */
+    NULL,  /* check_media_change */
+    NULL,  /* revalidate */
+    NULL,  /* lock */
+};
+
+#ifdef CONFIG_APM
+int alloc_dsp_savemem(struct ess_card *card)
+{
+    int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH);
+
+    if( (card->suspend_dsp_mem = vmalloc(len)) == NULL)
+        return 1;
+
+    return 0;
+}
+void free_dsp_savemem(struct ess_card *card)
+{
+   if(card->suspend_dsp_mem)
+       vfree(card->suspend_dsp_mem);
+}
+
+#else
+#define alloc_dsp_savemem(args...) 0
+#define free_dsp_savemem(args...) 
+#endif
+
+/*
+ * great day!  this function is ugly as hell.
+ */
+static int 
+maestro_install(struct pci_dev *pcidev, struct card_type *ct)
+{
+    u32 n;
+    int i;
+    int iobase;
+    struct ess_card *card = NULL;
+    int num = 0;
+
+    /* don't pick up modems */
+    if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+        return 1;
+
+    DPRINTK(DPMOD, "in maestro_install\n");
+
+    iobase = SILLY_PCI_BASE_ADDRESS(pcidev); 
+
+    if(check_region(iobase, 256))
+    {
+        printk(KERN_WARNING PFX "can't allocate 256 bytes I/O at 0x%4.4x\n", iobase);
+        return 1;
+    }
+    /* stake our claim on the iospace */
+    request_region(iobase, 256, ct->name);
+
+    /* this was tripping up some machines */
+    if(pcidev->irq == 0) 
+        printk(KERN_WARNING PFX "pci subsystem reports irq 0, this might not be correct.\n");
+
+    /* just to be sure */
+    pci_set_master(pcidev);
+
+    card = kmalloc(sizeof(struct ess_card), GFP_KERNEL);
+    if(card == NULL)
+    {
+        printk(KERN_WARNING PFX "out of memory\n");
+        release_region(card->iobase, 256);        
+        return 1;
+    }
+
+    memset(card, 0, sizeof(*card));
+    card->pcidev = pcidev;
+
+    if(alloc_dsp_savemem(card)) {
+        printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n",
+                REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH);
+        release_region(card->iobase, 256);
+        free_dsp_savemem(card);
+        kfree(card);
+        return 1;
+    }
+
+    if (register_reboot_notifier(&maestro_nb)) {
+        printk(KERN_WARNING PFX "reboot notifier registration failed\n");
+        release_region(card->iobase, 256);        
+        free_dsp_savemem(card);
+        kfree(card);
+        return 1;
+    }
+
+    card->iobase = iobase;
+    card->card_type = ct->type;
+    card->irq = pcidev->irq;
+    card->next = devs;
+    card->magic = M3_CARD_MAGIC;
+    spin_lock_init(&card->lock);
+    spin_lock_init(&card->ac97_lock);
+    devs = card;
+
+    printk(KERN_INFO PFX "Configuring %s found at IO 0x%04X IRQ %d\n", 
+        ct->name,iobase,card->irq);
+    pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n);
+    printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); 
+
+    maestro_config(card);
+    m3_assp_halt(card);
+
+    m3_codec_reset(card, 0);
+
+    if(m3_codec_install(card))  {
+        unregister_reboot_notifier(&maestro_nb);
+        release_region(card->iobase, 256);        
+        free_dsp_savemem(card);
+        kfree(card);
+        return 1;
+    }
+
+    m3_assp_init(card);
+    m3_amp_enable(card, 1);
+    
+    for(i=0;i<NR_DSPS;i++)
+    {
+        struct ess_state *s=&card->channels[i];
+
+        s->index = i;
+
+        s->card = card;
+        init_waitqueue_head(&s->dma_adc.wait);
+        init_waitqueue_head(&s->dma_dac.wait);
+        init_waitqueue_head(&s->open_wait);
+        spin_lock_init(&s->lock);
+        SILLY_INIT_SEM(s->open_sem);
+        s->magic = M3_STATE_MAGIC;
+
+        m3_assp_client_init(s);
+        
+        if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
+            printk("maestro: BOTCH!\n");
+        /* register devices */
+        if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
+            break;
+    }
+    
+    num = i;
+    
+    /* clear the rest if we ran out of slots to register */
+    for(;i<NR_DSPS;i++)
+    {
+        struct ess_state *s=&card->channels[i];
+        s->dev_audio = -1;
+    }
+
+    
+    if(request_irq(card->irq, ess_interrupt, SA_SHIRQ, ct->name, card))
+    {
+        printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq);
+        for(i=0;i<NR_DSPS;i++)
+        {
+            struct ess_state *s = &card->channels[i];
+            if(s->dev_audio != -1)
+                unregister_sound_dsp(s->dev_audio);
+        }
+        unregister_reboot_notifier(&maestro_nb);
+        unregister_sound_mixer(card->ac97->dev_mixer);
+        kfree(card->ac97);
+        release_region(card->iobase, 256);        
+        free_dsp_savemem(card);
+        kfree(card);
+        return 1;
+    }
+
+#ifdef CONFIG_APM
+    if (apm_register_callback(m3_apm_callback)) {
+        printk(KERN_WARNING PFX "couldn't register apm callback, suspend/resume might not work.\n");
+    }
+#endif
+
+    m3_enable_ints(card);
+    m3_assp_continue(card);
+
+    printk(KERN_INFO PFX "%d channels configured.\n", num);
+
+    return 0; 
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+SILLY_MAKE_INIT(int init_maestro3(void))
+#endif
+{
+    struct pci_dev *pcidev = NULL;
+    int found = 0;
+    int i;
+
+    if (!pci_present())   /* No PCI bus in this machine! */
+        return -ENODEV;
+
+    printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n");
+
+    switch(global_dsp_speed) {
+        case 33: case 36: case 49:
+            break;
+        default:
+            printk(KERN_INFO PFX "invalid global_dsp_speed: %d, must be 33, 36, or 49.\n",
+                    global_dsp_speed);
+            return -EINVAL;
+            break;
+    }
+
+#ifdef CONFIG_APM
+    init_waitqueue_head(&suspend_queue);
+#endif
+
+    for(i = 0; i < NUM_CARD_TYPES; i++) {
+        struct card_type *ct = &m3_card_types[i];
+
+        pcidev = NULL;
+        while( (pcidev = pci_find_device(PCI_VENDOR_ESS, ct->pci_id, pcidev)) != NULL ) {
+            if (! maestro_install(pcidev, ct))
+                found++;
+        }
+    }
+
+    printk(KERN_INFO PFX "%d maestros installed.\n",found);
+
+    if(found == 0 ) 
+        return -ENODEV;
+
+    return 0;
+}
+
+void nuke_maestros(void)
+{
+    struct ess_card *card;
+
+    unregister_reboot_notifier(&maestro_nb);
+#ifdef CONFIG_APM
+    apm_unregister_callback(m3_apm_callback);
+#endif
+
+    while ((card = devs)) {
+        int i;
+        devs = devs->next;
+    
+        free_irq(card->irq, card);
+        unregister_sound_mixer(card->ac97->dev_mixer);
+        kfree(card->ac97);
+
+        for(i=0;i<NR_DSPS;i++)
+        {
+            struct ess_state *ess = &card->channels[i];
+            if(ess->dev_audio != -1)
+                unregister_sound_dsp(ess->dev_audio);
+        }
+
+        release_region(card->iobase, 256);
+        free_dsp_savemem(card);
+        kfree(card);
+    }
+    devs = NULL;
+}
+
+static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+{
+    /* this notifier is called when the kernel is really shut down. */
+    DPRINTK(DPMOD,"shutting down\n");
+    nuke_maestros();
+    return NOTIFY_OK;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>");
+MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver");
+#ifdef M_DEBUG
+MODULE_PARM(debug,"i");
+MODULE_PARM(global_dsp_speed,"i");
+#endif
+MODULE_PARM(external_amp,"i");
+
+void cleanup_module(void) {
+    DPRINTK(DPMOD,"unloading\n");
+    nuke_maestros();
+}
+
+#endif /* MODULE */
+
+#ifdef CONFIG_APM
+void
+check_suspend(void)
+{
+    DECLARE_WAITQUEUE(wait, current);
+
+    if(!in_suspend) 
+        return;
+
+    in_suspend++;
+    add_wait_queue(&suspend_queue, &wait);
+    current->state = TASK_UNINTERRUPTIBLE;
+    schedule();
+    remove_wait_queue(&suspend_queue, &wait);
+    current->state = TASK_RUNNING;
+}
+
+static int 
+m3_suspend(void)
+{
+    struct ess_card *card;
+    unsigned long flags;
+    int index;
+
+    save_flags(flags);
+    cli();
+
+    for (card = devs; card ; card = card->next) {
+        int i;
+
+        DPRINTK(DPMOD, "apm in dev %p\n",card);
+
+        for(i=0;i<NR_DSPS;i++) {
+            struct ess_state *s = &card->channels[i];
+
+            if(s->dev_audio == -1)
+                continue;
+
+            DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i);
+            stop_dac(s);
+            stop_adc(s);
+        }
+
+        mdelay(10); /* give the assp a chance to idle.. */
+
+        m3_assp_halt(card);
+
+        index = 0;
+        DPRINTK(DPMOD, "saving code\n");
+        for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++)
+            card->suspend_dsp_mem[index++] = 
+                m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i);
+        DPRINTK(DPMOD, "saving data\n");
+        for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+            card->suspend_dsp_mem[index++] = 
+                m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i);
+
+        DPRINTK(DPMOD, "powering down apci regs\n");
+        m3_outw(card, 0xffff, 0x54);
+        m3_outw(card, 0xffff, 0x56);
+    }
+    in_suspend=1;
+
+    restore_flags(flags);
+
+    return 0;
+}
+
+static int 
+m3_resume(void)
+{
+    struct ess_card *card;
+    unsigned long flags;
+    int index;
+
+    save_flags(flags); /* paranoia */
+    cli();
+    in_suspend=0;
+
+    DPRINTK(DPMOD, "resuming\n");
+
+    /* first lets just bring everything back. .*/
+    for (card = devs; card ; card = card->next) {
+        int i;
+
+        DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card);
+        m3_outw(card, 0, 0x54);
+        m3_outw(card, 0, 0x56);
+
+        DPRINTK(DPMOD, "restoring pci configs and reseting codec\n");
+        maestro_config(card);
+        m3_assp_halt(card);
+        m3_codec_reset(card, 1);
+
+        DPRINTK(DPMOD, "restoring dsp code\n");
+        index = 0;
+        for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++)
+            m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, 
+                card->suspend_dsp_mem[index++]);
+        for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+            m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, 
+                card->suspend_dsp_mem[index++]);
+
+         /* tell the dma engine to restart itself */
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+                KDATA_DMA_ACTIVE, 0);
+
+        DPRINTK(DPMOD, "resuming dsp\n");
+        m3_assp_continue(card);
+
+        DPRINTK(DPMOD, "enabling ints\n");
+        m3_enable_ints(card);
+
+        /* bring back the old school flavor */
+        for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) {
+            int state = card->ac97->mixer_state[i];
+            if (!supported_mixer(card->ac97, i)) 
+                continue;
+
+            card->ac97->write_mixer(card->ac97, i, 
+                    state & 0xff, (state >> 8) & 0xff);
+        }
+
+        m3_amp_enable(card, 1);
+    }
+
+    /* 
+     * now we flip on the music 
+     */
+    for (card = devs; card ; card = card->next) {
+        int i;
+
+
+        for(i=0;i<NR_DSPS;i++) {
+            struct ess_state *s = &card->channels[i];
+            if(s->dev_audio == -1)
+                continue;
+            /*
+             * db->ready makes it so these guys can be
+             * called unconditionally..
+             */
+            DPRINTK(DPMOD, "turning on dacs ind %d\n",i);
+            start_dac(s);    
+            start_adc(s);    
+        }
+    }
+
+    restore_flags(flags);
+
+    /* 
+     * all right, we think things are ready, 
+     * wake up people who were using the device 
+     * when we suspended
+     */
+    wake_up(&suspend_queue);
+
+    return 0;
+}
+
+int 
+m3_apm_callback(apm_event_t ae) {
+
+    DPRINTK(DPMOD, "APM event received: 0x%x\n",ae);
+
+    switch(ae) {
+        case APM_SYS_SUSPEND: 
+        case APM_CRITICAL_SUSPEND: 
+        case APM_USER_SUSPEND: 
+
+            m3_suspend();
+            break;
+
+        case APM_NORMAL_RESUME: 
+        case APM_CRITICAL_RESUME: 
+        case APM_STANDBY_RESUME: 
+
+            m3_resume();
+            break;
+
+        default: 
+            break;
+    }
+
+    return 0;
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/maestro3.h linux.19p6/drivers/sound/maestro3.h
--- linux.vanilla/drivers/sound/maestro3.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/sound/maestro3.h	Wed Jan  3 01:01:13 2001
@@ -0,0 +1,821 @@
+/*
+ *      ESS Technology allegro audio driver.
+ *
+ *      Copyright (C) 1992-2000  Don Kim (don.kim@esstech.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.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *      Hacked for the maestro3 driver by zab
+ */
+
+// Allegro PCI configuration registers
+#define PCI_LEGACY_AUDIO_CTRL   0x40
+#define SOUND_BLASTER_ENABLE    0x00000001
+#define FM_SYNTHESIS_ENABLE     0x00000002
+#define GAME_PORT_ENABLE        0x00000004
+#define MPU401_IO_ENABLE        0x00000008
+#define MPU401_IRQ_ENABLE       0x00000010
+#define ALIAS_10BIT_IO          0x00000020
+#define SB_DMA_MASK             0x000000C0
+#define SB_DMA_0                0x00000040
+#define SB_DMA_1                0x00000040
+#define SB_DMA_R                0x00000080
+#define SB_DMA_3                0x000000C0
+#define SB_IRQ_MASK             0x00000700
+#define SB_IRQ_5                0x00000000
+#define SB_IRQ_7                0x00000100
+#define SB_IRQ_9                0x00000200
+#define SB_IRQ_10               0x00000300
+#define MIDI_IRQ_MASK           0x00003800
+#define SERIAL_IRQ_ENABLE       0x00004000
+#define DISABLE_LEGACY          0x00008000
+
+#define PCI_ALLEGRO_CONFIG      0x50
+#define SB_ADDR_240             0x00000004
+#define MPU_ADDR_MASK           0x00000018
+#define MPU_ADDR_330            0x00000000
+#define MPU_ADDR_300            0x00000008
+#define MPU_ADDR_320            0x00000010
+#define MPU_ADDR_340            0x00000018
+#define USE_PCI_TIMING          0x00000040
+#define POSTED_WRITE_ENABLE     0x00000080
+#define DMA_POLICY_MASK         0x00000700
+#define DMA_DDMA                0x00000000
+#define DMA_TDMA                0x00000100
+#define DMA_PCPCI               0x00000200
+#define DMA_WBDMA16             0x00000400
+#define DMA_WBDMA4              0x00000500
+#define DMA_WBDMA2              0x00000600
+#define DMA_WBDMA1              0x00000700
+#define DMA_SAFE_GUARD          0x00000800
+#define HI_PERF_GP_ENABLE       0x00001000
+#define PIC_SNOOP_MODE_0        0x00002000
+#define PIC_SNOOP_MODE_1        0x00004000
+#define SOUNDBLASTER_IRQ_MASK   0x00008000
+#define RING_IN_ENABLE          0x00010000
+#define SPDIF_TEST_MODE         0x00020000
+#define CLK_MULT_MODE_SELECT_2  0x00040000
+#define EEPROM_WRITE_ENABLE     0x00080000
+#define CODEC_DIR_IN            0x00100000
+#define HV_BUTTON_FROM_GD       0x00200000
+#define REDUCED_DEBOUNCE        0x00400000
+#define HV_CTRL_ENABLE          0x00800000
+#define SPDIF_ENABLE            0x01000000
+#define CLK_DIV_SELECT          0x06000000
+#define CLK_DIV_BY_48           0x00000000
+#define CLK_DIV_BY_49           0x02000000
+#define CLK_DIV_BY_50           0x04000000
+#define CLK_DIV_RESERVED        0x06000000
+#define PM_CTRL_ENABLE          0x08000000
+#define CLK_MULT_MODE_SELECT    0x30000000
+#define CLK_MULT_MODE_SHIFT     28
+#define CLK_MULT_MODE_0         0x00000000
+#define CLK_MULT_MODE_1         0x10000000
+#define CLK_MULT_MODE_2         0x20000000
+#define CLK_MULT_MODE_3         0x30000000
+#define INT_CLK_SELECT          0x40000000
+#define INT_CLK_MULT_RESET      0x80000000
+
+// M3
+#define INT_CLK_SRC_NOT_PCI     0x00100000
+#define INT_CLK_MULT_ENABLE     0x80000000
+
+#define PCI_ACPI_CONTROL        0x54
+#define PCI_ACPI_D0             0x00000000
+#define PCI_ACPI_D1             0xB4F70000
+#define PCI_ACPI_D2             0xB4F7B4F7
+
+#define PCI_USER_CONFIG         0x58
+#define EXT_PCI_MASTER_ENABLE   0x00000001
+#define SPDIF_OUT_SELECT        0x00000002
+#define TEST_PIN_DIR_CTRL       0x00000004
+#define AC97_CODEC_TEST         0x00000020
+#define TRI_STATE_BUFFER        0x00000080
+#define IN_CLK_12MHZ_SELECT     0x00000100
+#define MULTI_FUNC_DISABLE      0x00000200
+#define EXT_MASTER_PAIR_SEL     0x00000400
+#define PCI_MASTER_SUPPORT      0x00000800
+#define STOP_CLOCK_ENABLE       0x00001000
+#define EAPD_DRIVE_ENABLE       0x00002000
+#define REQ_TRI_STATE_ENABLE    0x00004000
+#define REQ_LOW_ENABLE          0x00008000
+#define MIDI_1_ENABLE           0x00010000
+#define MIDI_2_ENABLE           0x00020000
+#define SB_AUDIO_SYNC           0x00040000
+#define HV_CTRL_TEST            0x00100000
+#define SOUNDBLASTER_TEST       0x00400000
+
+#define PCI_USER_CONFIG_C       0x5C
+
+#define PCI_DDMA_CTRL           0x60
+#define DDMA_ENABLE             0x00000001
+
+
+// Allegro registers
+#define HOST_INT_CTRL           0x18
+#define SB_INT_ENABLE           0x0001
+#define MPU401_INT_ENABLE       0x0002
+#define ASSP_INT_ENABLE         0x0010
+#define RING_INT_ENABLE         0x0020
+#define HV_INT_ENABLE           0x0040
+#define CLKRUN_GEN_ENABLE       0x0100
+#define HV_CTRL_TO_PME          0x0400
+#define SOFTWARE_RESET_ENABLE   0x8000
+
+/*
+ * should be using the above defines, probably.
+ */
+#define REGB_ENABLE_RESET               0x01
+#define REGB_STOP_CLOCK                 0x10
+
+#define HOST_INT_STATUS         0x1A
+#define SB_INT_PENDING          0x01
+#define MPU401_INT_PENDING      0x02
+#define ASSP_INT_PENDING        0x10
+#define RING_INT_PENDING        0x20
+#define HV_INT_PENDING          0x40
+
+#define HARDWARE_VOL_CTRL       0x1B
+#define SHADOW_MIX_REG_VOICE    0x1C
+#define HW_VOL_COUNTER_VOICE    0x1D
+#define SHADOW_MIX_REG_MASTER   0x1E
+#define HW_VOL_COUNTER_MASTER   0x1F
+
+#define CODEC_COMMAND           0x30
+#define CODEC_READ_B            0x80
+
+#define CODEC_STATUS            0x30
+#define CODEC_BUSY_B            0x01
+
+#define CODEC_DATA              0x32
+
+#define RING_BUS_CTRL_A         0x36
+#define RAC_PME_ENABLE          0x0100
+#define RAC_SDFS_ENABLE         0x0200
+#define LAC_PME_ENABLE          0x0400
+#define LAC_SDFS_ENABLE         0x0800
+#define SERIAL_AC_LINK_ENABLE   0x1000
+#define IO_SRAM_ENABLE          0x2000
+#define IIS_INPUT_ENABLE        0x8000
+
+#define RING_BUS_CTRL_B         0x38
+#define SECOND_CODEC_ID_MASK    0x0003
+#define SPDIF_FUNC_ENABLE       0x0010
+#define SECOND_AC_ENABLE        0x0020
+#define SB_MODULE_INTF_ENABLE   0x0040
+#define SSPE_ENABLE             0x0040
+#define M3I_DOCK_ENABLE         0x0080
+
+#define SDO_OUT_DEST_CTRL       0x3A
+#define COMMAND_ADDR_OUT        0x0003
+#define PCM_LR_OUT_LOCAL        0x0000
+#define PCM_LR_OUT_REMOTE       0x0004
+#define PCM_LR_OUT_MUTE         0x0008
+#define PCM_LR_OUT_BOTH         0x000C
+#define LINE1_DAC_OUT_LOCAL     0x0000
+#define LINE1_DAC_OUT_REMOTE    0x0010
+#define LINE1_DAC_OUT_MUTE      0x0020
+#define LINE1_DAC_OUT_BOTH      0x0030
+#define PCM_CLS_OUT_LOCAL       0x0000
+#define PCM_CLS_OUT_REMOTE      0x0040
+#define PCM_CLS_OUT_MUTE        0x0080
+#define PCM_CLS_OUT_BOTH        0x00C0
+#define PCM_RLF_OUT_LOCAL       0x0000
+#define PCM_RLF_OUT_REMOTE      0x0100
+#define PCM_RLF_OUT_MUTE        0x0200
+#define PCM_RLF_OUT_BOTH        0x0300
+#define LINE2_DAC_OUT_LOCAL     0x0000
+#define LINE2_DAC_OUT_REMOTE    0x0400
+#define LINE2_DAC_OUT_MUTE      0x0800
+#define LINE2_DAC_OUT_BOTH      0x0C00
+#define HANDSET_OUT_LOCAL       0x0000
+#define HANDSET_OUT_REMOTE      0x1000
+#define HANDSET_OUT_MUTE        0x2000
+#define HANDSET_OUT_BOTH        0x3000
+#define IO_CTRL_OUT_LOCAL       0x0000
+#define IO_CTRL_OUT_REMOTE      0x4000
+#define IO_CTRL_OUT_MUTE        0x8000
+#define IO_CTRL_OUT_BOTH        0xC000
+
+#define SDO_IN_DEST_CTRL        0x3C
+#define STATUS_ADDR_IN          0x0003
+#define PCM_LR_IN_LOCAL         0x0000
+#define PCM_LR_IN_REMOTE        0x0004
+#define PCM_LR_RESERVED         0x0008
+#define PCM_LR_IN_BOTH          0x000C
+#define LINE1_ADC_IN_LOCAL      0x0000
+#define LINE1_ADC_IN_REMOTE     0x0010
+#define LINE1_ADC_IN_MUTE       0x0020
+#define MIC_ADC_IN_LOCAL        0x0000
+#define MIC_ADC_IN_REMOTE       0x0040
+#define MIC_ADC_IN_MUTE         0x0080
+#define LINE2_DAC_IN_LOCAL      0x0000
+#define LINE2_DAC_IN_REMOTE     0x0400
+#define LINE2_DAC_IN_MUTE       0x0800
+#define HANDSET_IN_LOCAL        0x0000
+#define HANDSET_IN_REMOTE       0x1000
+#define HANDSET_IN_MUTE         0x2000
+#define IO_STATUS_IN_LOCAL      0x0000
+#define IO_STATUS_IN_REMOTE     0x4000
+
+#define SPDIF_IN_CTRL           0x3E
+#define SPDIF_IN_ENABLE         0x0001
+
+#define GPIO_DATA               0x60
+#define GPIO_DATA_MASK          0x0FFF
+#define GPIO_HV_STATUS          0x3000
+#define GPIO_PME_STATUS         0x4000
+
+#define GPIO_MASK               0x64
+#define GPIO_DIRECTION          0x68
+#define GPO_PRIMARY_AC97        0x0001
+#define GPI_LINEOUT_SENSE       0x0004
+#define GPO_SECONDARY_AC97      0x0008
+#define GPI_VOL_DOWN            0x0010
+#define GPI_VOL_UP              0x0020
+#define GPI_IIS_CLK             0x0040
+#define GPI_IIS_LRCLK           0x0080
+#define GPI_IIS_DATA            0x0100
+#define GPI_DOCKING_STATUS      0x0100
+#define GPI_HEADPHONE_SENSE     0x0200
+#define GPO_EXT_AMP_SHUTDOWN    0x1000
+
+// M3
+#define GPO_M3_EXT_AMP_SHUTDN   0x0002
+
+#define ASSP_INDEX_PORT         0x80
+#define ASSP_MEMORY_PORT        0x82
+#define ASSP_DATA_PORT          0x84
+
+#define MPU401_DATA_PORT        0x98
+#define MPU401_STATUS_PORT      0x99
+
+#define CLK_MULT_DATA_PORT      0x9C
+
+#define ASSP_CONTROL_A          0xA2
+#define ASSP_0_WS_ENABLE        0x01
+#define ASSP_CTRL_A_RESERVED1   0x02
+#define ASSP_CTRL_A_RESERVED2   0x04
+#define ASSP_CLK_49MHZ_SELECT   0x08
+#define FAST_PLU_ENABLE         0x10
+#define ASSP_CTRL_A_RESERVED3   0x20
+#define DSP_CLK_36MHZ_SELECT    0x40
+
+#define ASSP_CONTROL_B          0xA4
+#define RESET_ASSP              0x00
+#define RUN_ASSP                0x01
+#define ENABLE_ASSP_CLOCK       0x00
+#define STOP_ASSP_CLOCK         0x10
+#define RESET_TOGGLE            0x40
+
+#define ASSP_CONTROL_C          0xA6
+#define ASSP_HOST_INT_ENABLE    0x01
+#define FM_ADDR_REMAP_DISABLE   0x02
+#define HOST_WRITE_PORT_ENABLE  0x08
+
+#define ASSP_HOST_INT_STATUS    0xAC
+#define DSP2HOST_REQ_PIORECORD  0x01
+#define DSP2HOST_REQ_I2SRATE    0x02
+#define DSP2HOST_REQ_TIMER      0x04
+
+// AC97 registers
+// XXX fix this crap up
+/*#define AC97_RESET              0x00*/
+
+#define AC97_VOL_MUTE_B         0x8000
+#define AC97_VOL_M              0x1F
+#define AC97_LEFT_VOL_S         8
+
+#define AC97_MASTER_VOL         0x02
+#define AC97_LINE_LEVEL_VOL     0x04
+#define AC97_MASTER_MONO_VOL    0x06
+#define AC97_PC_BEEP_VOL        0x0A
+#define AC97_PC_BEEP_VOL_M      0x0F
+#define AC97_SROUND_MASTER_VOL  0x38
+#define AC97_PC_BEEP_VOL_S      1
+
+/*#define AC97_PHONE_VOL          0x0C
+#define AC97_MIC_VOL            0x0E*/
+#define AC97_MIC_20DB_ENABLE    0x40
+
+/*#define AC97_LINEIN_VOL         0x10
+#define AC97_CD_VOL             0x12
+#define AC97_VIDEO_VOL          0x14
+#define AC97_AUX_VOL            0x16*/
+#define AC97_PCM_OUT_VOL        0x18
+/*#define AC97_RECORD_SELECT      0x1A*/
+#define AC97_RECORD_MIC         0x00
+#define AC97_RECORD_CD          0x01
+#define AC97_RECORD_VIDEO       0x02
+#define AC97_RECORD_AUX         0x03
+#define AC97_RECORD_MONO_MUX    0x02
+#define AC97_RECORD_DIGITAL     0x03
+#define AC97_RECORD_LINE        0x04
+#define AC97_RECORD_STEREO      0x05
+#define AC97_RECORD_MONO        0x06
+#define AC97_RECORD_PHONE       0x07
+
+/*#define AC97_RECORD_GAIN        0x1C*/
+#define AC97_RECORD_VOL_M       0x0F
+
+/*#define AC97_GENERAL_PURPOSE    0x20*/
+#define AC97_POWER_DOWN_CTRL    0x26
+#define AC97_ADC_READY          0x0001
+#define AC97_DAC_READY          0x0002
+#define AC97_ANALOG_READY       0x0004
+#define AC97_VREF_ON            0x0008
+#define AC97_PR0                0x0100
+#define AC97_PR1                0x0200
+#define AC97_PR2                0x0400
+#define AC97_PR3                0x0800
+#define AC97_PR4                0x1000
+
+#define AC97_RESERVED1          0x28
+
+#define AC97_VENDOR_TEST        0x5A
+
+#define AC97_CLOCK_DELAY        0x5C
+#define AC97_LINEOUT_MUX_SEL    0x0001
+#define AC97_MONO_MUX_SEL       0x0002
+#define AC97_CLOCK_DELAY_SEL    0x1F
+#define AC97_DAC_CDS_SHIFT      6
+#define AC97_ADC_CDS_SHIFT      11
+
+#define AC97_MULTI_CHANNEL_SEL  0x74
+
+/*#define AC97_VENDOR_ID1         0x7C
+#define AC97_VENDOR_ID2         0x7E*/
+
+/*
+ * ASSP control regs
+ */
+#define DSP_PORT_TIMER_COUNT    0x06
+
+#define DSP_PORT_MEMORY_INDEX   0x80
+
+#define DSP_PORT_MEMORY_TYPE    0x82
+#define MEMTYPE_INTERNAL_CODE   0x0002
+#define MEMTYPE_INTERNAL_DATA   0x0003
+#define MEMTYPE_MASK            0x0003
+
+#define DSP_PORT_MEMORY_DATA    0x84
+
+#define DSP_PORT_CONTROL_REG_A  0xA2
+#define DSP_PORT_CONTROL_REG_B  0xA4
+#define DSP_PORT_CONTROL_REG_C  0xA6
+
+#define REV_A_CODE_MEMORY_BEGIN         0x0000
+#define REV_A_CODE_MEMORY_END           0x0FFF
+#define REV_A_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_A_CODE_MEMORY_LENGTH        (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1)
+
+#define REV_B_CODE_MEMORY_BEGIN         0x0000
+#define REV_B_CODE_MEMORY_END           0x0BFF
+#define REV_B_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_B_CODE_MEMORY_LENGTH        (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1)
+
+#define REV_A_DATA_MEMORY_BEGIN         0x1000
+#define REV_A_DATA_MEMORY_END           0x2FFF
+#define REV_A_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_A_DATA_MEMORY_LENGTH        (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1)
+
+#define REV_B_DATA_MEMORY_BEGIN         0x1000
+#define REV_B_DATA_MEMORY_END           0x2BFF
+#define REV_B_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_B_DATA_MEMORY_LENGTH        (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1)
+
+
+#define NUM_UNITS_KERNEL_CODE          16
+#define NUM_UNITS_KERNEL_DATA           2
+
+#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16
+#define NUM_UNITS_KERNEL_DATA_WITH_HSP  5
+
+/*
+ * Kernel data layout
+ */
+
+#define DP_SHIFT_COUNT                  7
+
+#define KDATA_BASE_ADDR                 0x1000
+#define KDATA_BASE_ADDR2                0x1080
+
+#define KDATA_TASK0                     (KDATA_BASE_ADDR + 0x0000)
+#define KDATA_TASK1                     (KDATA_BASE_ADDR + 0x0001)
+#define KDATA_TASK2                     (KDATA_BASE_ADDR + 0x0002)
+#define KDATA_TASK3                     (KDATA_BASE_ADDR + 0x0003)
+#define KDATA_TASK4                     (KDATA_BASE_ADDR + 0x0004)
+#define KDATA_TASK5                     (KDATA_BASE_ADDR + 0x0005)
+#define KDATA_TASK6                     (KDATA_BASE_ADDR + 0x0006)
+#define KDATA_TASK7                     (KDATA_BASE_ADDR + 0x0007)
+#define KDATA_TASK_ENDMARK              (KDATA_BASE_ADDR + 0x0008)
+
+#define KDATA_CURRENT_TASK              (KDATA_BASE_ADDR + 0x0009)
+#define KDATA_TASK_SWITCH               (KDATA_BASE_ADDR + 0x000A)
+
+#define KDATA_INSTANCE0_POS3D           (KDATA_BASE_ADDR + 0x000B)
+#define KDATA_INSTANCE1_POS3D           (KDATA_BASE_ADDR + 0x000C)
+#define KDATA_INSTANCE2_POS3D           (KDATA_BASE_ADDR + 0x000D)
+#define KDATA_INSTANCE3_POS3D           (KDATA_BASE_ADDR + 0x000E)
+#define KDATA_INSTANCE4_POS3D           (KDATA_BASE_ADDR + 0x000F)
+#define KDATA_INSTANCE5_POS3D           (KDATA_BASE_ADDR + 0x0010)
+#define KDATA_INSTANCE6_POS3D           (KDATA_BASE_ADDR + 0x0011)
+#define KDATA_INSTANCE7_POS3D           (KDATA_BASE_ADDR + 0x0012)
+#define KDATA_INSTANCE8_POS3D           (KDATA_BASE_ADDR + 0x0013)
+#define KDATA_INSTANCE_POS3D_ENDMARK    (KDATA_BASE_ADDR + 0x0014)
+
+#define KDATA_INSTANCE0_SPKVIRT         (KDATA_BASE_ADDR + 0x0015)
+#define KDATA_INSTANCE_SPKVIRT_ENDMARK  (KDATA_BASE_ADDR + 0x0016)
+
+#define KDATA_INSTANCE0_SPDIF           (KDATA_BASE_ADDR + 0x0017)
+#define KDATA_INSTANCE_SPDIF_ENDMARK    (KDATA_BASE_ADDR + 0x0018)
+
+#define KDATA_INSTANCE0_MODEM           (KDATA_BASE_ADDR + 0x0019)
+#define KDATA_INSTANCE_MODEM_ENDMARK    (KDATA_BASE_ADDR + 0x001A)
+
+#define KDATA_INSTANCE0_SRC             (KDATA_BASE_ADDR + 0x001B)
+#define KDATA_INSTANCE1_SRC             (KDATA_BASE_ADDR + 0x001C)
+#define KDATA_INSTANCE_SRC_ENDMARK      (KDATA_BASE_ADDR + 0x001D)
+
+#define KDATA_INSTANCE0_MINISRC         (KDATA_BASE_ADDR + 0x001E)
+#define KDATA_INSTANCE1_MINISRC         (KDATA_BASE_ADDR + 0x001F)
+#define KDATA_INSTANCE2_MINISRC         (KDATA_BASE_ADDR + 0x0020)
+#define KDATA_INSTANCE3_MINISRC         (KDATA_BASE_ADDR + 0x0021)
+#define KDATA_INSTANCE_MINISRC_ENDMARK  (KDATA_BASE_ADDR + 0x0022)
+
+#define KDATA_INSTANCE0_CPYTHRU         (KDATA_BASE_ADDR + 0x0023)
+#define KDATA_INSTANCE1_CPYTHRU         (KDATA_BASE_ADDR + 0x0024)
+#define KDATA_INSTANCE_CPYTHRU_ENDMARK  (KDATA_BASE_ADDR + 0x0025)
+
+#define KDATA_CURRENT_DMA               (KDATA_BASE_ADDR + 0x0026)
+#define KDATA_DMA_SWITCH                (KDATA_BASE_ADDR + 0x0027)
+#define KDATA_DMA_ACTIVE                (KDATA_BASE_ADDR + 0x0028)
+
+#define KDATA_DMA_XFER0                 (KDATA_BASE_ADDR + 0x0029)
+#define KDATA_DMA_XFER1                 (KDATA_BASE_ADDR + 0x002A)
+#define KDATA_DMA_XFER2                 (KDATA_BASE_ADDR + 0x002B)
+#define KDATA_DMA_XFER3                 (KDATA_BASE_ADDR + 0x002C)
+#define KDATA_DMA_XFER4                 (KDATA_BASE_ADDR + 0x002D)
+#define KDATA_DMA_XFER5                 (KDATA_BASE_ADDR + 0x002E)
+#define KDATA_DMA_XFER6                 (KDATA_BASE_ADDR + 0x002F)
+#define KDATA_DMA_XFER7                 (KDATA_BASE_ADDR + 0x0030)
+#define KDATA_DMA_XFER8                 (KDATA_BASE_ADDR + 0x0031)
+#define KDATA_DMA_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0032)
+
+#define KDATA_I2S_SAMPLE_COUNT          (KDATA_BASE_ADDR + 0x0033)
+#define KDATA_I2S_INT_METER             (KDATA_BASE_ADDR + 0x0034)
+#define KDATA_I2S_ACTIVE                (KDATA_BASE_ADDR + 0x0035)
+
+#define KDATA_TIMER_COUNT_RELOAD        (KDATA_BASE_ADDR + 0x0036)
+#define KDATA_TIMER_COUNT_CURRENT       (KDATA_BASE_ADDR + 0x0037)
+
+#define KDATA_HALT_SYNCH_CLIENT         (KDATA_BASE_ADDR + 0x0038)
+#define KDATA_HALT_SYNCH_DMA            (KDATA_BASE_ADDR + 0x0039)
+#define KDATA_HALT_ACKNOWLEDGE          (KDATA_BASE_ADDR + 0x003A)
+
+#define KDATA_ADC1_XFER0                (KDATA_BASE_ADDR + 0x003B)
+#define KDATA_ADC1_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x003C)
+#define KDATA_ADC1_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x003D)
+#define KDATA_ADC1_RIGHT_VOLUME  		(KDATA_BASE_ADDR + 0x003E)
+#define KDATA_ADC1_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x003F)
+#define KDATA_ADC1_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0040)
+
+#define KDATA_ADC2_XFER0                (KDATA_BASE_ADDR + 0x0041)
+#define KDATA_ADC2_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x0042)
+#define KDATA_ADC2_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0043)
+#define KDATA_ADC2_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x0044)
+#define KDATA_ADC2_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x0045)
+#define KDATA_ADC2_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0046)
+
+#define KDATA_CD_XFER0					(KDATA_BASE_ADDR + 0x0047)					
+#define KDATA_CD_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x0048)
+#define KDATA_CD_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0049)
+#define KDATA_CD_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x004A)
+#define KDATA_CD_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x004B)
+#define KDATA_CD_RIGHT_SUR_VOL			(KDATA_BASE_ADDR + 0x004C)
+
+#define KDATA_MIC_XFER0					(KDATA_BASE_ADDR + 0x004D)
+#define KDATA_MIC_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x004E)
+#define KDATA_MIC_VOLUME				(KDATA_BASE_ADDR + 0x004F)
+#define KDATA_MIC_SUR_VOL				(KDATA_BASE_ADDR + 0x0050)
+
+#define KDATA_I2S_XFER0                 (KDATA_BASE_ADDR + 0x0051)
+#define KDATA_I2S_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0052)
+
+#define KDATA_CHI_XFER0                 (KDATA_BASE_ADDR + 0x0053)
+#define KDATA_CHI_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0054)
+
+#define KDATA_SPDIF_XFER                (KDATA_BASE_ADDR + 0x0055)
+#define KDATA_SPDIF_CURRENT_FRAME       (KDATA_BASE_ADDR + 0x0056)
+#define KDATA_SPDIF_FRAME0              (KDATA_BASE_ADDR + 0x0057)
+#define KDATA_SPDIF_FRAME1              (KDATA_BASE_ADDR + 0x0058)
+#define KDATA_SPDIF_FRAME2              (KDATA_BASE_ADDR + 0x0059)
+
+#define KDATA_SPDIF_REQUEST             (KDATA_BASE_ADDR + 0x005A)
+#define KDATA_SPDIF_TEMP                (KDATA_BASE_ADDR + 0x005B)
+
+#define KDATA_SPDIFIN_XFER0             (KDATA_BASE_ADDR + 0x005C)
+#define KDATA_SPDIFIN_XFER_ENDMARK      (KDATA_BASE_ADDR + 0x005D)
+#define KDATA_SPDIFIN_INT_METER         (KDATA_BASE_ADDR + 0x005E)
+
+#define KDATA_DSP_RESET_COUNT           (KDATA_BASE_ADDR + 0x005F)
+#define KDATA_DEBUG_OUTPUT              (KDATA_BASE_ADDR + 0x0060)
+
+#define KDATA_KERNEL_ISR_LIST           (KDATA_BASE_ADDR + 0x0061)
+
+#define KDATA_KERNEL_ISR_CBSR1          (KDATA_BASE_ADDR + 0x0062)
+#define KDATA_KERNEL_ISR_CBER1          (KDATA_BASE_ADDR + 0x0063)
+#define KDATA_KERNEL_ISR_CBCR           (KDATA_BASE_ADDR + 0x0064)
+#define KDATA_KERNEL_ISR_AR0            (KDATA_BASE_ADDR + 0x0065)
+#define KDATA_KERNEL_ISR_AR1            (KDATA_BASE_ADDR + 0x0066)
+#define KDATA_KERNEL_ISR_AR2            (KDATA_BASE_ADDR + 0x0067)
+#define KDATA_KERNEL_ISR_AR3            (KDATA_BASE_ADDR + 0x0068)
+#define KDATA_KERNEL_ISR_AR4            (KDATA_BASE_ADDR + 0x0069)
+#define KDATA_KERNEL_ISR_AR5            (KDATA_BASE_ADDR + 0x006A)
+#define KDATA_KERNEL_ISR_BRCR           (KDATA_BASE_ADDR + 0x006B)
+#define KDATA_KERNEL_ISR_PASR           (KDATA_BASE_ADDR + 0x006C)
+#define KDATA_KERNEL_ISR_PAER           (KDATA_BASE_ADDR + 0x006D)
+
+#define KDATA_CLIENT_SCRATCH0           (KDATA_BASE_ADDR + 0x006E)
+#define KDATA_CLIENT_SCRATCH1           (KDATA_BASE_ADDR + 0x006F)
+#define KDATA_KERNEL_SCRATCH            (KDATA_BASE_ADDR + 0x0070)
+#define KDATA_KERNEL_ISR_SCRATCH        (KDATA_BASE_ADDR + 0x0071)
+
+#define KDATA_OUEUE_LEFT                (KDATA_BASE_ADDR + 0x0072)
+#define KDATA_QUEUE_RIGHT               (KDATA_BASE_ADDR + 0x0073)
+
+#define KDATA_ADC1_REQUEST              (KDATA_BASE_ADDR + 0x0074)
+#define KDATA_ADC2_REQUEST              (KDATA_BASE_ADDR + 0x0075)
+#define KDATA_CD_REQUEST				(KDATA_BASE_ADDR + 0x0076)
+#define KDATA_MIC_REQUEST				(KDATA_BASE_ADDR + 0x0077)
+
+#define KDATA_ADC1_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0078)
+#define KDATA_ADC2_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0079)
+#define KDATA_CD_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007A)
+#define KDATA_MIC_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007B)
+#define KDATA_MIC_SYNC_COUNTER			(KDATA_BASE_ADDR + 0x007C)
+
+/*
+ * second 'segment' (?) reserved for mixer
+ * buffers..
+ */
+
+#define KDATA_MIXER_WORD0               (KDATA_BASE_ADDR2 + 0x0000)
+#define KDATA_MIXER_WORD1               (KDATA_BASE_ADDR2 + 0x0001)
+#define KDATA_MIXER_WORD2               (KDATA_BASE_ADDR2 + 0x0002)
+#define KDATA_MIXER_WORD3               (KDATA_BASE_ADDR2 + 0x0003)
+#define KDATA_MIXER_WORD4               (KDATA_BASE_ADDR2 + 0x0004)
+#define KDATA_MIXER_WORD5               (KDATA_BASE_ADDR2 + 0x0005)
+#define KDATA_MIXER_WORD6               (KDATA_BASE_ADDR2 + 0x0006)
+#define KDATA_MIXER_WORD7               (KDATA_BASE_ADDR2 + 0x0007)
+#define KDATA_MIXER_WORD8               (KDATA_BASE_ADDR2 + 0x0008)
+#define KDATA_MIXER_WORD9               (KDATA_BASE_ADDR2 + 0x0009)
+#define KDATA_MIXER_WORDA               (KDATA_BASE_ADDR2 + 0x000A)
+#define KDATA_MIXER_WORDB               (KDATA_BASE_ADDR2 + 0x000B)
+#define KDATA_MIXER_WORDC               (KDATA_BASE_ADDR2 + 0x000C)
+#define KDATA_MIXER_WORDD               (KDATA_BASE_ADDR2 + 0x000D)
+#define KDATA_MIXER_WORDE               (KDATA_BASE_ADDR2 + 0x000E)
+#define KDATA_MIXER_WORDF               (KDATA_BASE_ADDR2 + 0x000F)
+
+#define KDATA_MIXER_XFER0               (KDATA_BASE_ADDR2 + 0x0010)
+#define KDATA_MIXER_XFER1               (KDATA_BASE_ADDR2 + 0x0011)
+#define KDATA_MIXER_XFER2               (KDATA_BASE_ADDR2 + 0x0012)
+#define KDATA_MIXER_XFER3               (KDATA_BASE_ADDR2 + 0x0013)
+#define KDATA_MIXER_XFER4               (KDATA_BASE_ADDR2 + 0x0014)
+#define KDATA_MIXER_XFER5               (KDATA_BASE_ADDR2 + 0x0015)
+#define KDATA_MIXER_XFER6               (KDATA_BASE_ADDR2 + 0x0016)
+#define KDATA_MIXER_XFER7               (KDATA_BASE_ADDR2 + 0x0017)
+#define KDATA_MIXER_XFER8               (KDATA_BASE_ADDR2 + 0x0018)
+#define KDATA_MIXER_XFER9               (KDATA_BASE_ADDR2 + 0x0019)
+#define KDATA_MIXER_XFER_ENDMARK        (KDATA_BASE_ADDR2 + 0x001A)
+
+#define KDATA_MIXER_TASK_NUMBER         (KDATA_BASE_ADDR2 + 0x001B)
+#define KDATA_CURRENT_MIXER             (KDATA_BASE_ADDR2 + 0x001C)
+#define KDATA_MIXER_ACTIVE              (KDATA_BASE_ADDR2 + 0x001D)
+#define KDATA_MIXER_BANK_STATUS         (KDATA_BASE_ADDR2 + 0x001E)
+#define KDATA_DAC_LEFT_VOLUME	        (KDATA_BASE_ADDR2 + 0x001F)
+#define KDATA_DAC_RIGHT_VOLUME          (KDATA_BASE_ADDR2 + 0x0020)
+
+#define MAX_INSTANCE_MINISRC            (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC)
+#define MAX_VIRTUAL_DMA_CHANNELS        (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0)
+#define MAX_VIRTUAL_MIXER_CHANNELS      (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0)
+#define MAX_VIRTUAL_ADC1_CHANNELS       (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0)
+
+/*
+ * client data area offsets
+ */
+#define CDATA_INSTANCE_READY            0x00
+
+#define CDATA_HOST_SRC_ADDRL            0x01
+#define CDATA_HOST_SRC_ADDRH            0x02
+#define CDATA_HOST_SRC_END_PLUS_1L      0x03
+#define CDATA_HOST_SRC_END_PLUS_1H      0x04
+#define CDATA_HOST_SRC_CURRENTL         0x05
+#define CDATA_HOST_SRC_CURRENTH         0x06
+
+#define CDATA_IN_BUF_CONNECT            0x07
+#define CDATA_OUT_BUF_CONNECT           0x08
+
+#define CDATA_IN_BUF_BEGIN              0x09
+#define CDATA_IN_BUF_END_PLUS_1         0x0A
+#define CDATA_IN_BUF_HEAD               0x0B
+#define CDATA_IN_BUF_TAIL               0x0C
+#define CDATA_OUT_BUF_BEGIN             0x0D
+#define CDATA_OUT_BUF_END_PLUS_1        0x0E
+#define CDATA_OUT_BUF_HEAD              0x0F
+#define CDATA_OUT_BUF_TAIL              0x10
+
+#define CDATA_DMA_CONTROL               0x11
+#define CDATA_RESERVED                  0x12
+
+#define CDATA_FREQUENCY                 0x13
+#define CDATA_LEFT_VOLUME               0x14
+#define CDATA_RIGHT_VOLUME              0x15
+#define CDATA_LEFT_SUR_VOL              0x16
+#define CDATA_RIGHT_SUR_VOL             0x17
+
+#define CDATA_HEADER_LEN                0x18
+
+#define SRC3_DIRECTION_OFFSET           CDATA_HEADER_LEN
+#define SRC3_MODE_OFFSET                (CDATA_HEADER_LEN + 1)
+#define SRC3_WORD_LENGTH_OFFSET         (CDATA_HEADER_LEN + 2)
+#define SRC3_PARAMETER_OFFSET           (CDATA_HEADER_LEN + 3)
+#define SRC3_COEFF_ADDR_OFFSET          (CDATA_HEADER_LEN + 8)
+#define SRC3_FILTAP_ADDR_OFFSET         (CDATA_HEADER_LEN + 10)
+#define SRC3_TEMP_INBUF_ADDR_OFFSET     (CDATA_HEADER_LEN + 16)
+#define SRC3_TEMP_OUTBUF_ADDR_OFFSET    (CDATA_HEADER_LEN + 17)
+
+#define MINISRC_IN_BUFFER_SIZE   ( 0x50 * 2 )
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_TMP_BUFFER_SIZE  ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
+#define MINISRC_BIQUAD_STAGE    2
+#define MINISRC_COEF_LOC          0X175
+
+#define DMACONTROL_BLOCK_MASK           0x000F
+#define  DMAC_BLOCK0_SELECTOR           0x0000
+#define  DMAC_BLOCK1_SELECTOR           0x0001
+#define  DMAC_BLOCK2_SELECTOR           0x0002
+#define  DMAC_BLOCK3_SELECTOR           0x0003
+#define  DMAC_BLOCK4_SELECTOR           0x0004
+#define  DMAC_BLOCK5_SELECTOR           0x0005
+#define  DMAC_BLOCK6_SELECTOR           0x0006
+#define  DMAC_BLOCK7_SELECTOR           0x0007
+#define  DMAC_BLOCK8_SELECTOR           0x0008
+#define  DMAC_BLOCK9_SELECTOR           0x0009
+#define  DMAC_BLOCKA_SELECTOR           0x000A
+#define  DMAC_BLOCKB_SELECTOR           0x000B
+#define  DMAC_BLOCKC_SELECTOR           0x000C
+#define  DMAC_BLOCKD_SELECTOR           0x000D
+#define  DMAC_BLOCKE_SELECTOR           0x000E
+#define  DMAC_BLOCKF_SELECTOR           0x000F
+#define DMACONTROL_PAGE_MASK            0x00F0
+#define  DMAC_PAGE0_SELECTOR            0x0030
+#define  DMAC_PAGE1_SELECTOR            0x0020
+#define  DMAC_PAGE2_SELECTOR            0x0010
+#define  DMAC_PAGE3_SELECTOR            0x0000
+#define DMACONTROL_AUTOREPEAT           0x1000
+#define DMACONTROL_STOPPED              0x2000
+#define DMACONTROL_DIRECTION            0x0100
+
+
+/*
+ * DSP Code images
+ */
+
+u16 assp_kernel_image[] = {
+    0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, 
+    0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, 
+    0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, 
+    0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, 
+    0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, 
+    0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, 
+    0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, 
+    0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, 
+    0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, 
+    0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, 
+    0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, 
+    0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, 
+    0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, 
+    0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, 
+    0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, 
+    0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, 
+    0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, 
+    0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, 
+    0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, 
+    0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, 
+    0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, 
+    0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, 
+    0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, 
+    0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, 
+    0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, 
+    0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, 
+    0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, 
+    0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, 
+    0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, 
+    0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, 
+    0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, 
+    0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 
+    0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, 
+    0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, 
+    0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, 
+    0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, 
+    0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 
+    0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, 
+    0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, 
+    0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, 
+    0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, 
+    0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, 
+    0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 
+    0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, 
+    0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, 
+    0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, 
+    0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 
+    0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, 
+    0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, 
+    0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, 
+    0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, 
+    0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, 
+    0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, 
+    0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, 
+    0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, 
+    0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 
+    0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, 
+    0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 
+    0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, 
+    0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, 
+    0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, 
+    0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, 
+    0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, 
+    0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, 
+    0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, 
+    0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, 
+    0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, 
+    0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, 
+    0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, 
+    0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, 
+    0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, 
+    0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, 
+    0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, 
+    0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, 
+    0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, 
+    0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, 
+    0xBE3A, 
+};
+
+/*
+ * Mini sample rate converter code image
+ * that is to be loaded at 0x400 on the DSP.
+ */
+u16 assp_minisrc_image[] = {
+
+    0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, 
+    0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, 
+    0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, 
+    0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, 
+    0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, 
+    0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, 
+    0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, 
+    0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, 
+    0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, 
+    0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, 
+    0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, 
+    0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, 
+    0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, 
+    0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, 
+    0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, 
+    0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, 
+    0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, 
+    0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, 
+    0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, 
+    0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, 
+    0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, 
+    0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, 
+    0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, 
+    0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, 
+    0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, 
+    0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, 
+    0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, 
+    0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, 
+    0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, 
+    0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, 
+    0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, 
+    0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sonicvibes.c linux.19p6/drivers/sound/sonicvibes.c
--- linux.vanilla/drivers/sound/sonicvibes.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/sonicvibes.c	Tue Dec 19 02:13:08 2000
@@ -77,11 +77,15 @@
  *    24.08.1999   0.19  get rid of the dmaio kludge, replace with allocate_resource
  *    31.08.1999   0.20  add spin_lock_init
  *                       __initlocaldata to fix gcc 2.7.x problems
- *                       use new resource allocation to allocate DDMA IO space
  *                       replaced current->state = x with set_current_state(x)
  *    03.09.1999   0.21  change read semantics for MIDI to match
  *                       OSS more closely; remove possible wakeup race
  *    28.10.1999   0.22  More waitqueue races fixed
+ *    08.01.2000   0.25  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *    21.11.2000   0.27  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.28  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
  *
  */
 
@@ -1543,6 +1547,7 @@
 	unsigned long flags;
         audio_buf_info abinfo;
         count_info cinfo;
+	int count;
 	int val, mapped, ret;
 	unsigned char fmtm, fmtd;
 
@@ -1578,7 +1583,8 @@
 		return 0;
 
         case SNDCTL_DSP_SPEED:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val >= 0) {
 			if (file->f_mode & FMODE_READ) {
 				stop_adc(s);
@@ -1594,7 +1600,8 @@
 		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
 		
         case SNDCTL_DSP_STEREO:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		fmtd = 0;
 		fmtm = ~0;
 		if (file->f_mode & FMODE_READ) {
@@ -1617,7 +1624,8 @@
 		return 0;
 
         case SNDCTL_DSP_CHANNELS:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 0) {
 			fmtd = 0;
 			fmtm = ~0;
@@ -1646,7 +1654,8 @@
                 return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
 		
 	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != AFMT_QUERY) {
 			fmtd = 0;
 			fmtm = ~0;
@@ -1683,7 +1692,8 @@
 		return put_user(val, (int *)arg);
 		
 	case SNDCTL_DSP_SETTRIGGER:
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			if (val & PCM_ENABLE_INPUT) {
 				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
@@ -1705,12 +1715,15 @@
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
-		if (!(s->enable & SV_CENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0)
+		if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
 		abinfo.fragsize = s->dma_dac.fragsize;
-                abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac.dmasize - count;
                 abinfo.fragstotal = s->dma_dac.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -1719,12 +1732,15 @@
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
-		if (!(s->enable & SV_CENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0)
+		if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0)
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
 		abinfo.fragsize = s->dma_adc.fragsize;
-                abinfo.bytes = s->dma_adc.count;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = count;
                 abinfo.fragstotal = s->dma_adc.numfrag;
                 abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -1737,19 +1753,28 @@
         case SNDCTL_DSP_GETODELAY:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
-                val = s->dma_dac.count;
+                count = s->dma_dac.count;
 		spin_unlock_irqrestore(&s->lock, flags);
-		return put_user(val, (int *)arg);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
 
         case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -1759,10 +1784,15 @@
         case SNDCTL_DSP_GETOPTR:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
                 cinfo.bytes = s->dma_dac.total_bytes;
-                cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac.fragshift;
                 cinfo.ptr = s->dma_dac.hwptr;
 		if (s->dma_dac.mapped)
 			s->dma_dac.count &= s->dma_dac.fragsize-1;
@@ -1780,7 +1810,8 @@
 		return put_user(s->dma_adc.fragsize, (int *)arg);
 
         case SNDCTL_DSP_SETFRAGMENT:
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
 			s->dma_adc.ossfragshift = val & 0xffff;
 			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
@@ -1807,7 +1838,8 @@
 		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
 		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
 			return -EINVAL;
-                get_user_ret(val, (int *)arg, -EFAULT);
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		if (file->f_mode & FMODE_READ)
@@ -2432,7 +2464,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.22 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.28 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sound_core.c linux.19p6/drivers/sound/sound_core.c
--- linux.vanilla/drivers/sound/sound_core.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/sound_core.c	Wed Jan  3 01:01:13 2001
@@ -52,6 +52,7 @@
 	struct sound_unit *next;
 };
 
+extern int init_cmpci(void);
 extern int init_sonicvibes(void);
 extern int init_maestro(void);
 extern int init_trident(void);
@@ -63,7 +64,6 @@
 extern int init_solo1(void);
 extern int init_ymf7xxsb_module(void);
 extern int cs_probe(void);
-extern int init_emu10k1(void);
 extern int cs4281_probe(void);
 extern void init_vwsnd(void);
 extern int ymf_probe(void);
@@ -396,6 +396,9 @@
 	/*
 	 *	Now init non OSS drivers
 	 */
+#ifdef CONFIG_SOUND_CMPCI
+        init_cmpci();
+#endif
 #ifdef CONFIG_SOUND_VWSND
 	init_vwsnd();
 #endif
@@ -411,6 +414,9 @@
 #ifdef CONFIG_SOUND_MAESTRO
 	init_maestro();
 #endif
+#ifdef CONFIG_SOUND_MAESTRO3
+    init_maestro3();
+#endif
 #ifdef CONFIG_SOUND_TRIDENT
 	init_trident();
 #endif
@@ -434,9 +440,6 @@
 #endif
 #ifdef CONFIG_SOUND_CS4281
 	cs4281_probe();
-#endif
-#ifdef CONFIG_SOUND_EMU10K1
-	init_emu10k1();
 #endif
 #ifdef CONFIG_SOUND_YMFPCI
 	ymf_probe();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/trident.c linux.19p6/drivers/sound/trident.c
--- linux.vanilla/drivers/sound/trident.c	Mon Dec 11 22:12:56 2000
+++ linux.19p6/drivers/sound/trident.c	Wed Jan  3 01:00:48 2001
@@ -2789,7 +2789,7 @@
 	u16 w;
 	unsigned long iobase;
 	struct trident_card *card;
-
+	unsigned long flags;
 
 	iobase = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
 	if (check_region(iobase, 256)) {
@@ -2882,10 +2882,17 @@
 		
 			/* reset command */
 			while (inb(card->iobase + ALI_MPUR1) & 0x40);
+			
+			/* After the out the chip goes into irq jammed on
+			   mode. We have to keep irqs off until it shuts up 
+			   again */
+			save_flags(flags);
+			cli();
 			outb(0xff, card->iobase + ALI_MPUR1);
 			while (inb(card->iobase + ALI_MPUR1) & 0x80);
 			while ((inb(card->iobase + ALI_MPUR0) != 0xfe) && (inb(card->iobase+ALI_MPUR1) != 0x10)) 
 				while (inb(card->iobase + ALI_MPUR1) & 0x80);
+			restore_flags(flags);
 			
 			midi->ird = midi->iwr = midi->icnt = 0;
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymf_sb.c linux.19p6/drivers/sound/ymf_sb.c
--- linux.vanilla/drivers/sound/ymf_sb.c	Sat Sep  9 18:42:45 2000
+++ linux.19p6/drivers/sound/ymf_sb.c	Thu Jan  1 01:00:00 1970
@@ -1,995 +0,0 @@
-/*
-  Legacy audio driver for YMF724, 740, 744, 754 series.
-  Copyright 2000 Daisuke Nagano <breeze.nagano@nifty.ne.jp>
-
-  Based on the VIA 82Cxxx driver by Jeff Garzik <jgarzik@pobox.com>
-
-  Distribued under the GNU PUBLIC LICENSE (GPL) Version 2.
-  See the "COPYING" file distributed with kernel source tree for more info.
-
-  -------------------------------------------------------------------------
-
-  It only supports SBPro compatible function of YMF7xx series s.t.
-    * 22.05kHz, 8-bit and stereo sample
-    * OPL3-compatible FM synthesizer
-    * MPU-401 compatible "external" MIDI interface
-
-  and AC'97 mixer of these cards.
-
-  -------------------------------------------------------------------------
-
-  Revision history
-
-   Tue May 14 19:00:00 2000   0.0.1
-   * initial release
-
-   Tue May 16 19:29:29 2000   0.0.2
-
-   * add a little delays for reset devices.
-   * fixed addressing bug.
-
-   Sun May 21 15:14:37 2000   0.0.3
-
-   * Add 'master_vol' module parameter to change 'PCM out Vol' of AC'97.
-   * remove native UART401 support. External MIDI port should be supported 
-     by sb_midi driver.
-   * add support for SPDIF OUT. Module parameter 'spdif_out' is now available.
-
-   Wed May 31 00:13:57 2000   0.0.4
-
-   * remove entries in Hwmcode.h. Now YMF744 / YMF754 sets instructions 
-     in 724hwmcode.h.
-   * fixed wrong legacy_io setting on YMF744/YMF754 .
-
-   Sat Jun  3 23:44:08 2000   0.0.5
-
-   * correct all return value -1 to appropriate -E* value.
-   * add pci_enable_device() for linux-2.3.x.
-
-   Sat Jun  7 00:17:32 2000   0.1.0
-
-   * Remove SBPro mixer support.
-   * Added AC'97 mixer support and remove option 'master_volume'.
-   * Correct all separated valuables into struct ymf_cards.
-
-   Mon Jun 12 21:36:50 2000   0.1.1
-
-   * Invalid dma setting fixed.
-     -- John A. Boyd Jr. <jaboydjr@netwalk.com>
-
-   * ymfsb only compiled as module: fixed
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/version.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-
-#include "sound_config.h"
-#include "soundmodule.h"
-#include "sb.h"
-#include "724hwmcode.h"
-
-#include "ac97.h"
-
-#undef YMF_DEBUG
-
-/* ---------------------------------------------------------------------- */
-
-#ifndef SOUND_LOCK
-# define SOUND_LOCK do {} while (0)
-# define SOUND_LOCK_END do {} while (0)
-#endif
-
-#ifndef PCI_VENDOR_ID_YAMAHA
-# define PCI_VENDOR_ID_YAMAHA  0x1073
-#endif
-#ifndef PCI_DEVICE_ID_YMF724
-# define PCI_DEVICE_ID_YMF724  0x0004
-#endif
-#ifndef PCI_DEVICE_ID_YMF740
-# define PCI_DEVICE_ID_YMF740  0x000A
-#endif
-#ifndef PCI_DEVICE_ID_YMF740C
-# define PCI_DEVICE_ID_YMF740C 0x000C
-#endif
-#ifndef PCI_DEVICE_ID_YMF724F
-# define PCI_DEVICE_ID_YMF724F 0x000D
-#endif
-#ifndef PCI_DEVICE_ID_YMF744
-# define PCI_DEVICE_ID_YMF744  0x0010
-#endif
-#ifndef PCI_DEVICE_ID_YMF754
-# define PCI_DEVICE_ID_YMF754  0x0012
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-#define YMFSB_RESET_DELAY               5
-
-#define YMFSB_REGSIZE                   0x8000
-
-#define YMFSB_AC97TIMEOUT               2000
-
-#define	YMFSB_WORKBITTIMEOUT            250000
-
-#define	YMFSB_DSPLENGTH                 0x0080
-#define	YMFSB_CTRLLENGTH                0x3000
-
-#define YMFSB_PCIR_VENDORID             0x00
-#define YMFSB_PCIR_DEVICEID             0x02
-#define YMFSB_PCIR_CMD                  0x04
-#define YMFSB_PCIR_REVISIONID           0x08
-#define YMFSB_PCIR_BASEADDR             0x10
-#define YMFSB_PCIR_IRQ                  0x3c
-
-#define	YMFSB_PCIR_LEGCTRL              0x40
-#define	YMFSB_PCIR_ELEGCTRL             0x42
-#define	YMFSB_PCIR_DSXGCTRL             0x48
-#define YMFSB_PCIR_OPLADR               0x60
-#define YMFSB_PCIR_SBADR                0x62
-#define YMFSB_PCIR_MPUADR               0x64
-
-#define	YMFSB_INTFLAG                   0x0004
-#define	YMFSB_ACTIVITY                  0x0006
-#define	YMFSB_GLOBALCTRL                0x0008
-#define	YMFSB_ZVCTRL                    0x000A
-#define	YMFSB_TIMERCTRL                 0x0010
-#define	YMFSB_TIMERCOUNT                0x0012
-#define	YMFSB_SPDIFOUTCTRL              0x0018
-#define	YMFSB_SPDIFOUTSTATUS            0x001C
-#define	YMFSB_EEPROMCTRL                0x0020
-#define	YMFSB_SPDIFINCTRL               0x0034
-#define	YMFSB_SPDIFINSTATUS             0x0038
-#define	YMFSB_DSPPROGRAMDL              0x0048
-#define	YMFSB_DLCNTRL                   0x004C
-#define	YMFSB_GPIOININTFLAG             0x0050
-#define	YMFSB_GPIOININTENABLE           0x0052
-#define	YMFSB_GPIOINSTATUS              0x0054
-#define	YMFSB_GPIOOUTCTRL               0x0056
-#define	YMFSB_GPIOFUNCENABLE            0x0058
-#define	YMFSB_GPIOTYPECONFIG            0x005A
-#define	YMFSB_AC97CMDDATA               0x0060
-#define	YMFSB_AC97CMDADR                0x0062
-#define	YMFSB_PRISTATUSDATA             0x0064
-#define	YMFSB_PRISTATUSADR              0x0066
-#define	YMFSB_SECSTATUSDATA             0x0068
-#define	YMFSB_SECSTATUSADR              0x006A
-#define	YMFSB_SECCONFIG                 0x0070
-#define	YMFSB_LEGACYOUTVOL              0x0080
-#define	YMFSB_LEGACYOUTVOLL             0x0080
-#define	YMFSB_LEGACYOUTVOLR             0x0082
-#define	YMFSB_NATIVEDACOUTVOL           0x0084
-#define	YMFSB_NATIVEDACOUTVOLL          0x0084
-#define	YMFSB_NATIVEDACOUTVOLR          0x0086
-#define	YMFSB_SPDIFOUTVOL               0x0088
-#define	YMFSB_SPDIFOUTVOLL              0x0088
-#define	YMFSB_SPDIFOUTVOLR              0x008A
-#define	YMFSB_AC3OUTVOL                 0x008C
-#define	YMFSB_AC3OUTVOLL                0x008C
-#define	YMFSB_AC3OUTVOLR                0x008E
-#define	YMFSB_PRIADCOUTVOL              0x0090
-#define	YMFSB_PRIADCOUTVOLL             0x0090
-#define	YMFSB_PRIADCOUTVOLR             0x0092
-#define	YMFSB_LEGACYLOOPVOL             0x0094
-#define	YMFSB_LEGACYLOOPVOLL            0x0094
-#define	YMFSB_LEGACYLOOPVOLR            0x0096
-#define	YMFSB_NATIVEDACLOOPVOL          0x0098
-#define	YMFSB_NATIVEDACLOOPVOLL         0x0098
-#define	YMFSB_NATIVEDACLOOPVOLR         0x009A
-#define	YMFSB_SPDIFLOOPVOL              0x009C
-#define	YMFSB_SPDIFLOOPVOLL             0x009E
-#define	YMFSB_SPDIFLOOPVOLR             0x009E
-#define	YMFSB_AC3LOOPVOL                0x00A0
-#define	YMFSB_AC3LOOPVOLL               0x00A0
-#define	YMFSB_AC3LOOPVOLR               0x00A2
-#define	YMFSB_PRIADCLOOPVOL             0x00A4
-#define	YMFSB_PRIADCLOOPVOLL            0x00A4
-#define	YMFSB_PRIADCLOOPVOLR            0x00A6
-#define	YMFSB_NATIVEADCINVOL            0x00A8
-#define	YMFSB_NATIVEADCINVOLL           0x00A8
-#define	YMFSB_NATIVEADCINVOLR           0x00AA
-#define	YMFSB_NATIVEDACINVOL            0x00AC
-#define	YMFSB_NATIVEDACINVOLL           0x00AC
-#define	YMFSB_NATIVEDACINVOLR           0x00AE
-#define	YMFSB_BUF441OUTVOL              0x00B0
-#define	YMFSB_BUF441OUTVOLL             0x00B0
-#define	YMFSB_BUF441OUTVOLR             0x00B2
-#define	YMFSB_BUF441LOOPVOL             0x00B4
-#define	YMFSB_BUF441LOOPVOLL            0x00B4
-#define	YMFSB_BUF441LOOPVOLR            0x00B6
-#define	YMFSB_SPDIFOUTVOL2              0x00B8
-#define	YMFSB_SPDIFOUTVOL2L             0x00B8
-#define	YMFSB_SPDIFOUTVOL2R             0x00BA
-#define	YMFSB_SPDIFLOOPVOL2             0x00BC
-#define	YMFSB_SPDIFLOOPVOL2L            0x00BC
-#define	YMFSB_SPDIFLOOPVOL2R            0x00BE
-#define	YMFSB_ADCSLOTSR                 0x00C0
-#define	YMFSB_RECSLOTSR                 0x00C4
-#define	YMFSB_ADCFORMAT                 0x00C8
-#define	YMFSB_RECFORMAT                 0x00CC
-#define	YMFSB_P44SLOTSR                 0x00D0
-#define	YMFSB_STATUS                    0x0100
-#define	YMFSB_CTRLSELECT                0x0104
-#define	YMFSB_MODE                      0x0108
-#define	YMFSB_SAMPLECOUNT               0x010C
-#define	YMFSB_NUMOFSAMPLES              0x0110
-#define	YMFSB_CONFIG                    0x0114
-#define	YMFSB_PLAYCTRLSIZE              0x0140
-#define	YMFSB_RECCTRLSIZE               0x0144
-#define	YMFSB_EFFCTRLSIZE               0x0148
-#define	YMFSB_WORKSIZE                  0x014C
-#define	YMFSB_MAPOFREC                  0x0150
-#define	YMFSB_MAPOFEFFECT               0x0154
-#define	YMFSB_PLAYCTRLBASE              0x0158
-#define	YMFSB_RECCTRLBASE               0x015C
-#define	YMFSB_EFFCTRLBASE               0x0160
-#define	YMFSB_WORKBASE                  0x0164
-#define	YMFSB_DSPINSTRAM                0x1000
-#define	YMFSB_CTRLINSTRAM               0x4000
-
-
-/* ---------------------------------------------------------------------- */
-
-#define MAX_CARDS	4
-
-#define PFX		"ymf_sb: "
-
-#define YMFSB_VERSION	"0.1.1"
-#define YMFSB_CARD_NAME	"YMF7xx Legacy Audio driver " YMFSB_VERSION
-
-#define ymf7xxsb_probe_midi probe_sbmpu
-#define ymf7xxsb_attach_midi attach_sbmpu
-#define ymf7xxsb_unload_midi unload_sbmpu
-
-/* ---------------------------------------------------------------------- */
-
-static struct ymf_card {
-  int                  card;
-  unsigned short      *ymfbase;
-
-  struct address_info sb_data, opl3_data, mpu_data;
-
-  struct ac97_hwint   ac97_dev;
-  int                 mixer_oss_dev;
-} ymf_cards[MAX_CARDS];
-
-static unsigned int cards = 0;
-
-/* ---------------------------------------------------------------------- */
-
-#ifdef MODULE
-static int mpu_io     = 0;
-static int synth_io   = 0;
-static int io         = 0;
-static int dma        = 0;
-static int spdif_out  = 1;
-MODULE_PARM(mpu_io, "i");
-MODULE_PARM(synth_io, "i");
-MODULE_PARM(io,"i");
-MODULE_PARM(dma,"i");
-MODULE_PARM(spdif_out,"i");
-#else
-static int mpu_io     = 0x330;
-static int synth_io   = 0x388;
-static int io         = 0x220;
-static int dma        = 1;
-static int spdif_out  = 1;
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-static int readRegWord( struct ymf_card *card, int adr ) {
-
-	if (card->ymfbase==NULL) return 0;
-
-	return readw(card->ymfbase+adr/2);
-}
-
-static void writeRegWord( struct ymf_card *card, int adr, int val ) {
-
-	if (card->ymfbase==NULL) return;
-
-	writew((unsigned short)(val&0xffff), card->ymfbase + adr/2);
-
-	return;
-}
-
-static int readRegDWord( struct ymf_card *card, int adr ) {
-
-	if (card->ymfbase==NULL) return 0;
-
-	return (readl(card->ymfbase+adr/2));
-}
-
-static void writeRegDWord( struct ymf_card *card, int adr, int val ) {
-
-	if (card->ymfbase==NULL) return;
-
-	writel((unsigned int)(val&0xffffffff), card->ymfbase+adr/2);
-
-	return;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int checkPrimaryBusy( struct ymf_card *card )
-{
-	int timeout=0;
-
-	while ( timeout++ < YMFSB_AC97TIMEOUT )
-	{
-		if ( (readRegWord(card, YMFSB_PRISTATUSADR) & 0x8000) == 0x0000 )
-			return 0;
-	}
-	return -EBUSY;
-}
-
-static int checkCodec( struct ymf_card *card, struct pci_dev *pcidev )
-{
-	u8 tmp8;
-
-	pci_read_config_byte( pcidev, YMFSB_PCIR_DSXGCTRL, &tmp8 );
-	if ( tmp8 & 0x03 ) {
-		pci_write_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, tmp8&0xfc);
-		mdelay(YMFSB_RESET_DELAY);
-		pci_write_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, tmp8|0x03);
-		mdelay(YMFSB_RESET_DELAY);
-		pci_write_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, tmp8&0xfc);
-		mdelay(YMFSB_RESET_DELAY);
-	}
-
-	if ( checkPrimaryBusy( card ) ) return -EBUSY;
-
-	return 0;
-}
-
-static int setupLegacyIO( struct ymf_card *card, struct pci_dev *pcidev )
-{
-	int v;
-	int sbio=0, mpuio=0, oplio=0,dma=0;
-
-	switch(card->sb_data.io_base) {
-	case 0x220:
-		sbio = 0;
-		break;
-	case 0x240:
-		sbio = 1;
-		break;
-	case 0x260:
-		sbio = 2;
-		break;
-	case 0x280:
-		sbio = 3;
-		break;
-	default:
-		return -EINVAL;
-		break;
-	}
-#ifdef YMF_DEBUG
-	printk(PFX "set SBPro I/O at 0x%x\n",card->sb_data.io_base);
-#endif
-
-	switch(card->mpu_data.io_base) {
-	case 0x330:
-		mpuio = 0;
-		break;
-	case 0x300:
-		mpuio = 1;
-		break;
-	case 0x332:
-		mpuio = 2;
-		break;
-	case 0x334:
-		mpuio = 3;
-		break;
-	default:
-		mpuio = 0;
-		break;
-	}
-#ifdef YMF_DEBUG
-	printk(PFX "set MPU401 I/O at 0x%x\n",card->mpu_data.io_base);
-#endif
-
-	switch(card->opl3_data.io_base) {
-	case 0x388:
-		oplio = 0;
-		break;
-	case 0x398:
-		oplio = 1;
-		break;
-	case 0x3a0:
-		oplio = 2;
-		break;
-	case 0x3a8:
-		oplio = 3;
-		break;
-	default:
-		return -EINVAL;
-		break;
-	}
-#ifdef YMF_DEBUG
-	printk(PFX "set OPL3 I/O at 0x%x\n",card->opl3_data.io_base);
-#endif
-
-	dma = card->sb_data.dma;
-#ifdef YMF_DEBUG
-	printk(PFX "set DMA address at 0x%x\n",card->sb_data.dma);
-#endif
-
-	v = 0x0000 | ((dma&0x03)<<6) | 0x003f;
-	pci_write_config_word(pcidev, YMFSB_PCIR_LEGCTRL, v);
-#ifdef YMF_DEBUG
-	printk(PFX "LEGCTRL: 0x%x\n",v);
-#endif
-	switch( pcidev->device ) {
-	case PCI_DEVICE_ID_YMF724:
-	case PCI_DEVICE_ID_YMF740:
-	case PCI_DEVICE_ID_YMF724F:
-	case PCI_DEVICE_ID_YMF740C:
-		v = 0x8800 | ((mpuio<<4)&0x03) | ((sbio<<2)&0x03) | (oplio&0x03);
-		pci_write_config_word(pcidev, YMFSB_PCIR_ELEGCTRL, v);
-#ifdef YMF_DEBUG
-		printk(PFX "ELEGCTRL: 0x%x\n",v);
-#endif
-		break;
-
-	case PCI_DEVICE_ID_YMF744:
-	case PCI_DEVICE_ID_YMF754:
-		v = 0x8800;
-		pci_write_config_word(pcidev, YMFSB_PCIR_ELEGCTRL, v);
-		pci_write_config_word(pcidev, YMFSB_PCIR_OPLADR, card->opl3_data.io_base);
-		pci_write_config_word(pcidev, YMFSB_PCIR_SBADR,  card->sb_data.io_base);
-		pci_write_config_word(pcidev, YMFSB_PCIR_MPUADR, card->mpu_data.io_base);
-		break;
-
-	default:
-		printk(KERN_ERR PFX "Invalid device ID: %d\n",pcidev->device);
-		return -EINVAL;
-		break;
-	}
-
-	return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* AC'97 stuff */
-static int ymfsb_readAC97Reg( struct ac97_hwint *dev, u8 reg )
-{
-	struct ymf_card *card = (struct ymf_card *)dev->driver_private;
-	unsigned long flags;
-	int ret;
-
-	if ( reg > 0x7f ) return -EINVAL;
-
-	save_flags(flags);
-	cli();
-	writeRegWord( card, YMFSB_AC97CMDADR, 0x8000 | reg );
-	if ( checkPrimaryBusy( card ) ) {
-		restore_flags(flags);
-		return -EBUSY;
-	}
-	ret = readRegWord( card, YMFSB_AC97CMDDATA );
-	restore_flags(flags);
-
-	return ret;
-}
-
-static int ymfsb_writeAC97Reg( struct ac97_hwint *dev, u8 reg, u16 value )
-{
-	struct ymf_card *card = (struct ymf_card *)dev->driver_private;
-	unsigned long flags;
-
-	if ( reg > 0x7f ) return -EINVAL;
-
-	save_flags(flags);
-	cli();
-	if ( checkPrimaryBusy( card ) ) {
-	  restore_flags(flags);
-	  return -EBUSY;
-	}
-
-	writeRegWord( card, YMFSB_AC97CMDADR, 0x0000 | reg );
-	writeRegWord( card, YMFSB_AC97CMDDATA, value );
-
-	restore_flags(flags);
-	return 0;
-}
-
-struct initialValues
-{
-    unsigned short port;
-    unsigned short value;
-};
-
-static struct initialValues ymfsb_ac97_initial_values[] =
-{
-    { AC97_RESET,             0x0000 },
-    { AC97_MASTER_VOL_STEREO, 0x0000 },
-    { AC97_HEADPHONE_VOL,     0x8000 },
-    { AC97_PCMOUT_VOL,        0x0606 },
-    { AC97_MASTER_VOL_MONO,   0x0000 },
-    { AC97_PCBEEP_VOL,        0x0000 },
-    { AC97_PHONE_VOL,         0x0008 },
-    { AC97_MIC_VOL,           0x8000 },
-    { AC97_LINEIN_VOL,        0x8808 },
-    { AC97_CD_VOL,            0x8808 },
-    { AC97_VIDEO_VOL,         0x8808 },
-    { AC97_AUX_VOL,           0x8808 },
-    { AC97_RECORD_SELECT,     0x0000 },
-    { AC97_RECORD_GAIN,       0x0B0B },
-    { AC97_GENERAL_PURPOSE,   0x0000 },
-    { 0xffff, 0xffff }
-};
-
-static int ymfsb_resetAC97( struct ac97_hwint *dev )
-{
-	int i;
-
-	for ( i=0 ; ymfsb_ac97_initial_values[i].port != 0xffff ; i++ )
-	{
-		ac97_put_register ( dev,
-				    ymfsb_ac97_initial_values[i].port,
-				    ymfsb_ac97_initial_values[i].value );
-	}
-
-	return 0;
-}
-
-static int ymfsb_ac97_mixer_ioctl( int dev, unsigned int cmd, caddr_t arg )
-{
-	int i;
-
-	for ( i=0 ; i < MAX_CARDS ; i++ )
-	{
-		if ( ymf_cards[i].mixer_oss_dev == dev ) break;
-	}
-
-	if ( i < MAX_CARDS )
-		return ac97_mixer_ioctl(&(ymf_cards[i].ac97_dev), cmd, arg);
-	else
-		return -ENODEV;
-}
-
-static struct mixer_operations ymfsb_ac97_mixer_operations = {
-	"YAMAHA",
-	"YAMAHA PCI",
-	ymfsb_ac97_mixer_ioctl
-};
-
-static struct ac97_mixer_value_list ymfsb_ac97_mixer_defaults[] = {
-    { SOUND_MIXER_VOLUME,  { { 85, 85 } } },
-    { SOUND_MIXER_SPEAKER, { { 100 } } },
-    { SOUND_MIXER_PCM,     { { 65, 65 } } },
-    { SOUND_MIXER_CD,      { { 65, 65 } } },
-    { -1,                  {  { 0,  0 } } }
-};
-
-/* ---------------------------------------------------------------------- */
-
-static void enableDSP( struct ymf_card *card )
-{
-	writeRegDWord( card, YMFSB_CONFIG, 0x00000001 );
-	return;
-}
-
-static void disableDSP( struct ymf_card *card )
-{
-	int val;
-	int i;
-
-	val = readRegDWord( card, YMFSB_CONFIG );
-	if ( val ) {
-		writeRegDWord( card, YMFSB_CONFIG, 0 );
-	}
-
-	i=0;
-	while( ++i < YMFSB_WORKBITTIMEOUT ) {
-		val = readRegDWord( card, YMFSB_STATUS );
-		if ( (val & 0x00000002) == 0x00000000 ) break;
-	}
-
-	return;
-}
-
-static int setupInstruction( struct ymf_card *card, struct pci_dev *pcidev )
-{
-	int i;
-	int val;
-
-	writeRegDWord( card, YMFSB_NATIVEDACOUTVOL, 0 ); /* mute dac */
-	disableDSP( card );
-
-	writeRegDWord( card, YMFSB_MODE, 0x00010000 );
-
-	/* DS-XG Software Reset */
-	writeRegDWord( card, YMFSB_MODE,         0x00000000 );
-	writeRegDWord( card, YMFSB_MAPOFREC,     0x00000000 );
-	writeRegDWord( card, YMFSB_MAPOFEFFECT,  0x00000000 );
-	writeRegDWord( card, YMFSB_PLAYCTRLBASE, 0x00000000 );
-	writeRegDWord( card, YMFSB_RECCTRLBASE,  0x00000000 );
-	writeRegDWord( card, YMFSB_EFFCTRLBASE,  0x00000000 );
-
-	val = readRegWord( card, YMFSB_GLOBALCTRL );
-	writeRegWord( card, YMFSB_GLOBALCTRL, (val&~0x0007) );
-
-	/* setup DSP instruction code */
-	for ( i=0 ; i<YMFSB_DSPLENGTH ; i+=4 ) {
-	  writeRegDWord( card, YMFSB_DSPINSTRAM+i, DspInst[i>>2] );
-	}
-
-	switch( pcidev->device ) {
-	case PCI_DEVICE_ID_YMF724:
-	case PCI_DEVICE_ID_YMF740:
-		/* setup Control instruction code */
-		for ( i=0 ; i<YMFSB_CTRLLENGTH ; i+=4 ) {
-			writeRegDWord( card, YMFSB_CTRLINSTRAM+i, CntrlInst[i>>2] );
-		}
-		break;
-
-	case PCI_DEVICE_ID_YMF724F:
-	case PCI_DEVICE_ID_YMF740C:
-	case PCI_DEVICE_ID_YMF744:
-	case PCI_DEVICE_ID_YMF754:
-		/* setup Control instruction code */
-	
-		for ( i=0 ; i<YMFSB_CTRLLENGTH ; i+=4 ) {
-			writeRegDWord( card, YMFSB_CTRLINSTRAM+i, CntrlInst1E[i>>2] );
-		}
-		break;
-
-	default:
-		return -ENXIO;
-	}
-
-	enableDSP( card );
-
-	return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int __init ymf7xx_init( struct ymf_card *card, struct pci_dev *pcidev )
-{
-	unsigned short v;
-	int mixer;
-
-	/* Read hardware information */
-#ifdef YMF_DEBUG
-	unsigned int   dv;
-	pci_read_config_word(pcidev, YMFSB_PCIR_VENDORID, &v);
-	printk(KERN_INFO PFX "Vendor ID = 0x%x\n",v);
-	pci_read_config_word(pcidev, YMFSB_PCIR_DEVICEID, &v);
-	printk(KERN_INFO PFX "Device ID = 0x%x\n",v);
-	pci_read_config_word(pcidev, YMFSB_PCIR_REVISIONID, &v);
-	printk(KERN_INFO PFX "Revision ID = 0x%x\n",v&0xff);
-	pci_read_config_dword(pcidev, YMFSB_PCIR_BASEADDR, &dv);
-	printk(KERN_INFO PFX "Base address = 0x%x\n",dv);
-	pci_read_config_word(pcidev, YMFSB_PCIR_IRQ, &v);
-	printk(KERN_INFO PFX "IRQ line = 0x%x\n",v&0xff);
-#endif
-
-	/* enables memory space access / bus mastering */
-	pci_read_config_word(pcidev, YMFSB_PCIR_CMD, &v);
-	pci_write_config_word(pcidev, YMFSB_PCIR_CMD, v|0x06);
-
-	/* check codec */
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "check codec...\n");
-#endif
-	if (checkCodec(card, pcidev)) return -EBUSY;
-
-	/* setup legacy I/O */
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "setup legacy I/O...\n");
-#endif
-	if (setupLegacyIO(card, pcidev)) return -EBUSY;
-	
-	/* setup instruction code */	
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "setup instructions...\n");
-#endif
-	if (setupInstruction(card, pcidev)) return -EBUSY;
-
-	/* AC'97 setup */	
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "setup AC'97...\n");
-#endif
-	if ( ac97_init( &card->ac97_dev ) ) return -EBUSY;
-
-	mixer = sound_alloc_mixerdev();
-	if ( num_mixers >= MAX_MIXER_DEV ) return -EBUSY;
-	
-	mixer_devs[mixer] = &ymfsb_ac97_mixer_operations;
-	card->mixer_oss_dev = mixer;
-	ac97_set_values( &card->ac97_dev, ymfsb_ac97_mixer_defaults );
-
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "setup Legacy Volume...\n");
-#endif
-	/* Legacy Audio Output Volume L & R ch */
-	writeRegDWord( card, YMFSB_LEGACYOUTVOL, 0x3fff3fff );
-
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "setup SPDIF output control...\n");
-#endif
-	/* SPDIF Output control */
-	v = spdif_out != 0 ? 0x0001 : 0x0000;
-	writeRegWord( card, YMFSB_SPDIFOUTCTRL, v );
-	/* no copyright protection, 
-	   sample-rate converted,
-	   re-recorded software comercially available (the 1st generation),
-	   original */
-	writeRegWord( card, YMFSB_SPDIFOUTSTATUS, 0x9a04 );
-
-	return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void __init ymf7xxsb_attach_sb(struct address_info *hw_config)
-{
-	hw_config->driver_use_1 |= SB_NO_MIXER;
-	if(!sb_dsp_init(hw_config))
-		hw_config->slots[0] = -1;
-}
-
-static int __init ymf7xxsb_probe_sb(struct address_info *hw_config)
-{
-	if (check_region(hw_config->io_base, 16))
-	{
-		printk(KERN_DEBUG PFX "SBPro port 0x%x is already in use\n",
-		       hw_config->io_base);
-		return 0;
-	}
-	return sb_dsp_detect(hw_config, SB_PCI_YAMAHA, 0);
-}
-
-
-static void ymf7xxsb_unload_sb(struct address_info *hw_config, int unload_mpu)
-{
-	if(hw_config->slots[0]!=-1)
-		sb_dsp_unload(hw_config, unload_mpu);
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int __init ymf7xxsb_install (struct pci_dev *pcidev)
-{
-	struct {
-		unsigned short deviceid;
-		char           *devicename;
-	} devicetable[] = 
-	{
-		{ PCI_DEVICE_ID_YMF724,  "YMF724A-E" },
-		{ PCI_DEVICE_ID_YMF724F, "YMF724F" },
-		{ PCI_DEVICE_ID_YMF740,  "YMF740A-B" },
-		{ PCI_DEVICE_ID_YMF740C, "YMF740C" },
-		{ PCI_DEVICE_ID_YMF744,  "YMF744" },
-		{ PCI_DEVICE_ID_YMF754,  "YMF754" },
-	};
-
-	char		*devicename = "unknown";
-	int		i;
-	unsigned long   iobase;
-	struct ymf_card *card;
-
-	if ( pcidev->irq == 0 ) return -ENODEV;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
-	if ( pci_enable_device(pcidev) ) {
-		printk (KERN_ERR PFX "cannot enable PCI device\n");
-		return -EIO;
-	}
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
-	iobase = pcidev->base_address[0]&PCI_BASE_ADDRESS_MEM_MASK;
-#else
-	iobase = pcidev->resource[0].start&PCI_BASE_ADDRESS_MEM_MASK;
-#endif
-	if ( iobase == 0x00000000 ) return -ENODEV;
-
-	for ( i=0 ; i<sizeof(devicetable) / sizeof(devicetable[0]); i++ )
-	{
-		if (devicetable[i].deviceid == pcidev->device)
-		{
-			devicename = devicetable[i].devicename;
-			break;
-		}
-	}
-
-	card = &ymf_cards[cards];
-
-	/* remap memory mapped I/O onto kernel virtual memory */
-	if ( (card->ymfbase = ioremap_nocache(iobase, YMFSB_REGSIZE)) == 0 )
-	{
-		printk(KERN_ERR PFX "ioremap (0x%lx) returns zero\n", iobase);
-		return -ENODEV;
-	}
-	printk(KERN_INFO PFX "found %s at 0x%lx\n", devicename, iobase);
-#ifdef YMF_DEBUG
-	printk(KERN_INFO PFX "remappling to 0x%p\n", card->ymfbase);
-#endif
-
-	memset (&card->sb_data,   0, sizeof (struct address_info));
-	memset (&card->opl3_data, 0, sizeof (struct address_info));
-	memset (&card->mpu_data,  0, sizeof (struct address_info));
-	memset (&card->ac97_dev,  0, sizeof (struct ac97_hwint));
-
-	card->card = cards;
-
-	card->sb_data.name   = YMFSB_CARD_NAME;
-	card->opl3_data.name = YMFSB_CARD_NAME;
-	card->mpu_data.name  = YMFSB_CARD_NAME;
-
-	card->sb_data.card_subtype = MDL_YMPCI;
-
-	if ( io == 0 ) io      = 0x220;
-	card->sb_data.io_base = io;
-	card->sb_data.irq     = pcidev->irq;
-	card->sb_data.dma     = dma;
-
-	if ( synth_io == 0 ) synth_io = 0x388;
-	card->opl3_data.io_base = synth_io;
-	card->opl3_data.irq     = -1;
-
-	if ( mpu_io == 0 ) mpu_io = 0x330;
-	card->mpu_data.io_base = mpu_io;
-	card->mpu_data.irq     = -1;
-
-	card->ac97_dev.reset_device   = ymfsb_resetAC97;
-	card->ac97_dev.read_reg       = ymfsb_readAC97Reg;
-	card->ac97_dev.write_reg      = ymfsb_writeAC97Reg;
-	card->ac97_dev.driver_private = (void *)card;
-
-	if ( ymf7xx_init(card, pcidev) ) {
-		printk (KERN_ERR PFX
-			"Cannot initialize %s, aborting\n",
-			devicename);
-		return -ENODEV;
-	}
-
-	/* regist legacy SoundBlaster Pro */
-	if (!ymf7xxsb_probe_sb(&card->sb_data)) {
-		printk (KERN_ERR PFX
-			"SB probe at 0x%X failed, aborting\n",
-			io);
-		return -ENODEV;
-	}
-	ymf7xxsb_attach_sb (&card->sb_data);
-
-	/* regist legacy MIDI */
-	if ( mpu_io > 0 && 0)
-	{
-		if (!ymf7xxsb_probe_midi (&card->mpu_data)) {
-			printk (KERN_ERR PFX
-				"MIDI probe @ 0x%X failed, aborting\n",
-				mpu_io);
-			ymf7xxsb_unload_sb (&card->sb_data, 0);
-			return -ENODEV;
-		}
-		ymf7xxsb_attach_midi (&card->mpu_data);
-	}
-
-	/* regist legacy OPL3 */
-
-	cards++;	
-	return 0;
-}
-
-static int __init probe_ymf7xxsb (void)
-{
-	struct pci_dev *pcidev = NULL;
-	int i;
-
-	for (i=0 ; i<MAX_CARDS ; i++ )
-		ymf_cards[i].ymfbase = NULL;
-
-	while ( pcidev == NULL && (
-	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
-					  PCI_DEVICE_ID_YMF724, pcidev)) ||
-	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
-					  PCI_DEVICE_ID_YMF724F,pcidev)) ||
-	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
-					  PCI_DEVICE_ID_YMF740, pcidev)) ||
-	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
-					  PCI_DEVICE_ID_YMF740C,pcidev)) ||
-	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
-					  PCI_DEVICE_ID_YMF744, pcidev)) ||
-	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
-					  PCI_DEVICE_ID_YMF754, pcidev)))) {
-		  if (ymf7xxsb_install (pcidev)) {
-			  printk (KERN_ERR PFX "audio init failed\n");
-			  return -ENODEV;
-		  }
-
-		  if (cards == MAX_CARDS) {
-		  	  printk (KERN_DEBUG PFX "maximum number of cards reached\n");
-			  break;
-		  }
-	}
-
-	return 0;
-}
-
-static void free_iomaps( void )
-{
-	int i;
-
-	for ( i=0 ; i<MAX_CARDS ; i++ ) {
-		if ( ymf_cards[i].ymfbase!=NULL )
-			iounmap(ymf_cards[i].ymfbase);
-	}
-
-	return;
-}
-
-int __init init_ymf7xxsb_module(void)
-{
-	if (!pci_present ()) {
-		printk (KERN_DEBUG PFX "PCI not present, exiting\n");
-		return -ENODEV;
-	}
-
-	if (probe_ymf7xxsb()) {
-		printk(KERN_ERR PFX "probe failed, aborting\n");
-		/* XXX unload cards registered so far, if any */
-		free_iomaps();
-		return -ENODEV;
-	}
-
-	if (cards == 0) {
-		printk(KERN_DEBUG PFX "No chips found, aborting\n");
-		free_iomaps();
-		return -ENODEV;
-	}
-
-	printk (KERN_INFO PFX YMFSB_CARD_NAME " loaded\n");
-	
-	/*
-	 *	Binds us to the sound subsystem	
-	 */
-	SOUND_LOCK;
-	return 0;
-}
-
-static void cleanup_ymf7xxsb_module(void)
-{
-	int i;
-	
-	for (i = 0; i < cards; i++) {
-		ymf7xxsb_unload_sb (&(ymf_cards[i].sb_data), 0);
-		ymf7xxsb_unload_midi (&(ymf_cards[i].mpu_data));
-		if ( ymf_cards[i].mixer_oss_dev >= 0 ) 
-			sound_unload_mixerdev( ymf_cards[i].mixer_oss_dev );
-	}
-
-	free_iomaps();
-
-	/*
-	 *	Final clean up with the sound layer
-	 */
-	SOUND_LOCK_END;
-}
-
-#ifdef MODULE
-
-MODULE_AUTHOR("Daisuke Nagano, breeze.nagano@nifty.ne.jp");
-MODULE_DESCRIPTION("YMF7xx Legacy Audio Driver");
-
-int init_module(void)
-{
-	return init_ymf7xxsb_module();
-}
-
-void cleanup_module(void)
-{
-	cleanup_ymf7xxsb_module();
-}
-
-#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci.c linux.19p6/drivers/sound/ymfpci.c
--- linux.vanilla/drivers/sound/ymfpci.c	Mon Dec 11 22:12:57 2000
+++ linux.19p6/drivers/sound/ymfpci.c	Wed Jan  3 13:26:18 2001
@@ -46,6 +46,9 @@
 #include <asm/dma.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+# include "sound_config.h"
+#endif
 #include "ymfpci.h"
 
 #define snd_magic_cast(t, p, err)	((t *)(p))
@@ -59,7 +62,7 @@
     int pair, ymfpci_voice_t **rvoice);
 static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
 static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state);
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance);
+static int ymf_state_alloc(ymfpci_t *unit, int nvirt);
 
 static ymfpci_t *ymf_devs = NULL;
 
@@ -600,11 +603,9 @@
 	char silence;
 
 	if ((ypcm = voice->ypcm) == NULL) {
-/* P3 */ printk("ymf_pcm_interrupt: voice %d: no ypcm\n", voice->number);
 		return;
 	}
 	if ((state = ypcm->state) == NULL) {
-/* P3 */ printk("ymf_pcm_interrupt: voice %d: no state\n", voice->number);
 		ypcm->running = 0;	// lock it
 		return;
 	}
@@ -626,7 +627,7 @@
 		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */
 			printk(KERN_ERR
 			    "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n",
-			    codec->inst, voice->number,
+			    codec->dev_audio, voice->number,
 			    dmabuf->hwptr, dmabuf->dmasize);
 			pos = 0;
 		}
@@ -643,7 +644,7 @@
 
 		if (dmabuf->count == 0) {
 			printk("ymfpci%d: %d: strain: hwptr %d\n",
-			    codec->inst, voice->number, dmabuf->hwptr);
+			    codec->dev_audio, voice->number, dmabuf->hwptr);
 			ymf_playback_trigger(codec, ypcm, 0);
 		}
 
@@ -662,7 +663,7 @@
 				 */
 				printk("ymfpci%d: %d: lost: delta %d"
 				    " hwptr %d swptr %d distance %d count %d\n",
-				    codec->inst, voice->number, delta,
+				    codec->dev_audio, voice->number, delta,
 				    dmabuf->hwptr, swptr, distance, dmabuf->count);
 			} else {
 				/*
@@ -670,7 +671,7 @@
 				 */
 //				printk("ymfpci%d: %d: done: delta %d"
 //				    " hwptr %d swptr %d distance %d count %d\n",
-//				    codec->inst, voice->number, delta,
+//				    codec->dev_audio, voice->number, delta,
 //				    dmabuf->hwptr, swptr, distance, dmabuf->count);
 			}
 			played = dmabuf->count;
@@ -736,7 +737,6 @@
 {
 
 	if (ypcm->voices[0] == NULL) {
-/* P3 */ printk("ymfpci: trigger %d no voice\n", cmd);
 		return -EINVAL;
 	}
 	if (cmd != 0) {
@@ -826,8 +826,8 @@
 		end >>= 1;
 	if (w_16)
 		end >>= 1;
-/* P3 */ // printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n",
-//  voice->number, rate, format, delta, end);
+/* P3 */ /** printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n",
+  voice->number, rate, format, delta, end); **/
 	for (nbank = 0; nbank < 2; nbank++) {
 		bank = &voice->bank[nbank];
 		bank->format = format;
@@ -909,7 +909,7 @@
 	if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
 		/* Cannot be unless we leak voices in ymf_release! */
 		printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n",
-		    codec->inst);
+		    codec->dev_audio);
 		return err;
 	}
 
@@ -1027,6 +1027,12 @@
 			if (voice->interrupt)
 				voice->interrupt(codec, voice);
 		}
+#if HAVE_RECORD
+		for (nvoice = 0; nvoice < 5; nvoice++) {
+			if (codec->capture_substream[nvoice])
+				snd_ymfpci_pcm_capture_interrupt(codec->capture_substream[nvoice]);
+		}
+#endif
 		spin_unlock(&codec->voice_lock);
 	}
 
@@ -1050,7 +1056,7 @@
 	}
 }
 
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance)
+static int ymf_state_alloc(ymfpci_t *unit, int nvirt)
 {
 	ymfpci_pcm_t *ypcm;
 	struct ymf_state *state;
@@ -1536,12 +1542,13 @@
 		return put_user(SOUND_VERSION, (int *)arg);
 
 	case SNDCTL_DSP_RESET:
-		/* FIXME: spin_lock ? */
 		if (file->f_mode & FMODE_WRITE) {
 			ymf_wait_dac(state);
+			// spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
 			dmabuf->swptr = dmabuf->hwptr = 0;
 			dmabuf->count = dmabuf->total_bytes = 0;
+			// spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
 #if HAVE_RECORD
 		if (file->f_mode & FMODE_READ) {
@@ -1570,9 +1577,7 @@
 
 	case SNDCTL_DSP_SPEED: /* set smaple rate */
 		get_user_ret(val, (int *)arg, -EFAULT);
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SPEED %d\n", val); */
 		if (val >= 8000 && val <= 48000) {
-			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			if (file->f_mode & FMODE_WRITE) {
 				ymf_wait_dac(state);
 			}
@@ -1581,6 +1586,7 @@
 				stop_adc(state);
 			}
 #endif
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
 			state->format.rate = val;
 			ymf_pcm_update_shift(&state->format);
@@ -1596,7 +1602,6 @@
 	 */
 	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
 		get_user_ret(val, (int *)arg, -EFAULT);
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_STEREO %d\n", val); */
 		if (file->f_mode & FMODE_WRITE) {
 			ymf_wait_dac(state); 
 			spin_lock_irqsave(&state->unit->reg_lock, flags);
@@ -1618,7 +1623,6 @@
 		return 0;
 
 	case SNDCTL_DSP_GETBLKSIZE:
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETBLKSIZE\n"); */
 		if (file->f_mode & FMODE_WRITE) {
 			if ((val = prog_dmabuf(state, 0)))
 				return val;
@@ -1632,14 +1636,11 @@
 		return -EINVAL;
 
 	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETFMTS\n"); */
 		return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
 
 	case SNDCTL_DSP_SETFMT: /* Select sample format */
 		get_user_ret(val, (int *)arg, -EFAULT);
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETFMT 0x%x\n", val); */
 		if (val == AFMT_S16_LE || val == AFMT_U8) {
-			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			if (file->f_mode & FMODE_WRITE) {
 				ymf_wait_dac(state);
 			}
@@ -1648,6 +1649,7 @@
 				stop_adc(state);
 			}
 #endif
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
 			state->format.format = val;
 			ymf_pcm_update_shift(&state->format);
@@ -1657,15 +1659,15 @@
 
 	case SNDCTL_DSP_CHANNELS:
 		get_user_ret(val, (int *)arg, -EFAULT);
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */
 		if (val != 0) {
-			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			if (file->f_mode & FMODE_WRITE) {
 				ymf_wait_dac(state);
 				if (val == 1 || val == 2) {
+					spin_lock_irqsave(&state->unit->reg_lock, flags);
 					dmabuf->ready = 0;
 					state->format.voices = val;
 					ymf_pcm_update_shift(&state->format);
+					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 				}
 			}
 #if HAVE_RECORD
@@ -1674,7 +1676,6 @@
 				dmabuf->ready = 0;
 			}
 #endif
-			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
 		return put_user(state->format.voices, (int *)arg);
 
@@ -1690,7 +1691,6 @@
 		 * The paragraph above is a clumsy way to say "flush ioctl".
 		 * This ioctl is used by mpg123.
 		 */
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_POST\n"); */
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
 		if (dmabuf->count != 0 && !state->ypcm.running) {
 			ymf_start_dac(state);
@@ -1726,7 +1726,6 @@
 		return 0;
 
 	case SNDCTL_DSP_GETOSPACE:
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOSPACE\n"); */
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
@@ -1757,12 +1756,10 @@
 #endif
 
 	case SNDCTL_DSP_NONBLOCK:
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_NONBLOCK\n"); */
 		file->f_flags |= O_NONBLOCK;
 		return 0;
 
 	case SNDCTL_DSP_GETCAPS:
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETCAPS\n"); */
 		/* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
 			    (int *)arg); */
 		return put_user(0, (int *)arg);
@@ -1814,7 +1811,6 @@
 #endif
 
 	case SNDCTL_DSP_GETOPTR:
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOPTR\n"); */
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
@@ -1828,7 +1824,6 @@
 		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
 
 	case SNDCTL_DSP_SETDUPLEX:	/* XXX TODO */
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETDUPLEX\n"); */
 		return -EINVAL;
 
 #if 0 /* old */
@@ -1859,7 +1854,7 @@
 		return -ENOTTY;
 
 	default:
-	/* P3 */ printk("ymfpci: ioctl cmd 0x%x\n", cmd);
+	/* P3 */ /* printk("ymfpci: ioctl cmd 0x%x\n", cmd); */
 		/*
 		 * Some programs mix up audio devices and ioctls
 		 * or perhaps they expect "universal" ioctls,
@@ -1870,10 +1865,94 @@
 	return -ENOTTY;
 }
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+/* MIDI stuff */
+
+/* */
+
+static int ymfpci_setup_legacy( ymfpci_t *codec, struct pci_dev *pcidev )
+{
+	int v;
+	int mpuio=-1, oplio=-1;
+
+	switch(codec->iomidi) {
+	case 0x330:
+		mpuio = 0;
+		break;
+	case 0x300:
+		mpuio = 1;
+		break;
+	case 0x332:
+		mpuio = 2;
+		break;
+	case 0x334:
+		mpuio = 3;
+		break;
+	default:
+		break;
+	}
+
+	switch(codec->iosynth) {
+	case 0x388:
+		oplio = 0;
+		break;
+	case 0x398:
+		oplio = 1;
+		break;
+	case 0x3a0:
+		oplio = 2;
+		break;
+	case 0x3a8:
+		oplio = 3;
+		break;
+	default:
+		break;
+	}
+
+	if ( mpuio >= 0 || oplio >= 0 ) {
+		v = 0x003e;
+		pci_write_config_word(pcidev, PCIR_LEGCTRL, v);
+	
+		switch( pcidev->device ) {
+		case PCI_DEVICE_ID_YAMAHA_724:
+		case PCI_DEVICE_ID_YAMAHA_740:
+		case PCI_DEVICE_ID_YAMAHA_724F:
+		case PCI_DEVICE_ID_YAMAHA_740C:
+			v = 0x8800;
+			if ( mpuio >= 0 ) { v|= (mpuio<<4)&0x03; }
+			if ( oplio >= 0 ) { v|= (oplio&0x03); }
+			pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+			break;
+
+		case PCI_DEVICE_ID_YAMAHA_744:
+		case PCI_DEVICE_ID_YAMAHA_754:
+			v = 0x8800;
+			pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+			if ( oplio >= 0 ) {
+				pci_write_config_word(pcidev, PCIR_OPLADR, codec->iosynth);
+			}
+			if ( mpuio >= 0 ) {
+				pci_write_config_word(pcidev, PCIR_MPUADR, codec->iomidi);
+			}
+		break;
+
+		default:
+			printk(KERN_ERR "ymfpci: Invalid device ID: %d\n",pcidev->device);
+			return -EINVAL;
+			break;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
+/* */
+
 static int ymf_open(struct inode *inode, struct file *file)
 {
 	ymfpci_t *unit;
-	int minor, instance;
+	int minor;
 	struct ymf_state *state;
 	int nvirt;
 	int err;
@@ -1890,21 +1969,21 @@
 	} else {
 		return -ENXIO;
 	}
-	instance = (minor >> 4) & 0x0F;
 	nvirt = 0;			/* Such is the partitioning of minor */
 
-	/* XXX Semaphore here! */
 	for (unit = ymf_devs; unit != NULL; unit = unit->next) {
-		if (unit->inst == instance) break;
+		if (((unit->dev_audio ^ minor) & ~0x0f) == 0) break;
 	}
 	if (unit == NULL) return -ENODEV;
 
+	down(&unit->open_sem);
 	if (unit->states[nvirt] != NULL) {
-		/* P3 */ printk("ymfpci%d: busy\n", unit->inst);
+		up(&unit->open_sem);
 		return -EBUSY;
 	}
 
-	if ((err = ymf_state_alloc(unit, nvirt, instance)) != 0) {
+	if ((err = ymf_state_alloc(unit, nvirt)) != 0) {
+		up(&unit->open_sem);
 		return err;
 	}
 	state = unit->states[nvirt];
@@ -1912,11 +1991,12 @@
 	file->private_data = state;
 
 	/*
-	 * XXX This ymf_playback_prepare is totally unneeded here.
-	 * The question is if we want to allow write to fail if
-	 * prog_dmabuf fails... Say, no memory in DMA zone?
+	 * ymf_read and ymf_write that we borrowed from cs46xx
+	 * allocate buffers with prog_dmabuf(). We call prog_dmabuf
+	 * here so that in case of DMA memory exhaustion open
+	 * fails rather than write.
 	 */
-	if ((err = ymf_playback_prepare(unit, state)) != 0) {
+	if (!state->dmabuf.ready && (err = prog_dmabuf(state, 0))) {
 		/* XXX This recovery is ugly as hell. */
 
 		ymf_pcm_free_substream(&state->ypcm);
@@ -1924,6 +2004,7 @@
 		unit->states[state->virt] = NULL;
 		kfree(state);
 
+		up(&unit->open_sem);
 		return err;
 	}
 
@@ -1933,6 +2014,9 @@
 	    (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));
 #endif
 
+	up(&unit->open_sem);
+	/* XXX Ask Alan why MOD_INC_USE_COUNT is outside the semaphore. */
+
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -1946,14 +2030,14 @@
 	ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0);
 #endif
 
-	/* XXX Use the semaphore to unrace us with opens */
-
 	if (state != codec->states[state->virt]) {
 		printk(KERN_ERR "ymfpci%d.%d: state mismatch\n",
-		    state->unit->inst, state->virt);
+		    state->unit->dev_audio, state->virt);
 		return -EIO;
 	}
 
+	down(&codec->open_sem);
+
 	/*
 	 * XXX Solve the case of O_NONBLOCK close - don't deallocate here.
 	 * Deallocate when unloading the driver and we can wait.
@@ -1965,6 +2049,8 @@
 	codec->states[state->virt] = NULL;
 	kfree(state);
 
+	up(&codec->open_sem);
+
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2057,8 +2143,7 @@
 	if (val)
 		ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000);
 	while (timeout-- > 0) {
-		val = ymfpci_readl(codec, YDSXGR_CONFIG);
-		if ((val & 0x00000002) == 0)
+		if ((ymfpci_readl(codec, YDSXGR_STATUS) & 2) == 0)
 			break;
 	}
 }
@@ -2135,6 +2220,12 @@
 	ptr += 0x00ff;
 	(long)ptr &= ~0x00ff;
 
+	/*
+	 * Hardware requires only ptr[playback_ctrl_size] zeroed,
+	 * but in our judgement it is a wrong kind of savings, so clear it all.
+	 */
+	memset(ptr, 0, size);
+
 	codec->bank_base_playback = ptr;
 	codec->ctrl_playback = (u32 *)ptr;
 	codec->ctrl_playback[0] = YDSXG_PLAYBACK_VOICES;
@@ -2207,7 +2298,6 @@
 	codec->codec_write = ymfpci_codec_write;
 
 	if (ac97_probe_codec(codec) == 0) {
-		/* Alan does not have this printout. P3 */
 		printk("ymfpci: ac97_probe_codec failed\n");
 		return -ENODEV;
 	}
@@ -2234,6 +2324,20 @@
 	return 0;
 }
 
+/* */
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+# ifdef MODULE
+static int mpu_io     = 0;
+static int synth_io   = 0;
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(synth_io, "i");
+# else
+static int mpu_io     = 0x330;
+static int synth_io   = 0x388;
+# endif
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+/* */
+
 static int /* __init */
 ymf_install(struct pci_dev *pcidev, int instance, int devx)
 {
@@ -2249,17 +2353,17 @@
 
 	spin_lock_init(&codec->reg_lock);
 	spin_lock_init(&codec->voice_lock);
+	codec->open_sem = MUTEX;
 	codec->pci = pcidev;
-	codec->inst = instance;
 	codec->irq = pcidev->irq;
 	codec->device_id = pcidev->device;
-	pci_read_config_byte(pcidev, PCI_REVISION_ID, (u8 *)&codec->rev);
+	pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev);
 	codec->reg_area_phys = pcidev->base_address[0]&PCI_BASE_ADDRESS_MEM_MASK;
 	codec->reg_area_virt = (unsigned long)ioremap(codec->reg_area_phys, 0x8000);
 	pci_set_master(pcidev);
 
-	/* XXX KERN_INFO */
-	printk("ymfpci%d: %s at 0x%lx IRQ %d\n", instance,
+	/* Not printing instance number because we use dev_audio elsewhere. */
+	printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
 	    ymf_devv[devx].name, codec->reg_area_phys, codec->irq);
 
 	ymfpci_aclink_reset(pcidev);
@@ -2268,6 +2372,17 @@
 		return -ENODEV;
 	}
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	if (instance == 0) {
+		codec->iomidi = mpu_io;
+		codec->iosynth = synth_io;
+		if (ymfpci_setup_legacy(codec, pcidev) < 0) {
+			ymfpci_free(codec);
+			return -ENODEV;
+		}
+	}
+#endif
+
 	ymfpci_download_image(codec);
 
 	udelay(100); /* seems we need some delay after downloading image.. */
@@ -2280,15 +2395,15 @@
 	/* ymfpci_proc_init(card, codec); */
 
 	if (request_irq(codec->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
-		printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n",
-		    codec->inst, codec->irq);
+		printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
+		    codec->irq);
 		ymfpci_free(codec);
 		return -ENODEV;
 	}
 
 	/* register /dev/dsp */
 	if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
-		printk(KERN_ERR "ymfpci%d: unable to register dsp\n", codec->inst);
+		printk(KERN_ERR "ymfpci: unable to register dsp\n");
 		free_irq(codec->irq, codec);
 		ymfpci_free(codec);
 		return -ENODEV;
@@ -2304,6 +2419,24 @@
 		return err;
 	}
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+
+	codec->opl3_data.name = "ymfpci";
+	codec->mpu_data.name  = "ymfpci";
+
+	codec->opl3_data.io_base = codec->iosynth;
+	codec->opl3_data.irq     = -1;
+
+	codec->mpu_data.io_base  = codec->iomidi;
+	codec->mpu_data.irq      = -1;	/* XXX Make it ours. */
+
+	if (codec->iomidi) {
+		if (probe_uart401(&codec->mpu_data)) {
+			attach_uart401(&codec->mpu_data);
+		}
+	}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
 	codec->next = ymf_devs;
 	ymf_devs = codec;
 
@@ -2339,6 +2472,11 @@
 		unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer);
 		kfree(codec->ac97_codec[0]);
 	}
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	if (codec->iomidi) {
+		unload_uart401(&(codec->mpu_data));
+	}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
 	kfree(codec);
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci.h linux.19p6/drivers/sound/ymfpci.h
--- linux.vanilla/drivers/sound/ymfpci.h	Mon Dec 11 22:12:57 2000
+++ linux.19p6/drivers/sound/ymfpci.h	Wed Jan  3 14:10:49 2001
@@ -131,7 +131,12 @@
 #define YDSXG_AC97READCMD		0x8000
 #define YDSXG_AC97WRITECMD		0x0000
 
+#define PCIR_LEGCTRL			0x40
+#define PCIR_ELEGCTRL			0x42
 #define PCIR_DSXGCTRL			0x48
+#define PCIR_OPLADR			0x60
+#define PCIR_SBADR			0x62
+#define PCIR_MPUADR			0x64
 
 #define YDSXG_DSPLENGTH			0x0080
 #define YDSXG_CTRLLENGTH		0x3000
@@ -242,27 +247,27 @@
 	ymfpci_pcm_type_t type;
 	struct ymf_state *state;
 	ymfpci_voice_t *voices[2];	/* playback only */
-	int running;			// +
+	int running;
 	int spdif;
 };
 
 struct ymf_unit {
 	unsigned int device_id;	/* PCI device ID */
-	unsigned int rev;	/* PCI revision */
+	unsigned char rev;	/* PCI revision */
 	unsigned long reg_area_phys;
 	unsigned long reg_area_virt;
 
-	void *work_ptr;				// +
+	void *work_ptr;
 
 	unsigned int bank_size_playback;
 	unsigned int bank_size_capture;
 	unsigned int bank_size_effect;
 	unsigned int work_size;
 
-	void *bank_base_playback;		// +
-	void *bank_base_capture;		// +
-	void *bank_base_effect;			// +
-	void *work_base;			// +
+	void *bank_base_playback;
+	void *bank_base_capture;
+	void *bank_base_effect;
+	void *work_base;
 
 	u32 *ctrl_playback;
 	ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
@@ -279,16 +284,22 @@
 
 	struct pci_dev *pci;
 	int irq;
-	int inst;		/* Unit number (instance) */
+
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+        /* legacy hardware resources */
+	unsigned int iosynth, iomidi;
+	struct address_info opl3_data, mpu_data;
+#endif
 
 	spinlock_t reg_lock;
 	spinlock_t voice_lock;
 
 	/* soundcore stuff */
 	int dev_audio;
+	struct semaphore open_sem;
 
-	ymfpci_t *next;					// *
-	struct ymf_state *states[1];			// *
+	ymfpci_t *next;
+	struct ymf_state *states[1];
 	/* ypcm may be the same thing as state, but not for record, effects. */
 };
 
@@ -336,16 +347,12 @@
 struct ymf_state {
 	struct ymf_unit *unit;			/* backpointer */
 
-	/* single open lock mechanism, only used for recording */
-	struct semaphore open_sem;
-	struct wait_queue *open_wait;
-
 	/* virtual channel number */
 	int virt;				// * unused a.t.m.
 
-	struct ymf_pcm ypcm;			// *
-	struct ymf_dmabuf dmabuf;		// *
-	struct ymf_pcm_format format;		// *
+	struct ymf_pcm ypcm;
+	struct ymf_dmabuf dmabuf;
+	struct ymf_pcm_format format;
 };
 
 #endif				/* __YMFPCI_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Config.in linux.19p6/drivers/usb/Config.in
--- linux.vanilla/drivers/usb/Config.in	Mon Dec 11 22:12:58 2000
+++ linux.19p6/drivers/usb/Config.in	Tue Dec 19 02:30:27 2000
@@ -50,6 +50,7 @@
          fi
          dep_tristate '    USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL
          dep_tristate '    USB Belkin and Peracom Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_BELKIN $CONFIG_USB_SERIAL
+         dep_tristate '    USB Empeg empeg-car Mark I/II Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EMPEG $CONFIG_USB_SERIAL
       fi
       bool '    USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG $CONFIG_USB_SERIAL
    fi
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dabusb.c linux.19p6/drivers/usb/dabusb.c
--- linux.vanilla/drivers/usb/dabusb.c	Mon Dec 11 22:12:58 2000
+++ linux.19p6/drivers/usb/dabusb.c	Tue Dec 19 02:13:08 2000
@@ -173,8 +173,8 @@
 
 	// process if URB was not killed
 	if (purb->status != -ENOENT) {
-		unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE);
-		int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
+		unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE);
+		int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe));
 		for (i = 0; i < purb->number_of_packets; i++)
 			if (!purb->iso_frame_desc[i].status) {
 				len = purb->iso_frame_desc[i].actual_length;
@@ -455,6 +455,8 @@
 			dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list);
 
 			end = list_entry (s->rec_buff_list.prev, buff_t, buff_list);
+
+			end->purb->dev=s->usbdev;
 
 			ret = usb_submit_urb (end->purb);
 			if (ret) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/devio.c linux.19p6/drivers/usb/devio.c
--- linux.vanilla/drivers/usb/devio.c	Mon Dec 11 22:12:58 2000
+++ linux.19p6/drivers/usb/devio.c	Sat Dec 30 20:00:54 2000
@@ -55,113 +55,6 @@
         urb_t urb;
 };
 
-/*
- * my own sync control and bulk methods. Here to experiment
- * and because the kernel ones set the process to TASK_UNINTERRUPTIBLE.
- */
-
-struct sync {
-	wait_queue_head_t wait;
-};
-
-static void sync_completed(purb_t urb)
-{
-        struct sync *s = (struct sync *)urb->context;
- 
-        wake_up(&s->wait);	
-}
-
-static int do_sync(purb_t urb, int timeout)
-{
-        DECLARE_WAITQUEUE(wait, current);
-	unsigned long tm;
-	signed long tmdiff;
-	struct sync s;
-	int ret;
-
-	tm = jiffies+timeout;
-	init_waitqueue_head(&s.wait);
-	add_wait_queue(&s.wait, &wait);
-	urb->context = &s;
-	urb->complete = sync_completed;
-	set_current_state(TASK_INTERRUPTIBLE);
-	if ((ret = usb_submit_urb(urb)))
-		goto out;
-	while (urb->status == -EINPROGRESS) {
-		tmdiff = tm - jiffies;
-		if (tmdiff <= 0) {
-			ret = -ETIMEDOUT;
-			goto out;
-		}
-		if (signal_pending(current)) {
-			ret = -EINTR;
-			goto out;
-		}
-		schedule_timeout(tmdiff);
-	}
-	ret = urb->status;
- out:
-	set_current_state(TASK_RUNNING);
-	usb_unlink_urb(urb);
-	remove_wait_queue(&s.wait, &wait);
-	return ret;
-}
-
-static int my_usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
-			      __u16 value, __u16 index, void *data, __u16 size, int timeout)
-{
-	urb_t *urb;
-	int ret;
-
-	if (!(urb = usb_alloc_urb(0)))
-		return -ENOMEM;
-	if (!(urb->setup_packet = kmalloc(8, GFP_KERNEL))) {
-		usb_free_urb(urb);
-		return -ENOMEM;
-	}
-	urb->setup_packet[0] = requesttype;
-	urb->setup_packet[1] = request;
-	urb->setup_packet[2] = value;
-	urb->setup_packet[3] = value >> 8;
-	urb->setup_packet[4] = index;
-	urb->setup_packet[5] = index >> 8;
-	urb->setup_packet[6] = size;
-	urb->setup_packet[7] = size >> 8;
-	urb->dev = dev;
-        urb->pipe = pipe;
-        urb->transfer_buffer = data;
-        urb->transfer_buffer_length = size;
-	ret = do_sync(urb, timeout);
-	//if (ret >= 0)
-	//	ret = urb->status;
-	if (ret >= 0)
-		ret = urb->actual_length;
-	kfree(urb->setup_packet);
-	usb_free_urb(urb);
-	return ret;
-}
-
-static int my_usb_bulk_msg(struct usb_device *dev, unsigned int pipe, 
-			   void *data, int len, int *actual_length, int timeout)
-{
-	urb_t *urb;
-	int ret;
-
-	if (!(urb = usb_alloc_urb(0)))
-		return -ENOMEM;
-        urb->dev = dev;
-        urb->pipe = pipe;
-        urb->transfer_buffer = data;
-        urb->transfer_buffer_length = len;
-	ret = do_sync(urb, timeout);
-	//if (ret >= 0)
-	//	ret = urb->status;
-	if (ret >= 0 && actual_length != NULL)
-		*actual_length = urb->actual_length;
-	usb_free_urb(urb);
-	return ret;
-}
-
 static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
 {
 	switch (orig) {
@@ -289,6 +182,8 @@
 {
         if (as->urb.transfer_buffer)
                 kfree(as->urb.transfer_buffer);
+        if (as->urb.setup_packet)
+                kfree(as->urb.setup_packet);
         kfree(as);
 }
 
@@ -536,6 +431,28 @@
 }
 #endif
 
+static int check_ctrlrecip(struct dev_state *ps, unsigned int recip, unsigned int index)
+{
+	int ret;
+
+	switch (recip & USB_RECIP_MASK) {
+	case USB_RECIP_ENDPOINT:
+		if ((ret = findintfep(ps->dev, index & 0xff)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+		break;
+
+	case USB_RECIP_INTERFACE:
+		if ((ret = findintfif(ps->dev, index & 0xff)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+		break;
+	}
+	return 0;
+}
+
 /*
  * file operations
  */
@@ -609,21 +526,8 @@
 
 	if (copy_from_user(&ctrl, (void *)arg, sizeof(ctrl)))
 		return -EFAULT;
-	switch (ctrl.requesttype & 0x1f) {
-	case USB_RECIP_ENDPOINT:
-		if ((ret = findintfep(ps->dev, ctrl.index & 0xff)) < 0)
-			return ret;
-		if ((ret = checkintf(ps, ret)))
-			return ret;
-		break;
-
-	case USB_RECIP_INTERFACE:
-		if ((ret = findintfif(ps->dev, ctrl.index & 0xff)) < 0)
-			return ret;
-		if ((ret = checkintf(ps, ret)))
-			return ret;
-		break;
-	}
+	if ((ret = check_ctrlrecip(ps, ctrl.requesttype, ctrl.index)))
+		return ret;
 	if (ctrl.length > PAGE_SIZE)
 		return -EINVAL;
 	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
@@ -634,7 +538,7 @@
 			free_page((unsigned long)tbuf);
 			return -EINVAL;
 		}
-		i = my_usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
+		i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
 				       ctrl.value, ctrl.index, tbuf, ctrl.length, tmo);
 		if ((i > 0) && ctrl.length) {
 			if (copy_to_user(ctrl.data, tbuf, ctrl.length)) {
@@ -649,7 +553,7 @@
 				return -EFAULT;
 			}
 		}
-		i = my_usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
+		i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
 				       ctrl.value, ctrl.index, tbuf, ctrl.length, tmo);
 	}
 	free_page((unsigned long)tbuf);
@@ -683,7 +587,7 @@
 		return -EINVAL;
 	len1 = bulk.len;
 	if (len1 > PAGE_SIZE)
-		len1 = PAGE_SIZE;
+		return -EINVAL;
 	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	tmo = (bulk.timeout * HZ + 999) / 1000;
@@ -692,7 +596,7 @@
 			free_page((unsigned long)tbuf);
 			return -EINVAL;
 		}
-		i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+		i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
 		if (!i && len2) {
 			if (copy_to_user(bulk.data, tbuf, len2)) {
 				free_page((unsigned long)tbuf);
@@ -706,7 +610,7 @@
 				return -EFAULT;
 			}
 		}
-		i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+		i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
 	}
 	free_page((unsigned long)tbuf);
 	if (i < 0) {
@@ -847,23 +751,61 @@
 {
 	struct usbdevfs_urb uurb;
 	struct usbdevfs_iso_packet_desc *isopkt = NULL;
+	struct usb_endpoint_descriptor *ep_desc;
 	struct async *as;
+	devrequest *dr = NULL;
 	unsigned int u, totlen, isofrmlen;
 	int ret;
 
 	if (copy_from_user(&uurb, arg, sizeof(uurb)))
 		return -EFAULT;
-	if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD))
+	if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD|USBDEVFS_URB_QUEUE_BULK))
 		return -EINVAL;
 	if (!uurb.buffer)
 		return -EINVAL;
 	if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX))
 		return -EINVAL;
-	if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0)
-		return ret;
-	if ((ret = checkintf(ps, ret)))
-		return ret;
+	if (!(uurb.type == USBDEVFS_URB_TYPE_CONTROL && (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
+		if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+	}
 	switch(uurb.type) {
+	case USBDEVFS_URB_TYPE_CONTROL:
+		if ((uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) != 0) {
+			if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint)))
+				return -ENOENT;
+			if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_CONTROL)
+				return -EINVAL;
+		}
+		/* min 8 byte setup packet, max arbitrary */
+		if (uurb.buffer_length < 8 || uurb.buffer_length > PAGE_SIZE)
+			return -EINVAL;
+		if (!(dr = kmalloc(sizeof(devrequest), GFP_KERNEL)))
+			return -ENOMEM;
+		if (copy_from_user(dr, (unsigned char*)uurb.buffer, 8)) {
+			kfree(dr);
+			return -EFAULT;
+		}
+		if (uurb.buffer_length < (le16_to_cpup(&dr->length) + 8)) {
+			kfree(dr);
+			return -EINVAL;
+		}
+		if ((ret = check_ctrlrecip(ps, dr->requesttype, le16_to_cpup(&dr->index)))) {
+			kfree(dr);
+			return ret;
+		}
+		uurb.endpoint = (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->requesttype & USB_ENDPOINT_DIR_MASK);
+		uurb.number_of_packets = 0;
+		uurb.buffer_length = le16_to_cpup(&dr->length);
+		uurb.buffer += 8;
+		if (!access_ok((uurb.endpoint & USB_DIR_IN) ?  VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) {
+			kfree(dr);
+			return -EFAULT;
+		}
+		break;
+
 	case USBDEVFS_URB_TYPE_BULK:
 		uurb.number_of_packets = 0;
 		if (uurb.buffer_length > 16384)
@@ -903,11 +845,15 @@
 	if (!(as = alloc_async(uurb.number_of_packets))) {
 		if (isopkt)
 			kfree(isopkt);
+		if (dr)
+			kfree(dr);
 		return -ENOMEM;
 	}
 	if (!(as->urb.transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) {
 		if (isopkt)
 			kfree(isopkt);
+		if (dr)
+			kfree(dr);
 		free_async(as);
 		return -ENOMEM;
 	}
@@ -916,6 +862,7 @@
         as->urb.pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN);
         as->urb.transfer_flags = uurb.flags;
 	as->urb.transfer_buffer_length = uurb.buffer_length;
+	as->urb.setup_packet = (unsigned char*)dr;
 	as->urb.start_frame = uurb.start_frame;
 	as->urb.number_of_packets = uurb.number_of_packets;
         as->urb.context = as;
@@ -1089,17 +1036,19 @@
 	int			retval = 0;
 
 	/* get input parameters and alloc buffer */
-	if (copy_from_user (&ctrl, (void *) arg, sizeof (ctrl)))
+	if (copy_from_user(&ctrl, (void *) arg, sizeof (ctrl)))
 		return -EFAULT;
 	if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
 		if ((buf = kmalloc (size, GFP_KERNEL)) == 0)
 			return -ENOMEM;
-		if ((_IOC_DIR (ctrl.ioctl_code) & _IOC_WRITE) != 0
-				&& copy_from_user (buf, ctrl.data, size) != 0) {
-			kfree (buf);
-			return -EFAULT;
-		} else
-			memset (arg, 0, size);
+		if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) {
+			if (copy_from_user (buf, ctrl.data, size)) {
+				kfree (buf);
+				return -EFAULT;
+			}
+		} else {
+			memset (buf, 0, size);
+		}
 	}
 
 	/* ioctl to device */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/Makefile linux.19p6/drivers/usb/serial/Makefile
--- linux.vanilla/drivers/usb/serial/Makefile	Mon Dec 11 22:13:00 2000
+++ linux.19p6/drivers/usb/serial/Makefile	Tue Dec 19 02:30:27 2000
@@ -23,6 +23,7 @@
 obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
 obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT)	+= digi_acceleport.o
 obj-$(CONFIG_USB_SERIAL_BELKIN)			+= belkin_sa.o
+obj-$(CONFIG_USB_SERIAL_EMPEG)			+= empeg.o
  
 # Objects that export symbols.
 export-objs	:= usbserial.o
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/empeg.c linux.19p6/drivers/usb/serial/empeg.c
--- linux.vanilla/drivers/usb/serial/empeg.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/drivers/usb/serial/empeg.c	Tue Dec 19 02:30:27 2000
@@ -0,0 +1,609 @@
+/*
+ * USB Empeg empeg-car player driver
+ *
+ *	Copyright (C) 2000
+ *	    Gary Brubaker (xavyer@ix.netcom.com)
+ *
+ *	Copyright (C) 1999, 2000
+ *	    Greg Kroah-Hartman (greg@kroah.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, version 2.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ * 
+ * (11/13/2000) gb
+ *	Moved tty->low_latency = 1 from empeg_read_bulk_callback() to empeg_open()
+ *	(It only needs to be set once - Doh!)
+ * 
+ * (11/11/2000) gb
+ *	Updated to work with id_table structure.
+ * 
+ * (11/04/2000) gb
+ *	Forked this from visor.c, and hacked it up to work with an
+ *	Empeg ltd. empeg-car player.  Constructive criticism welcomed.
+ *	I would like to say, 'Thank You' to Greg Kroah-Hartman for the
+ *	use of his code, and for his guidance, advice and patience. :)
+ *	A 'Thank You' is in order for John Ripley of Empeg ltd for his
+ *	advice, and patience too.
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#define EMPEG_VENDOR_ID                 0x084f
+#define EMPEG_PRODUCT_ID                0x0001
+
+#define MIN(a,b)		(((a)<(b))?(a):(b))
+
+/* function prototypes for an empeg-car player */
+static int  empeg_open		(struct usb_serial_port *port, struct file *filp);
+static void empeg_close		(struct usb_serial_port *port, struct file *filp);
+static int  empeg_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static void empeg_throttle	(struct usb_serial_port *port);
+static void empeg_unthrottle	(struct usb_serial_port *port);
+static int  empeg_startup	(struct usb_serial *serial);
+static void empeg_shutdown	(struct usb_serial *serial);
+static int  empeg_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+static void empeg_set_termios	(struct usb_serial_port *port, struct termios *old_termios);
+static void empeg_write_bulk_callback	(struct urb *urb);
+static void empeg_read_bulk_callback	(struct urb *urb);
+
+static __u16	empeg_vendor_id		= EMPEG_VENDOR_ID;
+static __u16	empeg_product_id	= EMPEG_PRODUCT_ID;
+struct usb_serial_device_type empeg_device = {
+	name:			"Empeg",
+	idVendor:		&empeg_vendor_id,
+	idProduct:		&empeg_product_id,
+	needs_interrupt_in:	MUST_HAVE_NOT,	/* must not have an interrupt in endpoint */
+	needs_bulk_in:		MUST_HAVE,	/* must have a bulk in endpoint */
+	needs_bulk_out:		MUST_HAVE,	/* must have a bulk out endpoint */
+	num_interrupt_in:	0,
+	num_bulk_in:		1,
+	num_bulk_out:		1,
+	num_ports:		1,
+	open:			empeg_open,
+	close:			empeg_close,
+	throttle:		empeg_throttle,
+	unthrottle:		empeg_unthrottle,
+	startup:		empeg_startup,
+	shutdown:		empeg_shutdown,
+	ioctl:			empeg_ioctl,
+	set_termios:		empeg_set_termios,
+	write:			empeg_write,
+	write_bulk_callback:	empeg_write_bulk_callback,
+	read_bulk_callback:	empeg_read_bulk_callback,
+};
+
+#define NUM_URBS			16
+#define URB_TRANSFER_BUFFER_SIZE	4096
+
+static struct urb	*write_urb_pool[NUM_URBS];
+static spinlock_t	write_urb_pool_lock;
+static int		bytes_in;
+static int		bytes_out;
+
+/******************************************************************************
+ * Empeg specific driver functions
+ ******************************************************************************/
+static int empeg_open (struct usb_serial_port *port, struct file *filp)
+{
+	unsigned long flags;
+	int result;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return -ENODEV;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+
+	/* gb - 2000/11/05
+	 *
+	 * personally, I think these termios should be set in
+	 * empeg_startup(), but it appears doing so leads to one
+	 * of those chicken/egg problems. :)
+	 *
+	 */
+	port->tty->termios->c_iflag
+		&= ~(IGNBRK
+		| BRKINT
+		| PARMRK
+		| ISTRIP
+		| INLCR
+		| IGNCR
+		| ICRNL
+		| IXON);
+
+	port->tty->termios->c_oflag
+		&= ~OPOST;
+
+	port->tty->termios->c_lflag
+		&= ~(ECHO
+		| ECHONL
+		| ICANON
+		| ISIG
+		| IEXTEN);
+
+	port->tty->termios->c_cflag
+		&= ~(CSIZE
+		| PARENB);
+
+	port->tty->termios->c_cflag
+		|= CS8;
+
+	/* gb - 2000/11/05
+	 *
+	 * force low_latency on
+	 *
+	 * The tty_flip_buffer_push()'s in empeg_read_bulk_callback() will actually
+	 * force the data through if low_latency is set.  Otherwise the pushes are
+	 * scheduled; this is bad as it opens up the possibility of dropping bytes
+	 * on the floor.  We are trying to sustain high data transfer rates; and
+	 * don't want to drop bytes on the floor.
+	 * Moral: use low_latency - drop no bytes - life is good. :)
+	 *
+	 */
+	port->tty->low_latency = 1;
+
+	if (!port->active) {
+		port->active = 1;
+		bytes_in = 0;
+		bytes_out = 0;
+
+		/* Start reading from the device */
+		port->read_urb->transfer_flags |= USB_QUEUE_BULK;
+
+		result = usb_submit_urb(port->read_urb);
+
+		if (result)
+			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	return 0;
+}
+
+
+static void empeg_close (struct usb_serial_port *port, struct file * filp)
+{
+	struct usb_serial *serial;
+	unsigned char *transfer_buffer;
+	unsigned long flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	serial = get_usb_serial (port, __FUNCTION__);
+	if (!serial)
+		return;
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	--port->open_count;
+	MOD_DEC_USE_COUNT;
+
+	if (port->open_count <= 0) {
+		transfer_buffer =  kmalloc (0x12, GFP_KERNEL);
+
+		if (!transfer_buffer) {
+			err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12);
+		} else {
+			kfree (transfer_buffer);
+		}
+
+		/* shutdown our bulk read */
+		usb_unlink_urb (port->read_urb);
+		port->active = 0;
+		port->open_count = 0;
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	/* Uncomment the following line if you want to see some statistics in your syslog */
+	/* info ("Bytes In = %d  Bytes Out = %d", bytes_in, bytes_out); */
+
+}
+
+
+static int empeg_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	struct urb *urb;
+	const unsigned char *current_position = buf;
+	unsigned long flags;
+	int status;
+	int i;
+	int bytes_sent = 0;
+	int transfer_size;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, count, buf);
+
+	while (count > 0) {
+
+		/* try to find a free urb in our list of them */
+		urb = NULL;
+
+		spin_lock_irqsave (&write_urb_pool_lock, flags);
+
+		for (i = 0; i < NUM_URBS; ++i) {
+			if (write_urb_pool[i]->status != -EINPROGRESS) {
+				urb = write_urb_pool[i];
+				break;
+			}
+		}
+
+		spin_unlock_irqrestore (&write_urb_pool_lock, flags);
+
+		if (urb == NULL) {
+			dbg (__FUNCTION__ " - no more free urbs");
+			goto exit;
+		}
+
+		if (urb->transfer_buffer == NULL) {
+			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+			if (urb->transfer_buffer == NULL) {
+				err(__FUNCTION__" no more kernel memory...");
+				goto exit;
+			}
+		}
+
+		transfer_size = MIN (count, URB_TRANSFER_BUFFER_SIZE);
+
+		if (from_user) {
+			copy_from_user (urb->transfer_buffer, current_position, transfer_size);
+		} else {
+			memcpy (urb->transfer_buffer, current_position, transfer_size);
+		}
+
+		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+		/* build up our urb */
+		FILL_BULK_URB (
+			urb,
+			serial->dev,
+			usb_sndbulkpipe(serial->dev,
+				port->bulk_out_endpointAddress), 
+			urb->transfer_buffer,
+			transfer_size,
+			empeg_write_bulk_callback,
+			port);
+
+		urb->transfer_flags |= USB_QUEUE_BULK;
+
+		/* send it down the pipe */
+		status = usb_submit_urb(urb);
+		if (status)
+			dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status);
+
+		current_position += transfer_size;
+		bytes_sent += transfer_size;
+		count -= transfer_size;
+		bytes_out += transfer_size;
+
+	}
+
+exit:
+	return bytes_sent;
+
+} 
+
+
+static void empeg_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	return;
+
+}
+
+
+static void empeg_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i;
+	int result;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	tty = port->tty;
+
+	if (urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			/* gb - 2000/11/13
+			 * If we insert too many characters we'll overflow the buffer.
+			 * This means we'll lose bytes - Decidedly bad.
+			 */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+				}
+			/* gb - 2000/11/13
+			 * This doesn't push the data through unless tty->low_latency is set.
+			 */
+			tty_insert_flip_char(tty, data[i], 0);
+		}
+		/* gb - 2000/11/13
+		 * Goes straight through instead of scheduling - if tty->low_latency is set.
+		 */
+		tty_flip_buffer_push(tty);
+		bytes_in += urb->actual_length;
+	}
+
+	/* Continue trying to always read  */
+	port->read_urb->transfer_flags |= USB_QUEUE_BULK;
+	result = usb_submit_urb(port->read_urb);
+
+	if (result)
+		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+
+	return;
+
+}
+
+
+static void empeg_throttle (struct usb_serial_port *port)
+{
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	usb_unlink_urb (port->read_urb);
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	return;
+
+}
+
+
+static void empeg_unthrottle (struct usb_serial_port *port)
+{
+	unsigned long flags;
+	int result;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	port->read_urb->dev = port->serial->dev;
+
+	result = usb_submit_urb(port->read_urb);
+
+	if (result)
+		err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	return;
+
+}
+
+
+static int  empeg_startup (struct usb_serial *serial)
+{
+
+	dbg(__FUNCTION__);
+
+	dbg(__FUNCTION__ " - Set config to 1");
+	usb_set_configuration (serial->dev, 1);
+
+	/* continue on with initialization */
+	return 0;
+
+}
+
+
+static void empeg_shutdown (struct usb_serial *serial)
+{
+	int i;
+
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			empeg_close (&serial->port[i], NULL);
+		}
+	}
+
+}
+
+
+static int empeg_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd);
+
+	return -ENOIOCTLCMD;
+}
+
+
+/* This function is all nice and good, but we don't change anything based on it :) */
+static void empeg_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+	unsigned int cflag = port->tty->termios->c_cflag;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	/* check that they really want us to change something */
+	if (old_termios) {
+		if ((cflag == old_termios->c_cflag) &&
+		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+			dbg(__FUNCTION__ " - nothing to change...");
+			return;
+		}
+	}
+
+	if ((!port->tty) || (!port->tty->termios)) {
+		dbg(__FUNCTION__" - no tty structures");
+		return;
+	}
+
+	/* get the byte size */
+	switch (cflag & CSIZE) {
+		case CS5:	dbg(__FUNCTION__ " - data bits = 5");   break;
+		case CS6:	dbg(__FUNCTION__ " - data bits = 6");   break;
+		case CS7:	dbg(__FUNCTION__ " - data bits = 7");   break;
+		default:
+		case CS8:	dbg(__FUNCTION__ " - data bits = 8");   break;
+	}
+
+	/* determine the parity */
+	if (cflag & PARENB)
+		if (cflag & PARODD)
+			dbg(__FUNCTION__ " - parity = odd");
+		else
+			dbg(__FUNCTION__ " - parity = even");
+	else
+		dbg(__FUNCTION__ " - parity = none");
+
+	/* figure out the stop bits requested */
+	if (cflag & CSTOPB)
+		dbg(__FUNCTION__ " - stop bits = 2");
+	else
+		dbg(__FUNCTION__ " - stop bits = 1");
+
+	/* figure out the flow control settings */
+	if (cflag & CRTSCTS)
+		dbg(__FUNCTION__ " - RTS/CTS is enabled");
+	else
+		dbg(__FUNCTION__ " - RTS/CTS is disabled");
+
+	/* determine software flow control */
+	if (I_IXOFF(port->tty))
+		dbg(__FUNCTION__ " - XON/XOFF is enabled, XON = %2x, XOFF = %2x", START_CHAR(port->tty), STOP_CHAR(port->tty));
+	else
+		dbg(__FUNCTION__ " - XON/XOFF is disabled");
+
+	/* get the baud rate wanted */
+	dbg(__FUNCTION__ " - baud rate = %d", tty_get_baud_rate(port->tty));
+
+	return;
+
+}
+
+
+static int __init empeg_init (void)
+{
+	struct urb *urb;
+	int i;
+
+	usb_serial_register (&empeg_device);
+
+	/* create our write urb pool and transfer buffers */ 
+	spin_lock_init (&write_urb_pool_lock);
+	for (i = 0; i < NUM_URBS; ++i) {
+		urb = usb_alloc_urb(0);
+		write_urb_pool[i] = urb;
+		if (urb == NULL) {
+			err("No more urbs???");
+			continue;
+		}
+
+		urb->transfer_buffer = NULL;
+		urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+		if (!urb->transfer_buffer) {
+			err (__FUNCTION__ " - out of memory for urb buffers.");
+			continue;
+		}
+	}
+
+	return 0;
+
+}
+
+
+static void __exit empeg_exit (void)
+{
+	int i;
+	unsigned long flags;
+
+	usb_serial_deregister (&empeg_device);
+
+	spin_lock_irqsave (&write_urb_pool_lock, flags);
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (write_urb_pool[i]) {
+			/* FIXME - uncomment the following usb_unlink_urb call when
+			 * the host controllers get fixed to set urb->dev = NULL after
+			 * the urb is finished.  Otherwise this call oopses. */
+			/* usb_unlink_urb(write_urb_pool[i]); */
+			if (write_urb_pool[i]->transfer_buffer)
+				kfree(write_urb_pool[i]->transfer_buffer);
+			usb_free_urb (write_urb_pool[i]);
+		}
+	}
+
+	spin_unlock_irqrestore (&write_urb_pool_lock, flags);
+
+}
+
+
+module_init(empeg_init);
+module_exit(empeg_exit);
+
+MODULE_AUTHOR("Gary Brubaker <xavyer@ix.netcom.com>");
+MODULE_DESCRIPTION("USB Empeg Mark I/II Driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/usbserial.c linux.19p6/drivers/usb/serial/usbserial.c
--- linux.vanilla/drivers/usb/serial/usbserial.c	Mon Dec 11 22:13:01 2000
+++ linux.19p6/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/drivers/usb/usb.c linux.19p6/drivers/usb/usb.c
--- linux.vanilla/drivers/usb/usb.c	Mon Dec 11 22:13:01 2000
+++ linux.19p6/drivers/usb/usb.c	Sat Dec 30 20:00:54 2000
@@ -187,6 +187,19 @@
 	return NULL;
 }
 
+struct usb_endpoint_descriptor *usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)
+{
+	int i, j, k;
+
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+		for (j = 0; j < dev->actconfig->interface[i].num_altsetting; j++)
+			for (k = 0; k < dev->actconfig->interface[i].altsetting[j].bNumEndpoints; k++)
+				if (epnum == dev->actconfig->interface[i].altsetting[j].endpoint[k].bEndpointAddress)
+					return &dev->actconfig->interface[i].altsetting[j].endpoint[k];
+
+	return NULL;
+}
+
 /*
  * usb_calc_bus_time:
  *
@@ -2037,6 +2050,7 @@
  * then these symbols need to be exported for the modules to use.
  */
 EXPORT_SYMBOL(usb_ifnum_to_if);
+EXPORT_SYMBOL(usb_epnum_to_ep_desc);
 
 EXPORT_SYMBOL(usb_register);
 EXPORT_SYMBOL(usb_deregister);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Config.in linux.19p6/drivers/video/Config.in
--- linux.vanilla/drivers/video/Config.in	Mon Dec 11 22:13:02 2000
+++ linux.19p6/drivers/video/Config.in	Sat Dec 30 20:05:19 2000
@@ -188,7 +188,8 @@
 	 "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \
          "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \
 	 "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \
-	 "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" ]; then
+	 "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \
+	 "$CONFIG_FB_ATY128" = "y" ]; then
       define_bool CONFIG_FBCON_CFB8 y
     else
       if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \
@@ -202,14 +203,15 @@
 	   "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \
            "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \
 	   "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \
-	   "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" ]; then
+	   "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" -o \
+	   "$CONFIG_FB_ATY128" = "m" ]; then
 	define_bool CONFIG_FBCON_CFB8 m
       fi
     fi
     if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \
 	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VESA" = "y" -o \
 	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_TBOX" = "y" -o \
-	 "$CONFIG_FB_Q40" = "y" -o \
+	 "$CONFIG_FB_Q40" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \
 	 "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \
 	 "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \
 	 "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \
@@ -221,7 +223,7 @@
       if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
 	   "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VESA" = "m" -o \
 	   "$CONFIG_FB_VIRTUAL" = "m" -o "$CONFIG_FB_TBOX" = "m" -o \
-	 "$CONFIG_FB_Q40" = "m" -o \
+	   "$CONFIG_FB_Q40" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \
 	   "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \
 	   "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \
  	   "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \
@@ -234,13 +236,13 @@
     if [ "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \
 	 "$CONFIG_FB_CLGEN" = "y" -o "$CONFIG_FB_VESA" = "y" -o \
 	  "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \
-	 "$CONFIG_FB_CYBER2000" = "y" ]; then
+	 "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_ATY128" = "y" ]; then
       define_bool CONFIG_FBCON_CFB24 y
     else
       if [ "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \
 	   "$CONFIG_FB_CLGEN" = "m" -o "$CONFIG_FB_VESA" = "m" -o \
 	   "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \
-	   "$CONFIG_FB_CYBER2000" = "m" ]; then
+	   "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_ATY128" = "m" ]; then
 	define_bool CONFIG_FBCON_CFB24 m
       fi
     fi
@@ -249,7 +251,8 @@
 	 "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \
 	 "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \
 	 "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \
-	 "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" ]; then
+	 "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \
+	 "$CONFIG_FB_ATY128" = "y" ]; then
       define_bool CONFIG_FBCON_CFB32 y
     else
       if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
@@ -257,7 +260,7 @@
 	   "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \
 	   "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \
 	   "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \
-           "$CONFIG_FB_SGIVW" = "m" ]; then
+           "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_ATY128" = "m" ]; then
 	define_bool CONFIG_FBCON_CFB32 m
       fi
     fi
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Makefile linux.19p6/drivers/video/Makefile
--- linux.vanilla/drivers/video/Makefile	Mon Dec 11 22:13:02 2000
+++ linux.19p6/drivers/video/Makefile	Sat Dec 30 20:05:19 2000
@@ -106,6 +106,10 @@
 
 ifeq ($(CONFIG_FB_ATY128),y)
   L_OBJS += aty128fb.o
+else
+  ifeq ($(CONFIG_FB_ATY128),m)
+  M_OBJS += aty128fb.o
+  endif
 endif
 
 ifeq ($(CONFIG_FB_IGA),y)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/mdacon.c linux.19p6/drivers/video/mdacon.c
--- linux.vanilla/drivers/video/mdacon.c	Sun Jun 11 21:44:18 2000
+++ linux.19p6/drivers/video/mdacon.c	Tue Dec 19 02:11:55 2000
@@ -75,14 +75,10 @@
 
 static struct vc_data	*mda_display_fg = NULL;
 
-#ifdef MODULE_PARM
 MODULE_PARM(mda_first_vc, "1-255i");
 MODULE_PARM(mda_last_vc,  "1-255i");
-#endif
-
 
-/* MDA register values
- */
+/* MDA register values */
 
 #define MDA_CURSOR_BLINKING	0x00
 #define MDA_CURSOR_OFF		0x20
@@ -200,11 +196,7 @@
 }
 #endif
 
-#ifdef MODULE
-static int mda_detect(void)
-#else
 __initfunc(static int mda_detect(void))
-#endif
 {
 	int count=0;
 	u16 *p, p_save;
@@ -287,11 +279,7 @@
 	return 1;
 }
 
-#ifdef MODULE
-static void mda_initialize(void)
-#else
 __initfunc(static void mda_initialize(void))
-#endif
 {
 	write_mda_b(97, 0x00);		/* horizontal total */
 	write_mda_b(80, 0x01);		/* horizontal displayed */
@@ -316,11 +304,7 @@
 	outb_p(0x00, mda_gfx_port);
 }
 
-#ifdef MODULE
-static const char *mdacon_startup(void)
-#else
 __initfunc(static const char *mdacon_startup(void))
-#endif
 {
 	mda_num_columns = 80;
 	mda_num_lines   = 25;
@@ -606,11 +590,7 @@
 	mdacon_invert_region,	/* con_invert_region */
 };
 
-#ifdef MODULE
-void mda_console_init(void)
-#else
 __initfunc(void mda_console_init(void))
-#endif
 {
 	if (mda_first_vc > mda_last_vc)
 		return;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/Config.in linux.19p6/fs/Config.in
--- linux.vanilla/fs/Config.in	Mon Dec 11 22:13:02 2000
+++ linux.19p6/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/binfmt_aout.c linux.19p6/fs/binfmt_aout.c
--- linux.vanilla/fs/binfmt_aout.c	Fri Apr 21 12:46:41 2000
+++ linux.19p6/fs/binfmt_aout.c	Fri Dec 22 12:06:49 2000
@@ -62,9 +62,9 @@
 static int dump_write(struct file *file, const void *addr, int nr)
 {
 	int r;
-	down(&file->f_dentry->d_inode->i_sem);
+	fs_down(&file->f_dentry->d_inode->i_sem);
 	r = file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-	up(&file->f_dentry->d_inode->i_sem);
+	fs_up(&file->f_dentry->d_inode->i_sem);
 	return r;
 }
 
@@ -112,7 +112,7 @@
 #       define START_STACK(u)   (u.start_stack)
 #endif
 
-	if (!current->dumpable || atomic_read(&current->mm->count) != 1)
+	if (current->dumpable != 1 || atomic_read(&current->mm->count) != 1)
 		return 0;
 	current->dumpable = 0;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/binfmt_elf.c linux.19p6/fs/binfmt_elf.c
--- linux.vanilla/fs/binfmt_elf.c	Mon Dec 11 22:13:02 2000
+++ linux.19p6/fs/binfmt_elf.c	Fri Dec 22 12:06:38 2000
@@ -948,9 +948,9 @@
 static int dump_write(struct file *file, const void *addr, int nr)
 {
 	int r;
-	down(&file->f_dentry->d_inode->i_sem);
+	fs_down(&file->f_dentry->d_inode->i_sem);
 	r = file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-	up(&file->f_dentry->d_inode->i_sem);
+	fs_up(&file->f_dentry->d_inode->i_sem);
 	return r;
 }
 
@@ -1056,7 +1056,7 @@
 #undef DUMP_SEEK
 
 #define DUMP_WRITE(addr, nr)	\
-	if (!dump_write(file, (addr), (nr))) \
+	if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
 		goto close_coredump;
 #define DUMP_SEEK(off)	\
 	if (!dump_seek(file, (off))) \
@@ -1078,7 +1078,7 @@
 	char corefile[6+sizeof(current->comm)];
 	int segs;
 	int i;
-	size_t size;
+	size_t size = 0;
 	struct vm_area_struct *vma;
 	struct elfhdr elf;
 	off_t offset = 0, dataoff;
@@ -1089,7 +1089,7 @@
 	elf_fpregset_t fpu;		/* NT_PRFPREG */
 	struct elf_prpsinfo psinfo;	/* NT_PRPSINFO */
 
-	if (!current->dumpable ||
+	if (current->dumpable != 1 ||
 	    limit < ELF_EXEC_PAGESIZE ||
 	    atomic_read(&current->mm->count) != 1)
 		return 0;
@@ -1099,24 +1099,10 @@
 	MOD_INC_USE_COUNT;
 #endif
 
-	/* Count what's needed to dump, up to the limit of coredump size */
-	segs = 0;
-	size = 0;
-	for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
-		if (maydump(vma))
-		{
-			unsigned long sz = vma->vm_end-vma->vm_start;
-
-			if (size+sz >= limit)
-				break;
-			else
-				size += sz;
-		}
+	segs = current->mm->map_count;
 
-		segs++;
-	}
 #ifdef DEBUG
-	printk("elf_core_dump: %d segs taking %d bytes\n", segs, size);
+	printk("elf_core_dump: %d segs %lu limit\n", segs, limit);
 #endif
 
 	/* Set up header */
@@ -1294,13 +1280,10 @@
 	dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
 
 	/* Write program headers for segments dump */
-	for(vma = current->mm->mmap, i = 0;
-		i < segs && vma != NULL; vma = vma->vm_next) {
+	for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
 		struct elf_phdr phdr;
 		size_t sz;
 
-		i++;
-
 		sz = vma->vm_end - vma->vm_start;
 
 		phdr.p_type = PT_LOAD;
@@ -1326,19 +1309,36 @@
 
 	DUMP_SEEK(dataoff);
 
-	for(i = 0, vma = current->mm->mmap;
-	    i < segs && vma != NULL;
-	    vma = vma->vm_next) {
-		unsigned long addr = vma->vm_start;
-		unsigned long len = vma->vm_end - vma->vm_start;
+	for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+		unsigned long addr;
 
-		i++;
 		if (!maydump(vma))
 			continue;
 #ifdef DEBUG
 		printk("elf_core_dump: writing %08lx %lx\n", addr, len);
 #endif
-		DUMP_WRITE((void *)addr, len);
+		for (addr = vma->vm_start;
+		     addr < vma->vm_end;
+		     addr += PAGE_SIZE) {
+			pgd_t *pgd;
+			pmd_t *pmd;
+			pte_t *pte;
+
+			pgd = pgd_offset(vma->vm_mm, addr);
+			pmd = pmd_alloc(pgd, addr);
+	
+			if (!pmd)
+				goto end_coredump;
+			pte = pte_alloc(pmd, addr);
+			if (!pte)
+				goto end_coredump;
+			if (!pte_present(*pte) &&
+			    pte_none(*pte)) {
+				DUMP_SEEK (file->f_pos + PAGE_SIZE);
+			} else {
+				DUMP_WRITE((void*)addr, PAGE_SIZE);
+			}
+		}
 	}
 
 	if ((off_t) file->f_pos != offset) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/buffer.c linux.19p6/fs/buffer.c
--- linux.vanilla/fs/buffer.c	Mon Dec 11 22:13:02 2000
+++ linux.19p6/fs/buffer.c	Sat Dec 16 17:21:47 2000
@@ -83,6 +83,7 @@
 
 static int nr_buffers = 0;
 static int nr_buffers_type[NR_LIST] = {0,};
+static unsigned long size_buffers_type[NR_LIST];
 static int nr_buffer_heads = 0;
 static int nr_unused_buffer_heads = 0;
 static int nr_hashed_buffers = 0;
@@ -359,9 +360,9 @@
 		goto out_putf;
 
 	/* We need to protect against concurrent writers.. */
-	down(&inode->i_sem);
+	fs_down(&inode->i_sem);
 	err = file->f_op->fsync(file, dentry);
-	up(&inode->i_sem);
+	fs_up(&inode->i_sem);
 
 out_putf:
 	fput(file);
@@ -396,9 +397,9 @@
 		goto out_putf;
 
 	/* this needs further work, at the moment it is identical to fsync() */
-	down(&inode->i_sem);
+	fs_down(&inode->i_sem);
 	err = file->f_op->fsync(file, dentry);
-	up(&inode->i_sem);
+	fs_up(&inode->i_sem);
 
 out_putf:
 	fput(file);
@@ -474,6 +475,7 @@
 		return;
 	}
 	nr_buffers_type[bh->b_list]--;
+	size_buffers_type[bh->b_list] -= bh->b_size;
 	remove_from_hash_queue(bh);
 	remove_from_lru_list(bh);
 }
@@ -523,6 +525,7 @@
 		(*bhp)->b_prev_free = bh;
 
 		nr_buffers_type[bh->b_list]++;
+		size_buffers_type[bh->b_list] += bh->b_size;
 
 		/* Put the buffer in new hash-queue if it has a device. */
 		bh->b_next = NULL;
@@ -571,8 +574,10 @@
 {
 	struct buffer_head * bh;
 	bh = find_buffer(dev,block,size);
-	if (bh)
+	if (bh) {
 		bh->b_count++;
+		touch_buffer(bh);
+	}
 	return bh;
 }
 
@@ -816,6 +821,46 @@
 	insert_into_queues(bh);
 }
 
+/* -1 -> no need to flush
+    0 -> async flush
+    1 -> sync flush (wait for I/O completation) */
+static int balance_dirty_state(kdev_t dev)
+{
+	unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit;
+
+	dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
+	tot = (buffermem >> PAGE_SHIFT) + nr_free_pages;
+	tot -= size_buffers_type[BUF_PROTECTED] >> PAGE_SHIFT;
+
+	dirty *= 200;
+	soft_dirty_limit = tot * bdf_prm.b_un.nfract;
+	hard_dirty_limit = soft_dirty_limit * 2;
+
+	if (dirty > soft_dirty_limit)
+	{
+		if (dirty > hard_dirty_limit)
+			return 1;
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * if a new dirty buffer is created we need to balance bdflush.
+ *
+ * in the future we might want to make bdflush aware of different
+ * pressures on different devices - thus the (currently unused)
+ * 'dev' parameter.
+ */
+void balance_dirty(kdev_t dev)
+{
+	int state = balance_dirty_state(dev);
+
+	if (state < 0)
+		return;
+	wakeup_bdflush(state);
+}
+
 /*
  * A buffer may need to be moved from one buffer list to another
  * (e.g. in case it is not shared any more). Handle this.
@@ -828,7 +873,9 @@
 		printk("Attempt to refile free buffer\n");
 		return;
 	}
-	if (buffer_dirty(buf))
+	if (buffer_protected(buf))
+		dispose = BUF_PROTECTED;
+	else if (buffer_dirty(buf))
 		dispose = BUF_DIRTY;
 	else if (buffer_locked(buf))
 		dispose = BUF_LOCKED;
@@ -837,13 +884,7 @@
 	if(dispose != buf->b_list) {
 		file_buffer(buf, dispose);
 		if(dispose == BUF_DIRTY) {
-			int too_many = (nr_buffers * bdf_prm.b_un.nfract/100);
-
-			/* This buffer is dirty, maybe we need to start flushing.
-			 * If too high a percentage of the buffers are dirty...
-			 */
-			if (nr_buffers_type[BUF_DIRTY] > too_many)
-				wakeup_bdflush(1);
+			balance_dirty(buf->b_dev);
 
 			/* If this is a loop device, and
 			 * more than half of the buffers are dirty...
@@ -864,7 +905,6 @@
 	/* If dirty, mark the time this buffer should be written back. */
 	set_writetime(buf, 0);
 	refile_buffer(buf);
-	touch_buffer(buf);
 
 	if (buf->b_count) {
 		buf->b_count--;
@@ -1457,6 +1497,7 @@
 	}
 	tmp->b_this_page = bh;
 	free_list[isize] = bh;
+	mem_map[MAP_NR(page)].flags = 0;
 	mem_map[MAP_NR(page)].buffers = bh;
 	buffermem += PAGE_SIZE;
 	return 1;
@@ -1468,33 +1509,34 @@
 #define BUFFER_BUSY_BITS	((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected))
 #define buffer_busy(bh)		((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS))
 
-static int sync_page_buffers(struct page * page, int wait)
+static void sync_page_buffers(struct page * page)
 {
-	struct buffer_head * bh = page->buffers;
-	struct buffer_head * tmp = bh;
+	struct buffer_head * tmp, * bh = page->buffers;
 
+	/*
+	 * Here we'll probably sleep and so we must make sure that
+	 * the page doesn't go away from under us. We also prefer any
+	 * concurrent try_to_free_buffers() not to work in any way on
+	 * our current page from under us since we're just working on it.
+	 * As always in 2.2.x we're serialized by the big kernel lock
+	 * during those hacky page-visibility manipulations.
+	 *
+	 * SUBTLE NOTE: for things like LVM snapshotting WRITEA will block too!
+	 */
 	page->buffers = NULL;
 
+	tmp = bh;
 	do {
 		struct buffer_head *p = tmp;
 		tmp = tmp->b_this_page;
-		if (buffer_locked(p)) {
-			if (wait)
-				__wait_on_buffer(p);
-		} else if (buffer_dirty(p))
-			ll_rw_block(WRITE, 1, &p);
-	} while (tmp != bh);
-
-	page->buffers = bh;
 
-	do {
-		struct buffer_head *p = tmp;
-		tmp = tmp->b_this_page;
-		if (buffer_busy(p))
-			return 1;
+		if (buffer_dirty(p))
+			if (test_and_set_bit(BH_Wait_IO, &p->b_state))
+				ll_rw_block(WRITE, 1, &p);
 	} while (tmp != bh);
 
-	return 0;
+	/* Restore the visibility of the page before returning. */
+	page->buffers = bh;
 }
 
 /*
@@ -1504,10 +1546,9 @@
  * Wake up bdflush() if this fails - if we're running low on memory due
  * to dirty buffers, we need to flush them out as quickly as possible.
  */
-int try_to_free_buffers(struct page * page_map, int wait)
+int try_to_free_buffers(struct page * page_map, int gfp_mask)
 {
 	struct buffer_head * tmp, * bh = page_map->buffers;
-	int too_many;
 
 	tmp = bh;
 	do {
@@ -1516,8 +1557,6 @@
 		tmp = tmp->b_this_page;
 	} while (tmp != bh);
 
- succeed:
-	tmp = bh;
 	do {
 		struct buffer_head * p = tmp;
 		tmp = tmp->b_this_page;
@@ -1536,25 +1575,12 @@
 	return 1;
 
  busy:
-	too_many = (nr_buffers * bdf_prm.b_un.nfract/100);
-
-	if (!sync_page_buffers(page_map, wait)) {
-
-		/* If a high percentage of the buffers are dirty, 
-		 * wake kflushd 
-		 */
-		if (nr_buffers_type[BUF_DIRTY] > too_many)
-			wakeup_bdflush(0);
-			
-		/*
-		 * We can jump after the busy check because
-		 * we rely on the kernel lock.
-		 */
-		goto succeed;
-	}
+	if (gfp_mask & __GFP_IO)
+		sync_page_buffers(page_map);
 
-	if(nr_buffers_type[BUF_DIRTY] > too_many)
+	if (balance_dirty_state(NODEV) >= 0)
 		wakeup_bdflush(0);
+
 	return 0;
 }
 
@@ -1566,7 +1592,7 @@
 	int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
 	int protected = 0;
 	int nlist;
-	static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","DIRTY"};
+	static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","DIRTY","PROTECTED",};
 
 	printk("Buffer memory:   %8ldkB\n",buffermem>>10);
 	printk("Buffer heads:    %6d\n",nr_buffer_heads);
@@ -1590,7 +1616,7 @@
 			used++, lastused = found;
 		bh = bh->b_next_free;
 	  } while (bh != lru_list[nlist]);
-	  printk("%8s: %d buffers, %d used (last=%d), "
+	  printk("%9s: %d buffers, %d used (last=%d), "
 		 "%d locked, %d protected, %d dirty\n",
 		 buf_types[nlist], found, used, lastused,
 		 locked, protected, dirty);
@@ -1935,7 +1961,8 @@
 		
 		/* If there are still a lot of dirty buffers around, skip the sleep
 		   and flush some more */
-		if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
+		if (!ndirty || balance_dirty_state(NODEV) < 0)
+		{
 			spin_lock_irq(&current->sigmask_lock);
 			flush_signals(current);
 			spin_unlock_irq(&current->sigmask_lock);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/coda/file.c linux.19p6/fs/coda/file.c
--- linux.vanilla/fs/coda/file.c	Sat Sep  9 18:42:46 2000
+++ linux.19p6/fs/coda/file.c	Sat Dec 16 17:21:47 2000
@@ -190,10 +190,10 @@
                 return -1;
         }
 
-	down(&cont_inode->i_sem);
+	fs_down(&cont_inode->i_sem);
         result = cont_file.f_op->write(&cont_file , buff, count, 
 				       &(cont_file.f_pos));
-	up(&cont_inode->i_sem);
+	fs_up(&cont_inode->i_sem);
         coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
 	
 	if (result)
@@ -228,14 +228,14 @@
         coda_prepare_openfile(coda_inode, coda_file, cont_inode, 
 			      &cont_file, &cont_dentry);
 
-	down(&cont_inode->i_sem);
+	fs_down(&cont_inode->i_sem);
 
         result = file_fsync(&cont_file ,&cont_dentry);
 	if ( result == 0 ) {
 		result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid));
 	}
 
-	up(&cont_inode->i_sem);
+	fs_up(&cont_inode->i_sem);
 
         coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
         return result;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/dcache.c linux.19p6/fs/dcache.c
--- linux.vanilla/fs/dcache.c	Sun Jun 11 21:44:18 2000
+++ linux.19p6/fs/dcache.c	Wed Jan  3 13:47:28 2001
@@ -253,10 +253,15 @@
 
 		if (tmp == &dentry_unused)
 			break;
-		dentry_stat.nr_unused--;
 		list_del(tmp);
-		INIT_LIST_HEAD(tmp);
 		dentry = list_entry(tmp, struct dentry, d_lru);
+		if (dentry->d_flags & DCACHE_REFERENCED) {
+			dentry->d_flags &= ~DCACHE_REFERENCED;
+			list_add(&dentry->d_lru, &dentry_unused);
+			continue;
+		}
+		dentry_stat.nr_unused--;
+		INIT_LIST_HEAD(tmp);
 		if (!dentry->d_count) {
 			i_nr -= prune_one_dentry(dentry);
 			if (!i_nr)
@@ -475,9 +480,9 @@
  */
 void shrink_dcache_memory(int priority, unsigned int gfp_mask)
 {
-	if (gfp_mask & __GFP_IO) {
+	if (gfp_mask & __GFP_IO && !current->fs_locks) {
 		int count = 0;
-		if (priority)
+		if (priority > 1)
 			count = dentry_stat.nr_unused / priority;
 		prune_dcache(count, -1);
 	}
@@ -598,6 +603,7 @@
 			if (memcmp(dentry->d_name.name, str, len))
 				continue;
 		}
+		dentry->d_flags |= DCACHE_REFERENCED;
 		return dget(dentry);
 	}
 	return NULL;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/dquot.c linux.19p6/fs/dquot.c
--- linux.vanilla/fs/dquot.c	Mon Dec 11 22:13:02 2000
+++ linux.19p6/fs/dquot.c	Wed Jan  3 13:50:15 2001
@@ -570,7 +570,7 @@
 	 */
 	if (prune_dcache(0, 128))
 	{
-		free_inode_memory(10);
+		free_inode_memory();
 		goto repeat;
 	}
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/exec.c linux.19p6/fs/exec.c
--- linux.vanilla/fs/exec.c	Mon Dec 11 22:13:02 2000
+++ linux.19p6/fs/exec.c	Wed Jan  3 00:55:37 2001
@@ -518,7 +518,7 @@
 	current->sas_ss_sp = current->sas_ss_size = 0;
 
 	if (current->euid == current->uid && current->egid == current->gid)
-		current->dumpable = 1;
+		current->dumpable = 2;
 	name = bprm->filename;
 	for (i=0; (ch = *(name++)) != '\0';) {
 		if (ch == '/')
@@ -864,6 +864,10 @@
 
 	if (retval >= 0)
 		retval = search_binary_handler(&bprm,regs);
+
+	if(current->dumpable == 2)
+		current->dumpable = 0;
+		
 	if (retval >= 0)
 		/* execve success */
 		return retval;
@@ -875,5 +879,6 @@
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
 		free_page(bprm.page[i]);
 
+		
 	return retval;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/fat/misc.c linux.19p6/fs/fat/misc.c
--- linux.vanilla/fs/fat/misc.c	Fri Apr 21 12:46:42 2000
+++ linux.19p6/fs/fat/misc.c	Tue Dec 19 02:06:27 2000
@@ -2,6 +2,8 @@
  *  linux/fs/fat/misc.c
  *
  *  Written 1992,1993 by Werner Almesberger
+ *  22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
+ *		 and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
  */
 
 #include <linux/fs.h>
@@ -288,7 +290,9 @@
 {
 	int month,year,secs;
 
-	month = ((date >> 5) & 15)-1;
+	/* first subtract and mask after that... Otherwise, if
+	   date == 0, bad things happen */
+	month = ((date >> 5) - 1) & 15;
 	year = date >> 9;
 	secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
 	    ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
@@ -309,6 +313,10 @@
 
 	unix_date -= sys_tz.tz_minuteswest*60;
 	if (sys_tz.tz_dsttime) unix_date += 3600;
+
+	/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+	if (unix_date < 315532800)
+		unix_date = 315532800;
 
 	*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
 	    (((unix_date/3600) % 24) << 11);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/file_table.c linux.19p6/fs/file_table.c
--- linux.vanilla/fs/file_table.c	Fri Apr 21 12:46:42 2000
+++ linux.19p6/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/inode.c linux.19p6/fs/inode.c
--- linux.vanilla/fs/inode.c	Mon Dec 11 22:13:02 2000
+++ linux.19p6/fs/inode.c	Wed Jan  3 13:50:15 2001
@@ -435,7 +435,7 @@
  * This is the externally visible routine for
  * inode memory management.
  */
-void free_inode_memory(int goal)
+void free_inode_memory(void)
 {
 	spin_lock(&inode_lock);
 	free_inodes();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/isofs/dir.c linux.19p6/fs/isofs/dir.c
--- linux.vanilla/fs/isofs/dir.c	Sat Sep  9 18:42:47 2000
+++ linux.19p6/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.19p6/fs/isofs/inode.c
--- linux.vanilla/fs/isofs/inode.c	Sat Sep  9 18:42:47 2000
+++ linux.19p6/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.19p6/fs/isofs/joliet.c
--- linux.vanilla/fs/isofs/joliet.c	Sun Jun 11 21:44:18 2000
+++ linux.19p6/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.19p6/fs/isofs/namei.c
--- linux.vanilla/fs/isofs/namei.c	Fri Apr 21 12:46:43 2000
+++ linux.19p6/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.19p6/fs/isofs/rock.c
--- linux.vanilla/fs/isofs/rock.c	Fri Apr 21 12:46:43 2000
+++ linux.19p6/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.19p6/fs/isofs/util.c
--- linux.vanilla/fs/isofs/util.c	Fri Apr 21 12:46:43 2000
+++ linux.19p6/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/fs/nls/Config.in linux.19p6/fs/nls/Config.in
--- linux.vanilla/fs/nls/Config.in	Mon Dec 11 22:13:03 2000
+++ linux.19p6/fs/nls/Config.in	Wed Jan  3 01:04:07 2001
@@ -47,5 +47,6 @@
   tristate 'NLS ISO 8859-14 (Latin 8; Celtic)'      CONFIG_NLS_ISO8859_14
   tristate 'NLS ISO 8859-15 (Latin 9; Western European Languages with Euro)' CONFIG_NLS_ISO8859_15
   tristate 'NLS KOI8-R (Russian)'                   CONFIG_NLS_KOI8_R
+  tristate 'NLS KOI8-RU (Belorussia, Ukraine)'      CONFIG_NLS_KOI8_RU
   endmenu
 fi
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/Makefile linux.19p6/fs/nls/Makefile
--- linux.vanilla/fs/nls/Makefile	Sun Jun 11 21:44:19 2000
+++ linux.19p6/fs/nls/Makefile	Wed Jan  3 01:05:38 2001
@@ -342,6 +342,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_NLS_KOI8_RU),y)
+NLS += nls_koi8-ru.o
+else
+  ifeq ($(CONFIG_NLS_KOI8_RU),m)
+  M_OBJS += nls_koi8-ru.o
+  endif
+endif
+
 O_TARGET = nls.o
 OX_OBJS  = $(NLS)
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_base.c linux.19p6/fs/nls/nls_base.c
--- linux.vanilla/fs/nls/nls_base.c	Mon Dec 11 22:13:03 2000
+++ linux.19p6/fs/nls/nls_base.c	Wed Jan  3 01:04:48 2001
@@ -529,6 +529,9 @@
 #ifdef CONFIG_NLS_KOI8_R
 	init_nls_koi8_r();
 #endif
+#ifdef CONFIG_NLS_KOI8_RU
+	init_nls_koi8_ru();
+#endif
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_koi8-ru.c linux.19p6/fs/nls/nls_koi8-ru.c
--- linux.vanilla/fs/nls/nls_koi8-ru.c	Thu Jan  1 01:00:00 1970
+++ linux.19p6/fs/nls/nls_koi8-ru.c	Wed Jan  3 01:04:24 2001
@@ -0,0 +1,401 @@
+/*
+ * linux/fs/nls_koi8-r.c
+ *
+ * Charset koi8-r translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/init.h>
+
+static struct nls_unicode charset2uni[256] = {
+	/* 0x00*/
+	{0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00},
+	{0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
+	{0x08, 0x00}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00},
+	{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
+	/* 0x10*/
+	{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00},
+	{0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
+	{0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00},
+	{0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
+	/* 0x20*/
+	{0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+	{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
+	{0x28, 0x00}, {0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00},
+	{0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00},
+	/* 0x30*/
+	{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+	{0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
+	{0x38, 0x00}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00},
+	{0x3c, 0x00}, {0x3d, 0x00}, {0x3e, 0x00}, {0x3f, 0x00},
+	/* 0x40*/
+	{0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00},
+	{0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+	{0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00},
+	{0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0x00},
+	/* 0x50*/
+	{0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00},
+	{0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+	{0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00},
+	{0x5c, 0x00}, {0x5d, 0x00}, {0x5e, 0x00}, {0x5f, 0x00},
+	/* 0x60*/
+	{0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00},
+	{0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+	{0x68, 0x00}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00},
+	{0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00},
+	/* 0x70*/
+	{0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+	{0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+	{0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00},
+	{0x7c, 0x00}, {0x7d, 0x00}, {0x7e, 0x00}, {0x7f, 0x00},
+	/* 0x80*/
+	{0x00, 0x25}, {0x02, 0x25}, {0x0c, 0x25}, {0x10, 0x25},
+	{0x14, 0x25}, {0x18, 0x25}, {0x1c, 0x25}, {0x24, 0x25},
+	{0x2c, 0x25}, {0x34, 0x25}, {0x3c, 0x25}, {0x80, 0x25},
+	{0x84, 0x25}, {0x88, 0x25}, {0x8c, 0x25}, {0x90, 0x25},
+	/* 0x90*/
+	{0x91, 0x25}, {0x92, 0x25}, {0x93, 0x25}, {0x20, 0x23},
+	{0xa0, 0x25}, {0x19, 0x22}, {0x1a, 0x22}, {0x48, 0x22},
+	{0x64, 0x22}, {0x65, 0x22}, {0xa0, 0x00}, {0x21, 0x23},
+	{0xb0, 0x00}, {0xb2, 0x00}, {0xb7, 0x00}, {0xf7, 0x00},
+	/* 0xa0*/
+	{0x50, 0x25}, {0x51, 0x25}, {0x52, 0x25}, {0x51, 0x04},
+	{0x54, 0x04}, {0x54, 0x25}, {0x56, 0x04}, {0x57, 0x04},
+	{0x57, 0x25}, {0x58, 0x25}, {0x59, 0x25}, {0x5a, 0x25},
+	{0x5b, 0x25}, {0x91, 0x04}, {0x5e, 0x04}, {0x5e, 0x25},
+	/* 0xb0*/
+	{0x5f, 0x25}, {0x60, 0x25}, {0x61, 0x25}, {0x01, 0x04},
+	{0x04, 0x04}, {0x63, 0x25}, {0x06, 0x04}, {0x07, 0x04},
+	{0x66, 0x25}, {0x67, 0x25}, {0x68, 0x25}, {0x69, 0x25},
+	{0x6a, 0x25}, {0x90, 0x04}, {0x0e, 0x04}, {0xa9, 0x00},
+	/* 0xc0*/
+	{0x4e, 0x04}, {0x30, 0x04}, {0x31, 0x04}, {0x46, 0x04},
+	{0x34, 0x04}, {0x35, 0x04}, {0x44, 0x04}, {0x33, 0x04},
+	{0x45, 0x04}, {0x38, 0x04}, {0x39, 0x04}, {0x3a, 0x04},
+	{0x3b, 0x04}, {0x3c, 0x04}, {0x3d, 0x04}, {0x3e, 0x04},
+	/* 0xd0*/
+	{0x3f, 0x04}, {0x4f, 0x04}, {0x40, 0x04}, {0x41, 0x04},
+	{0x42, 0x04}, {0x43, 0x04}, {0x36, 0x04}, {0x32, 0x04},
+	{0x4c, 0x04}, {0x4b, 0x04}, {0x37, 0x04}, {0x48, 0x04},
+	{0x4d, 0x04}, {0x49, 0x04}, {0x47, 0x04}, {0x4a, 0x04},
+	/* 0xe0*/
+	{0x2e, 0x04}, {0x10, 0x04}, {0x11, 0x04}, {0x26, 0x04},
+	{0x14, 0x04}, {0x15, 0x04}, {0x24, 0x04}, {0x13, 0x04},
+	{0x25, 0x04}, {0x18, 0x04}, {0x19, 0x04}, {0x1a, 0x04},
+	{0x1b, 0x04}, {0x1c, 0x04}, {0x1d, 0x04}, {0x1e, 0x04},
+	/* 0xf0*/
+	{0x1f, 0x04}, {0x2f, 0x04}, {0x20, 0x04}, {0x21, 0x04},
+	{0x22, 0x04}, {0x23, 0x04}, {0x16, 0x04}, {0x12, 0x04},
+	{0x2c, 0x04}, {0x2b, 0x04}, {0x17, 0x04}, {0x28, 0x04},
+	{0x2d, 0x04}, {0x29, 0x04}, {0x27, 0x04}, {0x2a, 0x04},
+};
+
+static unsigned char page00[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page04[256] = {
+	0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0xb7, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, /* 0x08-0x0f */
+	0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, /* 0x10-0x17 */
+	0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, /* 0x18-0x1f */
+	0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, /* 0x20-0x27 */
+	0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, /* 0x28-0x2f */
+	0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, /* 0x30-0x37 */
+	0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, /* 0x38-0x3f */
+	0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, /* 0x40-0x47 */
+	0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, /* 0x48-0x4f */
+	0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa6, 0xa7, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0xbd, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page22[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x95, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x98, 0x99, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page23[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x93, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page25[256] = {
+	0x80, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0xa0, 0xa1, 0xa2, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, /* 0x50-0x57 */
+	0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, /* 0x58-0x5f */
+	0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, /* 0x60-0x67 */
+	0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x8b, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x8d, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x8f, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+	page00, NULL,   NULL,   NULL,   page04, NULL,   NULL,   NULL,   
+	NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   
+	NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   
+	NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   
+	NULL,   NULL,   page22, page23, NULL,   page25, NULL,   NULL,   
+};
+
+#if 0
+static unsigned char charset2upper[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+	0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+	0xa0, 0xa1, 0xa2, 0x00, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+#endif
+
+static void uni2char(unsigned char ch, unsigned char cl, unsigned char *out, int boundlen, int *outlen)
+{
+	unsigned char *uni2charset;
+
+	if (boundlen <= 0)
+		return;
+
+	uni2charset = page_uni2charset[ch];
+	if (uni2charset && uni2charset[cl])
+		out[0] = uni2charset[cl];
+	else
+		out[0] = '?';
+	*outlen = 1;
+	return;
+}
+
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+{
+	*uni1 = charset2uni[*rawstring].uni1;
+	*uni2 = charset2uni[*rawstring].uni2;
+	*offset = 1;
+	return;
+}
+
+static void inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static struct nls_table table = {
+	"koi8-ru",
+	uni2char,
+	char2uni,
+	inc_use_count,
+	dec_use_count,
+	NULL
+};
+
+int __init init_nls_koi8_ru(void)
+{
+	return register_nls(&table);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_nls_koi8_ru();
+}
+
+
+void cleanup_module(void)
+{
+	unregister_nls(&table);
+	return;
+}
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/open.c linux.19p6/fs/open.c
--- linux.vanilla/fs/open.c	Mon Dec 11 22:13:05 2000
+++ linux.19p6/fs/open.c	Sat Dec 16 17:21:47 2000
@@ -73,7 +73,7 @@
 	if ((off_t) length < 0)
 		return -EINVAL;
 
-	down(&inode->i_sem);
+	fs_down(&inode->i_sem);
 	newattrs.ia_size = length;
 	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
 	error = notify_change(dentry, &newattrs);
@@ -83,7 +83,7 @@
 		if (inode->i_op && inode->i_op->truncate)
 			inode->i_op->truncate(inode);
 	}
-	up(&inode->i_sem);
+	fs_up(&inode->i_sem);
 	return error;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/read_write.c linux.19p6/fs/read_write.c
--- linux.vanilla/fs/read_write.c	Sat Sep  9 18:42:49 2000
+++ linux.19p6/fs/read_write.c	Sat Dec 16 17:21:47 2000
@@ -166,9 +166,9 @@
 	if (!file->f_op || !(write = file->f_op->write))
 		goto out;
 
-	down(&inode->i_sem);
+	fs_down(&inode->i_sem);
 	ret = write(file, buf, count, &file->f_pos);
-	up(&inode->i_sem);
+	fs_up(&inode->i_sem);
 out:
 	fput(file);
 bad_file:
@@ -314,9 +314,9 @@
 	if (!file)
 		goto bad_file;
 	if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) {
-		down(&file->f_dentry->d_inode->i_sem);
+		fs_down(&file->f_dentry->d_inode->i_sem);
 		ret = do_readv_writev(VERIFY_READ, file, vector, count);
-		up(&file->f_dentry->d_inode->i_sem);
+		fs_up(&file->f_dentry->d_inode->i_sem);
 	}
 	fput(file);
 
@@ -386,9 +386,9 @@
 	if (pos < 0)
 		goto out;
 
-	down(&file->f_dentry->d_inode->i_sem);
+	fs_down(&file->f_dentry->d_inode->i_sem);
 	ret = write(file, buf, count, &pos);
-	up(&file->f_dentry->d_inode->i_sem);
+	fs_up(&file->f_dentry->d_inode->i_sem);
 
 out:
 	fput(file);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/ChangeLog linux.19p6/fs/smbfs/ChangeLog
--- linux.vanilla/fs/smbfs/ChangeLog	Mon Dec 11 22:13:05 2000
+++ linux.19p6/fs/smbfs/ChangeLog	Tue Dec 19 02:06:27 2000
@@ -1,5 +1,10 @@
 ChangeLog for smbfs.
 
+2000-11-22 Igor Zhbanov <bsg@uniyar.ac.ru>
+
+	* proc.c: fixed date_unix2dos for dates earlier than 01/01/1980
+	  and date_dos2unix for date==0
+
 2000-11-04 Urban Widmark <urban@svenskatest.se>
 
 	* proc.c, sock.c: adjust max parameters & max data to follow max_xmit
@@ -41,7 +46,7 @@
 	* proc.c: removed support for old protocol levels. It didn't work
 	  anyway and was cluttering things up a lot.
 
-2000-01-?? cpg@aladdin.de
+2000-01-03 Christian Groessler <cpg@aladdin.de>
 
 	* proc.c: added posix semantics for unlink
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/proc.c linux.19p6/fs/smbfs/proc.c
--- linux.vanilla/fs/smbfs/proc.c	Mon Dec 11 22:13:05 2000
+++ linux.19p6/fs/smbfs/proc.c	Tue Dec 19 02:06:27 2000
@@ -280,7 +280,9 @@
 	int month, year;
 	time_t secs;
 
-	month = ((date >> 5) & 15) - 1;
+	/* first subtract and mask after that... Otherwise, if
+	   date == 0, bad things happen */
+	month = ((date >> 5) - 1) & 15;
 	year = date >> 9;
 	secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
 	    ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
@@ -299,6 +301,11 @@
 	int day, year, nl_day, month;
 
 	unix_date = utc2local(server, unix_date);
+
+	/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+	if (unix_date < 315532800)
+		unix_date = 315532800;
+
 	*time = (unix_date % 60) / 2 +
 		(((unix_date / 60) % 60) << 5) +
 		(((unix_date / 3600) % 24) << 11);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/mmu_context.h linux.19p6/include/asm-alpha/mmu_context.h
--- linux.vanilla/include/asm-alpha/mmu_context.h	Fri Apr 21 12:46:47 2000
+++ linux.19p6/include/asm-alpha/mmu_context.h	Wed Jan  3 13:33:37 2001
@@ -65,12 +65,7 @@
 #endif /* __SMP__ */
 
 #define WIDTH_HARDWARE_ASN	8
-#ifdef __SMP__
-#define WIDTH_THIS_PROCESSOR	5
-#else
-#define WIDTH_THIS_PROCESSOR	0
-#endif
-#define ASN_FIRST_VERSION (1UL << (WIDTH_THIS_PROCESSOR + WIDTH_HARDWARE_ASN))
+#define ASN_FIRST_VERSION (1UL << WIDTH_HARDWARE_ASN)
 #define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1)
 
 /*
@@ -100,6 +95,7 @@
 	/* If we've wrapped, flush the whole user TLB.  */
 	if ((asn & HARDWARE_ASN_MASK) >= MAX_ASN) {
 		tbiap();
+		imb();
 		next = (asn & ~HARDWARE_ASN_MASK) + ASN_FIRST_VERSION;
 	}
 	cpu_last_asn(smp_processor_id()) = next;
@@ -125,19 +121,21 @@
 __EXTERN_INLINE void
 ev5_get_mmu_context(struct task_struct *p)
 {
-	/* Check if our ASN is of an older version, or on a different CPU,
-	   and thus invalid.  */
-	/* ??? If we have two threads on different cpus, we'll continually
-	   fight over the context.  Find a way to record a per-mm, per-cpu
-	   value for the asn.  */
-
-	unsigned long asn = cpu_last_asn(smp_processor_id());
-	struct mm_struct *mm = p->mm;
-	unsigned long mmc = mm->context;
+	/* Check if our ASN is of an older version, and thus invalid. */
+	int cpu;
+	unsigned long asn;
+	struct mm_struct *mm;
+	unsigned long mmc;
 	
+	cpu = smp_processor_id();
+	mm = p->mm;
+	ctx_cli();
+	asn = cpu_last_asn(cpu);
+	mmc = mm->context[cpu];
+
 	if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) {
 		mmc = __get_new_mmu_context();
-		mm->context = mmc;
+		mm->context[cpu] = mmc;
 	}
 
 	/* Always update the PCB ASN.  Another thread may have allocated
@@ -159,7 +157,10 @@
 extern inline void
 init_new_context(struct mm_struct *mm)
 {
-	mm->context = 0;
+	int i;
+
+	for (i = 0; i < smp_num_cpus; i++)
+		mm->context[cpu_logical_map(i)] = 0;
 }
 
 extern inline void
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/pgtable.h linux.19p6/include/asm-alpha/pgtable.h
--- linux.vanilla/include/asm-alpha/pgtable.h	Sun Jun 11 21:44:22 2000
+++ linux.19p6/include/asm-alpha/pgtable.h	Wed Jan  3 13:33:37 2001
@@ -73,7 +73,13 @@
 __EXTERN_INLINE void
 ev5_flush_tlb_other(struct mm_struct *mm)
 {
-	mm->context = 0;
+	long * mmc = &mm->context[smp_processor_id()];
+	/*
+	 * Check it's not zero first to avoid cacheline ping pong when
+	 * possible.
+	 */
+	if (*mmc)
+		*mmc = 0;
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/ptrace.h linux.19p6/include/asm-alpha/ptrace.h
--- linux.vanilla/include/asm-alpha/ptrace.h	Fri Apr 21 12:46:47 2000
+++ linux.19p6/include/asm-alpha/ptrace.h	Wed Jan  3 13:37:59 2001
@@ -70,6 +70,7 @@
 #define user_mode(regs) (((regs)->ps & 8) != 0)
 #define instruction_pointer(regs) ((regs)->pc)
 extern void show_regs(struct pt_regs *);
+extern void __show_regs(struct pt_regs *);
 #endif
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/softirq.h linux.19p6/include/asm-alpha/softirq.h
--- linux.vanilla/include/asm-alpha/softirq.h	Fri Apr 21 12:46:47 2000
+++ linux.19p6/include/asm-alpha/softirq.h	Wed Jan  3 13:34:35 2001
@@ -9,6 +9,7 @@
 extern unsigned long local_bh_count;
 #else
 #define local_bh_count          (cpu_data[smp_processor_id()].bh_count)
+extern spinlock_t alpha_bh_lock;
 #endif
 
 #define get_active_bhs()	(bh_mask & bh_active)
@@ -28,24 +29,6 @@
 	:"Ir" (x), "m" (bh_active));
 }
 
-extern inline void init_bh(int nr, void (*routine)(void))
-{
-	bh_base[nr] = routine;
-	atomic_set(&bh_mask_count[nr], 0);
-	bh_mask |= 1 << nr;
-}
-
-extern inline void remove_bh(int nr)
-{
-	bh_base[nr] = NULL;
-	bh_mask &= ~(1 << nr);
-}
-
-extern inline void mark_bh(int nr)
-{
-	set_bit(nr, &bh_active);
-}
-
 #ifdef __SMP__
 
 /*
@@ -113,21 +96,58 @@
 
 #endif	/* SMP */
 
+extern inline void init_bh(int nr, void (*routine)(void))
+{
+	unsigned long flags;
+
+	bh_base[nr] = routine;
+	atomic_set(&bh_mask_count[nr], 0);
+
+	spin_lock_irqsave(&alpha_bh_lock, flags);
+	bh_mask |= 1 << nr;
+	spin_unlock_irqrestore(&alpha_bh_lock, flags);
+}
+
+extern inline void remove_bh(int nr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&alpha_bh_lock, flags);
+	bh_mask &= ~(1 << nr);
+	spin_unlock_irqrestore(&alpha_bh_lock, flags);
+
+	synchronize_bh();
+	bh_base[nr] = NULL;
+}
+
+extern inline void mark_bh(int nr)
+{
+	set_bit(nr, &bh_active);
+}
+
 /*
  * These use a mask count to correctly handle
  * nested disable/enable calls
  */
 extern inline void disable_bh(int nr)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&alpha_bh_lock, flags);
 	bh_mask &= ~(1 << nr);
 	atomic_inc(&bh_mask_count[nr]);
+	spin_unlock_irqrestore(&alpha_bh_lock, flags);
 	synchronize_bh();
 }
 
 extern inline void enable_bh(int nr)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&alpha_bh_lock, flags);
 	if (atomic_dec_and_test(&bh_mask_count[nr]))
 		bh_mask |= 1 << nr;
+	spin_unlock_irqrestore(&alpha_bh_lock, flags);
 }
 
 #endif /* _ALPHA_SOFTIRQ_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/spinlock.h linux.19p6/include/asm-alpha/spinlock.h
--- linux.vanilla/include/asm-alpha/spinlock.h	Fri Apr 21 12:46:47 2000
+++ linux.19p6/include/asm-alpha/spinlock.h	Wed Jan  3 13:36:23 2001
@@ -231,6 +231,7 @@
 static inline void read_unlock(rwlock_t * lock)
 {
 	long regx;
+	mb();
 	__asm__ __volatile__(
 	"1:	ldl_l	%1,%0\n"
 	"	addl	%1,2,%1\n"
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/system.h linux.19p6/include/asm-alpha/system.h
--- linux.vanilla/include/asm-alpha/system.h	Mon Dec 11 22:13:05 2000
+++ linux.19p6/include/asm-alpha/system.h	Wed Jan  3 13:33:37 2001
@@ -112,12 +112,21 @@
 
 extern void halt(void) __attribute__((noreturn));
 
+#ifdef CONFIG_SMP
+#define ctx_cli()	__cli()
+#define ctx_sti()	__sti()
+#else
+#define ctx_cli()	do { } while(0)
+#define ctx_sti()	do { } while(0)
+#endif
+
 #define switch_to(prev,next,last)			\
 do {							\
 	unsigned long pcbb;				\
 	current = (next);				\
 	pcbb = virt_to_phys(&current->tss);		\
 	(last) = alpha_switch_to(pcbb, (prev));		\
+	ctx_sti();					\
 } while (0)
 
 extern struct task_struct* alpha_switch_to(unsigned long, struct task_struct*);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/ptrace.h linux.19p6/include/asm-arm/proc-armo/ptrace.h
--- linux.vanilla/include/asm-arm/proc-armo/ptrace.h	Mon Dec 11 22:13:06 2000
+++ linux.19p6/include/asm-arm/proc-armo/ptrace.h	Wed Jan  3 00:57:43 2001
@@ -63,12 +63,6 @@
 #define condition_codes(regs) \
 	((regs)->ARM_pc & (CC_V_BIT|CC_C_BIT|CC_Z_BIT|CC_N_BIT))
 
-#define pc_pointer(v) \
-	((v) & 0x03fffffc)
-
-#define instruction_pointer(regs) \
-	(pc_pointer((regs)->ARM_pc))
-
 /* Are the current registers suitable for user mode?
  * (used to maintain security in signal handlers)
  */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/ptrace.h linux.19p6/include/asm-arm/proc-armv/ptrace.h
--- linux.vanilla/include/asm-arm/proc-armv/ptrace.h	Mon Dec 11 22:13:06 2000
+++ linux.19p6/include/asm-arm/proc-armv/ptrace.h	Wed Jan  3 00:57:43 2001
@@ -71,9 +71,6 @@
 #define condition_codes(regs) \
 	((regs)->ARM_cpsr & (CC_V_BIT|CC_C_BIT|CC_Z_BIT|CC_N_BIT))
 	
-#define instruction_pointer(regs)	((regs)->ARM_pc)
-#define pc_pointer(v)			(v)
-
 /* Are the current registers suitable for user mode?
  * (used to maintain security in signal handlers)
  */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/processor.h linux.19p6/include/asm-arm/processor.h
--- linux.vanilla/include/asm-arm/processor.h	Mon Dec 11 22:13:06 2000
+++ linux.19p6/include/asm-arm/processor.h	Wed Jan  3 00:57:43 2001
@@ -32,13 +32,25 @@
 #include <asm/arch/processor.h>
 #include <asm/proc/processor.h>
 
+struct debug_info {
+	int				nsaved;
+	struct {
+		unsigned long		address;
+		unsigned long		insn;
+	} bp[2];
+};
+
 struct thread_struct {
-	unsigned long			address;	  /* Address of fault	*/
-	unsigned long			trap_no;	  /* Trap number	*/
-	unsigned long			error_code;	  /* Error code of trap	*/
-	union fp_state			fpstate;	  /* FPE save state	*/
-	unsigned long			debug[NR_DEBUGS]; /* Debug/ptrace	*/
-	struct context_save_struct	*save;		  /* context save	*/
+							/* fault info	  */
+	unsigned long			address;
+	unsigned long			trap_no;
+	unsigned long			error_code;
+							/* floating point */
+	union fp_state			fpstate;
+							/* debugging	  */
+	struct debug_info		debug;
+							/* context info	  */
+	struct context_save_struct	*save;
 	unsigned long			memmap;		  /* page tables	*/
 	EXTRA_THREAD_STRUCT
 };
@@ -62,7 +74,7 @@
  */
 extern __inline__ unsigned long thread_saved_pc(struct thread_struct *t)
 {
-	return t->save ? t->save->pc & ~PCMASK : 0;
+	return t->save ? pc_pointer(t->save->pc) : 0;
 }
 
 extern __inline__ unsigned long get_css_fp(struct thread_struct *t)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/ptrace.h linux.19p6/include/asm-arm/ptrace.h
--- linux.vanilla/include/asm-arm/ptrace.h	Fri Apr 21 12:46:49 2000
+++ linux.19p6/include/asm-arm/ptrace.h	Wed Jan  3 00:57:43 2001
@@ -3,9 +3,27 @@
 
 #include <asm/proc/ptrace.h>
 
+#ifndef __ASSEMBLY__
+#define pc_pointer(v) \
+	((v) & ~PCMASK)
+
+#define instruction_pointer(regs) \
+	(pc_pointer((regs)->ARM_pc))
+
 #ifdef __KERNEL__
+#define PTRACE_GETREGS		12
+#define PTRACE_SETREGS		13
+#define PTRACE_GETFPREGS	14
+#define PTRACE_SETFPREGS	15
+
 extern void show_regs(struct pt_regs *);
+
+#define predicate(x)	(x & 0xf0000000)
+#define PREDICATE_ALWAYS	0xe0000000
+
 #endif
+
+#endif /* __ASSEMBLY__ */
 
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/bugs.h linux.19p6/include/asm-i386/bugs.h
--- linux.vanilla/include/asm-i386/bugs.h	Mon Dec 11 22:13:06 2000
+++ linux.19p6/include/asm-i386/bugs.h	Tue Dec 19 02:15:14 2000
@@ -188,6 +188,7 @@
 __initfunc(static void check_amd_k6(void))
 {
 	if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+	    boot_cpu_data.x86 == 5 &&
 	    boot_cpu_data.x86_model == 6 &&
 	    boot_cpu_data.x86_mask == 1)
 	{
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/e820.h linux.19p6/include/asm-i386/e820.h
--- linux.vanilla/include/asm-i386/e820.h	Thu Jan  1 01:00:00 1970
+++ linux.19p6/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.19p6/include/asm-i386/msr.h
--- linux.vanilla/include/asm-i386/msr.h	Fri Apr 21 12:46:49 2000
+++ linux.19p6/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.19p6/include/asm-i386/processor.h
--- linux.vanilla/include/asm-i386/processor.h	Mon Dec 11 22:13:06 2000
+++ linux.19p6/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/asm-i386/uaccess.h linux.19p6/include/asm-i386/uaccess.h
--- linux.vanilla/include/asm-i386/uaccess.h	Sun Jun 11 21:44:22 2000
+++ linux.19p6/include/asm-i386/uaccess.h	Wed Jan  3 13:57:49 2001
@@ -510,20 +510,20 @@
 			"	popl %0\n"			\
 			"	shl $2,%0\n"			\
 			"	addl $3,%0\n"			\
-			"	jmp 2b\n"			\
+			"	jmp 3b\n"			\
 			"5:	pushl %%eax\n"			\
 			"	xorl %%eax,%%eax\n"		\
 			"	stosw\n"			\
 			"	stosb\n"			\
 			"	popl %%eax\n"			\
 			"	addl $3,%0\n"			\
-			"	jmp 2b\n"			\
+			"	jmp 3b\n"			\
 			"6:	pushl %%eax\n"			\
 			"	xorl %%eax,%%eax\n"		\
 			"	stosb\n"			\
 			"	popl %%eax\n"			\
 			"	incl %0\n"			\
-			"	jmp 2b\n"			\
+			"	jmp 3b\n"			\
 			".previous\n"				\
 			".section __ex_table,\"a\"\n"		\
 			"	.align 4\n"			\
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/agp_backend.h linux.19p6/include/linux/agp_backend.h
--- linux.vanilla/include/linux/agp_backend.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/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.19p6/include/linux/blk.h
--- linux.vanilla/include/linux/blk.h	Sun Jun 11 21:44:23 2000
+++ linux.19p6/include/linux/blk.h	Wed Jan  3 13:57:43 2001
@@ -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.19p6/include/linux/capi.h
--- linux.vanilla/include/linux/capi.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/capi.h	Wed Jan  3 01:26:53 2001
@@ -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/dcache.h linux.19p6/include/linux/dcache.h
--- linux.vanilla/include/linux/dcache.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/dcache.h	Wed Jan  3 13:50:15 2001
@@ -106,6 +106,10 @@
 					 * If this dentry points to a directory, then
 					 * s_nfsd_free_path semaphore will be down
 					 */
+#define	DCACHE_REFERENCED 0x0008	/* This dentry is been recently
+					 * referenced so try to keep it in
+					 * cache.
+					 */
 
 /*
  * d_drop() unhashes the entry from the parent
@@ -149,7 +153,7 @@
 /* dcache memory management */
 extern void shrink_dcache_memory(int, unsigned int);
 extern void check_dcache_memory(void);
-extern void free_inode_memory(int);	/* defined in fs/inode.c */
+extern void free_inode_memory(void);	/* defined in fs/inode.c */
 
 /* only used at mount-time */
 extern struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/fs.h linux.19p6/include/linux/fs.h
--- linux.vanilla/include/linux/fs.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/fs.h	Wed Jan  3 13:57:42 2001
@@ -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 */
@@ -185,6 +190,7 @@
 #define BH_Lock		2	/* 1 if the buffer is locked */
 #define BH_Req		3	/* 0 if the buffer has been invalidated */
 #define BH_Protected	6	/* 1 if the buffer is protected */
+#define BH_Wait_IO	7	/* 1 if we should throttle on this buffer */
 
 /*
  * Try to keep the most commonly used fields in single cache lines (16
@@ -777,7 +783,7 @@
 
 extern void refile_buffer(struct buffer_head * buf);
 extern void set_writetime(struct buffer_head * buf, int flag);
-extern int try_to_free_buffers(struct page *, int wait);
+extern int try_to_free_buffers(struct page *, int);
 
 extern int nr_buffers;
 extern long buffermem;
@@ -786,15 +792,25 @@
 #define BUF_CLEAN	0
 #define BUF_LOCKED	1	/* Buffers scheduled for write */
 #define BUF_DIRTY	2	/* Dirty buffers, not yet scheduled for write */
-#define NR_LIST		3
+#define BUF_PROTECTED	3	/* Ramdisk persistent storage */
+#define NR_LIST		4
 
 void mark_buffer_uptodate(struct buffer_head * bh, int on);
 
+extern inline void mark_buffer_protected(struct buffer_head * bh)
+{
+	if (!test_and_set_bit(BH_Protected, &bh->b_state)) {
+		if (bh->b_list != BUF_PROTECTED)
+			refile_buffer(bh);
+	}
+}
+
 extern inline void mark_buffer_clean(struct buffer_head * bh)
 {
 	if (test_and_clear_bit(BH_Dirty, &bh->b_state)) {
 		if (bh->b_list == BUF_DIRTY)
 			refile_buffer(bh);
+		clear_bit(BH_Wait_IO, &bh->b_state);
 	}
 }
 
@@ -935,6 +951,9 @@
 extern void inode_setattr(struct inode *, struct iattr *);
 
 extern __u32 inode_generation_count;
+
+#define fs_down(sem)	do { current->fs_locks++; down(sem); } while (0)
+#define fs_up(sem)	do { up(sem); current->fs_locks--; } while (0)
 
 #endif /* __KERNEL__ */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/interrupt.h linux.19p6/include/linux/interrupt.h
--- linux.vanilla/include/linux/interrupt.h	Fri Apr 21 23:10:16 2000
+++ linux.19p6/include/linux/interrupt.h	Thu Dec 21 22:50:30 2000
@@ -5,6 +5,7 @@
 #include <linux/kernel.h>
 #include <asm/bitops.h>
 #include <asm/atomic.h>
+#include <asm/ptrace.h>
 
 struct irqaction {
 	void (*handler)(int, void *, struct pt_regs *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdn.h linux.19p6/include/linux/isdn.h
--- linux.vanilla/include/linux/isdn.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/isdn.h	Wed Jan  3 14:14:04 2001
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.107 2000/09/10 20:29:18 detabc Exp $
+/* $Id: isdn.h,v 1.111 2000/11/25 17:01:02 kai 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.19p6/include/linux/isdn_ppp.h
--- linux.vanilla/include/linux/isdn_ppp.h	Sun Jun 11 21:44:23 2000
+++ linux.19p6/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.19p6/include/linux/isdnif.h
--- linux.vanilla/include/linux/isdnif.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/isdnif.h	Wed Jan  3 14:14:04 2001
@@ -1,4 +1,4 @@
-/* $Id: isdnif.h,v 1.35 2000/06/16 13:19:38 keil Exp $
+/* $Id: isdnif.h,v 1.37 2000/11/19 17:01:54 kai Exp $
 
  * Linux ISDN subsystem
  *
@@ -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.19p6/include/linux/iso_fs.h
--- linux.vanilla/include/linux/iso_fs.h	Fri Apr 21 23:11:46 2000
+++ linux.19p6/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.19p6/include/linux/kernelcapi.h
--- linux.vanilla/include/linux/kernelcapi.h	Fri Apr 21 12:46:59 2000
+++ linux.19p6/include/linux/kernelcapi.h	Sat Dec 30 19:34:32 2000
@@ -1,58 +1,17 @@
 /*
- * $Id: kernelcapi.h,v 1.5 2000/01/28 16:45:40 calle Exp $
+ * $Id: kernelcapi.h,v 1.8.6.1 2000/11/28 09:36:56 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/locks.h linux.19p6/include/linux/locks.h
--- linux.vanilla/include/linux/locks.h	Fri Apr 21 23:10:16 2000
+++ linux.19p6/include/linux/locks.h	Wed Jan  3 13:57:43 2001
@@ -50,10 +50,12 @@
 	if (sb->s_lock)
 		__wait_on_super(sb);
 	sb->s_lock = 1;
+	current->fs_locks++;
 }
 
 extern inline void unlock_super(struct super_block * sb)
 {
+	current->fs_locks--;
 	sb->s_lock = 0;
 	wake_up(&sb->s_wait);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/major.h linux.19p6/include/linux/major.h
--- linux.vanilla/include/linux/major.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/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.19p6/include/linux/miscdevice.h
--- linux.vanilla/include/linux/miscdevice.h	Fri Apr 21 12:46:59 2000
+++ linux.19p6/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/mtio.h linux.19p6/include/linux/mtio.h
--- linux.vanilla/include/linux/mtio.h	Fri Apr 21 23:10:54 2000
+++ linux.19p6/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/nfs.h linux.19p6/include/linux/nfs.h
--- linux.vanilla/include/linux/nfs.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/nfs.h	Wed Jan  3 00:58:38 2001
@@ -94,7 +94,7 @@
  */
 #define NFS_MAXFHSIZE		64
 struct nfs_fh {
-	unsigned short		size;
+	unsigned int		size;
 	unsigned char		data[NFS_MAXFHSIZE];
 };
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nls.h linux.19p6/include/linux/nls.h
--- linux.vanilla/include/linux/nls.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/nls.h	Wed Jan  3 01:05:05 2001
@@ -60,3 +60,4 @@
 extern int init_nls_cp949(void);
 extern int init_nls_cp950(void);
 extern int init_nls_koi8_r(void);
+extern int init_nls_koi8_ru(void);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/pci.h linux.19p6/include/linux/pci.h
--- linux.vanilla/include/linux/pci.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/pci.h	Sat Dec 30 19:30:07 2000
@@ -307,6 +307,15 @@
  * (and according to card ID within vendor). Send all updates to
  * <linux-pcisupport@cck.uni-kl.de>.
  */
+#define PCI_VENDOR_ID_DYNALINK          0x0675
+#define PCI_DEVICE_ID_DYNALINK_IS64PH   0x1702
+
+#define PCI_VENDOR_ID_BERKOM		        0x0871
+#define PCI_DEVICE_ID_BERKOM_A1T	        0xFFA1
+#define PCI_DEVICE_ID_BERKOM_T_CONCEPT          0xFFA2
+#define PCI_DEVICE_ID_BERKOM_A4T	        0xFFA4
+#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO      0xFFA8
+
 #define PCI_VENDOR_ID_COMPAQ		0x0e11
 #define PCI_DEVICE_ID_COMPAQ_TOKENRING	0x0508
 #define PCI_DEVICE_ID_COMPAQ_1280	0x3033
@@ -477,6 +486,10 @@
 #define PCI_DEVICE_ID_CT_65554		0x00e4
 #define PCI_DEVICE_ID_CT_65555		0x00e5
 
+#define PCI_DEVICE_ID_PLX_R685		0x1030
+#define PCI_DEVICE_ID_PLX_DJINN_ITOO    0x1151
+#define PCI_DEVICE_ID_PLX_R753          0x1152
+
 #define PCI_VENDOR_ID_MIRO		0x1031
 #define PCI_DEVICE_ID_MIRO_36050	0x5601
 
@@ -515,6 +528,9 @@
 #define PCI_DEVICE_ID_PCTECH_SAMURAI_1	0x3010
 #define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020
 
+#define PCI_VENDOR_ID_ASUSTEK		0x1043
+#define PCI_DEVICE_ID_ASUSTEK_0675	0x0675
+
 #define PCI_VENDOR_ID_DPT               0x1044   
 #define PCI_DEVICE_ID_DPT               0xa400  
 
@@ -530,6 +546,10 @@
 #define PCI_DEVICE_ID_OPTI_82C861	0xc861
 #define PCI_DEVICE_ID_OPTI_82C825	0xd568
 
+#define PCI_VENDOR_ID_ELSA		0x1048
+#define PCI_DEVICE_ID_ELSA_MICROLINK	0x1000
+#define PCI_DEVICE_ID_ELSA_QS3000	0x3000
+
 #define PCI_VENDOR_ID_SGS		0x104a
 #define PCI_DEVICE_ID_SGS_2000		0x0008
 #define PCI_DEVICE_ID_SGS_1764		0x0009
@@ -554,6 +574,10 @@
 /* Winbond have two vendor IDs! See 0x10ad as well */
 #define PCI_VENDOR_ID_WINBOND2		0x1050
 #define PCI_DEVICE_ID_WINBOND2_89C940	0x0940
+#define PCI_DEVICE_ID_WINBOND2_6692	0x6692
+
+#define PCI_VENDOR_ID_ANIGMA		0x1051
+#define PCI_DEVICE_ID_ANIGMA_MC145575	0x0100
 
 #define PCI_VENDOR_ID_MOTOROLA		0x1057
 #define PCI_VENDOR_ID_MOTOROLA_OOPS	0x1507
@@ -873,6 +897,17 @@
 #define PCI_DEVICE_ID_PHILIPS_SAA7145	0x7145
 #define PCI_DEVICE_ID_PHILIPS_SAA7146	0x7146
 
+#define PCI_VENDOR_ID_EICON		0x1133
+#define PCI_DEVICE_ID_EICON_DIVA20PRO	0xe001
+#define PCI_DEVICE_ID_EICON_DIVA20	0xe002
+#define PCI_DEVICE_ID_EICON_DIVA20PRO_U	0xe003
+#define PCI_DEVICE_ID_EICON_DIVA20_U	0xe004
+#define PCI_DEVICE_ID_EICON_DIVA201	0xe005
+#define PCI_DEVICE_ID_EICON_MAESTRA	0xe010
+#define PCI_DEVICE_ID_EICON_MAESTRAQ	0xe012
+#define PCI_DEVICE_ID_EICON_MAESTRAQ_U	0xe013
+#define PCI_DEVICE_ID_EICON_MAESTRAP	0xe014
+
 #define PCI_VENDOR_ID_CYCLONE		0x113c
 #define PCI_DEVICE_ID_CYCLONE_SDK	0x0001
 
@@ -899,6 +934,10 @@
 #define PCI_DEVICE_ID_DIGI_XRJ		0x0009
 #define PCI_DEVICE_ID_DIGI_EPCJ		0x000a
 #define PCI_DEVICE_ID_DIGI_XR_920	0x0027
+#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E	0x0070
+#define PCI_DEVICE_ID_DIGI_DF_M_E	0x0071
+#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A	0x0072
+#define PCI_DEVICE_ID_DIGI_DF_M_A	0x0073
 
 #define PCI_VENDOR_ID_MUTECH		0x1159
 #define PCI_DEVICE_ID_MUTECH_MV1000	0x0001
@@ -1023,6 +1062,9 @@
 
 #define PCI_VENDOR_ID_AVM		0x1244
 #define PCI_DEVICE_ID_AVM_A1		0x0a00
+#define PCI_DEVICE_ID_AVM_B1		0x0700
+#define PCI_DEVICE_ID_AVM_C4		0x0800
+#define PCI_DEVICE_ID_AVM_T1		0x1200
 
 #define PCI_VENDOR_ID_DIPIX		0x1246
 
@@ -1039,6 +1081,7 @@
 #define PCI_DEVICE_ID_OPTIBASE_VQUEST	0x2130
 
 #define PCI_VENDOR_ID_SATSAGEM		0x1267
+#define PCI_DEVICE_ID_SATSAGEM_NICCY	0x1016
 #define PCI_DEVICE_ID_SATSAGEM_PCR2101	0x5352
 #define PCI_DEVICE_ID_SATSAGEM_TELSATTURBO 0x5a4b
 
@@ -1082,9 +1125,32 @@
 #define PCI_DEVICE_ID_SIIG_2S1P_20x_650	0x2061
 #define PCI_DEVICE_ID_SIIG_2S1P_20x_850	0x2062
 
+#define PCI_VENDOR_ID_HYPERCOPE		0x1365
+#define PCI_DEVICE_ID_HYPERCOPE_PLX	0x9050
+#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO     0x0104
+#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO         0x0106
+#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO        0x0107
+#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2       0x0108
+#define PCI_SUBDEVICE_ID_HYPERCOPE_PLEXUS       0x0109
+
 #define PCI_VENDOR_ID_NETGEAR		0x1385
 #define PCI_DEVICE_ID_NETGEAR_GA620	0x620a
 
+#define PCI_VENDOR_ID_CCD		0x1397
+#define PCI_DEVICE_ID_CCD_2BD0	        0x2BD0
+#define PCI_DEVICE_ID_CCD_B000	        0xB000
+#define PCI_DEVICE_ID_CCD_B006	        0xB006
+#define PCI_DEVICE_ID_CCD_B007	        0xB007
+#define PCI_DEVICE_ID_CCD_B008	        0xB008
+#define PCI_DEVICE_ID_CCD_B009	        0xB009
+#define PCI_DEVICE_ID_CCD_B00A	        0xB00A
+#define PCI_DEVICE_ID_CCD_B00B	        0xB00B
+#define PCI_DEVICE_ID_CCD_B00C	        0xB00C
+#define PCI_DEVICE_ID_CCD_B100	        0xB100
+
+#define PCI_VENDOR_ID_ABOCOM		0x13D1
+#define PCI_DEVICE_ID_ABOCOM_2BD1       0x2BD1
+
 #define PCI_VENDOR_ID_LAVA		0x1407
 #define PCI_DEVICE_ID_LAVA_PARALLEL	0x8000
 #define PCI_DEVICE_ID_LAVA_DUAL_PAR_A	0x8002 /* The Lava Dual Parallel is */
@@ -1102,6 +1168,9 @@
 #define PCI_VENDOR_ID_AFAVLAB		0x14db
 #define PCI_DEVICE_ID_AFAVLAB_TK9902	0x2120
 
+#define PCI_VENDOR_ID_ZOLTRIX		0x15b0
+#define PCI_DEVICE_ID_ZOLTRIX_2BD0	0x2BD0
+
 #define PCI_VENDOR_ID_SYMPHONY		0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101	0x0001
 
@@ -1258,6 +1327,7 @@
 
 #define PCI_VENDOR_ID_TIGERJET		0xe159
 #define PCI_DEVICE_ID_TIGERJET_300	0x0001
+#define PCI_DEVICE_ID_TIGERJET_100	0x0002
 
 #define PCI_VENDOR_ID_ARK		0xedd8
 #define PCI_DEVICE_ID_ARK_STING		0xa091
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sched.h linux.19p6/include/linux/sched.h
--- linux.vanilla/include/linux/sched.h	Mon Dec 11 22:13:07 2000
+++ linux.19p6/include/linux/sched.h	Wed Jan  3 13:57:43 2001
@@ -79,6 +79,7 @@
 #define TASK_ZOMBIE		4
 #define TASK_STOPPED		8
 #define TASK_SWAPPING		16
+#define TASK_EXCLUSIVE		32
 
 /*
  * Scheduling policies
@@ -180,7 +181,11 @@
 	atomic_t count;
 	int map_count;				/* number of VMAs */
 	struct semaphore mmap_sem;
+#ifdef __alpha__
+	unsigned long context[NR_CPUS];
+#else
 	unsigned long context;
+#endif
 	unsigned long start_code, end_code, start_data, end_data;
 	unsigned long start_brk, brk, start_stack;
 	unsigned long arg_start, arg_end, env_start, env_end;
@@ -196,12 +201,18 @@
 	void * segments;
 };
 
+#ifdef __alpha__
+#define CONTEXT_INIT	{ 0, }
+#else
+#define CONTEXT_INIT	0
+#endif
+
 #define INIT_MM {					\
 		&init_mmap, NULL, NULL,			\
 		swapper_pg_dir, 			\
 		ATOMIC_INIT(1), 1,			\
 		MUTEX,					\
-		0,					\
+		CONTEXT_INIT,				\
 		0, 0, 0, 0,				\
 		0, 0, 0, 				\
 		0, 0, 0, 0,				\
@@ -251,13 +262,14 @@
 	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;
 	int pdeath_signal;  /*  The signal sent when the parent dies  */
 	/* ??? */
 	unsigned long personality;
-	int dumpable:1;
+	unsigned int dumpable:2;
 	int did_exec:1;
 	pid_t pid;
 	pid_t pgrp;
@@ -317,6 +329,9 @@
 	struct files_struct *files;
 /* memory management info */
 	struct mm_struct *mm;
+	struct list_head local_pages;
+	int allocation_order, nr_local_pages;
+	int fs_locks;
 
 /* signal handlers */
 	spinlock_t sigmask_lock;	/* Protects signal and blocked */
@@ -349,6 +364,7 @@
 #define PF_SIGNALED	0x00000400	/* killed by a signal */
 #define PF_MEMALLOC	0x00000800	/* Allocating memory */
 #define PF_VFORK	0x00001000	/* Wake up parent in mm_release */
+#define PF_FREE_PAGES	0x00002000	/* The current-> */
 
 #define PF_USEDFPU	0x00100000	/* task used FPU this quantum (SMP) */
 #define PF_DTRACE	0x00200000	/* delayed trace (used on m68k, i386) */
@@ -370,6 +386,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, \
@@ -397,7 +414,7 @@
 /* tss */	INIT_TSS, \
 /* fs */	&init_fs, \
 /* files */	&init_files, \
-/* mm */	&init_mm, \
+/* mm */	&init_mm, { &init_task.local_pages, &init_task.local_pages}, 0, 0, 0, \
 /* signals */	SPIN_LOCK_UNLOCKED, &init_signals, {{0}}, {{0}}, NULL, &init_task.sigqueue, 0, 0, \
 /* exec cts */	0,0, \
 /* oom */	0, \
@@ -496,8 +513,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/linux/synclink.h linux.19p6/include/linux/synclink.h
--- linux.vanilla/include/linux/synclink.h	Fri Apr 21 12:47:01 2000
+++ linux.19p6/include/linux/synclink.h	Tue Dec 19 02:09:27 2000
@@ -1,9 +1,9 @@
 /*
  * SyncLink Multiprotocol Serial Adapter Driver
  *
- * ==FILEDATE 19990810==
+ * $Id: synclink.h,v 2.2 2000/11/08 17:08:30 paul Exp $
  *
- * Copyright (C) 1998 by Microgate Corporation
+ * Copyright (C) 1998-2000 by Microgate Corporation
  * 
  * Redistribution of this file is permitted under 
  * the terms of the GNU Public License (GPL)
@@ -11,6 +11,7 @@
 
 #ifndef _SYNCLINK_H_
 #define _SYNCLINK_H_
+#define SYNCLINK_H_VERSION 2.2
 
 #define BOOLEAN int
 #define TRUE 1
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/usb.h linux.19p6/include/linux/usb.h
--- linux.vanilla/include/linux/usb.h	Mon Dec 11 22:13:08 2000
+++ linux.19p6/include/linux/usb.h	Wed Jan  3 14:04:20 2001
@@ -29,6 +29,7 @@
 /*
  * USB recipients
  */
+#define USB_RECIP_MASK			0x1f
 #define USB_RECIP_DEVICE		0x00
 #define USB_RECIP_INTERFACE		0x01
 #define USB_RECIP_ENDPOINT		0x02
@@ -554,6 +555,7 @@
 };
 
 extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum);
+extern struct usb_endpoint_descriptor *usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum);
 
 extern int usb_register(struct usb_driver *);
 extern void usb_deregister(struct usb_driver *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/usbdevice_fs.h linux.19p6/include/linux/usbdevice_fs.h
--- linux.vanilla/include/linux/usbdevice_fs.h	Mon Dec 11 22:13:08 2000
+++ linux.19p6/include/linux/usbdevice_fs.h	Sat Dec 30 20:00:54 2000
@@ -80,6 +80,7 @@
 
 #define USBDEVFS_URB_DISABLE_SPD           1
 #define USBDEVFS_URB_ISO_ASAP              2
+#define USBDEVFS_URB_QUEUE_BULK            0x10
 
 #define USBDEVFS_URB_TYPE_ISO		   0
 #define USBDEVFS_URB_TYPE_INTERRUPT	   1
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/watchdog.h linux.19p6/include/linux/watchdog.h
--- linux.vanilla/include/linux/watchdog.h	Fri Apr 21 12:47:01 2000
+++ linux.19p6/include/linux/watchdog.h	Sat Dec 30 19:24:34 2000
@@ -6,14 +6,17 @@
  *
  */
 
+#ifndef _LINUX_WATCHDOG_H
+#define _LINUX_WATCHDOG_H
+
 #include <linux/ioctl.h>
 
 #define	WATCHDOG_IOCTL_BASE	'W'
 
 struct watchdog_info {
-	u32 options;		/* Options the card/driver supports */
-	u32 firmware_version;	/* Firmware version of the card */
-	u8  identity[32];	/* Identity of the board */
+	__u32 options;		/* Options the card/driver supports */
+	__u32 firmware_version;	/* Firmware version of the card */
+	__u8  identity[32];	/* Identity of the board */
 };
 
 #define	WDIOC_GETSUPPORT	_IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
@@ -38,3 +41,5 @@
 #define	WDIOS_DISABLECARD	0x0001	/* Turn off the watchdog timer */
 #define	WDIOS_ENABLECARD	0x0002	/* Turn on the watchdog timer */
 #define	WDIOS_TEMPPANIC		0x0004	/* Kernel panic on temperature trip */
+
+#endif  /* ifndef _LINUX_WATCHDOG_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/wireless.h linux.19p6/include/linux/wireless.h
--- linux.vanilla/include/linux/wireless.h	Fri Apr 21 23:11:57 2000
+++ linux.19p6/include/linux/wireless.h	Thu Dec 21 22:59:22 2000
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	9
+#define WIRELESS_EXT	10
 
 /*
  * Changes :
@@ -104,26 +104,33 @@
  *	- Change encoding to support larger tokens (>64 bits)
  *	- Updated iw_params (disable, flags) and use it for NWID
  *	- Extracted iw_point from iwreq for clarity
+ *
+ * V9 to V10
+ * ---------
+ *	- Add PM capability to range structure
+ *	- Add PM modifier : MAX/MIN/RELATIVE
+ *	- Add encoding option : IW_ENCODE_NOKEY
+ *	- Add TxPower ioctls (work like TxRate)
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
 
 /* Basic operations */
-#define SIOCSIWNAME	0x8B00		/* Unused ??? */
-#define SIOCGIWNAME	0x8B01		/* get name */
+#define SIOCSIWNAME	0x8B00		/* Unused */
+#define SIOCGIWNAME	0x8B01		/* get name == wireless protocol */
 #define SIOCSIWNWID	0x8B02		/* set network id (the cell) */
 #define SIOCGIWNWID	0x8B03		/* get network id */
-#define SIOCSIWFREQ	0x8B04		/* set channel/frequency */
-#define SIOCGIWFREQ	0x8B05		/* get channel/frequency */
+#define SIOCSIWFREQ	0x8B04		/* set channel/frequency (Hz) */
+#define SIOCGIWFREQ	0x8B05		/* get channel/frequency (Hz) */
 #define SIOCSIWMODE	0x8B06		/* set operation mode */
 #define SIOCGIWMODE	0x8B07		/* get operation mode */
-#define SIOCSIWSENS	0x8B08		/* set sensitivity */
-#define SIOCGIWSENS	0x8B09		/* get sensitivity */
+#define SIOCSIWSENS	0x8B08		/* set sensitivity (dBm) */
+#define SIOCGIWSENS	0x8B09		/* get sensitivity (dBm) */
 
 /* Informative stuff */
-#define SIOCSIWRANGE	0x8B0A		/* Unused ??? */
+#define SIOCSIWRANGE	0x8B0A		/* Unused */
 #define SIOCGIWRANGE	0x8B0B		/* Get range of parameters */
-#define SIOCSIWPRIV	0x8B0C		/* Unused ??? */
+#define SIOCSIWPRIV	0x8B0C		/* Unused */
 #define SIOCGIWPRIV	0x8B0D		/* get private ioctl interface info */
 
 /* Mobile IP support */
@@ -153,6 +160,8 @@
 #define SIOCGIWRTS	0x8B23		/* get RTS/CTS threshold (bytes) */
 #define SIOCSIWFRAG	0x8B24		/* set fragmentation thr (bytes) */
 #define SIOCGIWFRAG	0x8B25		/* get fragmentation thr (bytes) */
+#define SIOCSIWTXPOW	0x8B26		/* set transmit power (dBm) */
+#define SIOCGIWTXPOW	0x8B27		/* get transmit power (dBm) */
 
 /* Encoding stuff (scrambling, hardware security, WEP...) */
 #define SIOCSIWENCODE	0x8B2A		/* set encoding token & mode */
@@ -205,6 +214,9 @@
 /* Maximum bit rates in the range struct */
 #define IW_MAX_BITRATES		8
 
+/* Maximum tx powers in the range struct */
+#define IW_MAX_TXPOWER		8
+
 /* Maximum of address that you may set with SPY */
 #define IW_MAX_SPY		8
 
@@ -232,11 +244,13 @@
 
 /* Flags for encoding (along with the token) */
 #define IW_ENCODE_INDEX		0x00FF	/* Token index (if needed) */
-#define IW_ENCODE_FLAGS		0xF000	/* Flags defined below */
+#define IW_ENCODE_FLAGS		0xFF00	/* Flags defined below */
+#define IW_ENCODE_MODE		0xF000	/* Modes defined below */
 #define IW_ENCODE_DISABLED	0x8000	/* Encoding disabled */
 #define IW_ENCODE_ENABLED	0x0000	/* Encoding enabled */
 #define IW_ENCODE_RESTRICTED	0x4000	/* Refuse non-encoded packets */
 #define IW_ENCODE_OPEN		0x2000	/* Accept non-encoded packets */
+#define IW_ENCODE_NOKEY         0x0800  /* Key is write only, so not present */
 
 /* Power management flags available (along with the value, if any) */
 #define IW_POWER_ON		0x0000	/* No details... */
@@ -249,6 +263,14 @@
 #define IW_POWER_ALL_R		0x0300	/* Receive all messages though PM */
 #define IW_POWER_FORCE_S	0x0400	/* Force PM procedure for sending unicast */
 #define IW_POWER_REPEATER	0x0800	/* Repeat broadcast messages in PM period */
+#define IW_POWER_MODIFIER	0x000F	/* Modify a parameter */
+#define IW_POWER_MIN		0x0001	/* Value is a minimum  */
+#define IW_POWER_MAX		0x0002	/* Value is a maximum */
+#define IW_POWER_RELATIVE	0x0004	/* Value is not in seconds/ms/us */
+
+/* Transmit Power flags available */
+#define IW_TXPOW_DBM		0x0000	/* Value is in dBm */
+#define IW_TXPOW_MWATT		0x0001	/* Value is in mW */
 
 /****************************** TYPES ******************************/
 
@@ -359,6 +381,7 @@
 
 		struct iw_param	sens;		/* signal level threshold */
 		struct iw_param	bitrate;	/* default bit rate */
+		struct iw_param	txpower;	/* default transmit power */
 		struct iw_param	rts;		/* RTS threshold threshold */
 		struct iw_param	frag;		/* Fragmentation threshold */
 		__u32		mode;		/* Operation mode */
@@ -422,15 +445,23 @@
 	__s32		max_frag;	/* Maximal frag threshold */
 
 	/* Power Management duration & timeout */
-	__s32		min_pmd;	/* Minimal PM duration */
-	__s32		max_pmd;	/* Maximal PM duration */
+	__s32		min_pmp;	/* Minimal PM period */
+	__s32		max_pmp;	/* Maximal PM period */
 	__s32		min_pmt;	/* Minimal PM timeout */
 	__s32		max_pmt;	/* Maximal PM timeout */
+	__u16		pmp_flags;	/* How to decode max/min PM period */
+	__u16		pmt_flags;	/* How to decode max/min PM timeout */
+	__u16		pm_capa;	/* What PM options are supported */
 
 	/* Encoder stuff */
 	__u16	encoding_size[IW_MAX_ENCODING_SIZES];	/* Different token sizes */
 	__u8	num_encoding_sizes;	/* Number of entry in the list */
 	__u8	max_encoding_tokens;	/* Max number of tokens */
+
+	/* Transmit power */
+	__u16		txpower_capa;	/* What options are supported */
+	__u8		num_txpower;	/* Number of entries in the list */
+	__s32		txpower[IW_MAX_TXPOWER];	/* list, in bps */
 };
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/ip_masq.h linux.19p6/include/net/ip_masq.h
--- linux.vanilla/include/net/ip_masq.h	Fri Apr 21 23:12:18 2000
+++ linux.19p6/include/net/ip_masq.h	Wed Jan  3 14:00:53 2001
@@ -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.19p6/init/main.c
--- linux.vanilla/init/main.c	Mon Dec 11 22:13:08 2000
+++ linux.19p6/init/main.c	Sat Dec 16 17:21:48 2000
@@ -80,7 +80,6 @@
 extern int bdflush(void *);
 extern int kupdate(void *);
 extern int kswapd(void *);
-extern int kpiod(void *);
 extern void kswapd_setup(void);
 extern unsigned long init_IRQ( unsigned long);
 extern void init_modules(void);
@@ -216,6 +215,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);
@@ -841,6 +841,9 @@
 #ifdef CONFIG_CHR_DEV_ST
 	{ "st=", st_setup },
 #endif
+#ifdef CONFIG_CHR_DEV_OSST
+	{ "osst=", osst_setup },
+#endif
 #ifdef CONFIG_BUSMOUSE
 	{ "bmouse=", bmouse_setup },
 #endif
@@ -1580,7 +1583,6 @@
 	kernel_thread(kupdate, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 	/* Start the background pageout daemon. */
 	kswapd_setup();
-	kernel_thread(kpiod, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 	kernel_thread(kswapd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 
 #if CONFIG_AP1000
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/ipc/shm.c linux.19p6/ipc/shm.c
--- linux.vanilla/ipc/shm.c	Sun Jun 11 21:44:23 2000
+++ linux.19p6/ipc/shm.c	Thu Dec 21 22:56:15 2000
@@ -340,6 +340,8 @@
 			shp->u.shm_perm.mode |= SHM_DEST;
 			if (shp->u.shm_nattch <= 0)
 				killseg (id);
+			/* Do not find it any more */
+			shp->u.shm_perm.key = IPC_PRIVATE;
 			break;
 		}
 		err = -EPERM;
@@ -679,7 +681,7 @@
 }
 
 /*
- * Goes through counter = (shm_rss >> prio) present shm pages.
+ * Goes through counter = (shm_rss / prio) present shm pages.
  */
 static unsigned long swap_id = 0; /* currently being swapped */
 static unsigned long swap_idx = 0; /* next to swap */
@@ -693,7 +695,7 @@
 	int loop = 0;
 	int counter;
 	
-	counter = shm_rss >> prio;
+	counter = shm_rss / prio;
 	if (!counter || !(swap_nr = get_swap_page()))
 		return 0;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/fork.c linux.19p6/kernel/fork.c
--- linux.vanilla/kernel/fork.c	Fri Apr 21 12:47:02 2000
+++ linux.19p6/kernel/fork.c	Sat Dec 16 17:21:48 2000
@@ -665,6 +665,8 @@
 	p->lock_depth = -1;		/* -1 = no lock */
 	p->start_time = jiffies;
 
+	INIT_LIST_HEAD(&p->local_pages);
+
 	retval = -ENOMEM;
 	/* copy all the process information */
 	if (copy_files(clone_flags, p))
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/sched.c linux.19p6/kernel/sched.c
--- linux.vanilla/kernel/sched.c	Mon Dec 11 22:13:08 2000
+++ linux.19p6/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.19p6/kernel/sysctl.c
--- linux.vanilla/kernel/sysctl.c	Mon Dec 11 22:13:08 2000
+++ linux.19p6/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.19p6/mm/filemap.c
--- linux.vanilla/mm/filemap.c	Mon Dec 11 22:13:08 2000
+++ linux.19p6/mm/filemap.c	Sat Dec 16 17:21:48 2000
@@ -19,7 +19,6 @@
 #include <linux/blkdev.h>
 #include <linux/file.h>
 #include <linux/swapctl.h>
-#include <linux/slab.h>
 #include <linux/init.h>
 
 #include <asm/pgtable.h>
@@ -36,25 +35,6 @@
 unsigned int page_hash_bits, page_hash_mask;
 struct page **page_hash_table;
 
-/* 
- * Define a request structure for outstanding page write requests
- * to the background page io daemon
- */
-
-struct pio_request 
-{
-	struct pio_request *	next;
-	struct file *		file;
-	unsigned long		offset;
-	unsigned long		page;
-};
-static struct pio_request *pio_first = NULL, **pio_last = &pio_first;
-static kmem_cache_t *pio_request_cache;
-static struct wait_queue *pio_wait = NULL;
-
-static inline void 
-make_pio_request(struct file *, unsigned long, unsigned long);
-
 static inline int sync_page(struct page *page)
 {
 	struct inode *inode = page->inode;
@@ -150,16 +130,21 @@
 	unsigned long limit = num_physpages;
 	struct page * page;
 	int count;
-	int nr_dirty = 0;
-	
+
 	/* Make sure we scan all pages twice at priority 0. */
-	count = (limit << 1) >> priority;
+	count = limit / priority;
 
  refresh_clock:
 	page = mem_map + clock;
 	do {
 		int referenced;
 
+		if (current->need_resched) {
+			current->state = TASK_RUNNING;
+			schedule();
+			goto refresh_clock;
+		}
+		
 		/* 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,6 +161,8 @@
 			clock = page - mem_map;
 		}
 		
+		count--;
+
 		/* We can't free pages unless there's just one user */
 		if (atomic_read(&page->count) != 1)
 			continue;
@@ -185,8 +172,10 @@
 		if (PageLocked(page))
 			continue;
 
-		if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
+		if ((gfp_mask & __GFP_DMA) && !PageDMA(page)) {
+			count++;
 			continue;
+		}
 
 		/*
 		 * Is it a page swap page? If so, we want to
@@ -205,14 +194,6 @@
 
 		/* Is it a buffer page? */
 		if (page->buffers) {
-			/*
-			 * Wait for async IO to complete
-			 * at each 64 buffers
-			 */ 
-
-			int wait = ((gfp_mask & __GFP_IO) 
-				&& (!(nr_dirty++ % 64)));
-
 			if (buffer_under_min())
 				continue;
 			/*
@@ -220,10 +201,8 @@
 			 * throttling.
 			 */
 
-			if (!try_to_free_buffers(page, wait)) { 
-				if(--count < 0) break;
+			if (!try_to_free_buffers(page, gfp_mask))
 				goto refresh_clock;
-			}
 			return 1;
 		}
 
@@ -234,8 +213,7 @@
 			remove_inode_page(page);
 			return 1;
 		}
-
-	} while (--count > 0);
+	} while (count > 0);
 	return 0;
 }
 
@@ -302,7 +280,7 @@
 	struct page **hash)
 {
 	atomic_inc(&page->count);
-	page->flags = (page->flags & ~((1 << PG_uptodate) | (1 << PG_error))) | (1 << PG_referenced);
+	page->flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
 	page->offset = offset;
 	add_page_to_inode_queue(inode, page);
 	__add_page_to_hash_queue(page, hash);
@@ -881,12 +859,12 @@
 
 	if (size > count)
 		size = count;
-	down(&inode->i_sem);
+	fs_down(&inode->i_sem);
 	old_fs = get_fs();
 	set_fs(KERNEL_DS);
 	written = file->f_op->write(file, area, size, &file->f_pos);
 	set_fs(old_fs);
-	up(&inode->i_sem);
+	fs_up(&inode->i_sem);
 	if (written < 0) {
 		desc->error = written;
 		written = 0;
@@ -1163,8 +1141,7 @@
 
 static int filemap_write_page(struct vm_area_struct * vma,
 			      unsigned long offset,
-			      unsigned long page,
-			      int wait)
+			      unsigned long page)
 {
 	int result;
 	struct file * file;
@@ -1182,20 +1159,9 @@
 	 * and file could be released ... increment the count to be safe.
 	 */
 	file->f_count++;
-
-	/* 
-	 * If this is a swapping operation rather than msync(), then
-	 * leave the actual IO, and the restoration of the file count,
-	 * to the kpiod thread.  Just queue the request for now.
-	 */
-	if (!wait) {
-		make_pio_request(file, offset, page);
-		return 0;
-	}
-	
-	down(&inode->i_sem);
+	fs_down(&inode->i_sem);
 	result = do_write_page(inode, file, (const char *) page, offset);
-	up(&inode->i_sem);
+	fs_up(&inode->i_sem);
 	fput(file);
 	return result;
 }
@@ -1208,7 +1174,7 @@
  */
 int filemap_swapout(struct vm_area_struct * vma, struct page * page)
 {
-	return filemap_write_page(vma, page->offset, page_address(page), 0);
+	return filemap_write_page(vma, page->offset, page_address(page));
 }
 
 static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
@@ -1245,7 +1211,7 @@
 			return 0;
 		}
 	}
-	error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, page, 1);
+	error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, page);
 	page_cache_free(page);
 	return error;
 }
@@ -1417,9 +1383,9 @@
 			if (file) {
 				struct dentry * dentry = file->f_dentry;
 				struct inode * inode = dentry->d_inode;
-				down(&inode->i_sem);
+				fs_down(&inode->i_sem);
 				error = file_fsync(file, dentry);
-				up(&inode->i_sem);
+				fs_up(&inode->i_sem);
 			}
 		}
 		return error;
@@ -1746,130 +1712,6 @@
 	clear_bit(PG_locked, &page->flags);
 	wake_up(&page->wait);
 	page_cache_release(page);
-}
-
-
-/* Add request for page IO to the queue */
-
-static inline void put_pio_request(struct pio_request *p)
-{
-	*pio_last = p;
-	p->next = NULL;
-	pio_last = &p->next;
-}
-
-/* Take the first page IO request off the queue */
-
-static inline struct pio_request * get_pio_request(void)
-{
-	struct pio_request * p = pio_first;
-	pio_first = p->next;
-	if (!pio_first)
-		pio_last = &pio_first;
-	return p;
-}
-
-/* Make a new page IO request and queue it to the kpiod thread */
-
-static inline void make_pio_request(struct file *file,
-				    unsigned long offset,
-				    unsigned long page)
-{
-	struct pio_request *p;
-
-	atomic_inc(&page_cache_entry(page)->count);
-
-	/* 
-	 * We need to allocate without causing any recursive IO in the
-	 * current thread's context.  We might currently be swapping out
-	 * as a result of an allocation made while holding a critical
-	 * filesystem lock.  To avoid deadlock, we *MUST* not reenter
-	 * the filesystem in this thread.
-	 *
-	 * We can wait for kswapd to free memory, or we can try to free
-	 * pages without actually performing further IO, without fear of
-	 * deadlock.  --sct
-	 */
-
-	while ((p = kmem_cache_alloc(pio_request_cache, GFP_BUFFER)) == NULL) {
-		if (try_to_free_pages(__GFP_WAIT))
-			continue;
-		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(HZ/10);
-	}
-	
-	p->file   = file;
-	p->offset = offset;
-	p->page   = page;
-
-	put_pio_request(p);
-	wake_up(&pio_wait);
-}
-
-
-/*
- * This is the only thread which is allowed to write out filemap pages
- * while swapping.
- * 
- * To avoid deadlock, it is important that we never reenter this thread.
- * Although recursive memory allocations within this thread may result
- * in more page swapping, that swapping will always be done by queuing
- * another IO request to the same thread: we will never actually start
- * that IO request until we have finished with the current one, and so
- * we will not deadlock.  
- */
-
-int kpiod(void * unused)
-{
-	struct task_struct *tsk = current;
-	struct wait_queue wait = { tsk, };
-	struct inode * inode;
-	struct dentry * dentry;
-	struct pio_request * p;
-	
-	tsk->session = 1;
-	tsk->pgrp = 1;
-	strcpy(tsk->comm, "kpiod");
-	sigfillset(&tsk->blocked);
-	init_waitqueue(&pio_wait);
-	/*
-	 * Mark this task as a memory allocator - we don't want to get caught
-	 * up in the regular mm freeing frenzy if we have to allocate memory
-	 * in order to write stuff out.
-	 */
-	tsk->flags |= PF_MEMALLOC;
-
-	lock_kernel();
-	
-	pio_request_cache = kmem_cache_create("pio_request", 
-					      sizeof(struct pio_request),
-					      0, SLAB_HWCACHE_ALIGN, 
-					      NULL, NULL);
-	if (!pio_request_cache)
-		panic ("Could not create pio_request slab cache");
-
-	while (1) {
-		tsk->state = TASK_INTERRUPTIBLE;
-		add_wait_queue(&pio_wait, &wait);
-		if (!pio_first)
-			schedule();
-		remove_wait_queue(&pio_wait, &wait);
-		tsk->state = TASK_RUNNING;
-
-		while (pio_first) {
-			p = get_pio_request();
-			dentry = p->file->f_dentry;
-			inode = dentry->d_inode;
-			
-			down(&inode->i_sem);
-			do_write_page(inode, p->file,
-				      (const char *) p->page, p->offset);
-			up(&inode->i_sem);
-			fput(p->file);
-			page_cache_free(p->page);
-			kmem_cache_free(pio_request_cache, p);
-		}
-	}
 }
 
 void __init page_cache_init(unsigned long memory_size)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/page_alloc.c linux.19p6/mm/page_alloc.c
--- linux.vanilla/mm/page_alloc.c	Sat Sep  9 18:42:51 2000
+++ linux.19p6/mm/page_alloc.c	Sat Dec 16 17:21:48 2000
@@ -93,34 +93,69 @@
  */
 spinlock_t page_alloc_lock = SPIN_LOCK_UNLOCKED;
 
+#define list(x) (mem_map+(x))
+#define __free_pages_ok(map_nr, mask, area, index)		\
+	nr_free_pages -= (mask);				\
+	while ((mask) + (1 << (NR_MEM_LISTS-1))) {		\
+		if (!test_and_change_bit((index), (area)->map))	\
+			break;					\
+		(area)->count--;				\
+		remove_mem_queue(list((map_nr) ^ -(mask)));	\
+		(mask) <<= 1;					\
+		(area)++;					\
+		(index) >>= 1;					\
+		(map_nr) &= (mask);				\
+	}							\
+	add_mem_queue(area, list(map_nr));
+
+static void free_local_pages(struct page * page) {
+	unsigned long order = page->offset;
+	unsigned int type = PageDMA(page) ? 1 : 0;
+	struct free_area_struct *area;
+	unsigned long map_nr = page - mem_map;
+	unsigned long mask = (~0UL) << order;
+	unsigned long index = map_nr >> (1 + order);
+
+	area = free_area[type] + order;
+	__free_pages_ok(map_nr, mask, area, index);
+}
+
 static inline void free_pages_ok(unsigned long map_nr, unsigned long order, unsigned type)
 {
-	struct free_area_struct *area = free_area[type] + order;
-	unsigned long index = map_nr >> (1 + order);
-	unsigned long mask = (~0UL) << order;
+	struct free_area_struct *area;
+	unsigned long index;
+	unsigned long mask;
 	unsigned long flags;
+	struct page * page;
 
-	spin_lock_irqsave(&page_alloc_lock, flags);
-
-#define list(x) (mem_map+(x))
+	if (current->flags & PF_FREE_PAGES)
+		goto local_freelist;
+ back_local_freelist:
 
+	index = map_nr >> (1 + order);
+	mask = (~0UL) << order;
 	map_nr &= mask;
-	nr_free_pages -= mask;
-	while (mask + (1 << (NR_MEM_LISTS-1))) {
-		if (!test_and_change_bit(index, area->map))
-			break;
-		area->count--;
-		remove_mem_queue(list(map_nr ^ -mask));
-		mask <<= 1;
-		area++;
-		index >>= 1;
-		map_nr &= mask;
-	}
-	add_mem_queue(area, list(map_nr));
-
-#undef list
 
+	spin_lock_irqsave(&page_alloc_lock, flags);
+	area = free_area[type] + order;
+	__free_pages_ok(map_nr, mask, area, index);
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
+	return;
+
+ local_freelist:
+	/*
+	 * This is a little subtle: if the allocation order
+	 * wanted is major than zero we'd better take all the pages
+	 * local since we must deal with fragmentation too and we
+	 * can't rely on the nr_local_pages information.
+	 */
+	if (current->nr_local_pages && !current->allocation_order)
+		goto back_local_freelist;
+
+	page = mem_map + map_nr;
+	list_add((struct list_head *) page, &current->local_pages);
+	page->offset = order;
+	current->nr_local_pages++;
 }
 
 void __free_pages(struct page *page, unsigned long order)
@@ -179,13 +214,32 @@
 	atomic_set(&map->count, 1); \
 } while (0)
 
+static void refile_local_pages(void)
+{
+	if (current->nr_local_pages) {
+		struct page * page;
+		struct list_head * entry;
+		int nr_pages = current->nr_local_pages;
+
+		while ((entry = current->local_pages.next) != &current->local_pages) {
+			list_del(entry);
+			page = (struct page *) entry;
+			free_local_pages(page);
+			if (!nr_pages--)
+				panic("__get_free_pages local_pages list corrupted I");
+		}
+		if (nr_pages)
+			panic("__get_free_pages local_pages list corrupted II");
+		current->nr_local_pages = 0;
+	}
+}
+
 unsigned long __get_free_pages(int gfp_mask, unsigned long order)
 {
 	unsigned long flags;
-	static atomic_t free_before_allocate = ATOMIC_INIT(0);
 
 	if (order >= NR_MEM_LISTS)
-		goto nopage;
+		goto out;
 
 #ifdef ATOMIC_MEMORY_DEBUGGING
 	if ((gfp_mask & __GFP_WAIT) && in_interrupt()) {
@@ -194,26 +248,24 @@
 			printk("gfp called nonatomically from interrupt %p\n",
 				__builtin_return_address(0));
 		}
-		goto nopage;
+		goto out;
 	}
 #endif
 
 	/*
+	 * Acquire lock before reading nr_free_pages to make sure it
+	 * won't change from under us.
+	 */
+	spin_lock_irqsave(&page_alloc_lock, flags);
+
+	/*
 	 * If this is a recursive call, we'd better
 	 * do our best to just allocate things without
 	 * further thought.
 	 */
 	if (!(current->flags & PF_MEMALLOC)) {
-		int freed;
 		extern struct wait_queue * kswapd_wait;
 
-		/* Somebody needs to free pages so we free some of our own. */
-		if (atomic_read(&free_before_allocate)) {
-			current->flags |= PF_MEMALLOC;
-			try_to_free_pages(gfp_mask);
-			current->flags &= ~PF_MEMALLOC;
-		}
-
 		if (nr_free_pages > freepages.low)
 			goto ok_to_allocate;
 
@@ -223,35 +275,44 @@
 		/* Do we have to block or can we proceed? */
 		if (nr_free_pages > freepages.min)
 			goto ok_to_allocate;
+		if (gfp_mask & __GFP_WAIT) {
+			int freed;
+			/*
+			 * If the task is ok to sleep it's fine also
+			 * if we release irq here.
+			 */
+			spin_unlock_irq(&page_alloc_lock);
+
+			current->flags |= PF_MEMALLOC|PF_FREE_PAGES;
+			current->allocation_order = order;
+			freed = try_to_free_pages(gfp_mask);
+			current->flags &= ~(PF_MEMALLOC|PF_FREE_PAGES);
+
+			spin_lock_irq(&page_alloc_lock);
+			refile_local_pages();
+
+			/*
+			 * Re-check we're still low on memory after we blocked
+			 * for some time. Somebody may have released lots of
+			 * memory from under us while we was trying to free
+			 * the pages. We check against pages_high to be sure
+			 * to succeed only if lots of memory is been released.
+			 */
+			if (nr_free_pages > freepages.high)
+				goto ok_to_allocate;
 
-		current->flags |= PF_MEMALLOC;
-		atomic_inc(&free_before_allocate);
-		freed = try_to_free_pages(gfp_mask);
-		atomic_dec(&free_before_allocate);
-		current->flags &= ~PF_MEMALLOC;
-
-		/*
-		 * Re-check we're still low on memory after we blocked
-		 * for some time. Somebody may have released lots of
-		 * memory from under us while we was trying to free
-		 * the pages. We check against pages_high to be sure
-		 * to succeed only if lots of memory is been released.
-		 */
-		if (nr_free_pages > freepages.high)
-			goto ok_to_allocate;
-
-		if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
-			goto nopage;
+			if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
+				goto nopage;
+		}
 	}
 ok_to_allocate:
-	spin_lock_irqsave(&page_alloc_lock, flags);
 	/* if it's not a dma request, try non-dma first */
 	if (!(gfp_mask & __GFP_DMA))
 		RMQUEUE_TYPE(order, 0);
 	RMQUEUE_TYPE(order, 1);
+ nopage:
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
-
-nopage:
+ out:
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/swap_state.c linux.19p6/mm/swap_state.c
--- linux.vanilla/mm/swap_state.c	Fri Apr 21 12:47:02 2000
+++ linux.19p6/mm/swap_state.c	Sat Dec 16 17:21:48 2000
@@ -63,6 +63,7 @@
 		return 0;
 	}
 	atomic_inc(&page->count);
+	page->flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
 	page->inode = &swapper_inode;
 	page->offset = entry;
 	add_page_to_hash_queue(page, &swapper_inode, entry);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/vmscan.c linux.19p6/mm/vmscan.c
--- linux.vanilla/mm/vmscan.c	Sat Sep  9 18:42:51 2000
+++ linux.19p6/mm/vmscan.c	Sat Dec 16 17:21:48 2000
@@ -96,6 +96,9 @@
 	 * some real work in the future in "shrink_mmap()".
 	 */
 	if (!pte_dirty(pte)) {
+		if (page_map->inode && pgcache_under_min())
+			/* unmapping this page would be useless */
+			return 0;
 		flush_cache_page(vma, address);
 		pte_clear(page_table);
 		goto drop_pte;
@@ -106,7 +109,7 @@
 	 * we cannot do I/O! Avoid recursing on FS
 	 * locks etc.
 	 */
-	if (!(gfp_mask & __GFP_IO))
+	if (!(gfp_mask & __GFP_IO) || current->fs_locks)
 		return 0;
 
 	/*
@@ -208,6 +211,8 @@
 		result = try_to_swap_out(tsk, vma, address, pte, gfp_mask);
 		if (result)
 			return result;
+		if (current->need_resched)
+			return 2;
 		address += PAGE_SIZE;
 		pte++;
 	} while (address < end);
@@ -327,7 +332,7 @@
 	 * Think of swap_cnt as a "shadow rss" - it tells us which process
 	 * we want to page out (always try largest first).
 	 */
-	counter = nr_tasks / (priority+1);
+	counter = nr_tasks / priority;
 	if (counter < 1)
 		counter = 1;
 
@@ -361,8 +366,13 @@
 			goto out;
 		}
 
-		if (swap_out_process(pbest, gfp_mask))
+		switch (swap_out_process(pbest, gfp_mask)) {
+		case 1:
 			return 1;
+		case 2:
+			current->state = TASK_RUNNING;
+			schedule();
+		}
 	}
 out:
 	return 0;
@@ -377,11 +387,9 @@
  * cluster them so that we get good swap-out behaviour. See
  * the "free_memory()" macro for details.
  */
-static int do_try_to_free_pages(unsigned int gfp_mask)
+int try_to_free_pages(unsigned int gfp_mask)
 {
 	int priority;
-	int ret = 0;
-	int swapcount;
 	int count = SWAP_CLUSTER_MAX;
 
 	lock_kernel();
@@ -389,41 +397,34 @@
 	/* Always trim SLAB caches when memory gets low. */
 	kmem_cache_reap(gfp_mask);
 
-	priority = 6;
+	priority = 5;
 	do {
 		while (shrink_mmap(priority, gfp_mask)) {
-			ret = 1;
 			if (!--count)
 				goto done;
 		}
 
 		/* Try to get rid of some shared memory pages.. */
-		if (gfp_mask & __GFP_IO) {
+		if (gfp_mask & __GFP_IO && !current->fs_locks) {
 			while (shm_swap(priority, gfp_mask)) {
-				ret = 1;
 				if (!--count)
 					goto done;
 			}
 		}
 
 		/* Then, try to page stuff out.. */
-		swapcount = count;
 		while (swap_out(priority, gfp_mask)) {
-			ret = 1;
-			if (!--swapcount)
-				break;
+			if (!--count)
+				goto done;
 		}
 
 		shrink_dcache_memory(priority, gfp_mask);
-	} while (--priority >= 0);
+	} while (--priority > 0);
 done:
 	unlock_kernel();
 
-	if (!ret)
-		printk("VM: do_try_to_free_pages failed for %s...\n",
-				current->comm);
 	/* Return success if we freed a page. */
-	return ret;
+	return priority > 0;
 }
 
 /*
@@ -499,7 +500,7 @@
 
 		while (nr_free_pages < freepages.high)
 		{
-			if (do_try_to_free_pages(GFP_KSWAPD))
+			if (try_to_free_pages(GFP_KSWAPD))
 			{
 				if (tsk->need_resched)
 					schedule();
@@ -510,17 +511,3 @@
 		}
 	}
 }
-
-/*
- * Called by non-kswapd processes when kswapd really cannot
- * keep up with the demand for free memory.
- */
-int try_to_free_pages(unsigned int gfp_mask)
-{
-	int retval = 1;
-
-	if (gfp_mask & __GFP_WAIT)
-		retval = do_try_to_free_pages(gfp_mask);
-	return retval;
-}
-	
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq.c linux.19p6/net/ipv4/ip_masq.c
--- linux.vanilla/net/ipv4/ip_masq.c	Fri Apr 21 12:47:14 2000
+++ linux.19p6/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.19p6/net/ipv4/ip_masq_app.c
--- linux.vanilla/net/ipv4/ip_masq_app.c	Fri Apr 21 12:47:14 2000
+++ linux.19p6/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.19p6/net/ipv4/ip_masq_ftp.c
--- linux.vanilla/net/ipv4/ip_masq_ftp.c	Fri Apr 21 12:47:14 2000
+++ linux.19p6/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.19p6/net/ipv4/ip_masq_user.c
--- linux.vanilla/net/ipv4/ip_masq_user.c	Mon Dec 11 22:13:10 2000
+++ linux.19p6/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.19p6/net/ipv4/tcp.c
--- linux.vanilla/net/ipv4/tcp.c	Mon Dec 11 22:13:10 2000
+++ linux.19p6/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.19p6/net/netsyms.c
--- linux.vanilla/net/netsyms.c	Mon Dec 11 22:13:10 2000
+++ linux.19p6/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.19p6/net/unix/af_unix.c
--- linux.vanilla/net/unix/af_unix.c	Mon Dec 11 22:13:12 2000
+++ linux.19p6/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;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/scripts/Configure linux.19p6/scripts/Configure
--- linux.vanilla/scripts/Configure	Sat Sep  9 18:42:51 2000
+++ linux.19p6/scripts/Configure	Tue Dec 19 02:16:58 2000
@@ -422,7 +422,18 @@
 function string () {
 	old=$(eval echo "\${$2}")
 	def=${old:-$3}
-	readln "$1 ($2) [$def] " "$def" "$old"
+	while :; do
+          if [ "$old" = "?" ]; then
+             readln "$1 ($2) [$def] " "$def" ""
+          else
+	     readln "$1 ($2) [$def] " "$def" "$old"
+          fi
+	  if [ "$ans" = "?" ]; then
+	    help "$2"
+	  else
+	    break
+	  fi
+	done
 	define_string "$2" "$ans"
 }
 #
