diff -u --recursive --new-file v2.3.99-pre2/linux/CREDITS linux/CREDITS
--- v2.3.99-pre2/linux/CREDITS	Sun Mar 19 18:35:30 2000
+++ linux/CREDITS	Thu Mar 23 10:34:53 2000
@@ -891,6 +891,10 @@
 S: 72076 Tübingen
 S: Germany
 
+N: Justin Guyett
+E: jguyett@andrew.cmu.edu
+D: via-rhine net driver hacking
+
 N: Danny ter Haar
 E: dth@cistron.nl
 D: /proc/procinfo, reboot on panic , kernel pre-patch tester ;)
@@ -1143,6 +1147,7 @@
 W: http://www.image.dk/~nkbj
 D: 4.4BSD and NeXTstep filesystem support in the old ufs.
 D: Openstep filesystem and NeXTstep CDROM support in the new ufs.
+D: Replace CPU==[3-6]86 and __i386__ submodel flags by configuration options.
 D: Danish HOWTO, Linux+FreeBSD mini-HOWTO.
 S: Dr. Holsts Vej 34, lejl. 164
 S: DK-8230 Åbyhøj
@@ -1327,6 +1332,13 @@
 E: akrebs@altavista.net
 D: CYPRESS CY82C693 chipset IDE, Digital's PC-Alpha 164SX boards
 
+N: Greg Kroah-Hartman
+E: greg@kroah.com
+W: http://www.kroah.com/linux-usb/
+D: USB Serial Converter driver framework, USB Handspring Visor driver
+D: ConnectTech WHITEHeat USB driver, Generic USB Serial driver
+D: bits and pieces of USB core code.
+
 N: Russell Kroll
 E: rkroll@exploits.org
 W: http://www.exploits.org/
@@ -1360,10 +1372,13 @@
 S: Germany
 
 N: Jaroslav Kysela
-E: perex@jcu.cz
-W: http://www.pf.jcu.cz/~perex
+E: perex@suse.cz
+W: http://www.perex.cz
 D: Original Author and Maintainer for HP 10/100 Mbit Network Adapters
-S: Unix Centre of Pedagogical Faculty, University of South Bohemia
+D: ISA PnP
+S: Sindlovy Dvory 117
+S: 370 01  Ceske Budejovice
+S: Czech Republic
 
 N: Bas Laarhoven
 E: bas@vimec.nl
@@ -1947,6 +1962,16 @@
 S: 91054 Erlangen
 S: Germany
 
+N: Giuliano Procida
+E: myxie@debian.org,gprocida@madge.com
+D: Madge Ambassador driver (Collage 155 Server ATM adapter)
+D: Madge Horizon driver (Collage 25 and 155 Client ATM adapters)
+P: 1024/93898735 D3 9E F4 F7 6D 8D 2F 3A  38 BA 06 7C 2B 33 43 7D
+S: Madge Networks
+S: Framewood Road
+S: Wexham SL3 6PJ
+S: United Kingdom
+
 N: Daniel Quinlan
 E: quinlan@pathname.com
 W: http://www.pathname.com/~quinlan/
@@ -2037,6 +2062,14 @@
 S: 7000 Stuttgart 50
 S: Germany
 
+N: Christoph Rohland
+E: hans-christoph.rohland@sap.com
+E: ch.rohland@gmx.net
+D: shm fs, SYSV semaphores, af_unix
+S: Neue Heimat Str. 8
+S: D-68789 St.Leon-Rot
+S: Germany
+
 N: Stephen Rothwell
 E: sfr@linuxcare.com
 W: http://linuxcare.com.au/sfr
@@ -2455,6 +2488,7 @@
 D: Amiga Buddha and Catweasel chipset IDE
 D: Atari Falcon chipset IDE
 D: Amiga Gayle chipset IDE
+D: mipsel NEC DDB Vrc-5074
 S: C. Huysmansstraat 12
 S: B-3128 Baal
 S: Belgium
@@ -2598,6 +2632,10 @@
 S: Eichenweg 16
 S: 73650 Winterbach
 S: Germany
+
+N: Urban Widmark
+E: urban@svenskatest.se
+D: via-rhine, misc net driver hacking
 
 N: Marco van Wieringen
 E: mvw@planets.elm.net
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/Changes linux/Documentation/Changes
--- v2.3.99-pre2/linux/Documentation/Changes	Sun Mar 19 18:35:30 2000
+++ linux/Documentation/Changes	Wed Mar 22 09:39:11 2000
@@ -73,8 +73,11 @@
 ===================
 
    To use System V shared memory, you have to mount the shm filesystem
-somewhere and put the mountpoint into /proc/sys/kernel/shmpath.
-Default is /var/shm.
+somewhere. You can do that automatically by adding this line to /etc/fstab:
+
+none		/var/shm	shm		defaults	0 0
+
+Remember to create the mountpoint directory; it does not have to be /var/shm.
 
    <CTRL><ALT><DEL> now performs a cold reboot instead of a warm reboot
 for increased hardware compatibility.  If you want a warm reboot and
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.99-pre2/linux/Documentation/Configure.help	Sun Mar 19 18:35:30 2000
+++ linux/Documentation/Configure.help	Thu Mar 23 08:38:57 2000
@@ -186,10 +186,22 @@
 CONFIG_NOHIGHMEM
   If you are compiling a kernel which will never run on a machine
   with more than 1 Gigabyte total physical RAM, answer "off"
-  here (default choice).
+  here (default choice).  This will result in the old "3GB/1GB"
+  virtual/physical memory split. 3BG are mapped so as each processus
+  sees a 3GB virtual memory space.
+  The remaining part of the 4G virtual memory space is used by the
+  kernel to 'permanently map' as much physical memory as possible.
+  Certain types of applications perform better if there is more
+  'permanently mapped' kernel memory.
+  Certain types of applications (eg. database servers) perform
+  better if they have as much virtual memory per process as possible.
 
   Linux can use up to 64 Gigabytes of physical memory on x86 systems.
-  High memory is all the physical RAM that could not be directly
+  However 32-bit x86 processors have only 4 Gigabytes of virtual memory
+  space. 
+
+  Any potentially remaining part of physical memory is called
+  'high memory' that is all the physical RAM that could not be directly
   mapped by the kernel - ie. 3GB if there is 4GB RAM in the system,
   7GB if there is 8GB RAM in the system.
 
@@ -202,6 +214,13 @@
   processors (PPro and better). NOTE: The "64GB" kernel will not
   boot CPUs that not support PAE!
 
+  The actual amount of total physical memory will either be
+  autodetected or can be forced by using a kernel command line option
+  such as "mem=256M". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time. The lilo procedure is also explained in the
+  SCSI-HOWTO, available from http://www.linuxdoc.org/docs.html#howto .)
+
 Normal PC floppy disk support
 CONFIG_BLK_DEV_FD
   If you want to use the floppy disk drive(s) of your PC under Linux,
@@ -372,7 +391,7 @@
   Useful information about large (>540 MB) IDE disks, multiple
   interfaces, what to do if ATA/IDE devices are not automatically
   detected, sound card ATA/IDE ports, module support, and other topics, is
-  contained in Documentation/ata-ide.txt. For detailed information about
+  contained in Documentation/ide.txt. For detailed information about
   hard drives, consult the Disk-HOWTO and the Multi-Disk-HOWTO,
   available from http://www.linuxdoc.org/docs.html#howto .
 
@@ -494,7 +513,7 @@
   along with other IDE devices, as "hdb" or "hdc", or something
   similar, and will be mapped to a character device such as "ht0"
   (check the boot messages with dmesg). Be sure to consult the
-  drivers/block/ide-tape.c and Documentation/ide.txt files for usage
+  drivers/ide/ide-tape.c and Documentation/ide.txt files for usage
   information.
 
   If you want to compile the driver as a module ( = code which can be
@@ -627,7 +646,7 @@
   the latest version of the hdparm utility from
   ftp://metalab.unc.edu/pub/Linux/system/hardware/ .
 
-  Read the comments at the beginning of drivers/block/ide-dma.c and the
+  Read the comments at the beginning of drivers/ide/ide-dma.c and the
   file Documentation/ide.txt for more information. 
 
   It is safe to say Y to this question.
@@ -707,12 +726,12 @@
   should say Y here, and preferably also to "Use DMA by default when
   available".
 
-  Please read the comments at the top of drivers/block/aec6210.c
+  Please read the comments at the top of drivers/ide/aec6210.c
   If you say Y here, then say Y to "Use DMA by default when available" as well.
 
 AEC6210 Tuning support (WIP)
 CONFIG_AEC6210_TUNING
-  Please read the comments at the top of drivers/block/aec6210.c  
+  Please read the comments at the top of drivers/ide/aec6210.c  
   If unsure, say N.
 
 ALI M15x3 chipset support
@@ -721,16 +740,28 @@
   1535, 1535D onboard chipsets.  It also tests for Simplex mode and
   enables normal dual channel support.
 
-  Please read the comments at the top of drivers/block/alim15x3.c
+  Please read the comments at the top of drivers/ide/alim15x3.c
   If you say Y here, then say Y to "Use DMA by default when available" as well.
 
   If unsure, say N.
 
+ALI M15x3 WDC support (DANGEROUS)
+CONFIG_WDC_ALI15X3
+  This allows for UltraDMA support for WDC drives that ignore CRC checking.
+  You are a fool for enabling this option, but there have been requests.
+  DO NOT COMPLAIN IF YOUR DRIVE HAS FS CORUPTION, IF YOU ENABLE THIS!
+  No one will listen, just laugh for ignoring this SERIOUS WARNING.
+
+  Using this option can allow WDC drives to run at ATA-4/5 transfer rates with
+  only an ATA-2 support structure.
+
+  SAY NO!
+
 AMD7409 chipset support (EXPERIMENTAL)
 CONFIG_BLK_DEV_AMD7409
   This driver ensures (U)DMA support for AMD756 Viper chipset.
 
-  Please read the comments at the top of drivers/block/amd7409.c
+  Please read the comments at the top of drivers/ide/amd7409.c
   If you say Y here, then say Y to "Use DMA by default when available" as well.
   If unsure, say N.
 
@@ -776,7 +807,7 @@
 HPT34X AUTODMA support (WIP)
 CONFIG_HPT34X_AUTODMA
   This is a dangerous thing to attempt currently!
-  Please read the comments at the top of drivers/block/hpt34x.c
+  Please read the comments at the top of drivers/ide/hpt34x.c
   If you say Y here, then say Y to "Use DMA by default when available" as well.
 
   If unsure, say N.
@@ -795,7 +826,7 @@
   the ide-probe at boot. It is reported to support DVD II drives, by the
   manufacturer.
 
-  Please read the comments at the top of drivers/block/hpt366.c
+  Please read the comments at the top of drivers/ide/hpt366.c
   If you say Y here, then say Y to "Use DMA by default when available" as well.
 
 HPT366 Fast Interrupts (WIP)
@@ -813,12 +844,12 @@
   This driver adds detection and support for the NS87415 chip
   (used in SPARC64, among others).
 
-  Please read the comments at the top of drivers/block/ns87415.c.
+  Please read the comments at the top of drivers/ide/ns87415.c.
 
 OPTi 82C621 enhanced support (EXPERIMENTAL)
 CONFIG_BLK_DEV_OPTI621
   This is a driver for the OPTi 82C621 EIDE controller.
-  Please read the comments at the top of drivers/block/opti621.c.
+  Please read the comments at the top of drivers/ide/opti621.c.
 
 Intel PIIXn chipsets support
 CONFIG_BLK_DEV_PIIX
@@ -827,7 +858,7 @@
   PIO 0-4 mode settings, this allows dynamic tuning of the chipset
   via the standard end-user tool 'hdparm'.
 
-  Please read the comments at the top of drivers/block/piix.c
+  Please read the comments at the top of drivers/ide/piix.c
 
   Should also include "PIIXn Tuning support" CONFIG_PIIX_TUNING
   If unsure, say Y.
@@ -866,7 +897,7 @@
   1.11 or newer required.
 
   If you say Y here, then say Y to "Use DMA by default when available" as well.
-  Please read the comments at the top of drivers/block/pdc202xx.c
+  Please read the comments at the top of drivers/ide/pdc202xx.c
 
   If unsure, say N.
 
@@ -876,7 +907,7 @@
   for PDC20246/Ultra33 that has BIOS setup failures when using 3 or
   more cards.
 
-  Please read the comments at the top of drivers/block/pdc202xx.c
+  Please read the comments at the top of drivers/ide/pdc202xx.c
 
   If unsure, say N.
 
@@ -898,7 +929,7 @@
   This driver adds support for bus master DMA transfers
   using the Tekram TRM290 PCI IDE chip. Volunteers are
   needed for further tweaking and development.
-  Please read the comments at the top of drivers/block/trm290.c.
+  Please read the comments at the top of drivers/ide/trm290.c.
 
 VIA82CXXX chipset support (EXPERIMENTAL)
 CONFIG_BLK_DEV_VIA82CXXX
@@ -909,7 +940,7 @@
    If no command line is provided, it will try to set fifo configuration
    at its best. It will allow you to get a proc/ide/via display
   (while running a "cat") provided you enabled "proc" support.
-  Please read the comments at the top of drivers/block/via82cxxx.c
+  Please read the comments at the top of drivers/ide/via82cxxx.c
 
   If you say Y here, then say Y to "Use DMA by default when available" as well.
 
@@ -943,14 +974,14 @@
   boot parameter. It enables support for the secondary IDE interface
   of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster
   I/O speeds to be set as well. See the files Documentation/ide.txt
-  and drivers/block/ali14xx.c for more info.
+  and drivers/ide/ali14xx.c for more info.
 
 DTC-2278 support
 CONFIG_BLK_DEV_DTC2278
   This driver is enabled at runtime using the "ide0=dtc2278" kernel
   boot parameter. It enables support for the secondary IDE interface
   of the DTC-2278 card, and permits faster I/O speeds to be set as
-  well. See the Documentation/ide.txt and drivers/block/dtc2278.c
+  well. See the Documentation/ide.txt and drivers/ide/dtc2278.c
   files for more info.
 
 Holtek HT6560B support
@@ -958,7 +989,7 @@
   This driver is enabled at runtime using the "ide0=ht6560b" kernel
   boot parameter. It enables support for the secondary IDE interface
   of the Holtek card, and permits faster I/O speeds to be set as well.
-  See the Documentation/ide.txt and drivers/block/ht6560b.c files for
+  See the Documentation/ide.txt and drivers/ide/ht6560b.c files for
   more info.
 
 PROMISE DC4030 support (EXPERIMENTAL)
@@ -969,13 +1000,13 @@
   attached to the secondary interface. CDROM and TAPE devices are not
   supported yet. This driver is enabled at runtime using the
   "ide0=dc4030" kernel boot parameter. See the Documentation/ide.txt
-  and drivers/block/pdc4030.c files for more info.
+  and drivers/ide/pdc4030.c files for more info.
 
 QDI QD6580 support
 CONFIG_BLK_DEV_QD6580
   This driver is enabled at runtime using the "ide0=qd6580" kernel
   boot parameter. It permits faster I/O speeds to be set. See the
-  files Documentation/ide.txt and drivers/block/qd6580.c for more
+  files Documentation/ide.txt and drivers/ide/qd6580.c for more
   info.
 
 UMC 8672 support
@@ -983,7 +1014,7 @@
   This driver is enabled at runtime using the "ide0=umc8672" kernel
   boot parameter. It enables support for the secondary IDE interface
   of the UMC-8672, and permits faster I/O speeds to be set as well.
-  See the files Documentation/ide.txt and drivers/block/umc8672.c for
+  See the files Documentation/ide.txt and drivers/ide/umc8672.c for
   more info.
 
 Amiga builtin Gayle IDE interface support
@@ -2135,6 +2166,14 @@
   Say Y here to enable support in the dumb serial driver to support
   the HUB6 card.
 
+Support for hot-pluggable devices
+CONFIG_HOTPLUG
+  Say Y here to enable support for hot plugin of certain hardware such as
+  PCMCIA cards and the like.
+
+  At this moment, few drivers support it, but as they get converted to use the
+  new ressource allocator/manager, their number will increase.
+
 PCMCIA serial device support
 CONFIG_PCMCIA_SERIAL_CS
   Say Y here to enable support for 16-bit PCMCIA serial devices,
@@ -2728,6 +2767,27 @@
 CONFIG_FB_S3TRIO
   If you have a S3 Trio say Y. Say N for S3 Virge. 
 
+3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)
+CONFIG_FB_3DFX
+  This driver supports graphics boards with the 3Dfx Banshee/Voodoo3 chips.
+  Say Y if you have such a graphics board.
+
+  The driver is also available as a module ( = code which can be
+  inserted and removed from the running kernel whenever you want). The
+  module will be called tdfxfb.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+nVidia Riva support (EXPERIMENTAL)
+CONFIG_FB_RIVA
+  This driver supports graphics boards with the nVidia Riva (aka TNTx)
+  chips.
+  Say Y if you have such a graphics board.
+
+  The driver is also available as a module ( = code which can be
+  inserted and removed from the running kernel whenever you want). The
+  module will be called rivafb.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
 ATI Mach64 display support (EXPERIMENTAL)
 CONFIG_FB_ATY
   This driver supports graphics boards with the ATI Mach64 chips.
@@ -2808,19 +2868,36 @@
   running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called vga16fb.o.
 
+Select other compiled-in fonts
+CONFIG_FBCON_FONTS
+  Say Y here if you would like to use fonts other than the default your frame
+  buffer console usually use.
+
+  Note that the answer to this question won't directly affect the kernel:
+  saying N will just cause this configure script to skip all the questions
+  about foreign fonts.
+
+  If unsure, say N (the default choices are safe).
+
 VGA 8x16 font
 CONFIG_FONT_8x16
   This is the "high resolution" font for the VGA frame buffer (the one
-  provided by the text console 80x25 mode.
+  provided by the VGA text console 80x25 mode.
+
+  If unsure, say Y.
 
 Support only 8 pixels wide fonts
 CONFIG_FBCON_FONTWIDTH8_ONLY
   Answer Y here will make the kernel provide only the 8x8 fonts (these
   are the less readable).
 
+  If unsure, say N.
+
 Sparc console 8x16 font
 CONFIG_FONT_SUN8x16
-  This is the high resolution console font for Sun machines. Say Y.
+  This is the high resolution console font for Sun machines.
+
+  Say Y.
 
 Sparc console 12x22 font (not supported by all drivers)
 CONFIG_FONT_SUN12x22
@@ -2834,6 +2911,7 @@
   provided by the text console 80x50 (and higher) modes.
   Note this is a poor quality font. The VGA 8x16 font is quite a lot
   more readable.
+
   Given the resolution provided by the frame buffer device, answer N
   here is safe.
 
@@ -2844,13 +2922,25 @@
   includes a server that supports the frame buffer device directly
   (XF68_FBDev).
 
+HGA monochrome support (EXPERIMENTAL)
+Hercules mono graphics console (EXPERIMENTAL)
+CONFIG_FBCON_HGA
+  Say Y here if you have a Hercules mono graphics card.
+
+  This driver is also available as a module ( = code which can be
+  inserted and removed from the running kernel whenever you want).
+  The module will be called hgafb.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+  As this card technology is 15 years old, most people will answer N here.
+
 Matrox unified accelerated driver (EXPERIMENTAL)
 CONFIG_FB_MATROX
   Say Y here if you have Matrox Millennium, Matrox Millennium II,
   Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox
-  Mystique G200, Matrox Millennium G200 or Matrox Marvel G200 video
-  card in your box. At this time, support for the G100, Mystique G200
-  and Marvel G200 is untested.
+  Mystique G200, Matrox Millennium G200, Matrox Marvel G200 video or
+  Matrox G400 card in your box. At this time, support for the G100,
+  Mystique G200 and Marvel G200 is untested.
 
   This driver is also available as a module ( = code which can be
   inserted and removed from the running kernel whenever you want).
@@ -3784,6 +3874,12 @@
 
    See Documentation/networking/decnet.txt for more information.
 
+Appletalk interfaces support
+CONFIG_APPLETALK
+  AppleTalk is the way Apple computers speak to each other on a
+  network. If your Linux box is connected to such a network and you
+  want to join the conversation, say Y.
+
 AppleTalk DDP
 CONFIG_ATALK
   AppleTalk is the way Apple computers speak to each other on a
@@ -6069,7 +6165,7 @@
   with a similar Bonding Linux driver, a Cisco 5500 switch or a
   SunTrunking SunSoft driver.
 
-  This is similar to the EQL driver, but it merge etherner segments instead
+  This is similar to the EQL driver, but it merge ethernet segments instead
   of serial lines.
 
   If you want to compile this as a module ( = code which can be
@@ -6718,6 +6814,17 @@
   Say Y here if you have a native Econet network card installed in
   your computer.
 
+Wan interfaces support
+CONFIG_WAN
+  Wide Area Networks (WANs), such as X.25, frame relay and leased
+  lines, are used to interconnect Local Area Networks (LANs) over vast
+  distances with data transfer rates significantly higher than those
+  achievable with commonly used asynchronous modem connections.
+
+  Say Y here if you want to use such interconnections.
+
+  It is safe to say N. Most people won't need it.
+
 WAN Router
 CONFIG_WAN_ROUTER
   Wide Area Networks (WANs), such as X.25, frame relay and leased
@@ -6743,6 +6850,18 @@
 
   If unsure, say N.
 
+WAN router drivers
+CONFIG_WAN_ROUTER_DRIVERS
+  Wide Area Networks (WANs), such as X.25, frame relay and leased
+  lines, are used to interconnect Local Area Networks (LANs) over vast
+  distances with data transfer rates significantly higher than those
+  achievable with commonly used asynchronous modem connections.
+  Usually, a quite expensive external device called a `WAN router' is
+  needed to connect to a WAN.
+
+  Say Y here will enable the kernel to a??? as a WAN router betwenn LAN by
+  means of WAN adapters.
+
 Fast switching (read help!)
 CONFIG_NET_FASTROUTE
   Saying Y here enables direct NIC-to-NIC (NIC = Network Interface
@@ -7037,8 +7156,14 @@
   The module will be called cosa.o. For general information about
   modules read Documentation/modules.txt.
 
-# Fibre Channel driver support
-# CONFIG_NET_FC
+Fibre Channel driver support
+CONFIG_NET_FC
+  Say Y here provide support for storage arrays connected to
+  the system using Fibre Optic and the "X3.269-199X Fibre Channel
+  Protocol for SCSI" specification. You'll also need the generic SCSI
+  support, as well as the drivers for the storage array itself and
+  for the interface adapter such as SOC or SOC+. This subsystem could even
+  serve for IP networking, with some code extensions. If unsure, say N.
 
 # Interphase 5526 Tachyon chipset based adaptor support
 # CONFIG_IPHASE5526
@@ -9154,6 +9279,21 @@
   The module will be called dc2xx.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
 
+
+USB Mustek MDC800 Digital Camera Support
+CONFIG_USB_MDC800
+  Say Y here if you want to connect this type of still camera to
+  your computer's USB port. This driver can be used with gphoto 0.4.3
+  and higher (look at www.gphoto.org).
+  To use it create a devicenode with mknod /dev/mustek c 10 171 and
+  configure it in your software.
+
+  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 mdc800.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+
 USB Mass Storage support
 CONFIG_USB_STORAGE
   Say Y here if you want to connect USB mass storage devices to your
@@ -9473,6 +9613,23 @@
   say M here and read Documentation/modules.txt. The module will be
   called vfat.o.
 
+Compressed ROM file system support
+CONFIG_CRAMFS
+  This option provides support for CramFs (Compressed ROM File System).
+  Cramfs is designed to be a simple, small, and compressed file system for ROM
+  based embedded systems.
+  CramFs is read-only, limited to 256MB file systems (with 16MB files), don't
+  support neither 16/32 bits uid/gid nor hard links. Neither are timestamps.
+  It isn't endian aware.
+  
+  See Documentation/filesystems/cramfs.txt and fs/cramfs/README
+  for further information.
+
+  If you want to compile this 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 cramfs.o.
+
 UMSDOS: Unix-like file system on top of standard MSDOS fs
 CONFIG_UMSDOS_FS
   Say Y here if you want to run Linux from within an existing DOS
@@ -9871,7 +10028,9 @@
 
   Note that the answer to this question won't directly affect the
   kernel: saying N will just cause this configure script to skip all
-  the questions about foreign partitioning schemes. If unsure, say N.
+  the questions about foreign partitioning schemes.
+
+  If unsure, say N.
 
 Alpha OSF partition support
 CONFIG_OSF_PARTITION
@@ -11375,11 +11534,17 @@
   This enable the kernel to lower the requested computer power by making some
   devices enter in lower power levels (standy, sleep, ... modes).
   Basically, this let you save power.
+
   Two majors interfaces exist between the hardware and the OS, the older
   Advanced Power Management (APM) and the newer Advanced Configuration and
   Power Interface (ACPI).
+
   Both are supported by the Linux Kernel.
 
+  Note that on some architectures (such as ia32), the idle task perform hlt
+  instructions which makes the CPU enter a low power mode. This can be seen as
+  the first kernel PM level.
+
 Enter S1 for sleep (EXPERIMENTAL)
 CONFIG_ACPI_S1_SLEEP
   This enable ACPI compliant devices to enter level 1 of ACPI saving
@@ -12882,22 +13047,12 @@
   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
+CAPI2.0 support
+CONFIG_ISDN_CAPI
+  This provides the 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.
+  http://www.capi.org/. This is needed for AVM's set of active ISDN
+  controllers like B1, T1, M1.
 
 AVM B1 ISA support
 CONFIG_ISDN_DRV_AVMB1_B1ISA
@@ -12907,6 +13062,10 @@
 CONFIG_ISDN_DRV_AVMB1_B1PCI
   Enable support for the PCI version of the AVM B1 card.
 
+AVM B1 PCI V4 support
+CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+  Enable support for the V4 version of AVM B1 PCI card.
+
 AVM T1/T1-B ISA support
 CONFIG_ISDN_DRV_AVMB1_T1ISA
   Enable support for the AVM T1 T1B card.
@@ -12921,6 +13080,11 @@
   Enable support for the AVM T1 T1B card.
   Note: This is a PRI card and handle 30 B-channels.
 
+AVM C4 support
+CONFIG_ISDN_DRV_AVMB1_C4
+  Enable support for the AVM C4 PCI card.
+  This card handle 4 BRI ISDN lines (8 channels).
+
 Verbose reason code reporting (kernel size +=7K)
 CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
   If you say Y here, the AVM B1 driver will give verbose reasons for
@@ -13144,7 +13308,7 @@
   say M here and read Documentation/modules.txt. The module will be
   called ohci1394.o.
 
-RAW IEEE 1394 I/O support
+Raw IEEE 1394 I/O support
 CONFIG_IEEE1394_RAWIO
   Say Y here if you want support for the raw device. This is generally
   a good idea, so you should say Y here. The raw device enables
@@ -13155,6 +13319,17 @@
   say M here and read Documentation/modules.txt. The module will be
   called raw1394.o.
 
+Excessive debugging output
+CONFIG_IEEE1394_VERBOSEDEBUG
+  If you say Y here, you will get very verbose debugging logs from the
+  subsystem which includes a dump of the header of every sent and
+  received packet.  This can amount to a high amount of data collected
+  in a very short time which is usually also saved to disk by the
+  system logging daemons.
+
+  Say Y if you really want or need the debugging output, everyone else
+  says N.
+
 #
 # m68k-specific kernel options
 # Documented by Chris Lawrence <quango@themall.net> et al.
@@ -13193,6 +13368,14 @@
   If you plan to try to use the kernel on such a machine say Y here.
   Everybody else says N.
 
+Sun 3X support
+CONFIG_SUN3X
+  This option enables support for the Sun 3x series of workstations. Be
+  warned that this support is very experimental. You will also want to
+  say Y to 68020 support and N to the other processors below.
+
+  If you don't want to compile a kernel for a Sun 3x, say N.
+
 Sun 3 support
 CONFIG_SUN3
   This option enables support for the Sun 3 series of workstations. Be
@@ -13256,8 +13439,8 @@
   mantissa and round slightly incorrect, what is more then enough
   for normal usage.
 
-Advanced processor options
-CONFIG_ADVANCED_CPU
+Advanced configuration options
+CONFIG_ADVANCED
   This gives you access to some advanced options for the CPU. The
   defaults should be fine for most users, but these options may make
   it possible for you to improve performance somewhat if you know what
@@ -14223,45 +14406,6 @@
   
   If you have any questions or comments about the  Compaq Personal
   Server, send e-mail to skiff@crl.dec.com
-
-Virtual/Physical Memory Split
-CONFIG_1GB
-  If you are compiling a kernel which will never run on a machine
-  with more than 1 Gigabyte total physical RAM, answer "3GB/1GB"
-  here (default choice).
-
-  On 32-bit x86 systems Linux can use up to 64 Gigabytes of physical
-  memory. However 32-bit x86 processors have only 4 Gigabytes of
-  virtual memory space. This option specifies the maximum amount of
-  virtual memory space one process can potentially use. Certain types
-  of applications (eg. database servers) perform better if they have
-  as much virtual memory per process as possible.
-
-  The remaining part of the 4G virtual memory space is used by the
-  kernel to 'permanently map' as much physical memory as possible.
-  Certain types of applications perform better if there is more
-  'permanently mapped' kernel memory.
-
-  [WARNING! Certain boards do not support PCI DMA to physical addresses
-  bigger than 2 Gigabytes. Non-DMA-able memory must not be permanently
-  mapped by the kernel, thus a 1G/3G split will not work on such boxes.]
-
-  As you can see there is no 'perfect split' - the fundamental
-  problem is that 4G of 32-bit virtual memory space is short. So
-  you'll have to pick your own choice - depending on the application
-  load of your box. A 2G/2G split is typically a good choice for a
-  generic Linux server with lots of RAM.
-
-  Any potentially remaining (not permanently mapped) part of physical
-  memory is called 'high memory'. How much total high memory the kernel
-  can handle is influenced by the (next) High Memory configuration option.
-
-  The actual amount of total physical memory will either be
-  autodetected or can be forced by using a kernel command line option
-  such as "mem=256M". (Try "man bootparam" or see the documentation of
-  your boot loader (lilo or loadlin) about how to pass options to the
-  kernel at boot time. The lilo procedure is also explained in the
-  SCSI-HOWTO, available from http://www.linuxdoc.org/docs.html#howto .)
 
 Math emulation
 CONFIG_NWFPE
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/DocBook/Makefile linux/Documentation/DocBook/Makefile
--- v2.3.99-pre2/linux/Documentation/DocBook/Makefile	Sun Mar 19 18:35:30 2000
+++ linux/Documentation/DocBook/Makefile	Tue Mar 21 11:03:17 2000
@@ -26,8 +26,11 @@
 		$(TOPDIR)/arch/i386/kernel/mca.c \
 		$(TOPDIR)/arch/i386/kernel/mtrr.c \
 		$(TOPDIR)/drivers/char/misc.c \
-		$(TOPDIR)/drivers/char/serial.c \
 		$(TOPDIR)/drivers/char/videodev.c \
+		$(TOPDIR)/drivers/net/net_init.c \
+		$(TOPDIR)/drivers/net/8390.c \
+		$(TOPDIR)/drivers/char/serial.c \
+		$(TOPDIR)/drivers/pci/pci.c \
 		$(TOPDIR)/drivers/sound/sound_core.c \
 		$(TOPDIR)/drivers/sound/sound_firmware.c \
 		$(TOPDIR)/drivers/net/wan/syncppp.c \
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/DocBook/kernel-api.tmpl linux/Documentation/DocBook/kernel-api.tmpl
--- v2.3.99-pre2/linux/Documentation/DocBook/kernel-api.tmpl	Sun Mar 19 18:35:30 2000
+++ linux/Documentation/DocBook/kernel-api.tmpl	Tue Mar 21 11:03:17 2000
@@ -49,7 +49,7 @@
   </chapter>
 
   <chapter id="miscdev">
-     <title>Miscellaneous Device</title>
+     <title>Miscellaneous Devices</title>
 !Edrivers/char/misc.c
   </chapter>
 
@@ -58,6 +58,12 @@
 !Edrivers/char/videodev.c
   </chapter>
 
+  <chapter id="netdev">
+     <title>Network devices</title>
+!Idrivers/net/net_init.c
+!Edrivers/net/8390.c
+  </chapter>
+
   <chapter id="snddev">
      <title>Sound Devices</title>
 !Edrivers/sound/sound_core.c
@@ -87,6 +93,11 @@
   <chapter id="z85230">
      <title>Z85230 Support Library</title>
 !Edrivers/net/wan/z85230.c
+  </chapter>
+
+  <chapter id="pcilib">
+     <title>PCI Support Library</title>
+!Edrivers/pci/pci.c
   </chapter>
 
 </book>
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.3.99-pre2/linux/Documentation/devices.tex	Fri Jan  7 19:13:21 2000
+++ linux/Documentation/devices.tex	Wed Dec 31 16:00:00 1969
@@ -1,2209 +0,0 @@
-\documentstyle{article}
-% $Id: devices.tex,v 1.14 1998/08/10 22:39:24 hpa Exp $
-% ---------------------------------------------------------------------------
-% Adopt somewhat reasonable margins, so it doesn't take a million
-% pages to print... :-)  If you're actually putting this in print, you
-% may wish to change these.
-\oddsidemargin=0in
-\textwidth=6.5in
-\topmargin=0in
-\headheight=0.5in
-\headsep=0.25in
-\textheight=7.5in
-\footskip=0.75in
-\footheight=0.5in
-%
-\begin{document}
-\newcommand{\file}{\tt}			% Style to use for a filename
-\newcommand{\url}{\it}		        % Style to use for an URL
-\newcommand{\hex}{\tt}			% Style to use for a hex number
-\newcommand{\ud}{(Under development)}	% Abbreviation
-\newcommand{\1}{\({}^1\)}
-\newcommand{\2}{\({}^2\)}
-\newcommand{\3}{\({}^3\)}
-\newcommand{\4}{\({}^4\)}
-\newlength{\dig}
-\settowidth{\dig}{0}			% Get width of digits
-\newcommand{\num}[2]{\makebox[#1\dig][r]{#2}}
-\newcommand{\major}[4]{\num{3}{#1}#2 \> #3 \> #4 \\}
-\newcommand{\minor}[3]{\> \> \num{3}{#1} \> {\file #2} \> #3 \\}
-\newcommand{\minordots}{\> \> \> \dots \\}
-\newenvironment{devicelist}%
- {\begin{tabbing}%
-000--000 \= blockxxx \= 000 \= {\file /dev/input/keyboardxxx} \= foo \kill}%
- {\end{tabbing}}
-\newcommand{\link}[4]{{\file #1} \> {\file #2} \> #3 \> #4 \\}
-\newcommand{\vlink}[4]{{\file #1} \> {\em #2 \/} \> #3 \> #4 \\}
-\newcommand{\node}[3]{{\file #1} \> #2 \> #3 \\}
-\newcommand{\tum}{$''$}
-\newenvironment{nodelist}%
- {\begin{tabbing}%
-{\file /dev/crambamboli} \= {\file /proc/self/fd/99} \= symbolicxxx \=
-foo \kill}%
- {\end{tabbing}}
-%
-% If you reformat this document, *please* make sure this information
-% gets included!  This list changes frequently, so it is crucial to
-% know the date of the revision.
-%
-\title{{\bf Linux Allocated Devices}}
-\author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: August 10, 1998}
-\maketitle
-%
-\noindent
-This list is the Linux Device List, the official registry of allocated
-device numbers and {\file /dev} directory nodes for the Linux
-operating system.
-
-The latest version of this list is included with the Linux kernel
-sources in \LaTeX\ and ASCII form.  It is also available separately
-from {\url ftp://ftp.kernel.org/pub/linux/docs/device-list/}.  In case
-of discrepancy between the text and \LaTeX\ versions, the \LaTeX\
-version is authoritative.
-
-This document is included by reference into the Linux Filesystem
-Standard (FSSTND).  The FSSTND is available from
-{\url ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/}.
-
-Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
-platform only.  Allocations marked (68k/Atari) apply to Linux/68k on
-the Atari platform only.
-
-This document is in the public domain.  The author requests, however,
-that semantically altered versions are not distributed without
-permission of the author, assuming the author can be contacted without
-an unreasonable effort.
-
-In particular, please don't sent patches for this list to Linus, at
-least not without contacting me first.
-
-I do not have any information about these devices beyond what appears
-on this list.  Any such information requests will be deleted without
-reply.
-
-\section{How to submit a device entry}
-
-To have a major number allocated, or a minor number in situations
-where that applies (e.g. busmice), please contact me with the
-appropriate device information.  Also, if you have additional
-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.
-This is necessary for me to be able to keep this list up to date and
-correct in a timely manner.  First of all, {\em 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.
-
-Second, please include a description of the device {\em 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.
-
-Your cooperation is appreciated.
-
-\section{Major numbers}
-
-\begin{devicelist}
-\major{ 0}{}{     }{Unnamed devices (e.g. non-device mounts)}
-\major{ 1}{}{char }{Memory devices}
-\major{  }{}{block}{RAM disk}
-\major{ 2}{}{char }{Pseudo-TTY masters}
-\major{  }{}{block}{Floppy disks}
-\major{ 3}{}{char }{Pseudo-TTY slaves}
-\major{  }{}{block}{First MFM, RLL or IDE hard disk/CD-ROM interface}
-\major{ 4}{}{char }{TTY devices}
-\major{ 5}{}{char }{Alternate TTY devices}
-\major{ 6}{}{char }{Parallel printer devices}
-\major{ 7}{}{char }{Virtual console access devices}
-\major{  }{}{block}{Loopback devices}
-\major{ 8}{}{block}{SCSI disk devices (0-15)}
-\major{ 9}{}{char }{SCSI tape devices}
-\major{  }{}{block}{Metadisk (RAID) devices}
-\major{10}{}{char }{Non-serial mice, misc features}
-\major{11}{}{char }{Raw keyboard device}
-\major{  }{}{block}{SCSI CD-ROM devices}
-\major{12}{}{char }{QIC-02 tape}
-\major{  }{}{block}{MSCDEX CD-ROM callback support}
-\major{13}{}{char }{PC speaker}
-\major{  }{}{block}{8-bit MFM/RLL/IDE controller}
-\major{14}{}{char }{Sound card}
-\major{  }{}{block}{BIOS harddrive callback support}
-\major{15}{}{char }{Joystick}
-\major{  }{}{block}{Sony CDU-31A/CDU-33A CD-ROM}
-\major{16}{}{char }{Non-SCSI scanners}
-\major{  }{}{block}{GoldStar CD-ROM}
-\major{17}{}{char }{Chase serial card}
-\major{  }{}{block}{Optics Storage CD-ROM}
-\major{18}{}{char }{Chase serial card -- alternate devices}
-\major{  }{}{block}{Sanyo CD-ROM}
-\major{19}{}{char }{Cyclades serial card}
-\major{  }{}{block}{``Double'' compressed disk}
-\major{20}{}{char }{Cyclades serial card -- alternate devices}
-\major{  }{}{block}{Hitachi CD-ROM}
-\major{21}{}{char }{Generic SCSI access}
-\major{  }{}{block }{Acorn MFM hard drive interface}
-\major{22}{}{char }{Digiboard serial card}
-\major{  }{}{block}{Second IDE hard disk/CD-ROM interface}
-\major{23}{}{char }{Digiboard serial card -- alternate devices}
-\major{  }{}{block}{Mitsumi proprietary CD-ROM}
-\major{24}{}{char }{Stallion serial card}
-\major{  }{}{block}{Sony CDU-535 CD-ROM}
-\major{25}{}{char }{Stallion serial card -- alternate devices}
-\major{  }{}{block}{First Matsushita (Panasonic/SoundBlaster) CD-ROM}
-\major{26}{}{char }{Quanta WinVision frame grabber}
-\major{  }{}{block}{Second Matsushita (Panasonic/SoundBlaster) CD-ROM}
-\major{27}{}{char }{QIC-117 tape}
-\major{  }{}{block}{Third Matsushita (Panasonic/SoundBlaster) CD-ROM}
-\major{28}{}{char }{Stallion serial card -- card programming}
-\major{  }{}{char }{Atari SLM ACSI laser printer (68k/Atari)}
-\major{  }{}{block}{Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM}
-\major{  }{}{block}{ACSI disk/CD-ROM (68k/Atari)}
-\major{29}{}{char }{Universal frame buffer}
-\major{  }{}{block}{Aztech/Orchid/Okano/Wearnes CD-ROM}
-\major{30}{}{char }{iBCS-2 compatibility devices}
-\major{  }{}{block}{Philips LMS CM-205 CD-ROM}
-\major{31}{}{char }{MPU-401 MIDI}
-\major{  }{}{block}{ROM/flash memory card}
-\major{32}{}{char }{Specialix serial card}
-\major{  }{}{block}{Philips LMS CM-206 CD-ROM}
-\major{33}{}{char }{Specialix serial card -- alternate devices}
-\major{  }{}{block}{Third IDE hard disk/CD-ROM interface}
-\major{34}{}{char }{Z8530 HDLC driver}
-\major{  }{}{block}{Fourth IDE hard disk/CD-ROM interface}
-\major{35}{}{char }{tclmidi MIDI driver}
-\major{  }{}{block}{Slow memory ramdisk}
-\major{36}{}{char }{Netlink support}
-\major{  }{}{block}{MCA ESDI hard disk}
-\major{37}{}{char }{IDE tape}
-\major{  }{}{block}{Zorro II ramdisk}
-\major{38}{}{char }{Myricom PCI Myrinet board}
-\major{  }{}{block}{Reserved for Linux/AP+}
-\major{39}{}{char }{ML-16P experimental I/O board}
-\major{  }{}{block}{Reserved for Linux/AP+}
-\major{40}{}{char }{Matrox Meteor frame grabber}
-\major{  }{}{block}{Syquest EZ135 parallel port removable drive}
-\major{41}{}{char }{Yet Another Micro Monitor}
-\major{  }{}{block}{MicroSolutions BackPack parallel port CD-ROM}
-\major{42}{}{}{Demo/sample use}
-\major{43}{}{char }{isdn4linux virtual modem}
-\major{  }{}{block}{Network block devices}
-\major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
-\major{  }{}{block}{Flash Translation Layer (FTL) filesystems}
-\major{45}{}{char }{isdn4linux ISDN BRI driver}
-\major{  }{}{block}{Parallel port IDE disk devices}
-\major{46}{}{char }{Comtrol Rocketport serial card}
-\major{  }{}{block}{Parallel port ATAPI CD-ROM devices}
-\major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices}
-\major{  }{}{block}{Parallel port ATAPI disk devices}
-\major{48}{}{char }{SDL RISCom serial card}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{49}{}{char }{SDL RISCom serial card -- alternate devices}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{50}{}{char}{Reserved for GLINT}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{51}{}{char }{Baycom radio modem}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{52}{}{char }{Spellcaster DataComm/BRI ISDN card}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{53}{}{char }{BDM interface for remote debugging MC683xx microcontrollers}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{54}{}{char }{Electrocardiognosis Holter serial card}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{55}{}{char }{DSP56001 digital signal processor}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\major{56}{}{char }{Apple Desktop Bus}
-\major{  }{}{block}{Fifth IDE hard disk/CD-ROM interface}
-\major{57}{}{char }{Hayes ESP serial card}
-\major{  }{}{block}{Sixth IDE hard disk/CD-ROM interface}
-\major{58}{}{char }{Hayes ESP serial card -- alternate devices}
-\major{  }{}{block}{Reserved for logical volume manager}
-\major{59}{}{char }{sf firewall package}
-\major{60}{--63}{}{Local/experimental use}
-\major{64}{}{char }{ENskip kernel encryption package}
-\major{65}{}{char }{Sundance ``plink'' Transputer boards}
-\major{  }{}{block}{SCSI disk devices (16-31)}
-\major{66}{}{char }{YARC PowerPC PCI coprocessor card}
-\major{  }{}{block}{SCSI disk devices (32-47)}
-\major{67}{}{char }{Coda network filesystem}
-\major{  }{}{block}{SCSI disk devices (48-63)}
-\major{68}{}{char }{CAPI 2.0 interface}
-\major{  }{}{block}{SCSI disk devices (64-79)}
-\major{69}{}{char }{MA16 numeric accelerator card}
-\major{  }{}{block}{SCSI disk devices (80-95)}
-\major{70}{}{char }{SpellCaster Protocol Services Interface}
-\major{  }{}{block}{SCSI disk devices (96-111)}
-\major{71}{}{char }{Computone IntelliPort II serial card}
-\major{  }{}{block}{SCSI disk devices (112-127)}
-\major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices}
-\major{73}{}{char }{Computone IntelliPort II serial card -- control devices}
-\major{74}{}{char }{SCI bridge}
-\major{75}{}{char }{Specialix IO8+ serial card}
-\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices}
-\major{77}{}{char }{ComScire Quantum Noise Generator}
-\major{78}{}{char }{PAM Software's multimodem boards}
-\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices}
-\major{80}{}{char }{Photometrics AT200 CCD camera}
-\major{81}{}{char }{video4linux}
-\major{82}{}{char }{WiNRADiO communications receiver card}
-\major{83}{}{char }{Teletext/videotext interfaces}
-\major{84}{}{char }{Ikon 1011[57] Versatec Greensheet Interface}
-\major{85}{}{char }{Linux/SGI shared memory input queue}
-\major{86}{}{char }{SCSI media changer}
-\major{87}{}{char }{Sony Control-A1 stereo control bus}
-\major{88}{}{char }{COMX synchronous serial card}
-\major{  }{}{block}{Seventh IDE hard disk/CD-ROM interface}
-\major{89}{}{char }{I$^2$C bus interface}
-\major{  }{}{block}{Eighth IDE hard disk/CD-ROM interface}
-\major{90}{}{char }{Memory Technology Device (RAM, ROM, Flash)}
-\major{  }{}{block}{Ninth IDE hard disk/CD-ROM interface}
-\major{91}{}{char }{CAN-Bus controller}
-\major{  }{}{block}{Tenth IDE hard disk/CD-ROM interface}
-\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card}
-\major{93}{}{char }{IBM Smart Capture Card frame grabber}
-\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device}
-\major{95}{}{char }{IP filter}
-\major{96}{}{char }{Parallel port ATAPI tape devices}
-\major{97}{}{char }{Parallel port generic ATAPI interface}
-\major{98}{}{char }{Control and Mesurement Device (comedi)}
-\major{99}{}{char }{Raw parallel ports}
-\major{100}{}{char }{POTS (analogue telephone) A/B port}
-\major{101}{}{char }{Motorola DSP 56xxx board}
-\major{102}{}{char }{Philips SAA5249 Teletext signal decoder}
-\major{103}{}{char }{Arla network file system}
-\major{104}{}{char }{Flash BIOS support}
-\major{105}{}{char }{Comtrol VS-1000 serial card}
-\major{106}{}{char }{Comtrol VS-1000 serial card -- alternate devices}
-\major{107}{}{char }{3Dfx Voodoo Graphics device}
-\major{108}{}{char }{Device independent PPP interface}
-\major{109}{}{char }{Reserved for logical volume manager}
-\major{110}{}{char }{miroMEDIA Surround board}
-\major{111}{--119}{}{Unallocated}
-\major{120}{--127}{}{Local/experimental use}
-\major{128}{--135}{char }{Unix98 PTY masters}
-\major{136}{--143}{char }{Unix98 PTY slaves}
-\major{144}{--239}{}{Unallocated}
-\major{240}{--254}{}{Local/experimental use}
-\major{255}{}{}{Reserved}
-\end{devicelist}
-
-\section{Minor numbers}
-
-\begin{devicelist}
-\major{ 0}{}{}{Unnamed devices (e.g. non-device mounts)}
-	\minor{0}{}{reserved as null device number}
-\end{devicelist}
-
-\begin{devicelist}
-\major{ 1}{}{char}{Memory devices}
-	\minor{1}{/dev/mem}{Physical memory access}
-	\minor{2}{/dev/kmem}{Kernel virtual memory access}
-	\minor{3}{/dev/null}{Null device}
-	\minor{4}{/dev/port}{I/O port access}
-	\minor{5}{/dev/zero}{Null byte source}
-	\minor{6}{/dev/core}{OBSOLETE -- should be a link to {\file /proc/kcore}}
-	\minor{7}{/dev/full}{Returns ENOSPC on write}
-	\minor{8}{/dev/random}{Nondeterministic random number generator}
-	\minor{9}{/dev/urandom}{Less secure, but faster random number generator}
-\\
-\major{}{}{block}{RAM disk}
-	\minor{0}{/dev/ram0}{First RAM disk}
-	\minordots
-	\minor{7}{/dev/ram7}{Eighth RAM disk}
-	\minor{250}{/dev/initrd}{Initial RAM disk}
-\end{devicelist}
-
-\noindent
-Earlier kernels had {\file /dev/ramdisk} (1, 1) here.  {\file /dev/initrd}
-refers to a RAM disk which was preloaded by the boot loader.
-
-\begin{devicelist}
-\major{ 2}{}{char}{Pseudo-TTY masters}
-	\minor{0}{/dev/ptyp0}{First PTY master}
-	\minor{1}{/dev/ptyp1}{Second PTY master}
-	\minordots
-	\minor{255}{/dev/ptyef}{256th PTY master}
-\end{devicelist}
-
-\noindent
-Pseudo-TTY's are named as follows:
-\begin{itemize}
-\item Masters are {\file pty}, slaves are {\file tty};
-\item the fourth letter is one of {\file pqrstuvwxyzabcde} indicating
-the 1st through 16th series of 16 pseudo-ttys each, and
-\item the fifth letter is one of {\file 0123456789abcdef} indicating
-the position within the series.
-\end{itemize}
-
-\noindent
-These are the old-style (BSD) PTY devices; Unix98 devices are on major
-128 and above and use the PTY master multiplex ({\file /dev/ptmx}) to
-acquire a PTY on demand.
-
-\begin{devicelist}
-\major{}{}{block}{Floppy disks}
-	\minor{0}{/dev/fd0}{Controller 1, drive 1 autodetect}
-	\minor{1}{/dev/fd1}{Controller 1, drive 2 autodetect}
-	\minor{2}{/dev/fd2}{Controller 1, drive 3 autodetect}
-	\minor{3}{/dev/fd3}{Controller 1, drive 4 autodetect}
-	\minor{128}{/dev/fd4}{Controller 2, drive 1 autodetect}
-	\minor{129}{/dev/fd5}{Controller 2, drive 2 autodetect}
-	\minor{130}{/dev/fd6}{Controller 2, drive 3 autodetect}
-	\minor{131}{/dev/fd7}{Controller 2, drive 4 autodetect}
-\\
-\major{}{}{}{To specify format, add to the autodetect device number}
-	\minor{  0}{/dev/fd?}{Autodetect format}
-	\minor{}{}{}
-	\minor{  4}{/dev/fd?d360}{5.25\tum\ \num{4}{360}K in a \num{4}{360}K drive\1}
-	\minor{ 20}{/dev/fd?h360}{5.25\tum\ \num{4}{360}K in a 1200K drive\1}
-	\minor{ 48}{/dev/fd?h410}{5.25\tum\ \num{4}{410}K in a 1200K drive}
-	\minor{ 64}{/dev/fd?h420}{5.25\tum\ \num{4}{420}K in a 1200K drive}
-	\minor{ 24}{/dev/fd?h720}{5.25\tum\ \num{4}{720}K in a 1200K drive}
-	\minor{ 80}{/dev/fd?h880}{5.25\tum\ \num{4}{880}K in a 1200K drive\1}
-	\minor{  8}{/dev/fd?h1200}{5.25\tum\ 1200K in a 1200K drive\1}
-	\minor{ 40}{/dev/fd?h1440}{5.25\tum\ 1440K in a 1200K drive\1}
-	\minor{ 56}{/dev/fd?h1476}{5.25\tum\ 1476K in a 1200K drive}
-	\minor{ 72}{/dev/fd?h1494}{5.25\tum\ 1494K in a 1200K drive}
-	\minor{ 92}{/dev/fd?h1600}{5.25\tum\ 1600K in a 1200K drive\1}
-	\minor{}{}{}
-	\minor{ 12}{/dev/fd?u360}{3.5\tum\ \num{4}{360}K Double Density\2}
-	\minor{ 16}{/dev/fd?u720}{3.5\tum\ \num{4}{720}K Double Density\1}
-	\minor{120}{/dev/fd?u800}{3.5\tum\ \num{4}{800}K Double Density\2}
-	\minor{ 52}{/dev/fd?u820}{3.5\tum\ \num{4}{820}K Double Density}
-	\minor{ 68}{/dev/fd?u830}{3.5\tum\ \num{4}{830}K Double Density}
-	\minor{ 84}{/dev/fd?u1040}{3.5\tum\ 1040K Double Density\1}
-	\minor{ 88}{/dev/fd?u1120}{3.5\tum\ 1120K Double Density\1}
-	\minor{ 28}{/dev/fd?u1440}{3.5\tum\ 1440K High Density\1}
-	\minor{124}{/dev/fd?u1600}{3.5\tum\ 1600K High Density\1}
-	\minor{ 44}{/dev/fd?u1680}{3.5\tum\ 1680K High Density\3}
-	\minor{ 60}{/dev/fd?u1722}{3.5\tum\ 1722K High Density}
-	\minor{ 76}{/dev/fd?u1743}{3.5\tum\ 1743K High Density}
-	\minor{ 96}{/dev/fd?u1760}{3.5\tum\ 1760K High Density}
-	\minor{116}{/dev/fd?u1840}{3.5\tum\ 1840K High Density\3}
-	\minor{100}{/dev/fd?u1920}{3.5\tum\ 1920K High Density\1}
-	\minor{ 32}{/dev/fd?u2880}{3.5\tum\ 2880K Extra Density\1}
-	\minor{104}{/dev/fd?u3200}{3.5\tum\ 3200K Extra Density}
-	\minor{108}{/dev/fd?u3520}{3.5\tum\ 3520K Extra Density}
-	\minor{112}{/dev/fd?u3840}{3.5\tum\ 3840K Extra Density\1}
-	\minor{}{}{}
-	\minor{36}{/dev/fd?CompaQ}{Compaq 2880K drive; probably obsolete}
-\\
-\major{}{}{}{\1 Autodetectable format}
-\major{}{}{}{\2 Autodetectable format in a Double Density (720K) drive only}
-\major{}{}{}{\3 Autodetectable format in a High Density (1440K) drive only}
-\end{devicelist}
-
-NOTE: The letter in the device name ({\file d}, {\file q}, {\file h}
-or {\file u}) signifies the type of drive supported: 5.25\tum\ Double
-Density ({\file d}), 5.25\tum\ Quad Density ({\file q}), 5.25\tum\
-High Density ({\file h}) or 3.5\tum\ (any type, {\file u}).  The
-capital letters {\file D}, {\file H}, or {\file E} for the 3.5\tum\
-models have been deprecated, since the drive type is insignificant for
-these devices.
-
-\begin{devicelist}
-\major{ 3}{}{char}{Pseudo-TTY slaves}
-	\minor{0}{/dev/ttyp0}{First PTY slave}
-	\minor{1}{/dev/ttyp1}{Second PTY slave}
-	\minordots
-	\minor{255}{/dev/ttyef}{256th PTY slave}
-\end{devicelist}
-
-\noindent
-These are the old-style (BSD) PTY devices; Unix98 devices are on major
-136 and above.
-
-\begin{devicelist}
-\major{}{}{block}{First MFM, RLL and IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hda}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdb}{Slave: whole disk (or CD-ROM)}
-\\
-\major{}{}{}{For partitions, add to the whole disk device number}
-	\minor{0}{/dev/hd?}{Whole disk}
-	\minor{1}{/dev/hd?1}{First partition}
-	\minor{2}{/dev/hd?2}{Second partition}
-	\minordots
-	\minor{63}{/dev/hd?63}{63rd partition}
-\end{devicelist}
-
-\noindent
-For MS-DOS style partition tables (typically used by Linux/i386 and
-sometimes on Linux/Alpha), partitions 1-4 are the primary partitions,
-partitions 5 and up are logical partitions.  For other partitioning
-schemes, the meaning of the numbers vary.
-
-\begin{devicelist}
-\major{ 4}{}{char }{TTY devices}
-	\minor{0}{/dev/tty0}{Current virtual console}
-	\minor{1}{/dev/tty1}{First virtual console}
-	\minordots
-	\minor{63}{/dev/tty63}{63rd virtual console}
-	\minor{64}{/dev/ttyS0}{First serial port}
-	\minordots
-	\minor{127}{/dev/ttyS63}{64th serial port}
-	\minor{128}{/dev/ptyp0}{OBSOLETE}
-	\minordots
-	\minor{191}{/dev/ptysf}{OBSOLETE}
-	\minor{192}{/dev/ttyp0}{OBSOLETE}
-	\minordots
-	\minor{255}{/dev/ttysf}{OBSOLETE}
-\end{devicelist}
-
-\noindent
-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.
-
-\begin{devicelist}
-\major{ 5}{}{char }{Alternate TTY devices}
-	\minor{0}{/dev/tty}{Current TTY device}
-	\minor{1}{/dev/console}{System console}
-	\minor{2}{/dev/ptmx}{PTY master multiplex}
-	\minor{64}{/dev/cua0}{Callout device corresponding to {\file ttyS0}}
-	\minordots
-	\minor{127}{/dev/cua63}{Callout device corresponding to {\file ttyS63}}
-\end{devicelist}
-
-\noindent
-(5,1) is {\file /dev/console} starting with Linux 2.1.71.  See the
-section on terminal devices for more information on {\file /dev/console}.
-
-\begin{devicelist}
-\major{ 6}{}{char }{Parallel printer devices}
-	\minor{0}{/dev/lp0}{First parallel printer ({\hex 0x3bc})}
-	\minor{1}{/dev/lp1}{Second parallel printer ({\hex 0x378})}
-	\minor{2}{/dev/lp2}{Third parallel printer ({\hex 0x278})}
-\end{devicelist}
-
-\noindent
-Not all computers have the {\hex 0x3bc} parallel port, hence the
-"first" printer may be either {\file /dev/lp0} or {\file /dev/lp1}.
-
-\begin{devicelist}
-\major{ 7}{}{char }{Virtual console access devices}
-	\minor{0}{/dev/vcs}{Current vc text access}
-	\minor{1}{/dev/vcs1}{tty1 text access}
-	\minordots
-	\minor{63}{/dev/vcs63}{tty63 text access}
-	\minor{128}{/dev/vcsa}{Current vc text/attribute access}
-	\minor{129}{/dev/vcsa1}{tty1 text/attribute access}
-	\minordots
-	\minor{191}{/dev/vcsa63}{tty63 text/attribute access}
-\end{devicelist}
-
-\noindent
-NOTE: These devices permit both read and write access.
-
-\begin{devicelist}
-\major{  }{}{block}{Loopback devices}
-	\minor{0}{/dev/loop0}{First loopback device}
-	\minor{1}{/dev/loop1}{Second loopback device}
-	\minordots
-\end{devicelist}
-
-\noindent
-The loopback devices are used to mount filesystems not associated with
-block devices.  The binding to the loopback devices is handled by
-{\bf mount}(8) or {\bf losetup}(8).
-
-\begin{devicelist}
-\major{ 8}{}{block}{SCSI disk devices (0-15)}
-	\minor{0}{/dev/sda}{First SCSI disk whole disk}
-	\minor{16}{/dev/sdb}{Second SCSI disk whole disk}
-	\minor{32}{/dev/sdc}{Third SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sdp}{Sixteenth SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{ 9}{}{char }{SCSI tape devices}
-	\minor{0}{/dev/st0}{First SCSI tape, mode 0}
-	\minor{1}{/dev/st1}{Second SCSI tape, mode 0}
-	\minordots
-	\minor{32}{/dev/st0l}{First SCSI tape, mode 1}
-	\minor{33}{/dev/st1l}{Second SCSI tape, mode 1}
-	\minordots
-	\minor{64}{/dev/st0m}{First SCSI tape, mode 2}
-	\minor{65}{/dev/st1m}{Second SCSI tape, mode 2}
-	\minordots
-	\minor{96}{/dev/st0a}{First SCSI tape, mode 3}
-	\minor{97}{/dev/st1a}{Second SCSI tape, mode 4}
-	\minordots
-	\minor{128}{/dev/nst0}{First SCSI tape, mode 0, no rewind}
-	\minor{129}{/dev/nst1}{Second SCSI tape, mode 0, no rewind}
-	\minordots
-	\minor{160}{/dev/nst0l}{First SCSI tape, mode 1, no rewind}
-	\minor{161}{/dev/nst1l}{Second SCSI tape, mode 1, no rewind}
-	\minordots
-	\minor{192}{/dev/nst0m}{First SCSI tape, mode 2, no rewind}
-	\minor{193}{/dev/nst1m}{Second SCSI tape, mode 2, no rewind}
-	\minordots
-	\minor{224}{/dev/nst0a}{First SCSI tape, mode 3, no rewind}
-	\minor{225}{/dev/nst1a}{Second SCSI tape, mode 3, no rewind}
-	\minordots
-\end{devicelist}
-
-\noindent
-``No rewind'' refers to the omission of the default automatic rewind
-on device close.  The {\file MTREW} or {\file MTOFFL} ioctl()s can be
-used to rewind the tape regardless of the device used to access it.
-
-\begin{devicelist}
-\major{  }{}{block}{Metadisk (RAID) devices}
-	\minor{0}{/dev/md0}{First metadisk group}
-	\minor{1}{/dev/md1}{Second metadisk group}
-	\minordots
-\end{devicelist}
-
-\noindent
-The metadisk driver is used to span a filesystem across multiple
-physical disks.
-
-\begin{devicelist}
-\major{10}{}{char }{Non-serial mice, misc features}
-	\minor{0}{/dev/logibm}{Logitech bus mouse}
-	\minor{1}{/dev/psaux}{PS/2-style mouse port}
-	\minor{2}{/dev/inportbm}{Microsoft Inport bus mouse}
-	\minor{3}{/dev/atibm}{ATI XL bus mouse}
-	\minor{4}{/dev/jbm}{J-mouse}
-	\minor{4}{/dev/amigamouse}{Amiga mouse (68k/Amiga)}
-	\minor{5}{/dev/atarimouse}{Atari mouse}
-	\minor{6}{/dev/sunmouse}{Sun mouse}
-	\minor{7}{/dev/amigamouse1}{Second Amiga mouse}
-	\minor{8}{/dev/smouse}{Simple serial mouse driver}
-	\minor{9}{/dev/pc110pad}{IBM PC-110 digitizer pad}
-	\minor{128}{/dev/beep}{Fancy beep device}
-	\minor{129}{/dev/modreq}{Kernel module load request}
-	\minor{130}{/dev/watchdog}{Watchdog timer port}
-	\minor{131}{/dev/temperature}{Machine internal temperature}
-	\minor{132}{/dev/hwtrap}{Hardware fault trap}
-	\minor{133}{/dev/exttrp}{External device trap}
-	\minor{134}{/dev/apm\_bios}{Advanced Power Management BIOS}
-	\minor{135}{/dev/rtc}{Real Time Clock}
-	\minor{139}{/dev/openprom}{SPARC OpenBoot PROM}
-	\minor{140}{/dev/relay8}{Berkshire Products Octal relay card}
-	\minor{141}{/dev/relay16}{Berkshire Products ISO-16 relay card}
-	\minor{142}{/dev/msr}{x86 model specific registers}
-	\minor{143}{/dev/pciconf}{PCI configuration space}
-	\minor{144}{/dev/nvram}{Non-volatile configuration RAM}
-	\minor{145}{/dev/hfmodem}{Soundcard shortwave modem control}
-	\minor{146}{/dev/graphics}{Linux/SGI graphics device}
-	\minor{147}{/dev/opengl}{Linux/SGI OpenGL pipe}
-	\minor{148}{/dev/gfx}{Linux/SGI graphics effects device}
-	\minor{149}{/dev/input/mouse}{Linux/SGI Irix emulation mouse}
-	\minor{150}{/dev/input/keyboard}{Linux/SGI Irix emulation keyboard}
-	\minor{151}{/dev/led}{Front panel LEDs}
-	\minor{153}{/dev/mergemem}{Memory merge device}
-	\minor{154}{/dev/pmu}{Macintosh PowerBook power manager}
-\end{devicelist}
-
-\begin{devicelist}
-\major{11}{}{char }{Raw keyboard device}
-	\minor{0}{/dev/kbd}{Raw keyboard device}
-\end{devicelist}
-
-\noindent
-The raw keyboard device is used on Linux/SPARC only.
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI CD-ROM devices}
-	\minor{0}{/dev/sr0}{First SCSI CD-ROM}
-	\minor{1}{/dev/sr1}{Second SCSI CD-ROM}
-	\minordots
-\end{devicelist}
-
-\noindent
-The prefix {\file /dev/scd} instead of {\file /dev/sr} has been used
-as well, and might make more sense.
-
-\begin{devicelist}
-\major{12}{}{char }{QIC-02 tape}
-	\minor{2}{/dev/ntpqic11}{QIC-11, no rewind-on-close}
-	\minor{3}{/dev/tpqic11}{QIC-11, rewind-on-close}
-	\minor{4}{/dev/ntpqic24}{QIC-24, no rewind-on-close}
-	\minor{5}{/dev/tpqic24}{QIC-24, rewind-on-close}
-	\minor{6}{/dev/ntpqic120}{QIC-120, no rewind-on-close}
-	\minor{7}{/dev/tpqic120}{QIC-120, rewind-on-close}
-	\minor{8}{/dev/ntpqic150}{QIC-150, no rewind-on-close}
-	\minor{9}{/dev/tpqic150}{QIC-150, rewind-on-close}
-\end{devicelist}
-
-\noindent
-The device names specified are proposed -- if there are ``standard''
-names for these devices, please let me know.
-
-\begin{devicelist}
-\major{  }{}{block}{MSCDEX CD-ROM callback support}
-	\minor{0}{/dev/dos\_cd0}{First MSCDEX CD-ROM}
-	\minor{1}{/dev/dos\_cd1}{Second MSCDEX CD-ROM}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{13}{}{char }{PC speaker}
-	\minor{0}{/dev/pcmixer}{Emulates {\file /dev/mixer}}
-	\minor{3}{/dev/pcsp}{Emulates {\file /dev/dsp} (8-bit)}
-	\minor{4}{/dev/pcaudio}{Emulates {\file /dev/audio}}
-	\minor{5}{/dev/pcsp16}{Emulates {\file /dev/dsp} (16-bit)}
-\\
-\major{  }{}{block}{8-bit MFM/RLL/IDE controller}
-	\minor{0}{/dev/xda}{First XT disk whole disk}
-	\minor{64}{/dev/xdb}{Second XT disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3).
-
-\begin{devicelist}
-\major{14}{}{char }{Sound card}
-	\minor{0}{/dev/mixer}{Mixer control}
-	\minor{1}{/dev/sequencer}{Audio sequencer}
-	\minor{2}{/dev/midi00}{First MIDI port}
-	\minor{3}{/dev/dsp}{Digital audio}
-	\minor{4}{/dev/audio}{Sun-compatible digital audio}
-	\minor{6}{/dev/sndstat}{Sound card status information}
-	\minor{8}{/dev/sequencer2}{Sequencer -- alternate device}
-	\minor{16}{/dev/mixer1}{Second soundcard mixer control}
-	\minor{17}{/dev/patmgr0}{Sequencer patch manager}
-	\minor{18}{/dev/midi01}{Second MIDI port}
-	\minor{19}{/dev/dsp1}{Second soundcard digital audio}
-	\minor{20}{/dev/audio1}{Second soundcard Sun digital audio}
-	\minor{33}{/dev/patmgr1}{Sequencer patch manager}
-	\minor{34}{/dev/midi02}{Third MIDI port}
-	\minor{50}{/dev/midi03}{Fourth MIDI port}
-\\
-\major{  }{}{block}{BIOS harddrive callback support}
-	\minor{0}{/dev/dos\_hda}{First BIOS harddrive whole disk}
-	\minor{64}{/dev/dos\_hdb}{Second BIOS harddrive whole disk}
-	\minor{128}{/dev/dos\_hdc}{Third BIOS harddrive whole disk}
-	\minor{192}{/dev/dos\_hdd}{Fourth BIOS harddrive whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3).
-
-\begin{devicelist}
-\major{15}{}{char }{Joystick}
-	\minor{0}{/dev/js0}{First analog joystick}
-	\minor{1}{/dev/js1}{Second analog joystick}
-	\minordots
-	\minor{128}{/dev/djs0}{First digital joystick}
-	\minor{129}{/dev/djs1}{Second digital joystick}
-	\minordots
-\\
-\major{  }{}{block}{Sony CDU-31A/CDU-33A CD-ROM}
-	\minor{0}{/dev/sonycd}{Sony CDU-31A CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{16}{}{char }{Non-SCSI scanners}
-	\minor{0}{/dev/gs4500}{Genius 4500 handheld scanner}
-\\
-\major{  }{}{block}{GoldStar CD-ROM}
-	\minor{0}{/dev/gscd}{GoldStar CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{17}{}{char }{Chase serial card}
-	\minor{0}{/dev/ttyH0}{First Chase port}
-	\minor{1}{/dev/ttyH1}{Second Chase port}
-	\minordots
-\\
-\major{  }{}{block}{Optics Storage CD-ROM}
-	\minor{0}{/dev/optcd}{Optics Storage CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{18}{}{char }{Chase serial card -- alternate devices}
-	\minor{0}{/dev/cuh0}{Callout device corresponding to {\file ttyH0}}
-	\minor{1}{/dev/cuh1}{Callout device corresponding to {\file ttyH1}}
-	\minordots
-\\
-\major{  }{}{block}{Sanyo CD-ROM}
-	\minor{0}{/dev/sjcd}{Sanyo CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{19}{}{char }{Cyclades serial card}
-	\minor{0}{/dev/ttyC0}{First Cyclades port}
-	\minordots
-	\minor{31}{/dev/ttyC31}{32nd Cyclades port}
-\\
-\major{  }{}{block}{``Double'' compressed disk}
-	\minor{0}{/dev/double0}{First compressed disk}
-	\minordots
-	\minor{7}{/dev/double7}{Eighth compressed disk}
-	\minor{128}{/dev/cdouble0}{Mirror of first compressed disk}
-	\minordots
-	\minor{135}{/dev/cdouble7}{Mirror of eighth compressed disk}
-\end{devicelist}
-
-\noindent
-See the Double documentation for an explanation of the ``mirror'' devices.
-
-\begin{devicelist}
-\major{20}{}{char }{Cyclades serial card -- alternate devices}
-	\minor{0}{/dev/cub0}{Callout device corresponding to {\file ttyC0}}
-	\minordots
-	\minor{31}{/dev/cub31}{Callout device corresponding to {\file ttyC31}}
-\\
-\major{  }{}{block}{Hitachi CD-ROM}
-	\minor{0}{/dev/hitcd}{Hitachi CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{21}{}{char }{Generic SCSI access}
-	\minor{0}{/dev/sg0}{First generic SCSI device}
-	\minor{1}{/dev/sg1}{Second generic SCSI device}
-	\minordots
-\end{devicelist}
-
-\noindent
-Most distributions name these {\file /dev/sga}, {\file /dev/sgb}...
-This sets an unneccesary limit of 26 SCSI devices in the system, and
-is counter to standard Linux device-naming practice.
-
-\begin{devicelist}
-\major{  }{}{block }{Acorn MFM hard drive interface}
-	\minor{0}{/dev/mfma}{First MFM drive whole disk}
-	\minor{64}{/dev/mfmb}{Second MFM drive whole disk}
-\end{devicelist}
-
-\noindent
-This device is used on the ARM-based Acorn RiscPC.  Partitions are
-handled the same way as for IDE disks (see major number 3).
-
-\begin{devicelist}
-\major{22}{}{char }{Digiboard serial card}
-	\minor{0}{/dev/ttyD0}{First Digiboard port}
-	\minor{1}{/dev/ttyD1}{Second Digiboard port}
-	\minordots
-\\
-\major{  }{}{block}{Second IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hdc}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdd}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{23}{}{char }{Digiboard serial card -- alternate devices}
-	\minor{0}{/dev/cud0}{Callout device corresponding to {\file ttyD0}}
-	\minor{1}{/dev/cud1}{Callout device corresponding to {\file ttyD1}}
-	\minordots
-\major{  }{}{block}{Mitsumi proprietary CD-ROM}
-	\minor{0}{/dev/mcd}{Mitsumi CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}\
-\major{24}{}{char }{Stallion serial card}
-	\minor{0}{/dev/ttyE0}{Stallion port 0 board 0}
-	\minor{1}{/dev/ttyE1}{Stallion port 1 board 0}
-	\minordots
-	\minor{64}{/dev/ttyE64}{Stallion port 0 board 1}
-	\minor{65}{/dev/ttyE65}{Stallion port 1 board 1}
-	\minordots
-	\minor{128}{/dev/ttyE128}{Stallion port 0 board 2}
-	\minor{129}{/dev/ttyE129}{Stallion port 1 board 2}
-	\minordots
-	\minor{192}{/dev/ttyE192}{Stallion port 0 board 3}
-	\minor{193}{/dev/ttyE193}{Stallion port 1 board 3}
-	\minordots
-\\
-\major{  }{}{block}{Sony CDU-535 CD-ROM}
-	\minor{0}{/dev/cdu535}{Sony CDU-535 CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{25}{}{char }{Stallion serial card -- alternate devices}
-	\minor{0}{/dev/cue0}{Callout device corresponding to {\file ttyE0}}
-	\minor{1}{/dev/cue1}{Callout device corresponding to {\file ttyE1}}
-	\minordots
-	\minor{64}{/dev/cue64}{Callout device corresponding to {\file ttyE64}}
-	\minor{65}{/dev/cue65}{Callout device corresponding to {\file ttyE65}}
-	\minordots
-	\minor{128}{/dev/cue128}{Callout device corresponding to {\file ttyE128}}
-	\minor{129}{/dev/cue129}{Callout device corresponding to {\file ttyE129}}
-	\minordots
-	\minor{192}{/dev/cue192}{Callout device corresponding to {\file ttyE192}}
-	\minor{193}{/dev/cue193}{Callout device corresponding to {\file ttyE193}}
-	\minordots
-\\
-\major{  }{}{block}{First Matsushita (Panasonic/SoundBlaster) CD-ROM}
-	\minor{0}{/dev/sbpcd0}{Panasonic CD-ROM controller 0 unit 0}
-	\minor{1}{/dev/sbpcd1}{Panasonic CD-ROM controller 0 unit 1}
-	\minor{2}{/dev/sbpcd2}{Panasonic CD-ROM controller 0 unit 2}
-	\minor{3}{/dev/sbpcd3}{Panasonic CD-ROM controller 0 unit 3}
-\end{devicelist}
-
-\begin{devicelist}
-\major{26}{}{char }{Quanta WinVision frame grabber}
-	\minor{0}{/dev/wvisfgrab}{Quanta WinVision frame grabber}
-\\
-\major{  }{}{block}{Second Matsushita (Panasonic/SoundBlaster) CD-ROM}
-	\minor{0}{/dev/sbpcd4}{Panasonic CD-ROM controller 1 unit 0}
-	\minor{1}{/dev/sbpcd5}{Panasonic CD-ROM controller 1 unit 1}
-	\minor{2}{/dev/sbpcd6}{Panasonic CD-ROM controller 1 unit 2}
-	\minor{3}{/dev/sbpcd7}{Panasonic CD-ROM controller 1 unit 3}
-\end{devicelist}
-
-\begin{devicelist}
-\major{27}{}{char }{QIC-117 tape}
-	\minor{0}{/dev/qft0}{Unit 0, rewind-on-close}
-	\minor{1}{/dev/qft1}{Unit 1, rewind-on-close}
-	\minor{2}{/dev/qft2}{Unit 2, rewind-on-close}
-	\minor{3}{/dev/qft3}{Unit 3, rewind-on-close}
-	\minor{4}{/dev/nqft0}{Unit 0, no rewind-on-close}
-	\minor{5}{/dev/nqft1}{Unit 1, no rewind-on-close}
-	\minor{6}{/dev/nqft2}{Unit 2, no rewind-on-close}
-	\minor{7}{/dev/nqft3}{Unit 3, no rewind-on-close}
-	\minor{16}{/dev/zqft0}{Unit 0, rewind-on-close, compression}
-	\minor{17}{/dev/zqft1}{Unit 1, rewind-on-close, compression}
-	\minor{18}{/dev/zqft2}{Unit 2, rewind-on-close, compression}
-	\minor{19}{/dev/zqft3}{Unit 3, rewind-on-close, compression}
-	\minor{20}{/dev/nzqft0}{Unit 0, no rewind-on-close, compression}
-	\minor{21}{/dev/nzqft1}{Unit 1, no rewind-on-close, compression}
-	\minor{22}{/dev/nzqft2}{Unit 2, no rewind-on-close, compression}
-	\minor{23}{/dev/nzqft3}{Unit 3, no rewind-on-close, compression}
-	\minor{32}{/dev/rawqft0}{Unit 0, rewind-on-close, no file marks}
-	\minor{33}{/dev/rawqft1}{Unit 1, rewind-on-close, no file marks}
-	\minor{34}{/dev/rawqft2}{Unit 2, rewind-on-close, no file marks}
-	\minor{35}{/dev/rawqft3}{Unit 3, rewind-on-close, no file marks}
-	\minor{36}{/dev/nrawqft0}{Unit 0, no rewind-on-close, no file marks}
-	\minor{37}{/dev/nrawqft1}{Unit 1, no rewind-on-close, no file marks}
-	\minor{38}{/dev/nrawqft2}{Unit 2, no rewind-on-close, no file marks}
-	\minor{39}{/dev/nrawqft3}{Unit 3, no rewind-on-close, no file marks}
-\\
-\major{  }{}{block}{Third Matsushita (Panasonic/SoundBlaster) CD-ROM}
-	\minor{0}{/dev/sbpcd8}{Panasonic CD-ROM controller 2 unit 0}
-	\minor{1}{/dev/sbpcd9}{Panasonic CD-ROM controller 2 unit 1}
-	\minor{2}{/dev/sbpcd10}{Panasonic CD-ROM controller 2 unit 2}
-	\minor{3}{/dev/sbpcd11}{Panasonic CD-ROM controller 2 unit 3}
-\end{devicelist}
-
-\begin{devicelist}
-\major{28}{}{char }{Stallion serial card -- card programming}
-	\minor{0}{/dev/staliomem0}{First Stallion I/O card memory}
-	\minor{1}{/dev/staliomem1}{Second Stallion I/O card memory}
-	\minor{2}{/dev/staliomem2}{Third Stallion I/O card memory}
-	\minor{3}{/dev/staliomem3}{Fourth Stallion I/O card memory}
-\\
-\major{  }{}{char }{Atari SLM ACSI laser printer (68k/Atari)}
-	\minor{0}{/dev/slm0}{First SLM laser printer}
-	\minor{1}{/dev/slm1}{Second SLM laser printer}
-	\minordots
-\\
-\major{  }{}{block}{Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM}
-	\minor{0}{/dev/sbpcd12}{Panasonic CD-ROM controller 3 unit 0}
-	\minor{1}{/dev/sbpcd13}{Panasonic CD-ROM controller 3 unit 1}
-	\minor{2}{/dev/sbpcd14}{Panasonic CD-ROM controller 3 unit 2}
-	\minor{3}{/dev/sbpcd15}{Panasonic CD-ROM controller 3 unit 3}
-\\
-\major{  }{}{block}{ACSI disk/CD-ROM (68k/Atari)}
-	\minor{0}{/dev/ada}{First ACSI disk whole disk}
-	\minor{16}{/dev/adb}{Second ACSI disk whole disk}
-	\minor{32}{/dev/adc}{Third ACSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/adp}{Sixteenth ACSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk (same as SCSI.)
-
-\begin{devicelist}
-\major{29}{}{char }{Universal frame buffer}
-	\minor{0}{/dev/fb0}{First frame buffer}
-	\minor{1}{/dev/fb1}{Second frame buffer}
-	\minor{2}{/dev/fb2}{Third frame buffer}
-	\minordots
-	\minor{31}{/dev/fb31}{32nd frame buffer}
-\end{devicelist}
-
-\noindent
-All additional minor device numbers are reserved.
-
-\begin{devicelist}
-\major{  }{}{block}{Aztech/Orchid/Okano/Wearnes CD-ROM}
-	\minor{0}{/dev/aztcd}{Aztech CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{30}{}{char }{iBCS-2 compatibility devices}
-	\minor{0}{/dev/socksys}{Socket access}
-	\minor{1}{/dev/spx}{SVR3 local X interface}
-	\minor{2}{/dev/inet/arp}{Network access}
-	\minor{2}{/dev/inet/icmp}{Network access}
-	\minor{2}{/dev/inet/ip}{Network access}
-	\minor{2}{/dev/inet/udp}{Network access}
-	\minor{2}{/dev/inet/tcp}{Network access}
-\end{devicelist}
-
-\noindent
-Additionally, iBCS-2 requires {\file /dev/nfsd} to be a link to {\file
-/dev/socksys} and {\file /dev/X0R} to be a link to {\file /dev/null}.
-
-\begin{devicelist}
-\major{  }{}{block}{Philips LMS CM-205 CD-ROM}
-	\minor{0}{/dev/cm205cd}{Philips LMS CM-205 CD-ROM}
-\end{devicelist}
-
-\noindent
-{\file /dev/lmscd} is an older name for this drive.  This driver does
-not work with the CM-205MS CD-ROM.
-
-\begin{devicelist}
-\major{31}{}{char }{MPU-401 MIDI}
-	\minor{0}{/dev/mpu401data}{MPU-401 data port}
-	\minor{1}{/dev/mpu401stat}{MPU-401 status port}
-\\
-\major{  }{}{block}{ROM/flash memory card}
-	\minor{0}{/dev/rom0}{First ROM card (rw)}
-	\minordots
-	\minor{7}{/dev/rom7}{Eighth ROM card (rw)}
-	\minor{8}{/dev/rrom0}{First ROM card (ro)}
-	\minordots
-	\minor{15}{/dev/rrom0}{Eighth ROM card (ro)}
-	\minor{16}{/dev/flash0}{First flash memory card (rw)}
-	\minordots
-	\minor{23}{/dev/flash7}{Eighth flash memory card (rw)}
-	\minor{24}{/dev/rflash0}{First flash memory card (ro)}
-	\minordots
-	\minor{31}{/dev/rflash7}{Eighth flash memory card (ro)}
-\end{devicelist}
-
-\noindent
-The read-write (rw) devices support back-caching written data in RAM,
-as well as writing to flash RAM devices.  The read-only devices (ro)
-support reading only.
-
-\begin{devicelist}
-\major{32}{}{char }{Specialix serial card}
-	\minor{0}{/dev/ttyX0}{First Specialix port}
-	\minor{1}{/dev/ttyX1}{Second Specialix port}
-	\minordots
-\\
-\major{  }{}{block}{Philips LMS CM-206 CD-ROM}
-	\minor{0}{/dev/cm206cd}{Philips LMS CM-206 CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{33}{}{char }{Specialix serial card -- alternate devices}
-	\minor{0}{/dev/cux0}{Callout device corresponding to {\file ttyX0}}
-	\minor{1}{/dev/cux1}{Callout device corresponding to {\file ttyX1}}
-	\minordots
-\\
-\major{  }{}{block}{Third IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hde}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdf}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{34}{}{char }{Z8530 HDLC driver}
-	\minor{0}{/dev/scc0}{First Z8530, first port}
-	\minor{1}{/dev/scc1}{First Z8530, second port}
-	\minor{2}{/dev/scc2}{Second Z8530, first port}
-	\minor{3}{/dev/scc3}{Second Z8530, second port}
-	\minordots
-\end{devicelist}
-
-\noindent
-In a previous version these devices were named {\file /dev/sc1} for
-{\file /dev/scc0}, {\file /dev/sc2} for {\file /dev/scc1}, and so on.
-
-\begin{devicelist}
-\major{  }{}{block}{Fourth IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hdg}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdh}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{35}{}{char }{tclmidi MIDI driver}
-	\minor{0}{/dev/midi0}{First MIDI port, kernel timed}
-	\minor{1}{/dev/midi1}{Second MIDI port, kernel timed}
-	\minor{2}{/dev/midi2}{Third MIDI port, kernel timed}
-	\minor{3}{/dev/midi3}{Fourth MIDI port, kernel timed}
-	\minor{64}{/dev/rmidi0}{First MIDI port, untimed}
-	\minor{65}{/dev/rmidi1}{Second MIDI port, untimed}
-	\minor{66}{/dev/rmidi2}{Third MIDI port, untimed}
-	\minor{67}{/dev/rmidi3}{Fourth MIDI port, untimed}
-	\minor{128}{/dev/smpte0}{First MIDI port, SMPTE timed}
-	\minor{129}{/dev/smpte1}{Second MIDI port, SMPTE timed}
-	\minor{130}{/dev/smpte2}{Third MIDI port, SMPTE timed}
-	\minor{131}{/dev/smpte3}{Fourth MIDI port, SMPTE timed}
-\\
-\major{  }{}{block}{Slow memory ramdisk}
-	\minor{0}{/dev/slram}{Slow memory ramdisk}
-\end{devicelist}
-
-\begin{devicelist}
-\major{36}{}{char }{Netlink support}
-	\minor{0}{/dev/route}{Routing, device updates (kernel to user)}
-	\minor{1}{/dev/skip}{enSKIP security cache control}
-\\
-\major{  }{}{block}{MCA ESDI hard disk}
-	\minor{0}{/dev/eda}{First ESDI disk whole disk}
-	\minor{64}{/dev/edb}{Second ESDI disk whole disk}
-	\minordots
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for IDE disks (see major number
-3).
-
-\begin{devicelist}
-\major{37}{}{char }{IDE tape}
-	\minor{0}{/dev/ht0}{First IDE tape}
-	\minor{128}{/dev/nht0}{First IDE tape, no rewind-on-close}
-\end{devicelist}
-
-\noindent
-Currently, only one IDE tape drive is supported.
-
-\begin{devicelist}
-\major{  }{}{block}{Zorro II ramdisk}
-	\minor{0}{/dev/z2ram}{Zorro II ramdisk}
-\end{devicelist}
-
-\begin{devicelist}
-\major{38}{}{char }{Myricom PCI Myrinet board}
-	\minor{0}{/dev/mlanai0}{First Myrinet board}
-	\minor{1}{/dev/mlanai1}{Second Myrinet board}
-	\minordots
-\end{devicelist}
-
-\noindent
-This device is used for board control, status query and ``user level
-packet I/O''.  The board is also accessible as a regular {\file eth}
-networking device.
-
-\begin{devicelist}
-\major{  }{}{block}{Reserved for Linux/AP+}
-\end{devicelist}
-
-\begin{devicelist}
-\major{39}{}{char }{ML-16P experimental I/O board}
-	\minor{0}{/dev/ml16pa-a0}{First card, first analog channel}
-	\minor{1}{/dev/ml16pa-a1}{First card, second analog channel}
-	\minordots
-	\minor{15}{/dev/ml16pa-a15}{First card, 16th analog channel}
-	\minor{16}{/dev/ml16pa-d}{First card, digital lines}
-	\minor{17}{/dev/ml16pa-c0}{First card, first counter/timer}
-	\minor{18}{/dev/ml16pa-c1}{First card, second counter/timer}
-	\minor{19}{/dev/ml16pa-c2}{First card, third counter/timer}
-	\minor{32}{/dev/ml16pb-a0}{Second card, first analog channel}
-	\minor{33}{/dev/ml16pb-a1}{Second card, second analog channel}
-	\minordots
-	\minor{47}{/dev/ml16pb-a15}{Second card, 16th analog channel}
-	\minor{48}{/dev/ml16pb-d}{Second card, digital lines}
-	\minor{49}{/dev/ml16pb-c0}{Second card, first counter/timer}
-	\minor{50}{/dev/ml16pb-c1}{Second card, second counter/timer}
-	\minor{51}{/dev/ml16pb-c2}{Second card, third counter/timer}
-	\minordots
-\\
-\major{  }{}{block}{Reserved for Linux/AP+}
-\end{devicelist}
-
-\begin{devicelist}
-\major{40}{}{char }{Matrox Meteor frame grabber}
-	\minor{0}{/dev/mmetfgrab}{Matrox Meteor frame grabber}
-\\
-\major{  }{}{block}{Syquest EZ135 parallel port removable drive}
-	\minor{0}{/dev/eza}{Parallel EZ135 drive whole disk}
-\end{devicelist}
-
-\noindent
-This device is obsolete and will be removed in a future version of
-Linux.  It has been replaced with the parallel port IDE disk driver at
-major number 45.  Partitions are handled the same way as for IDE disks
-(see major number 3).
-
-\begin{devicelist}
-\major{41}{}{char }{Yet Another Micro Monitor}
-	\minor{0}{/dev/yamm}{Yet Another Micro Monitor}
-\\
-\major{  }{}{block}{MicroSolutions BackPack parallel port CD-ROM}
-	\minor{0}{/dev/bpcd}{BackPack CD-ROM}
-\end{devicelist}
-
-\noindent
-This device is obsolete and will be removed in a future version of
-Linux.  It has been replaced with the parallel port ATAPI CD-ROM
-driver at major number 46.
-
-\begin{devicelist}
-\major{42}{}{}{Demo/sample use}
-\end{devicelist}
-
-\noindent
-This number is intended for use in sample code, as well as a general
-``example'' device number.  It should never be used for a device
-driver that is being distributed; either obtain an official number or
-use the local/experimental range.  The sudden addition or removal of a
-driver with this number should not cause ill effects to the system
-(bugs excepted.)
-
-IN PARTICULAR, ANY DISTRIBUTION WHICH CONTAINS A DEVICE DRIVER USING
-MAJOR NUMBER 42 IS NONCOMPLIANT.
-
-\begin{devicelist}
-\major{43}{}{char }{isdn4linux virtual modem}
-	\minor{0}{/dev/ttyI0}{First virtual modem}
-	\minordots
-	\minor{63}{/dev/ttyI63}{64th virtual modem}
-\\
-\major{  }{}{block}{Network block devices}
-	\minor{0}{/dev/nd0}{First network block device}
-	\minor{1}{/dev/nd1}{Second network block device}
-	\minordots
-\end{devicelist}
-
-\noindent
-Network Block Device is somehow similar to loopback devices: If you
-read from it, it sends packet accross network asking server for
-data. If you write to it, it sends packet telling server to write. It
-could be used to mounting filesystems over the net, swapping over the
-net, implementing block device in userland etc.
-
-\begin{devicelist}
-\major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
-	\minor{0}{/dev/cui0}{Callout device corresponding to {\file ttyI0}}
-	\minordots
-	\minor{63}{/dev/cui63}{Callout device corresponding to {\file ttyI63}}
-\\
-\major{  }{}{block}{Flash Translation Layer (FTL) filesystems}
-        \minor{0}{/dev/ftla}{FTL on first Memory Technology Device}
-        \minor{16}{/dev/ftlb}{FTL on second Memory Technology Device}
-        \minor{32}{/dev/ftlc}{FTL on third Memory Technology Device}
-        \minordots
-        \minor{240}{/dev/ftlp}{FTL on 16th Memory Technology Device}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) expect that the partition limit is 15 rather than 63 per
-disk (same as SCSI.)
-
-\begin{devicelist}
-\major{45}{}{char }{isdn4linux ISDN BRI driver}
-	\minor{0}{/dev/isdn0}{First virtual B channel raw data}
-	\minordots
-	\minor{63}{/dev/isdn63}{64th virtual B channel raw data}
-	\minor{64}{/dev/isdnctrl0}{First channel control/debug}
-	\minordots
-	\minor{127}{/dev/isdnctrl63}{64th channel control/debug}
-	\minor{128}{/dev/ippp0}{First SyncPPP device}
-	\minordots
-	\minor{191}{/dev/ippp63}{64th SyncPPP device}
-	\minor{255}{/dev/isdninfo}{ISDN monitor interface}
-\\
-\major{  }{}{block}{Parallel port IDE disk devices}
-	\minor{0}{/dev/pda}{First parallel port IDE disk}
-	\minor{16}{/dev/pdb}{Second parallel port IDE disk}
-	\minor{32}{/dev/pdc}{Third parallel port IDE disk}
-	\minor{48}{/dev/pdd}{Fourth parallel port IDE disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{46}{}{char }{Comtrol Rocketport serial card}
-	\minor{0}{/dev/ttyR0}{First Rocketport port}
-	\minor{1}{/dev/ttyR1}{Second Rocketport port}
-	\minordots
-\\
-\major{  }{}{block}{Parallel port ATAPI CD-ROM devices}
-	\minor{0}{/dev/pcd0}{First parallel port ATAPI CD-ROM}
-	\minor{1}{/dev/pcd1}{Second parallel port ATAPI CD-ROM}
-	\minor{2}{/dev/pcd2}{Third parallel port ATAPI CD-ROM}
-	\minor{3}{/dev/pcd3}{Fourth parallel port ATAPI CD-ROM}
-\end{devicelist}
-
-\begin{devicelist}
-\major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices}
-	\minor{0}{/dev/cur0}{Callout device corresponding to {\file ttyR0}}
-	\minor{1}{/dev/cur1}{Callout device corresponding to {\file ttyR1}}
-	\minordots
-\\
-\major{  }{}{block}{Parallel port ATAPI disk devices}
-	\minor{0}{/dev/pf0}{First parallel port ATAPI disk}
-	\minor{1}{/dev/pf1}{Second parallel port ATAPI disk}
-	\minor{2}{/dev/pf2}{Third parallel port ATAPI disk}
-	\minor{3}{/dev/pf3}{Fourth parallel port ATAPI disk}
-\end{devicelist}
-
-\noindent
-This driver is intended for floppy disks and similar devices and hence
-does not support partitioning.
-
-\begin{devicelist}
-\major{48}{}{char }{SDL RISCom serial card}
-	\minor{0}{/dev/ttyL0}{First RISCom port}
-	\minor{1}{/dev/ttyL1}{Second RISCom port}
-	\minordots
-\\
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{49}{}{char }{SDL RISCom serial card -- alternate devices}
-	\minor{0}{/dev/cul0}{Callout device corresponding to {\file ttyL0}}
-	\minor{1}{/dev/cul1}{Callout device corresponding to {\file ttyL1}}
-	\minordots
-\\
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{50}{}{char}{Reserved for GLINT}
-\\
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{51}{}{char }{Baycom radio modem}
-	\minor{0}{/dev/bc0}{First Baycom radio modem}
-	\minor{1}{/dev/bc1}{Second Baycom radio modem}
-	\minordots
-\\
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{52}{}{char }{Spellcaster DataComm/BRI ISDN card}
-	\minor{0}{/dev/dcbri0}{First DataComm card}
-	\minor{1}{/dev/dcbri1}{Second DataComm card}
-	\minor{2}{/dev/dcbri2}{Third DataComm card}
-	\minor{3}{/dev/dcbri3}{Fourth DataComm card}
-\\
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{53}{}{char }{BDM interface for remote debugging MC683xx microcontrollers}
-	\minor{0}{/dev/pd\_bdm0}{PD BDM interface on {\file lp0}}
-	\minor{1}{/dev/pd\_bdm1}{PD BDM interface on {\file lp1}}
-	\minor{2}{/dev/pd\_bdm2}{PD BDM interface on {\file lp2}}
-	\minor{4}{/dev/icd\_bdm0}{ICD BDM interface on {\file lp0}}
-	\minor{5}{/dev/icd\_bdm1}{ICD BDM interface on {\file lp1}}
-	\minor{6}{/dev/icd\_bdm2}{ICD BDM interface on {\file lp2}}
-\end{devicelist}
-
-\noindent
-This device is used for the interfacing to the MC683xx
-microcontrollers via Background Debug Mode by use of a Parallel Port
-interface. PD is the Motorola Public Domain Interface and ICD is the
-commercial interface by P\&E.
-
-\begin{devicelist}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{54}{}{char }{Electrocardiognosis Holter serial card}
-	\minor{0}{/dev/holter0}{First Holter port}
-	\minor{1}{/dev/holter1}{Second Holter port}
-	\minor{2}{/dev/holter2}{Third Holter port}
-\end{devicelist}
-
-\noindent
-A custom serial card used by Electrocardiognosis SRL
-$<$mseritan@ottonel.pub.ro$>$ to transfer data from Holter 24-hour
-heart monitoring equipment.
-
-\begin{devicelist}
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{55}{}{char }{DSP56001 digital signal processor}
-	\minor{0}{/dev/dsp56k}{First DSP56001}
-\\
-\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
-\end{devicelist}
-
-\begin{devicelist}
-\major{56}{}{char }{Apple Desktop Bus}
-	\minor{0}{/dev/adb}{ADB bus control}
-\end{devicelist}
-
-\noindent
-Additional devices will be added to this number, all starting with
-{\file /dev/adb}.
-
-\begin{devicelist}
-\major{  }{}{block}{Fifth IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hdi}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdj}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{57}{}{char }{Hayes ESP serial card}
-	\minor{0}{/dev/ttyP0}{First ESP port}
-	\minor{1}{/dev/ttyP1}{Second ESP port}
-	\minordots
-\\
-\major{  }{}{block}{Sixth IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hdk}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdl}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{58}{}{char }{Hayes ESP serial card -- alternate devices}
-	\minor{0}{/dev/cup0}{Callout device corresponding to {\file ttyP0}}
-	\minor{1}{/dev/cup1}{Callout device corresponding to {\file ttyP1}}
-	\minordots
-\\
-\major{  }{}{block}{Reserved for logical volume manager}
-\end{devicelist}
-
-\begin{devicelist}
-\major{59}{}{char }{sf firewall package}
-	\minor{0}{/dev/firewall}{Communication with sf kernel module}
-\end{devicelist}
-
-\begin{devicelist}
-\major{60}{--63}{}{Local/experimental use}
-\end{devicelist}
-
-\noindent
-For devices not assigned official numbers, these ranges should be
-used, in order to avoid conflict with future assignments.
-
-\begin{devicelist}
-\major{64}{}{char }{ENskip kernel encryption package}
-	\minor{0}{/dev/enskip}{Communication with ENskip kernel
-	module}
-\end{devicelist}
-
-\begin{devicelist}
-\major{65}{}{char }{Sundance ``plink'' Transputer boards}
-	\minor{0}{/dev/plink0}{First plink device}
-	\minor{1}{/dev/plink1}{Second plink device}
-	\minor{2}{/dev/plink2}{Third plink device}
-	\minor{3}{/dev/plink3}{Fourth plink device}
-	\minor{64}{/dev/rplink0}{First plink device, raw}
-	\minor{65}{/dev/rplink1}{Second plink device, raw}
-	\minor{66}{/dev/rplink2}{Third plink device, raw}
-	\minor{67}{/dev/rplink3}{Fourth plink device, raw}
-	\minor{128}{/dev/plink0d}{First plink device, debug}
-	\minor{129}{/dev/plink1d}{Second plink device, debug}
-	\minor{130}{/dev/plink2d}{Third plink device, debug}
-	\minor{131}{/dev/plink3d}{Fourth plink device, debug}
-	\minor{192}{/dev/rplink0d}{First plink device, raw, debug}
-	\minor{193}{/dev/rplink1d}{Second plink device, raw, debug}
-	\minor{194}{/dev/rplink2d}{Third plink device, raw, debug}
-	\minor{195}{/dev/rplink3d}{Fourth plink device, raw, debug}
-\end{devicelist}
-
-\noindent
-This is a commercial driver; contact James Howes
-$<$jth@prosig.demon.co.uk$>$ for information. 
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (16-31)}
-	\minor{0}{/dev/sdq}{17th SCSI disk whole disk}
-	\minor{16}{/dev/sdr}{18th SCSI disk whole disk}
-	\minor{32}{/dev/sds}{19th SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sdaf}{32nd SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{66}{}{char }{YARC PowerPC PCI coprocessor card}
-	\minor{0}{/dev/yppcpci0}{First YARC card}
-	\minor{1}{/dev/yppcpci1}{Second YARC card}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (32-47)}
-	\minor{0}{/dev/sdag}{33rd SCSI disk whole disk}
-	\minor{16}{/dev/sdah}{34th SCSI disk whole disk}
-	\minor{32}{/dev/sdai}{35th SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sdav}{48th SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{67}{}{char }{Coda network filesystem}
-	\minor{0}{/dev/cfs0}{Coda cache manager}
-\end{devicelist}
-
-\noindent
-See {\url http://www.coda.cs.cmu.edu\/} for information about Coda.
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (48-63)}
-	\minor{0}{/dev/sdaw}{49th SCSI disk whole disk}
-	\minor{16}{/dev/sdax}{50th SCSI disk whole disk}
-	\minor{32}{/dev/sday}{51st SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sdbl}{64th SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{68}{}{char }{CAPI 2.0 interface}
-	\minor{0}{/dev/capi20}{Control device}
-	\minor{1}{/dev/capi20.00}{First CAPI 2.0 application}
-	\minor{2}{/dev/capi20.01}{Second CAPI 2.0 application}
-	\minordots
-	\minor{20}{/dev/capi20.19}{19th CAPI 2.0 application}
-\end{devicelist}
-
-\noindent
-ISDN CAPI 2.0 driver for use with CAPI 2.0 applications; currently
-supports the AVM B1 card.
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (64-79)}
-	\minor{0}{/dev/sdbm}{65th SCSI disk whole disk}
-	\minor{16}{/dev/sdbn}{66th SCSI disk whole disk}
-	\minor{32}{/dev/sdbo}{67th SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sdcb}{80th SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{69}{}{char }{MA16 numeric accelerator card}
-	\minor{0}{/dev/ma16}{Board memory access}
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (80-95)}
-	\minor{0}{/dev/sdcc}{81st SCSI disk whole disk}
-	\minor{16}{/dev/sdcd}{82nd SCSI disk whole disk}
-	\minor{32}{/dev/sdce}{83th SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sdcr}{96th SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{70}{}{char }{SpellCaster Protocol Services Interface}
-	\minor{0}{/dev/apscfg}{Configuration interface}
-	\minor{1}{/dev/apsauth}{Authentication interface}
-	\minor{2}{/dev/apslog}{Logging interface}
-	\minor{3}{/dev/apsdbg}{Debugging interface}
-	\minor{64}{/dev/apsisdn}{ISDN command interface}
-	\minor{65}{/dev/apsasync}{Async command interface}
-	\minor{128}{/dev/apsmon}{Monitor interface}
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (96-111)}
-	\minor{0}{/dev/sdcs}{97th SCSI disk whole disk}
-	\minor{16}{/dev/sdct}{98th SCSI disk whole disk}
-	\minor{32}{/dev/sdcu}{99th SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sddh}{112th SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{71}{}{char }{Computone IntelliPort II serial card}
-	\minor{0}{/dev/ttyF0}{IntelliPort II board 0, port 0}
-	\minor{1}{/dev/ttyF1}{IntelliPort II board 0, port 1}
-	\minordots
-	\minor{63}{/dev/ttyF63}{IntelliPort II board 0, port 63}
-	\minor{64}{/dev/ttyF64}{IntelliPort II board 1, port 0}
-	\minor{65}{/dev/ttyF65}{IntelliPort II board 1, port 1}
-	\minordots
-	\minor{127}{/dev/ttyF127}{IntelliPort II board 1, port 63}
-	\minor{128}{/dev/ttyF128}{IntelliPort II board 2, port 0}
-	\minor{129}{/dev/ttyF129}{IntelliPort II board 2, port 1}
-	\minordots
-	\minor{191}{/dev/ttyF191}{IntelliPort II board 2, port 63}
-	\minor{192}{/dev/ttyF192}{IntelliPort II board 3, port 0}
-	\minor{193}{/dev/ttyF193}{IntelliPort II board 3, port 1}
-	\minordots
-	\minor{255}{/dev/ttyF255}{IntelliPort II board 3, port 63}
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{SCSI disk devices (112-127)}
-	\minor{0}{/dev/sddi}{113th SCSI disk whole disk}
-	\minor{16}{/dev/sddj}{114th SCSI disk whole disk}
-	\minor{32}{/dev/sddk}{115th SCSI disk whole disk}
-	\minordots
-	\minor{240}{/dev/sddx}{128th SCSI disk whole disk}
-\end{devicelist}
-
-\noindent
-Partitions are handled in the same way as for IDE disks (see major
-number 3) except that the partition limit is 15 rather than 63 per
-disk.
-
-\begin{devicelist}
-\major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices}
-	\minor{0}{/dev/cuf0}{Callout device corresponding to {\file ttyF0}}
-	\minor{1}{/dev/cuf1}{Callout device corresponding to {\file ttyF1}}
-	\minordots
-	\minor{63}{/dev/cuf63}{Callout device corresponding to {\file ttyF63}}
-	\minor{64}{/dev/cuf64}{Callout device corresponding to {\file ttyF64}}
-	\minor{65}{/dev/cuf65}{Callout device corresponding to {\file ttyF65}}
-	\minordots
-	\minor{127}{/dev/cuf127}{Callout device corresponding to {\file ttyF127}}
-	\minor{128}{/dev/cuf128}{Callout device corresponding to {\file ttyF128}}
-	\minor{129}{/dev/cuf129}{Callout device corresponding to {\file ttyF129}}
-	\minordots
-	\minor{191}{/dev/cuf191}{Callout device corresponding to {\file ttyF191}}
-	\minor{192}{/dev/cuf192}{Callout device corresponding to {\file ttyF192}}
-	\minor{193}{/dev/cuf193}{Callout device corresponding to {\file ttyF193}}
-	\minordots
-	\minor{255}{/dev/cuf255}{Callout device corresponding to {\file ttyF255}}
-\end{devicelist}
-
-\begin{devicelist}
-\major{73}{}{char }{Computone IntelliPort II serial card -- control devices}
-	\minor{0}{/dev/ip2ipl0}{Loadware device for board 0}
-	\minor{1}{/dev/ip2stat0}{Status device for board 0}
-	\minor{4}{/dev/ip2ipl1}{Loadware device for board 1}
-	\minor{5}{/dev/ip2stat1}{Status device for board 1}
-	\minor{8}{/dev/ip2ipl2}{Loadware device for board 2}
-	\minor{9}{/dev/ip2stat2}{Status device for board 2}
-	\minor{12}{/dev/ip2ipl3}{Loadware device for board 3}
-	\minor{13}{/dev/ip2stat3}{Status device for board 3}
-\end{devicelist}
-
-\begin{devicelist}
-\major{74}{}{char }{SCI bridge}
-	\minor{0}{/dev/SCI/0}{SCI device 0}
-	\minor{1}{/dev/SCI/1}{SCI device 1}
-	\minordots
-\end{devicelist}
-
-\noindent
-Currently for Dolphin Interconnect Solutions' PCI-SCI bridge.
-
-\begin{devicelist}
-\major{75}{}{char }{Specialix IO8+ serial card}
-	\minor{0}{/dev/ttyW0}{First IO8+ port, first card}
-	\minor{1}{/dev/ttyW1}{Second IO8+ port, first card}
-	\minordots
-	\minor{8}{/dev/ttyW8}{First IO8+ port, second card}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices}
-	\minor{0}{/dev/cuw0}{Callout device corresponding to {\file ttyW0}}
-	\minor{1}{/dev/cuw1}{Callout device corresponding to {\file ttyW1}}
-	\minordots
-	\minor{8}{/dev/cuw8}{Callout device corresponding to {\file ttyW8}}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{77}{}{char }{ComScire Quantum Noise Generator}
-	\minor{0}{/dev/qng}{ComScire Quantum Noise Generator}
-\end{devicelist}
-
-\begin{devicelist}
-\major{78}{}{char }{PAM Software's multimodem boards}
-	\minor{0}{/dev/ttyM0}{First PAM modem}
-	\minor{1}{/dev/ttyM1}{Second PAM modem}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices}
-	\minor{0}{/dev/cum0}{Callout device corresponding to {\file ttyM0}}
-	\minor{1}{/dev/cum1}{Callout device corresponding to {\file ttyM1}}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{80}{}{char }{Photometrics AT200 CCD camera}
-	\minor{0}{/dev/at200}{Photometrics AT200 CCD camera}
-\end{devicelist}
-
-\begin{devicelist}
-\major{81}{}{char }{video4linux}
-	\minor{0}{/dev/video0}{Video capture/overlay device}
-	\minordots
-	\minor{63}{/dev/video63}{Video capture/overlay device}
-	\minor{64}{/dev/radio0}{Radio device}
-	\minordots
-	\minor{127}{/dev/radio63}{Radio device}
-	\minor{192}{/dev/vtx0}{Teletext device}
-	\minordots
-	\minor{223}{/dev/vtx31}{Teletext device}
-	\minor{224}{/dev/vbi0}{Vertical blank interupt}
-	\minordots
-	\minor{255}{/dev/vbi31}{Vertical blank interupt}
-\end{devicelist}
-
-\begin{devicelist}
-\major{82}{}{char }{WiNRADiO communications receiver card}
-	\minor{0}{/dev/winradio0}{First WiNRADiO card}
-	\minor{1}{/dev/winradio1}{Second WiNRADiO card}
-	\minordots
-\end{devicelist}
-
-\noindent
-The driver and documentation may be obtained from
-{\url http://www.proximity.com.au/~brian/winradio/\/}.
-
-\begin{devicelist}
-\major{83}{}{char }{Teletext/videotext interfaces}
-        \minor{0}{/dev/vtx}{Teletext decoder}
-        \minor{16}{/dev/vttuner}{TV tuner on teletext interface}
-\end{devicelist}
-
-\noindent
-Devices for the driver contained in the VideoteXt package. More information
-on {\url http://home.pages.de/~videotext/\/}.
-
-\begin{devicelist}
-\major{84}{}{char }{Ikon 1011[57] Versatec Greensheet Interface}
-	\minor{0}{/dev/ihcp0}{First Greensheet port}
-	\minor{1}{/dev/ihcp1}{Second Greensheet port}
-\end{devicelist}
-
-\begin{devicelist}
-\major{85}{}{char }{Linux/SGI shared memory input queue}
-	\minor{0}{/dev/shmiq}{Master shared input queue}
-	\minor{1}{/dev/qcntl0}{First device pushed}
-	\minor{2}{/dev/qcntl1}{Second device pushed}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{86}{}{char }{SCSI media changer}
-	\minor{0}{/dev/sch0}{First SCSI media changer}
-	\minor{1}{/dev/sch1}{Second SCSI media changer}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{87}{}{char }{Sony Control-A1 stereo control bus}
-	\minor{0}{/dev/controla0}{First device on chain}
-	\minor{1}{/dev/controla1}{Second device on chain}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{88}{}{char }{COMX synchronous serial card}
-	\minor{0}{/dev/comx0}{Channel 0}
-	\minor{1}{/dev/comx1}{Channel 1}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{Seventh IDE hard disk/CD-ROM interface}
-        \minor{0}{/dev/hdm}{Master: whole disk (or CD-ROM)}
-        \minor{64}{/dev/hdn}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{89}{}{char }{I$^2$C bus interface}
-	\minor{0}{/dev/i2c0}{First I$^2$C adapter}
-	\minor{1}{/dev/i2c1}{Second I$^2$C adapter}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{Eighth IDE hard disk/CD-ROM interface}
-        \minor{0}{/dev/hdo}{Master: whole disk (or CD-ROM)}
-        \minor{64}{/dev/hdp}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{90}{}{char }{Memory Technology Device (RAM, ROM, Flash)}
-        \minor{0}{/dev/mtd0}{First MTD (rw)}
-        \minor{1}{/dev/mtdr0}{First MTD (ro)}
-        \minordots
-        \minor{30}{/dev/mtd15}{16th MTD (rw)}
-        \minor{31}{/dev/mtdr15}{16th MTD (ro)}
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{Ninth IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hdq}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdr}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{91}{}{char }{CAN-Bus controller}
-	\minor{0}{/dev/can0}{First CAN-Bus controller}
-	\minor{1}{/dev/can1}{Second CAN-Bus controller}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{  }{}{block}{Tenth IDE hard disk/CD-ROM interface}
-	\minor{0}{/dev/hds}{Master: whole disk (or CD-ROM)}
-	\minor{64}{/dev/hdt}{Slave: whole disk (or CD-ROM)}
-\end{devicelist}
-
-\noindent
-Partitions are handled the same way as for the first interface (see
-major number 3).
-
-\begin{devicelist}
-\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card}
-\end{devicelist}
-
-\begin{devicelist}
-\major{93}{}{char }{IBM Smart Capture Card frame grabber}
-	\minor{0}{/dev/iscc0}{First Smart Capture Card}
-	\minor{1}{/dev/iscc1}{Second Smart Capture Card}
-	\minordots
-	\minor{128}{/dev/isccctl0}{First Smart Capture Card control}
-	\minor{129}{/dev/isccctl1}{Second Smart Capture Card control}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device}
-	\minor{0}{/dev/dcxx0}{First capture card}
-	\minor{1}{/dev/dcxx1}{Second capture card}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{95}{}{char }{IP filter}
-	\minor{0}{/dev/ipl}{Filter control device/log file}
-	\minor{1}{/dev/ipnat}{NAT control device/log file}
-	\minor{2}{/dev/ipstate}{State information log file}
-	\minor{3}{/dev/ipauth}{Authentication control device/log file}
-\end{devicelist}
-
-\begin{devicelist}
-\major{96}{}{char }{Parallel port ATAPI tape devices}
-	\minor{0}{/dev/pt0}{First parallel port ATAPI tape}
-	\minor{1}{/dev/pt1}{Second parallel port ATAPI tape}
-	\minor{2}{/dev/pt2}{Third parallel port ATAPI tape}
-	\minor{3}{/dev/pt3}{Fourth parallel port ATAPI tape}
-	\minor{128}{/dev/npt0}{First parallel port ATAPI tape, no rewind}
-	\minor{129}{/dev/npt1}{Second parallel port ATAPI tape, no rewind}
-	\minor{130}{/dev/npt2}{Third parallel port ATAPI tape, no rewind}
-	\minor{131}{/dev/npt3}{Fourth parallel port ATAPI tape, no rewind}
-\end{devicelist}
-
-\begin{devicelist}
-\major{97}{}{char }{Parallel port generic ATAPI interface}
-	\minor{0}{/dev/pg0}{First parallel port ATAPI device}
-	\minor{1}{/dev/pg1}{Second parallel port ATAPI device}
-	\minor{2}{/dev/pg2}{Third parallel port ATAPI device}
-	\minor{3}{/dev/pg3}{Fourth parallel port ATAPI device}
-\end{devicelist}
-
-\noindent
-These devices support the same API as the generic SCSI devices.
-
-\begin{devicelist}
-\major{98}{}{char }{Control and Mesurement Device (comedi)}
-	\minor{0}{/dev/comedi0}{First comedi device}
-	\minor{1}{/dev/comedi1}{Second comedi device}
-	\minordots
-\end{devicelist}
-
-\noindent
-See {\url http://stm.lbl.gov/comedi/} or {\url
-http://www.llp.fu-berlin.de/}.
-
-\begin{devicelist}
-\major{99}{}{char }{Raw parallel ports}
-	\minor{0}{/dev/parport0}{First parallel port}
-	\minor{1}{/dev/parport1}{Second parallel port}
-	\minordots
-\end{devicelist}
-
-\noindent
-These devices can be used to drive parallel port devices from
-user-space while interacting with the parport sharing code.
-
-\begin{devicelist}
-\major{100}{}{char }{POTS (analogue telephone) A/B port}
-	\minor{0}{/dev/phone0}{First telephone port}
-	\minor{1}{/dev/phone1}{Second telephone port}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{101}{}{char }{Motorola DSP 56xxx board}
-	\minor{0}{/dev/mdspstat}{Status information}
-	\minor{1}{/dev/mdsp1}{First DSP board I/O and controls}
-	\minordots
-	\minor{16}{/dev/mdsp16}{16th DSP board I/O and controls}
-\end{devicelist}
-
-\begin{devicelist}
-\major{102}{}{char }{Philips SAA5249 Teletext signal decoder}
-	\minor{0}{/dev/tlk0}{First Teletext decoder}
-	\minor{1}{/dev/tlk1}{Second Teletext decoder}
-	\minor{2}{/dev/tlk2}{Third Teletext decoder}
-	\minor{3}{/dev/tlk3}{Fourth Teletext decoder}
-\end{devicelist}
-
-\begin{devicelist}
-\major{103}{}{char }{Arla network file system}
-	\minor{0}{/dev/xfs0}{Arla XFS}
-\end{devicelist}
-
-\noindent
-Arla is a free clone of the Andrew File System, AFS.  Any resemblance
-with the Swedish milk producer is coincidental.  For more information
-about the project, write to $<$arla-drinkers@stacken.kth.se$>$ or
-subscribe to the arla-announce mailing list by sending a mail to
-$<$arla-announce-request@stacken.kth.se$>$.
-
-\begin{devicelist}
-\major{104}{}{char }{Flash BIOS support}
-\end{devicelist}
-
-\begin{devicelist}
-\major{105}{}{char }{Comtrol VS-1000 serial card}
-	\minor{0}{/dev/ttyV0}{First VS-1000 port}
-	\minor{1}{/dev/ttyV1}{Second VS-1000 port}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{106}{}{char }{Comtrol VS-1000 serial card -- alternate devices}
-	\minor{0}{/dev/cuv0}{Callout device corresponding to {\file ttyV0}}
-	\minor{1}{/dev/cuv1}{Callout device corresponding to {\file ttyV1}}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{107}{}{char }{3Dfx Voodoo Graphics device}
-	\minor{0}{/dev/3dfx}{Primary 3Dfx graphics device}
-\end{devicelist}
-
-\begin{devicelist}
-\major{108}{}{char }{Device independent PPP interface}
-	\minor{0}{/dev/ppp}{Device independent PPP interface}
-\end{devicelist}
-
-\begin{devicelist}
-\major{109}{}{char }{Reserved for logical volume manager}
-\end{devicelist}
-
-\begin{devicelist}
-\major{110}{}{char }{miroMEDIA Surround board}
-	\minor{0}{/dev/srnd0}{First miroMEDIA Surround board}
-	\minor{1}{/dev/srnd1}{First miroMEDIA Surround board}
-	\minordots
-\end{devicelist}
-
-\begin{devicelist}
-\major{111}{--119}{}{Unallocated}
-\end{devicelist}
-
-\begin{devicelist}
-\major{120}{--127}{}{Local/experimental use}
-\end{devicelist}
-
-\begin{devicelist}
-\major{128}{--135}{char }{Unix98 PTY masters}
-\end{devicelist}
-
-\noindent
-These devices should not have corresponding device nodes; instead they
-should be accessed through the {\file /dev/ptmx} cloning device.
-
-\begin{devicelist}
-\major{136}{--143}{char }{Unix98 PTY slaves}
-	\minor{0}{/dev/pts/0}{First Unix98 pseudo-TTY}
-	\minor{1}{/dev/pts/1}{Second Unix98 pseudo-TTY}
-	\minordots
-\end{devicelist}
-
-\noindent
-These device nodes are automatically generated with the proper
-permissions and modes by mounting the {\file devpts} filesystem onto
-{\file /dev/pts} with the appropriate mount options (distribution
-dependent.)
-
-\begin{devicelist}
-\major{144}{--239}{}{Unallocated}
-\end{devicelist}
-
-\begin{devicelist}
-\major{240}{--254}{}{Local/experimental use}
-\end{devicelist}
-
-\begin{devicelist}
-\major{255}{}{}{Reserved}
-\end{devicelist}
-
-\noindent
-This major is reserved to assist the expansion to a larger number
-space.  No device nodes with this major should ever be created on any
-filesystem.
-
-\section{Additional /dev directory entries}
-
-This section details additional entries that should or may exist in the
-{\file /dev} directory.  It is preferred that symbolic links use the
-same form (absolute or relative) as is indicated here.  Links are
-classified as {\em hard\/} or {\em symbolic\/} depending on the
-preferred type of link; if possible, the indicated type of link should
-be used.
-
-\subsection{Compulsory links}
-
-These links should exist on all systems:
-
-\begin{nodelist}
-\link{/dev/fd}{/proc/self/fd}{symbolic}{File descriptors}
-\link{/dev/stdin}{fd/0}{symbolic}{Standard input file descriptor}
-\link{/dev/stdout}{fd/1}{symbolic}{Standard output file descriptor}
-\link{/dev/stderr}{fd/2}{symbolic}{Standard error file descriptor}
-\link{/dev/nfsd}{socksys}{symbolic}{Required by iBCS-2}
-\link{/dev/X0R}{null}{symbolic}{Required by iBCS-2}
-\end{nodelist}
-
-\noindent
-Note: The last device is: $<$letter {\tt X}$>$-$<$digit {\tt
-0}$>$-$<$letter {\tt R}$>$.
-
-\subsection{Recommended links}
-
-It is recommended that these links exist on all systems:
-
-\begin{nodelist}
-\link{/dev/core}{/proc/kcore}{symbolic}{Backward compatibility}
-\link{/dev/ramdisk}{ram0}{symbolic}{Backward compatibility}
-\link{/dev/ftape}{qft0}{symbolic}{Backward compatibility}
-\link{/dev/bttv0}{video0}{symbolic}{Backward compatibility}
-\link{/dev/radio}{radio0}{symbolic}{Backward compatibility}
-\link{/dev/scd?}{sr?}{hard}{Alternate name for CD-ROMs}
-\end{nodelist}
-
-\subsection{Locally defined links}
-
-The following links may be established locally to conform to the
-configuration of the system.  This is merely a tabulation of existing
-practice, and does not constitute a recommendation.  However, if they
-exist, they should have the following uses.
-
-\begin{nodelist}
-\vlink{/dev/mouse}{mouse port}{symbolic}{Current mouse device}
-\vlink{/dev/tape}{tape device}{symbolic}{Current tape device}
-\vlink{/dev/cdrom}{CD-ROM device}{symbolic}{Current CD-ROM device}
-\vlink{/dev/cdwriter}{CD-writer}{symbolic}{Current CD-writer device}
-\vlink{/dev/scanner}{scanner device}{symbolic}{Current scanner device}
-\vlink{/dev/modem}{modem port}{symbolic}{Current dialout device}
-\vlink{/dev/root}{root device}{symbolic}{Current root filesystem}
-\vlink{/dev/swap}{swap device}{symbolic}{Current swap device}
-\end{nodelist}
-
-\noindent
-{\file /dev/modem} should not be used for a modem which supports
-dialin as well as dialout, as it tends to cause lock file problems.
-If it exists, {\file /dev/modem} should point to the appropriate
-primary TTY device (the use of the alternate callout devices is
-deprecated.)
-
-For SCSI devices, {\file /dev/tape} and {\file /dev/cdrom} should
-point to the ``cooked'' devices ({\file /dev/st*} and {\file
-/dev/sr*}, respectively), whereas {\file /dev/cdwriter} and {\file
-/dev/scanner} should point to the appropriate generic SCSI devices
-({\file /dev/sg*}).
-
-{\file /dev/mouse} may point to a primary serial TTY device, a
-hardware mouse device, or a socket for a mouse driver program
-(e.g. {\file /dev/gpmdata}).
-
-\subsection{Sockets and pipes}
-
-Non-transient sockets or named pipes may exist in {\file /dev}.
-Common entries are:
-
-\begin{nodelist}
-\node{/dev/printer}{socket}{{\file lpd} local socket}
-\node{/dev/log}{socket}{{\file syslog} local socket}
-\node{/dev/gpmdata}{socket}{{\file gpm} mouse multiplexer}
-\end{nodelist}
-
-\section{Terminal devices}
-
-Terminal, or TTY devices are a special class of character devices.  A
-terminal device is any device that could act as a controlling terminal
-for a session; this includes virtual consoles, serial ports, and
-pseudoterminals (PTYs).
-
-All terminal devices share a common set of capabilities known as line
-diciplines; these include the common terminal line dicipline as well
-as SLIP and PPP modes.
-
-All terminal devices are named similarly; this section explains the
-naming and use of the various types of TTYs.  Note that the naming
-conventions include several historical warts; some of these are
-Linux-specific, some were inherited from other systems, and some
-reflect Linux outgrowing a borrowed convention.
-
-A hash mark ($\#$) in a device name is in all cases used here to
-indicate a decimal number without leading zeroes.
-
-\subsection{Virtual consoles and the console device}
-
-Virtual consoles are full-screen terminal displays on the system video
-monitor.  Virtual consoles are named {\file /dev/tty$\#$}, with
-numbering starting at {\file /dev/tty1}; {\file /dev/tty0} is the
-current virtual console.  {\file /dev/tty0} is the device that should
-be used to access the system video card on those architectures for
-which the frame buffer devices ({\file /dev/fb*}) are not applicable.
-Do not use {\file /dev/console} for this purpose.
-
-The {\em console device\/}, {\file /dev/console}, is the device to
-which system messages should be sent, and on which logins should be
-permitted in single-user mode.  Starting with Linux 2.1.71, {\file
-/dev/console} is managed by the kernel; for previous versions it
-should be a symbolic link to either {\file /dev/tty0}, a specific
-virtual console such as {\file /dev/tty1}, or to a serial port primary
-({\file tty*}, not {\file cu*}) device, depending on the configuration
-of the system.
-
-\subsection{Serial ports}
-
-Serial ports are RS-232 serial ports and any device which simulates
-one, either in hardware (such as internal modems) or in software (such
-as the ISDN driver.)  Under Linux, each serial ports has two device
-names, the primary or callin device and the alternate or callout one.
-Each kind of device is indicated by a different letter.  For any
-letter $X$, the names of the devices are {\file /dev/tty${X\#}$} and
-{\file /dev/cu${x\#}$}, respectively; for historical reasons, {\file
-/dev/ttyS$\#$} and {\file /dev/ttyC$\#$} correspond to {\file
-/dev/cua$\#$} and {\file /dev/cub$\#$}.  In the future, it should be
-expected that multiple letters will be used; all letters will be upper
-case for the {\file tty} device (e.g. {\file /dev/ttyDP$\#$} and lower
-case for the {\file cu} device (e.g. {\file /dev/cudp$\#$}.
-
-The use of the callout devices is deprecated.
-
-The names {\file /dev/ttyQ$\#$} and {\file /dev/cuq$\#$} are reserved
-for local use.
-
-The alternate devices provide for kernel-based exclusion and somewhat
-different defaults than the primary devices.  Their main purpose is to
-allow the use of serial ports with programs with no inherent or broken
-support for serial ports.  Their use is deprecated, and they may be
-removed from a future version of Linux.
-
-Arbitration of serial ports is provided by the use of lock files with
-the names {\file /var/lock/LCK..tty${X\#}$}.  The contents of the lock
-file should be the PID of the locking process as an ASCII number.
-
-It is common practice to install links such as {\file /dev/modem\/}
-which point to serial ports.  In order to ensure proper locking in the
-presence of these links, it is recommended that software chase
-symlinks and lock all possible names; additionally, it is recommended
-that a lock file be installed with the corresponding alternate
-device.  In order to avoid deadlocks, it is recommended that the locks
-are acquired in the following order, and released in the reverse:
-\begin{itemize}
-\item{The symbolic link name, if any ({\file /var/lock/LCK..modem})}
-\item{The {\file tty} name ({\file /var/lock/LCK..ttyS2})}
-\item{The alternate device name ({\file /var/lock/LCK..cua2})}
-\end{itemize}
-In the case of nested symbolic links, the lock files should be
-installed in the order the symlinks are resolved.
-
-Under no circumstances should an application hold a lock while waiting
-for another to be released.  In addition, applications which attempt
-to create lock files for the corresponding alternate device names
-should take into account the possibility of being used on a non-serial
-port TTY, for which no alternate device would exist.
-
-\subsection{Pseudoterminals (PTYs)}
-
-Pseudoterminals, or PTYs, are used to create login sessions or provide
-other capabilities requiring a TTY line dicipline (including SLIP or
-PPP capability) to arbitrary data-generation processes.  Each PTY has
-a {\em master\/} side, named {\file /dev/pty[p-za-e][0-9a-f]\/}, and a
-{\em slave\/} side, named {\file /dev/tty[p-za-e][0-9a-f]\/}.  The
-kernel arbitrates the use of PTYs by allowing each master side to be
-opened only once.
-
-Once the master side has been opened, the corresponding slave device
-can be used in the same manner as any TTY device.  The master and
-slave devices are connected by the kernel, generating the equivalent
-of a bidirectional pipe with TTY capabilities.
-
-Recent versions of the Linux kernels and GNU libc contain support for
-the System V/Unix98 naming scheme for PTYs, which assigns a common
-device {\file /dev/ptmx\/} to all the masters (opening it will
-automatically give you a previously unassigned PTY) and a subdirectory
-{\file /dev/pts\/} for the slaves; the slaves are named with decimal
-integers ({\file /dev/pts/$\#$\/} in our notation).  This removes the
-problem of exhausting the namespace and enables the kernel to
-automatically create the device nodes for the slaves on demand using
-the {\file devpts\/} filesystem.
-
-\end{document}
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.3.99-pre2/linux/Documentation/devices.txt	Fri Jan  7 19:13:21 2000
+++ linux/Documentation/devices.txt	Thu Mar 23 23:32:47 2000
@@ -1,7 +1,7 @@
 		       LINUX ALLOCATED DEVICES
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		   Last revised: December 16, 1999
+		     Last revised: March 23, 2000
 
 This list is the Linux Device List, the official registry of allocated
 device numbers and /dev directory nodes for the Linux operating
@@ -14,12 +14,15 @@
 of this document is no longer maintained.
 
 This document is included by reference into the Filesystem Hierarchy
-Standard (FHS).  The FHS is available from http://www.pathname.com/fhs/.
+Standard (FHS).	 The FHS is available from http://www.pathname.com/fhs/.
 
 Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
 platform only.	Allocations marked (68k/Atari) apply to Linux/68k on
 the Atari platform only.
 
+The symbol {2.6} means the allocation is obsolete and scheduled for
+removal once kernel version 2.6 (or equivalent) is released.
+
 This document is in the public domain.	The author requests, however,
 that semantically altered versions are not distributed without
 permission of the author, assuming the author can be contacted without
@@ -198,7 +201,7 @@
 
 		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.
+		is no longer supported.	 Use major numbers 2 and 3.
 
   5 char	Alternate TTY devices
 		  0 = /dev/tty		Current TTY device
@@ -304,8 +307,11 @@
 		  7 = /dev/amigamouse1	Second Amiga mouse
 		  8 = /dev/smouse	Simple serial mouse driver
 		  9 = /dev/pc110pad	IBM PC-110 digitizer pad
+		 10 = /dev/adbmouse	Apple Desktop Bus mouse
+		 11 = /dev/vrtpanel	Vr41xx embedded touch panel
+		 13 = /dev/vpcmouse	Connectix Virtual PC Mouse
 		128 = /dev/beep		Fancy beep device
-		129 = /dev/modreq	Kernel module load request
+		129 = /dev/modreq	Kernel module load request {2.6}
 		130 = /dev/watchdog	Watchdog timer port
 		131 = /dev/temperature	Machine internal temperature
 		132 = /dev/hwtrap	Hardware fault trap
@@ -318,7 +324,7 @@
 		142 = /dev/msr		x86 model-specific registers
 		143 = /dev/pciconf	PCI configuration space
 		144 = /dev/nvram	Non-volatile configuration RAM
-		145 = /dev/hfmodem	Soundcard shortwave modem control
+		145 = /dev/hfmodem	Soundcard shortwave modem control {2.6}
 		146 = /dev/graphics	Linux/SGI graphics device
 		147 = /dev/opengl	Linux/SGI OpenGL pipe
 		148 = /dev/gfx		Linux/SGI graphics effects device
@@ -352,6 +358,11 @@
 		177 = /dev/cbm		Serial CBM bus
 		178 = /dev/jsflash	JavaStation OS flash SIMM
 		179 = /dev/xsvc		High-speed shared-mem/semaphore service
+		180 = /dev/vrbuttons	Vr41xx button input device
+		181 = /dev/toshiba	Toshiba laptop SMM support
+		182 = /dev/perfctr	Performance-monitoring counters
+		183 = /dev/intel_rng	Intel i8x0 random number generator
+		184 = /dev/cpu/microcode CPU microcode update interface
 		240-255			Reserved for local use
 
  11 char	Raw keyboard device
@@ -380,7 +391,7 @@
 		The device names specified are proposed -- if there
 		are "standard" names for these devices, please let me know.
 
-    block	MSCDEX CD-ROM callback support
+    block	MSCDEX CD-ROM callback support {2.6}
 		  0 = /dev/dos_cd0	First MSCDEX CD-ROM
 		  1 = /dev/dos_cd1	Second MSCDEX CD-ROM
 		    ...
@@ -407,7 +418,7 @@
 		  2 = /dev/midi00	First MIDI port
 		  3 = /dev/dsp		Digital audio
 		  4 = /dev/audio	Sun-compatible digital audio
-		  6 = /dev/sndstat	Sound card status information
+		  6 = /dev/sndstat	Sound card status information {2.6}
 		  7 = /dev/audioctl	SPARC audio control device
 		  8 = /dev/sequencer2	Sequencer -- alternate device
 		 16 = /dev/mixer1	Second soundcard mixer control
@@ -418,7 +429,7 @@
 		 33 = /dev/patmgr1	Sequencer patch manager
 		 34 = /dev/midi02	Third MIDI port
 		 50 = /dev/midi03	Fourth MIDI port
-    block	BIOS harddrive callback support
+    block	BIOS harddrive callback support {2.6}
 		  0 = /dev/dos_hda	First BIOS harddrive whole disk
 		 64 = /dev/dos_hdb	Second BIOS harddrive whole disk
 		128 = /dev/dos_hdc	Third BIOS harddrive whole disk
@@ -549,7 +560,7 @@
 		  2 = /dev/sbpcd2	Panasonic CD-ROM controller 0 unit 2
 		  3 = /dev/sbpcd3	Panasonic CD-ROM controller 0 unit 3
 
- 26 char	Quanta WinVision frame grabber
+ 26 char	Quanta WinVision frame grabber {2.6}
 		  0 = /dev/wvisfgrab	Quanta WinVision frame grabber
     block	Second Matsushita (Panasonic/SoundBlaster) CD-ROM
 		  0 = /dev/sbpcd4	Panasonic CD-ROM controller 1 unit 0
@@ -569,7 +580,7 @@
 		 16 = /dev/zqft0	Unit 0, rewind-on-close, compression
 		 17 = /dev/zqft1	Unit 1, rewind-on-close, compression
 		 18 = /dev/zqft2	Unit 2, rewind-on-close, compression
-		 19 = /dev/zqtf3	Unit 3, rewind-on-close, compression
+		 19 = /dev/zqft3	Unit 3, rewind-on-close, compression
 		 20 = /dev/nzqft0	Unit 0, no rewind-on-close, compression
 		 21 = /dev/nzqft1	Unit 1, no rewind-on-close, compression
 		 22 = /dev/nzqft2	Unit 2, no rewind-on-close, compression
@@ -613,12 +624,11 @@
 		disks (see major number 3) except that the limit on
 		partitions is 15, like SCSI.
 
- 29 char	Universal frame buffers
+ 29 char	Universal frame buffer
 		  0 = /dev/fb0		First frame buffer
-		  1 = /dev/fb1		Second frame buffer
-		  2 = /dev/fb2		Third frame buffer
+		 32 = /dev/fb1		Second frame buffer
 		    ...
-		 31 = /dev/fb31		32nd frame buffer
+		224 = /dev/fb7		Eighth frame buffer
 
 		All additional minor numbers are reserved.
 
@@ -777,7 +787,7 @@
 		      ...
     block	Reserved for Linux/AP+
 
- 40 char	Matrox Meteor frame grabber
+ 40 char	Matrox Meteor frame grabber {2.6}
 		  0 = /dev/mmetfgrab	Matrox Meteor frame grabber
     block	Syquest EZ135 parallel port removable drive
 		  0 = /dev/eza		Parallel EZ135 drive, whole disk
@@ -1265,7 +1275,7 @@
 		disks (see major number 3) except that the limit on
 		partitions is 15.
 
- 83 char	Teletext/videotext interfaces
+ 83 char	Teletext/videotext interfaces {2.6}
 		  0 = /dev/vtx		Teletext decoder
 		 16 = /dev/vttuner	TV tuner on teletext interface
 
@@ -1356,8 +1366,8 @@
 
 
  89 char	I2C bus interface
-		  0 = /dev/i2c0		First I2C adapter
-		  1 = /dev/i2c1		Second I2C adapter
+		  0 = /dev/i2c-0	First I2C adapter
+		  1 = /dev/i2c-1	Second I2C adapter
 		    ...
 
     block	Eighth IDE hard disk/CD-ROM interface
@@ -1404,7 +1414,7 @@
 		disks (see major number 3) except that the limit on
 		partitions is 15.
 
- 93 char	IBM Smart Capture Card frame grabber
+ 93 char	IBM Smart Capture Card frame grabber {2.6}
 		  0 = /dev/iscc0	First Smart Capture Card
 		  1 = /dev/iscc1	Second Smart Capture Card
 		    ...
@@ -1418,7 +1428,7 @@
 		    ...
 		240 = /dev/nftlp	16th NTFL layer
 
- 94 char	miroVIDEO DC10/30 capture/playback device
+ 94 char	miroVIDEO DC10/30 capture/playback device {2.6}
 		  0 = /dev/dcxx0	First capture card
 		  1 = /dev/dcxx1	Second capture card
 		    ...
@@ -1462,6 +1472,11 @@
 		These devices support the same API as the generic SCSI
 		devices.
 
+    block	Packet writing for CD/DVD devices
+		  0 = /dev/pktcdvd0	First packet-writing module
+		  1 = /dev/pktcdvd1	Second packet-writing module
+		    ...
+
  98 char	Control and Measurement Device (comedi)
 		  0 = /dev/comedi0	First comedi device
 		  1 = /dev/comedi1	Second comedi device
@@ -1469,23 +1484,34 @@
 
 		See http://stm.lbl.gov/comedi or http://www.llp.fu-berlin.de/.
 
+    block	User-mode virtual block device
+		  0 = /dev/ubd0		First user-mode block device
+		  1 = /dev/ubd1		Second user-mode block device
+		    ...
+
+		This device is used by the user-mode virtual kernel port.
+
  99 char	Raw parallel ports
 		  0 = /dev/parport0	First parallel port
 		  1 = /dev/parport1	Second parallel port
 		    ...
 
-100 char	POTS (analogue telephone) A/B port
+100 char	POTS (analogue telephone) A/B port {2.6}
 		  0 = /dev/phone0	First telephone port
 		  1 = /dev/phone1	Second telephone port
 		    ...
 
+		The names have been reallocated to Telephony For
+		Linux, major 159.  All use of major 100 should be
+		considered legacy and deprecated.
+
 101 char	Motorola DSP 56xxx board
 		  0 = /dev/mdspstat	Status information
 		  1 = /dev/mdsp1	First DSP board I/O controls
 		    ...
 		 16 = /dev/mdsp16	16th DSP board I/O controls
 
-102 char	Philips SAA5249 Teletext signal decoder
+102 char	Philips SAA5249 Teletext signal decoder {2.6}
 		  0 = /dev/tlk0		First Teletext decoder
 		  1 = /dev/tlk1		Second Teletext decoder
 		  2 = /dev/tlk2		Third Teletext decoder
@@ -1526,7 +1552,7 @@
 		  1 = /dev/srnd1	Second miroMEDIA Surround board
 		    ...
 
-111 char	Philips SAA7146-based audio/video card
+111 char	Philips SAA7146-based audio/video card {2.6}
 		  0 = /dev/av0		First A/V card
 		  1 = /dev/av1		Second A/V card
 		    ...
@@ -1562,7 +1588,7 @@
 
 		Plays music using IBM BASIC style strings.
 
-116 char	Advanced Linux System Driver (ALSA)
+116 char	Advanced Linux Sound Driver (ALSA)
 
 117 char	COSA/SRP synchronous serial card
 		  0 = /dev/cosa0c0	1st board, 1st channel
@@ -1691,9 +1717,9 @@
 		  1 = /dev/gfax1	GammaLink channel 1
 		    ...
 
-159 char	Quicknet Technologies Internet PhoneJack/LineJack
-		  0 = /dev/ixj0		First device
-		  1 = /dev/ixj1		Second device
+159 char	Telephony for Linux
+		  0 = /dev/phone0	First telephony device
+		  1 = /dev/phone1	Second telephony device
 		    ...
 
 160 char	General Purpose Instrument Bus (GPIB)
@@ -1735,9 +1761,9 @@
 		 63 = /dev/ttyCH63	AT/PCI-Fast board 3, port 15
 
 165 char	Chase Research AT/PCI-Fast serial card - alternate devices
-		  0 = /dev/cuch0	Callout device corresponding to ttyCH0
+		  0 = /dev/cuch0	Callout device for ttyCH0
 		    ...
-		 63 = /dev/cuch63	Callout device corresponding to ttyCH63
+		 63 = /dev/cuch63	Callout device for ttyCH63
 
 166 char	ACM USB modems
 		  0 = /dev/ttyACM0	First ACM modem
@@ -1796,13 +1822,13 @@
 		    ...
 
 177 char	TI PCILynx memory spaces
-		  0 = /dev/pcilynx/aux0  AUX space of first PCILynx card
+		  0 = /dev/pcilynx/aux0	 AUX space of first PCILynx card
 		    ...
 		 15 = /dev/pcilynx/aux15 AUX space of 16th PCILynx card
-		 16 = /dev/pcilynx/rom0  ROM space of first PCILynx card
+		 16 = /dev/pcilynx/rom0	 ROM space of first PCILynx card
 		    ...
 		 31 = /dev/pcilynx/rom15 ROM space of 16th PCILynx card
-		 32 = /dev/pcilynx/ram0  RAM space of first PCILynx card
+		 32 = /dev/pcilynx/ram0	 RAM space of first PCILynx card
 		    ...
 		 47 = /dev/pcilynx/ram15 RAM space of 16th PCILynx card
 
@@ -1829,6 +1855,7 @@
 		 48 = /dev/usb/scanner0	First USB scanner
 		    ...
 		 63 = /dev/usb/scanner15 16th USB scanner
+		 64 = /dev/usb/rio500	Diamond Rio 500
 
 181 char	Conrad Electronic parallel port radio clocks
 		  0 = /dev/pcfclock0	First Conrad radio clock
@@ -1836,7 +1863,7 @@
 		    ...
 
 182 char	Picture Elements THR2 binarizer
-		  0 = /dev/pethr0       First THR2 board
+		  0 = /dev/pethr0	First THR2 board
 		  1 = /dev/pethr1	Second THR2 board
 		    ...
 
@@ -1853,7 +1880,12 @@
 		  1 = /dev/pevss1	Second sender board
 		    ...
 
-185 char	Reserved for InterMezzo high availability file system
+185 char	InterMezzo high availability file system
+		  0 = /dev/intermezzo0	First cache manager
+		  1 = /dev/intermezzo1	Second cache manager
+		    ...
+
+		See http://www.inter-mezzo.org/ for more information.
 
 186 char	Object-based storage control device
 		  0 = /dev/obd0		First obd control device
@@ -1862,7 +1894,10 @@
 
 		See ftp://ftp.lustre.org/pub/obd for code and information.
 
-187 char	UNALLOCATED
+187 char	DESkey hardware encryption device
+		  0 = /dev/deskey0	First DES key
+		  1 = /dev/deskey1	Second DES key
+		    ...
 
 188 char	USB serial converters
 		  0 = /dev/ttyUSB0	First USB serial converter
@@ -1870,11 +1905,46 @@
 		    ...
 
 189 char	USB serial converters - alternate devices
-		  0 = /dev/cuusb0	Callout device corresponding to ttyUSB0
-		  1 = /dev/cuusb1	Callout device corresponding to ttyUSB1
+		  0 = /dev/cuusb0	Callout device for ttyUSB0
+		  1 = /dev/cuusb1	Callout device for ttyUSB1
 		    ...
 
-190-239		UNALLOCATED
+190 char	Kansas City tracker/tuner card
+		  0 = /dev/kctt0	First KCT/T card
+		  1 = /dev/kctt1	Second KCT/T card
+		    ...
+
+191 char	Reserved for PCMCIA
+
+192 char	Kernel profiling interface
+		  0 = /dev/profile	Profiling control device
+		  1 = /dev/profile0	Profiling device for CPU 0
+		  2 = /dev/profile1	Profiling device for CPU 1
+		    ...
+
+193 char	Kernel event-tracing interface
+		  0 = /dev/trace	Tracing control device
+		  1 = /dev/trace0	Tracing device for CPU 0
+		  2 = /dev/trace1	Tracing device for CPU 1
+		    ...
+
+194 char	linVideoStreams (LINVS)
+		  0 = /dev/mvideo/status0	Video compression status
+		  1 = /dev/mvideo/stream0	Video stream
+		  2 = /dev/mvideo/frame0	Single compressed frame
+		  3 = /dev/mvideo/rawframe0	Raw uncompressed frame
+		  4 = /dev/mvideo/codec0	Direct codec access
+		  5 = /dev/mvideo/video4linux0	Video4Linux compatibility
+
+		 16 = /dev/mvideo/status1	Second device
+		    ...
+		 32 = /dev/mvideo/status2	Third device
+		    ...
+		    ...
+		240 = /dev/mvideo/status15	16th device 
+		    ...
+
+195-239		UNALLOCATED
 
 240-254		LOCAL/EXPERIMENTAL USE
 
@@ -1903,7 +1973,6 @@
 /dev/stderr	fd/2		symbolic	stderr file descriptor
 /dev/nfsd	socksys		symbolic	Required by iBCS-2
 /dev/X0R	null		symbolic	Required by iBCS-2
-/dev/i2o*	/dev/i2o/*	symbolic	Backward compatibility
 
 Note: /dev/X0R is <letter X>-<digit 0>-<letter R>.
 
@@ -1916,6 +1985,7 @@
 /dev/ftape	qft0		symbolic	Backward compatibility
 /dev/bttv0	video0		symbolic	Backward compatibility
 /dev/radio	radio0		symbolic	Backward compatibility
+/dev/i2o*	/dev/i2o/*	symbolic	Backward compatibility
 /dev/scd?	sr?		hard		Alternate SCSI CD-ROM name
 
 	Locally defined links
@@ -1983,7 +2053,7 @@
 starting at /dev/tty1; /dev/tty0 is the current virtual console.
 /dev/tty0 is the device that should be used to access the system video
 card on those architectures for which the frame buffer devices
-(/dev/fb*) are not applicable.  Do not use /dev/console
+(/dev/fb*) are not applicable.	Do not use /dev/console
 for this purpose.
 
 The console device, /dev/console, is the device to which system
@@ -2000,10 +2070,10 @@
 one, either in hardware (such as internal modems) or in software (such
 as the ISDN driver.)  Under Linux, each serial ports has two device
 names, the primary or callin device and the alternate or callout one.
-Each kind of device is indicated by a different letter.  For any
+Each kind of device is indicated by a different letter.	 For any
 letter X, the names of the devices are /dev/ttyX# and /dev/cux#,
 respectively; for historical reasons, /dev/ttyS# and /dev/ttyC#
-correspond to /dev/cua# and /dev/cub#.  In the future, it should be
+correspond to /dev/cua# and /dev/cub#.	In the future, it should be
 expected that multiple letters will be used; all letters will be upper
 case for the "tty" device (e.g. /dev/ttyDP#) and lower case for the
 "cu" device (e.g. /dev/cudp#).
@@ -2017,7 +2087,7 @@
 removed from a future version of Linux.
 
 Arbitration of serial ports is provided by the use of lock files with
-the names /var/lock/LCK..ttyX#.  The contents of the lock file should
+the names /var/lock/LCK..ttyX#.	 The contents of the lock file should
 be the PID of the locking process as an ASCII number.
 
 It is common practice to install links such as /dev/modem
@@ -2025,7 +2095,7 @@
 presence of these links, it is recommended that software chase
 symlinks and lock all possible names; additionally, it is recommended
 that a lock file be installed with the corresponding alternate
-device.  In order to avoid deadlocks, it is recommended that the locks
+device.	 In order to avoid deadlocks, it is recommended that the locks
 are acquired in the following order, and released in the reverse:
 
 	1. The symbolic link name, if any (/var/lock/LCK..modem)
@@ -2045,7 +2115,7 @@
 
 Pseudoterminals, or PTYs, are used to create login sessions or provide
 other capabilities requiring a TTY line dicipline (including SLIP or
-PPP capability) to arbitrary data-generation processes.  Each PTY has
+PPP capability) to arbitrary data-generation processes.	 Each PTY has
 a master side, named /dev/pty[p-za-e][0-9a-f], and a slave side, named
 /dev/tty[p-za-e][0-9a-f].  The kernel arbitrates the use of PTYs by
 allowing each master side to be opened only once.
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/isapnp.txt linux/Documentation/isapnp.txt
--- v2.3.99-pre2/linux/Documentation/isapnp.txt	Thu Aug 26 13:05:34 1999
+++ linux/Documentation/isapnp.txt	Sun Mar 19 18:19:22 2000
@@ -64,10 +64,10 @@
                                         unsigned short device,
                                         struct pci_bus *from);
 
-The above function finds a ISA PnP card. For the vendor device should
+This function finds a ISA PnP card. For the vendor device should
 be used ISAPNP_VENDOR(a,b,c) where a,b,c are characters or integers.
 For the device number should be used ISAPNP_DEVICE(x) macro where x is
-integer value. Both vendor and device numbers can be get from contents
+integer value. Both vendor and device numbers can be taken from contents
 of the /proc/isapnp file.
 
 extern struct pci_dev *isapnp_find_dev(struct pci_bus *card,
@@ -75,12 +75,37 @@
                                        unsigned short function,
                                        struct pci_dev *from);
 
-The above function finds the ISA PnP device. If card is NULL, then
+This function finds the ISA PnP device. If card is NULL, then
 the global search mode is used (all devices are used for the searching).
 Otherwise only devices which belongs to the specified card are verified.
 For the function number can be used ISAPNP_FUNCTION(x) macro which works
 similarly as the ISAPNP_DEVICE(x) macro.
 
+extern int isapnp_probe_cards(const struct isapnp_card_id *ids,
+			      int (*probe)(struct pci_bus *card,
+					   const struct isapnp_card_id *id));
+
+
+This function is a helper for drivers which requires to use more than
+one device from an ISA PnP card. For each cards is called the probe
+callback with appropriate information.
+
+Example for ids parameter initialization:
+
+static struct isapnp_card_id ids[] __devinitdata = {
+	{
+        	ISAPNP_CARD_ID('A','D','V', 0x550a),
+                devs: {
+			ISAPNP_DEVICE_ID('A', 'D', 'V', 0x0010),
+			ISAPNP_DEVICE_ID('A', 'D', 'V', 0x0011)
+		},
+		driver_data: 0x1234,
+	},
+	{
+		ISAPNP_CARD_END,
+	}
+};
+
 ISA PnP configuration
 =====================
 
@@ -99,18 +124,24 @@
 Second way is auto-configuration
 --------------------------------
 
-These two functions gives to the driver the real power of the ISA PnP
-feature. First function dev->prepare() only initialize the resource
-members in the device structure. This structure contains all resources
-set to auto configuration values after the initialization. The driver for
-ISA PnP device may modify (or not) some resources to skip auto configuration
-for the given resource.
+This feature gives to the driver the real power of the ISA PnP code.
+Function dev->prepare() initializes the resource members in the device
+structure. This structure contains all resources set to auto configuration
+values after the initialization. The device driver may modify some resources
+to skip the auto configuration for a given resource.
+
+Once the device structure contains all requested resource values, the function
+dev->activate() must be called to assign free resources to resource members
+with the auto configuration value.
 
-The function isapnp_configure does:
-	- resources which have the auto configuration value are configured
+Function dev->activate() does:
+	- resources with the auto configuration value are configured
 	- the auto configuration is created using ISA PnP resource map
 	- the function writes configuration to ISA PnP configuration registers
 	- the function returns to the caller actual used resources
+
+When the device driver is removing, function dev->deactivate() has to be
+called to free all assigned resources.
 
 Example (game port initialization)
 ==================================
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/isdn/README.eicon linux/Documentation/isdn/README.eicon
--- v2.3.99-pre2/linux/Documentation/isdn/README.eicon	Wed Feb 16 17:03:51 2000
+++ linux/Documentation/isdn/README.eicon	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-$Id: README.eicon,v 1.6 2000/01/27 09:54:44 armin Exp $
+$Id: README.eicon,v 1.7 2000/03/06 16:38:43 armin Exp $
 
 (c) 1999,2000 Armin Schindler (mac@melware.de)
 (c) 1999,2000 Cytronics & Melware (info@melware.de)
@@ -77,6 +77,10 @@
 Example for loading and starting a BRI card with E-DSS1 Protocol.
 
 	eiconctrl [-d DriverId] load etsi
+
+Example for a BRI card with E-DSS1 Protocol with PtP configuration.
+
+	eiconctrl [-d DriverId] load etsi -n -t0 -s1
 
 
 Example for loading and starting a PRI card with E-DSS1 Protocol.
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/networking/tlan.txt linux/Documentation/networking/tlan.txt
--- v2.3.99-pre2/linux/Documentation/networking/tlan.txt	Thu Feb 10 17:11:02 2000
+++ linux/Documentation/networking/tlan.txt	Wed Mar 22 09:37:59 2000
@@ -1,8 +1,8 @@
 (C) 1997-1998 Caldera, Inc.
 (C) 1998 James Banks
-(C) 1999-2000 Torben Mathiasen <torben.mathiasen@compaq.com>
+(C) 1999-2000 Torben Mathiasen <tmm@image.dk, torben.mathiasen@compaq.com>
 
-TLAN driver for Linux, version 1.3
+TLAN driver for Linux, version 1.5
 README
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/networking/tulip.txt linux/Documentation/networking/tulip.txt
--- v2.3.99-pre2/linux/Documentation/networking/tulip.txt	Sat Feb 26 22:31:37 2000
+++ linux/Documentation/networking/tulip.txt	Tue Mar 21 12:41:35 2000
@@ -125,7 +125,7 @@
 
 
 Source tree tour
-----------------
+================
 The following is a list of files comprising the Tulip ethernet driver in
 drivers/net/tulip subdirectory.
 
@@ -138,6 +138,25 @@
 tulip.h		- Private driver header
 tulip_core.c	- Driver core (a.k.a. where "everything else" goes)
 
+
+
+Version history
+===============
+0.9.4.2 (March 21, 2000):
+* Fix 21041 CSR7, CSR13/14/15 handling
+* Merge some PCI ids from tulip 0.91x
+* Merge some HAS_xxx flags and flag settings from tulip 0.91x
+* asm/io.h fix (submitted by many) and cleanup
+* s/HAS_NWAY143/HAS_NWAY/
+* Cleanup 21041 mode reporting
+* Small code cleanups
+
+0.9.4.1 (March 18, 2000):
+* Finish PCI DMA conversion (davem)
+* Do not netif_start_queue() at end of tulip_tx_timeout() (kuznet)
+* PCI DMA fix (kuznet)
+* eeprom.c code cleanup
+* Remove Xircom Tulip crud
 
 
 [EOF]
diff -u --recursive --new-file v2.3.99-pre2/linux/Documentation/video4linux/bttv/README linux/Documentation/video4linux/bttv/README
--- v2.3.99-pre2/linux/Documentation/video4linux/bttv/README	Tue Mar 14 19:10:38 2000
+++ linux/Documentation/video4linux/bttv/README	Sun Mar 19 19:11:37 2000
@@ -84,6 +84,13 @@
 mailing me directly.  The chance that someone with the same card
 listens there is much higher...
 
+For problems with sound:  There are alot of different systems used
+for TV sound all over the world.  And there are also different chips
+which decode the audio signal.  Reports about sound problems ("stereo
+does'nt work") are pretty useless unless you include some details
+about your hardware and the TV sound scheme used in your country (or
+at least the country you are living in).
+
 
 Finally: If you mail some patches for bttv around the world (to
 linux-kernel/Alan/Linus/...), please Cc: me.
diff -u --recursive --new-file v2.3.99-pre2/linux/MAINTAINERS linux/MAINTAINERS
--- v2.3.99-pre2/linux/MAINTAINERS	Sun Mar 19 18:35:30 2000
+++ linux/MAINTAINERS	Thu Mar 23 15:15:18 2000
@@ -495,36 +495,32 @@
 IEEE 1394 SUBSYSTEM
 P:	Andreas Bombe
 M:	andreas.bombe@munich.netsurf.de
-L:	linux1394-devel@eclipt.uni-klu.ac.at
-W:	http://eclipt.uni-klu.ac.at/ieee1394
+L:	linux1394-devel@lists.sourceforge.net
+W:	http://linux1394.sourceforge.net/
 S:	Maintained
 
 IEEE 1394 AIC5800 DRIVER
 P:	Emanuel Pirker
 M:	epirker@edu.uni-klu.ac.at
 L:	linux1394-devel@eclipt.uni-klu.ac.at
-W:	http://eclipt.uni-klu.ac.at/ieee1394
 S:	Maintained
 
 IEEE 1394 OHCI DRIVER
 P:	Sebastien Rougeaux
 M:	sebastien.rougeaux@anu.edu.au
 L:	linux1394-devel@eclipt.uni-klu.ac.at
-W:	http://eclipt.uni-klu.ac.at/ieee1394
 S:	Maintained
 
 IEEE 1394 PCILYNX DRIVER
 P:	Andreas Bombe
 M:	andreas.bombe@munich.netsurf.de
 L:	linux1394-devel@eclipt.uni-klu.ac.at
-W:	http://eclipt.uni-klu.ac.at/ieee1394
 S:	Maintained
 
 IEEE 1394 RAW I/O DRIVER
 P:	Andreas Bombe
 M:	andreas.bombe@munich.netsurf.de
 L:	linux1394-devel@eclipt.uni-klu.ac.at
-W:	http://eclipt.uni-klu.ac.at/ieee1394
 S:	Maintained
 
 INTEL APIC/IOAPIC, LOWLEVEL X86 SMP SUPPORT
@@ -1027,6 +1023,7 @@
 TLAN NETWORK DRIVER
 P:	Torben Mathiasen
 M:	torben.mathiasen@compaq.com
+M:	tmm@image.dk
 L:	tlan@vuser.vu.union.edu
 S:	Maintained
 
@@ -1135,6 +1132,13 @@
 L:	linux-usb@suse.com
 S:	Maintained
 W:	http://www.kroah.com/linux-usb/
+
+USB MASS STORAGE DRIVER
+P:	Matthew Dharm
+M:	mdharm-usb@one-eyed-alien.net
+L:	linux-usb@suse.com
+S:	Maintained
+W:	http://www.one-eyed-alien.net/~mdharm/linux-usb/
 
 USB UHCI DRIVER
 P:	Georg Acher
diff -u --recursive --new-file v2.3.99-pre2/linux/Makefile linux/Makefile
--- v2.3.99-pre2/linux/Makefile	Sun Mar 19 18:35:30 2000
+++ linux/Makefile	Tue Mar 21 23:38:26 2000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 3
 SUBLEVEL = 99
-EXTRAVERSION = -pre2
+EXTRAVERSION = -pre3
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -90,6 +90,9 @@
 CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
 AFLAGS := $(CPPFLAGS)
 
+# use '-fno-strict-aliasing', but only if the compiler can take it
+CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi)
+
 export	CPPFLAGS CFLAGS AFLAGS
 
 #
@@ -143,7 +146,7 @@
 DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
 DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.a
 DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
-DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.a
+DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.o
 DRIVERS-$(CONFIG_IDE) += drivers/ide/ide.a
 DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsi.a
 DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394.a
@@ -179,14 +182,10 @@
 
 include arch/$(ARCH)/Makefile
 
-export	CORE_FILES NETWORKS DRIVERS LIBS HEAD LDFLAGS LIBS LINKFLAGS \
-	MAKEBOOT ASFLAGS
-
-# use '-fno-strict-aliasing', but only if the compiler can take it
-CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi)
+export	NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS
 
 .S.s:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.s $<
+	$(CPP) -D__ASSEMBLY__ $(AFLAGS) -traditional -o $*.s $<
 .S.o:
 	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
 
@@ -399,6 +398,7 @@
 	rm -f .hdepend scripts/mkdep scripts/split-include scripts/docproc
 	rm -f $(TOPDIR)/include/linux/modversions.h
 	rm -rf $(TOPDIR)/include/linux/modules
+	make clean TOPDIR=$(TOPDIR) -C Documentation/DocBook
 
 distclean: mrproper
 	rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
diff -u --recursive --new-file v2.3.99-pre2/linux/Rules.make linux/Rules.make
--- v2.3.99-pre2/linux/Rules.make	Tue Aug 31 17:29:12 1999
+++ linux/Rules.make	Tue Mar 21 13:03:00 2000
@@ -244,7 +244,7 @@
 endif # CONFIG_MODVERSIONS
 
 ifneq "$(strip $(SYMTAB_OBJS))" ""
-$(SYMTAB_OBJS): $(TOPDIR)/include/linux/modversions.h $(SYMTAB_OBJS:.o=.c)
+$(SYMTAB_OBJS): $(SYMTAB_OBJS:.o=.c) $(TOPDIR)/include/linux/modversions.h
 	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
 	@ ( \
 	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.3.99-pre2/linux/arch/alpha/config.in	Sun Mar 19 18:35:30 2000
+++ linux/arch/alpha/config.in	Thu Mar 23 14:45:04 2000
@@ -55,7 +55,7 @@
 
 # clear all implied options (don't want default values for those):
 unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 CONFIG_ALPHA_EV6
-unset CONFIG_PCI CONFIG_ISA CONFIG_ALPHA_EISA
+unset CONFIG_ALPHA_EISA
 unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA
 unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS CONFIG_ALPHA_POLARIS
 unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA
@@ -66,10 +66,13 @@
 # and this doesn't activate hordes of code, so do it always.
 define_bool CONFIG_ISA y
 
-if [ "$CONFIG_ALPHA_GENERIC" = "y" ]
+if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
 then
+	define_bool CONFIG_PCI n
+else
 	define_bool CONFIG_PCI y
 fi
+
 if [ "$CONFIG_ALPHA_BOOK1" = "y" ]
 then
 	define_bool CONFIG_ALPHA_NONAME y
@@ -77,22 +80,18 @@
 if [ "$CONFIG_ALPHA_NONAME" = "y" -o "$CONFIG_ALPHA_EB66" = "y" \
 	-o "$CONFIG_ALPHA_EB66P" = "y" -o "$CONFIG_ALPHA_P2K" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV4 y
 	define_bool CONFIG_ALPHA_LCA y
 fi
 if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \
 	-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_XL" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV4 y
 	define_bool CONFIG_ALPHA_APECS y
 fi
 if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
-	-o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" \
-	-o "$CONFIG_ALPHA_TAKARA" = "y" ]
+	-o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_TAKARA" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_CIA y
 fi
@@ -107,11 +106,9 @@
 		define_bool CONFIG_ALPHA_EV4 y
 		define_bool CONFIG_ALPHA_APECS y
 	fi
-	define_bool CONFIG_PCI y
 fi
 if [ "$CONFIG_ALPHA_SABLE" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	bool 'EV5 CPU(s) (model 5/xxx)?' CONFIG_ALPHA_GAMMA
 	if [ "$CONFIG_ALPHA_GAMMA" = "y" ]
 	then
@@ -124,26 +121,22 @@
 if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \
 	-o "$CONFIG_ALPHA_SX164" = "y" -o "$CONFIG_ALPHA_RUFFIAN" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_CIA y
 	define_bool CONFIG_ALPHA_PYXIS y
 fi
 if [ "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_EIGER" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV6 y
 	define_bool CONFIG_ALPHA_TSUNAMI y
 fi
 if [ "$CONFIG_ALPHA_RAWHIDE" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_MCPCIA y
 fi
 if [ "$CONFIG_ALPHA_RX164" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_POLARIS y
 fi
@@ -153,7 +146,6 @@
 fi
 if [ "$CONFIG_ALPHA_NAUTILUS" = "y" ]
 then
-	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV6 y
 	define_bool CONFIG_ALPHA_IRONGATE y
 fi
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.3.99-pre2/linux/arch/alpha/defconfig	Tue Mar 14 19:10:38 2000
+++ linux/arch/alpha/defconfig	Thu Mar 23 14:45:04 2000
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
 #
+# CONFIG_UID16 is not set
 
 #
 # Code maturity level options
@@ -44,7 +45,9 @@
 # CONFIG_ALPHA_SX164 is not set
 # CONFIG_ALPHA_SABLE is not set
 # CONFIG_ALPHA_TAKARA is not set
+CONFIG_ISA=y
 CONFIG_PCI=y
+CONFIG_ALPHA_BROKEN_IRQ_MASK=y
 # CONFIG_SMP is not set
 CONFIG_PCI_NAMES=y
 # CONFIG_HOTPLUG is not set
@@ -58,6 +61,10 @@
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
 CONFIG_BINFMT_EM86=y
+
+#
+# Parallel port support
+#
 # CONFIG_PARPORT is not set
 
 #
@@ -70,7 +77,10 @@
 # Block devices
 #
 CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
 
 #
 # Additional Block Devices
@@ -79,10 +89,6 @@
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-CONFIG_PARIDE_PARPORT=y
-# CONFIG_PARIDE is not set
 
 #
 # Networking options
@@ -113,11 +119,14 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
 
 #
 # ATA/IDE/MFM/RLL support
 #
 # CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
@@ -128,9 +137,11 @@
 # SCSI support type (disk, tape, CD-ROM)
 #
 CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
 # CONFIG_CHR_DEV_ST is not set
 CONFIG_BLK_DEV_SR=y
 # CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_SR_EXTRA_DEVS=2
 # CONFIG_CHR_DEV_SG is not set
 
 #
@@ -144,6 +155,7 @@
 #
 # SCSI low-level drivers
 #
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
@@ -178,6 +190,7 @@
 # CONFIG_SCSI_QLOGIC_FAS is not set
 CONFIG_SCSI_QLOGIC_ISP=y
 # CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
 # CONFIG_SCSI_SEAGATE is not set
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
@@ -194,6 +207,7 @@
 #
 # CONFIG_ARCNET is not set
 CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_NET_SB1000 is not set
 
@@ -207,15 +221,17 @@
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_DEPCA is not set
 # CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
+CONFIG_NET_PCI=y
 # CONFIG_PCNET32 is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
 CONFIG_DE4X5=y
-# CONFIG_DEC_ELCP is not set
+# CONFIG_TULIP is not set
 # CONFIG_DGRS is not set
-# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_EEPRO100 is not set
 # CONFIG_NE2K_PCI is not set
+# CONFIG_8139TOO is not set
 # CONFIG_SIS900 is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
@@ -236,7 +252,7 @@
 # CONFIG_NET_RADIO is not set
 
 #
-# Token Ring driver support
+# Token Ring devices
 #
 # CONFIG_TR is not set
 # CONFIG_NET_FC is not set
@@ -321,15 +337,20 @@
 # CONFIG_USB is not set
 
 #
-# Filesystems
+# File systems
 #
 # CONFIG_QUOTA is not set
 # CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
 # CONFIG_FAT_FS is not set
 # CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
 # CONFIG_CRAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
@@ -337,7 +358,10 @@
 # CONFIG_NTFS_FS is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
 CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_EXT2_FS=y
 # CONFIG_SYSV_FS is not set
@@ -349,6 +373,7 @@
 #
 # CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
 # CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
@@ -361,8 +386,6 @@
 # CONFIG_PARTITION_ADVANCED is not set
 CONFIG_OSF_PARTITION=y
 CONFIG_MSDOS_PARTITION=y
-# CONFIG_SGI_PARTITION is not set
-# CONFIG_SUN_PARTITION is not set
 # CONFIG_NLS is not set
 
 #
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_apecs.c linux/arch/alpha/kernel/core_apecs.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_apecs.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/alpha/kernel/core_apecs.c	Tue Mar 21 10:46:21 2000
@@ -376,8 +376,12 @@
 	pci_isa_hose = hose = alloc_pci_controler();
 	hose->io_space = &ioport_resource;
 	hose->mem_space = &iomem_resource;
-	hose->config_space = APECS_CONF;
 	hose->index = 0;
+
+	hose->sparse_mem_base = APECS_SPARSE_MEM - IDENT_ADDR;
+	hose->dense_mem_base = APECS_DENSE_MEM - IDENT_ADDR;
+	hose->sparse_io_base = APECS_IO - IDENT_ADDR;
+	hose->dense_io_base = 0;
 
 	/*
 	 * Set up the PCI to main memory translation windows.
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_cia.c linux/arch/alpha/kernel/core_cia.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_cia.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/alpha/kernel/core_cia.c	Tue Mar 21 10:46:21 2000
@@ -529,7 +529,7 @@
 	/* Fifth, verify that a previously invalid PTE entry gets
 	   filled from the page table.  */
 
-	data0 = 0xabcdef123;
+	data0 = 0xabcdef12;
 	page[0] = data0;
 	arena->ptes[5] = pte0;
 	mcheck_expected(0) = 1;
@@ -640,7 +640,6 @@
 	pci_isa_hose = hose = alloc_pci_controler();
 	hose->io_space = &ioport_resource;
 	hose->mem_space = &iomem_resource;
-	hose->config_space = CIA_CONF;
 	hose->index = 0;
 
 	if (! is_pyxis) {
@@ -654,6 +653,16 @@
 
 		if (request_resource(&iomem_resource, hae_mem) < 0)
 			printk(KERN_ERR "Failed to request HAE_MEM\n");
+
+		hose->sparse_mem_base = CIA_SPARSE_MEM - IDENT_ADDR;
+		hose->dense_mem_base = CIA_DENSE_MEM - IDENT_ADDR;
+		hose->sparse_io_base = CIA_IO - IDENT_ADDR;
+		hose->dense_io_base = 0;
+	} else {
+		hose->sparse_mem_base = 0;
+		hose->dense_mem_base = CIA_BW_MEM - IDENT_ADDR;
+		hose->sparse_io_base = 0;
+		hose->dense_io_base = CIA_BW_IO - IDENT_ADDR;
 	}
 
 	/*
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_irongate.c linux/arch/alpha/kernel/core_irongate.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_irongate.c	Thu Feb 10 17:11:02 2000
+++ linux/arch/alpha/kernel/core_irongate.c	Tue Mar 21 10:46:21 2000
@@ -346,11 +346,21 @@
 	 * Create our single hose.
 	 */
 
-	hose = alloc_pci_controler();
+	pci_isa_hose = hose = alloc_pci_controler();
 	hose->io_space = &ioport_resource;
 	hose->mem_space = &iomem_resource;
-	hose->config_space = IRONGATE_CONF;
 	hose->index = 0;
+
+	/* This is for userland consumption.  For some reason, the 40-bit
+	   PIO bias that we use in the kernel through KSEG didn't work for
+	   the page table based user mappings.  So make sure we get the
+	   43-bit PIO bias.  */
+	hose->sparse_mem_base = 0;
+	hose->sparse_io_base = 0;
+	hose->dense_mem_base
+	  = (IRONGATE_MEM & 0xffffffffff) | 0x80000000000;
+	hose->dense_io_base
+	  = (IRONGATE_IO & 0xffffffffff) | 0x80000000000;
 
 	hose->sg_isa = hose->sg_pci = NULL;
 	__direct_map_base = 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_lca.c linux/arch/alpha/kernel/core_lca.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_lca.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/alpha/kernel/core_lca.c	Tue Mar 21 10:46:21 2000
@@ -298,8 +298,12 @@
 	pci_isa_hose = hose = alloc_pci_controler();
 	hose->io_space = &ioport_resource;
 	hose->mem_space = &iomem_resource;
-	hose->config_space = LCA_CONF;
 	hose->index = 0;
+
+	hose->sparse_mem_base = LCA_SPARSE_MEM - IDENT_ADDR;
+	hose->dense_mem_base = LCA_DENSE_MEM - IDENT_ADDR;
+	hose->sparse_io_base = LCA_IO - IDENT_ADDR;
+	hose->dense_io_base = 0;
 
 	/*
 	 * Set up the PCI to main memory translation windows.
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_mcpcia.c linux/arch/alpha/kernel/core_mcpcia.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_mcpcia.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/alpha/kernel/core_mcpcia.c	Tue Mar 21 10:46:21 2000
@@ -189,7 +189,7 @@
 		bus = 0;
 	addr = (bus << 16) | (devfn << 8) | (where);
 	addr <<= 5; /* swizzle for SPARSE */
-	addr |= hose->config_space;
+	addr |= hose->config_space_base;
 
 	*pci_addr = addr;
 	DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
@@ -337,13 +337,19 @@
 	int mid = MCPCIA_HOSE2MID(h);
 
 	hose = alloc_pci_controler();
+	if (h == 0)
+		pci_isa_hose = hose;
 	io = alloc_resource();
 	mem = alloc_resource();
 	hae_mem = alloc_resource();
 			
 	hose->io_space = io;
 	hose->mem_space = hae_mem;
-	hose->config_space = MCPCIA_CONF(mid);
+	hose->sparse_mem_base = MCPCIA_SPARSE(mid) - IDENT_ADDR;
+	hose->dense_mem_base = MCPCIA_DENSE(mid) - IDENT_ADDR;
+	hose->sparse_io_base = MCPCIA_IO(mid) - IDENT_ADDR;
+	hose->dense_io_base = 0;
+	hose->config_space_base = MCPCIA_CONF(mid);
 	hose->index = h;
 
 	io->start = MCPCIA_IO(mid) - MCPCIA_IO_BIAS;
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_polaris.c linux/arch/alpha/kernel/core_polaris.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_polaris.c	Sun Feb 20 21:12:38 2000
+++ linux/arch/alpha/kernel/core_polaris.c	Tue Mar 21 10:46:21 2000
@@ -192,11 +192,15 @@
 	 * Create our single hose.
 	 */
 
-	hose = alloc_pci_controler();
+	pci_isa_hose = hose = alloc_pci_controler();
 	hose->io_space = &ioport_resource;
 	hose->mem_space = &iomem_resource;
-	hose->config_space = POLARIS_DENSE_CONFIG_BASE;
 	hose->index = 0;
+
+	hose->sparse_mem_base = 0;
+	hose->dense_mem_base = POLARIS_DENSE_MEM_BASE - IDENT_ADDR;
+	hose->sparse_io_base = 0;
+	hose->dense_io_base = POLARIS_DENSE_IO_BASE - IDENT_ADDR;
 
 	hose->sg_isa = hose->sg_pci = NULL;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_t2.c linux/arch/alpha/kernel/core_t2.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_t2.c	Thu Feb 10 17:11:02 2000
+++ linux/arch/alpha/kernel/core_t2.c	Tue Mar 21 10:46:21 2000
@@ -384,11 +384,15 @@
 	 * Create our single hose.
 	 */
 
-	hose = alloc_pci_controler();
+	pci_isa_hose = hose = alloc_pci_controler();
 	hose->io_space = &ioport_resource;
 	hose->mem_space = &iomem_resource;
-	hose->config_space = T2_CONF;
 	hose->index = 0;
+
+	hose->sparse_mem_base = T2_SPARSE_MEM - IDENT_ADDR;
+	hose->dense_mem_base = T2_DENSE_MEM - IDENT_ADDR;
+	hose->sparse_io_base = T2_IO - IDENT_ADDR;
+	hose->dense_io_base = 0;
 
 	hose->sg_isa = hose->sg_pci = NULL;
 	__direct_map_base = 0x40000000;
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/core_tsunami.c linux/arch/alpha/kernel/core_tsunami.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/core_tsunami.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/alpha/kernel/core_tsunami.c	Tue Mar 21 10:46:21 2000
@@ -104,7 +104,7 @@
 	*type1 = (bus != 0);
 
 	addr = (bus << 16) | (device_fn << 8) | where;
-	addr |= hose->config_space;
+	addr |= hose->config_space_base;
 		
 	*pci_addr = addr;
 	DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
@@ -291,7 +291,18 @@
 	hose->io_space = alloc_resource();
 	hose->mem_space = alloc_resource();
 
-	hose->config_space = TSUNAMI_CONF(index);
+	/* This is for userland consumption.  For some reason, the 40-bit
+	   PIO bias that we use in the kernel through KSEG didn't work for
+	   the page table based user mappings.  So make sure we get the
+	   43-bit PIO bias.  */
+	hose->sparse_mem_base = 0;
+	hose->sparse_io_base = 0;
+	hose->dense_mem_base
+	  = (TSUNAMI_MEM(index) & 0xffffffffff) | 0x80000000000;
+	hose->dense_io_base
+	  = (TSUNAMI_IO(index) & 0xffffffffff) | 0x80000000000;
+
+	hose->config_space_base = TSUNAMI_CONF(index);
 	hose->index = index;
 
 	hose->io_space->start = TSUNAMI_IO(index) - TSUNAMI_IO_BIAS;
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.3.99-pre2/linux/arch/alpha/kernel/entry.S	Sun Mar 19 18:35:30 2000
+++ linux/arch/alpha/kernel/entry.S	Tue Mar 21 10:46:21 2000
@@ -8,7 +8,7 @@
 
 #define SIGCHLD 20
 
-#define NR_SYSCALLS 376
+#define NR_SYSCALLS 377
 
 /*
  * These offsets must match with alpha_mv in <asm/machvec.h>.
@@ -1156,3 +1156,4 @@
 	.quad sys_ni_syscall			/* sys_dipc */
 	.quad sys_pivot_root
 	.quad sys_mincore			/* 375 */
+	.quad sys_pciconfig_iobase
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/osf_sys.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/alpha/kernel/osf_sys.c	Tue Mar 21 10:47:06 2000
@@ -811,21 +811,8 @@
 		/* Return current software fp control & status bits.  */
 		/* Note that DU doesn't verify available space here.  */
 
-		/* EV6 implements most of the bits in hardware.  If
-		   UNDZ is not set, UNFD is maintained in software.  */
-		if (implver() == IMPLVER_EV6) {
-			unsigned long fpcr = rdfpcr();
-			w = ieee_fpcr_to_swcr(fpcr);
-			if (!(fpcr & FPCR_UNDZ)) {
-				w &= ~IEEE_TRAP_ENABLE_UNF;
-				w |= (current->thread.flags
-				      & IEEE_TRAP_ENABLE_UNF);
-			}
-		} else {
-			/* Otherwise we are forced to do everything in sw.  */
-			w = current->thread.flags & IEEE_SW_MASK;
-		}
-
+ 		w = current->thread.flags & IEEE_SW_MASK;
+ 		w = swcr_update_status(w, rdfpcr());
 		if (put_user(w, (unsigned long *) buffer))
 			return -EFAULT;
 		return 0;
@@ -876,7 +863,7 @@
 {
 	switch (op) {
 	case SSI_IEEE_FP_CONTROL: {
-		unsigned long swcr, fpcr, undz;
+		unsigned long swcr, fpcr;
 
 		/* 
 		 * Alpha Architecture Handbook 4.7.7.3:
@@ -891,14 +878,18 @@
 		current->thread.flags &= ~IEEE_SW_MASK;
 		current->thread.flags |= swcr & IEEE_SW_MASK;
 
-		/* Update the real fpcr.  Keep UNFD off if not UNDZ.  */
+		/* Update the real fpcr.  */
 		fpcr = rdfpcr();
-		undz = (fpcr & FPCR_UNDZ);
-		fpcr &= ~(FPCR_MASK | FPCR_DYN_MASK | FPCR_UNDZ);
+		fpcr &= FPCR_DYN_MASK;
 		fpcr |= ieee_swcr_to_fpcr(swcr);
-		fpcr &= ~(undz << 1);
 		wrfpcr(fpcr);
-		   
+
+ 		/* If any exceptions are now unmasked, send a signal.  */
+ 		if (((swcr & IEEE_STATUS_MASK)
+ 		     >> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr) {
+ 			send_sig(SIGFPE, current, 1);
+ 		}
+
 		return 0;
 	}
 
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/pci.c linux/arch/alpha/kernel/pci.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/pci.c	Sun Feb 20 21:12:38 2000
+++ linux/arch/alpha/kernel/pci.c	Tue Mar 21 10:46:21 2000
@@ -377,3 +377,39 @@
 
 	return res;
 }
+
+
+/* Provide information on locations of various I/O regions in physical
+   memory.  Do this on a per-card basis so that we choose the right hose.  */
+
+asmlinkage long
+sys_pciconfig_iobase(long which, unsigned long bus, unsigned long dfn)
+{
+	struct pci_controler *hose;
+	struct pci_dev *dev;
+
+	/* Special hook for ISA access.  */
+	if (bus == 0 && dfn == 0) {
+		hose = pci_isa_hose;
+	} else {
+		dev = pci_find_slot(bus, dfn);
+		if (!dev)
+			return -ENODEV;
+		hose = dev->sysdata;
+	}
+
+	switch (which) {
+	case IOBASE_HOSE:
+		return hose->index;
+	case IOBASE_SPARSE_MEM:
+		return hose->sparse_mem_base;
+	case IOBASE_DENSE_MEM:
+		return hose->dense_mem_base;
+	case IOBASE_SPARSE_IO:
+		return hose->sparse_io_base;
+	case IOBASE_DENSE_IO:
+		return hose->dense_io_base;
+	}
+
+	return -EOPNOTSUPP;
+}
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/process.c	Sat Feb 26 22:31:38 2000
+++ linux/arch/alpha/kernel/process.c	Tue Mar 21 10:47:06 2000
@@ -251,12 +251,9 @@
 flush_thread(void)
 {
 	/* Arrange for each exec'ed process to start off with a clean slate
-	   with respect to the FPU.  This is all exceptions disabled.  Note
-           that EV6 defines UNFD valid only with UNDZ, which we don't want
-	   for IEEE conformance -- so that disabled bit remains in software.  */
-
+	   with respect to the FPU.  This is all exceptions disabled.  */
 	current->thread.flags &= ~IEEE_SW_MASK;
-	wrfpcr(FPCR_DYN_NORMAL | FPCR_INVD | FPCR_DZED | FPCR_OVFD | FPCR_INED);
+	wrfpcr(FPCR_DYN_NORMAL | ieee_swcr_to_fpcr(0));
 }
 
 void
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/ptrace.c	Tue Dec  7 09:32:39 1999
+++ linux/arch/alpha/kernel/ptrace.c	Tue Mar 21 10:47:06 2000
@@ -17,6 +17,7 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
+#include <asm/fpu.h>
 
 #include "proto.h"
 
@@ -113,18 +114,30 @@
 /*
  * Get contents of register REGNO in task TASK.
  */
-static inline long
+static long
 get_reg(struct task_struct * task, unsigned long regno)
 {
+	/* Special hack for fpcr -- combine hardware and software bits.  */
+	if (regno == 63) {
+		unsigned long fpcr = *get_reg_addr(task, regno);
+		unsigned long swcr = task->thread.flags & IEEE_SW_MASK;
+		swcr = swcr_update_status(swcr, fpcr);
+		return fpcr | swcr;
+	}
 	return *get_reg_addr(task, regno);
 }
 
 /*
  * Write contents of register REGNO in task TASK.
  */
-static inline int
+static int
 put_reg(struct task_struct *task, unsigned long regno, long data)
 {
+	if (regno == 63) {
+		task->thread.flags = ((task->thread.flags & ~IEEE_SW_MASK)
+				      | (data & IEEE_SW_MASK));
+		data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data);
+	}
 	*get_reg_addr(task, regno) = data;
 	return 0;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/kernel/sys_jensen.c linux/arch/alpha/kernel/sys_jensen.c
--- v2.3.99-pre2/linux/arch/alpha/kernel/sys_jensen.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/alpha/kernel/sys_jensen.c	Tue Mar 21 10:46:21 2000
@@ -29,6 +29,7 @@
 
 #include "proto.h"
 #include "irq_impl.h"
+#include "pci_impl.h"
 #include "machvec_impl.h"
 
 
@@ -123,6 +124,22 @@
 static void __init
 jensen_init_arch(void)
 {
+	struct pci_controler *hose;
+
+	/* Create a hose so that we can report i/o base addresses to
+	   userland.  */
+
+	pci_isa_hose = hose = alloc_pci_controler();
+	hose->io_space = &ioport_resource;
+	hose->mem_space = &iomem_resource;
+	hose->index = 0;
+
+	hose->sparse_mem_base = EISA_MEM - IDENT_ADDR;
+	hose->dense_mem_base = 0;
+	hose->sparse_io_base = EISA_IO - IDENT_ADDR;
+	hose->dense_io_base = 0;
+
+	hose->sg_isa = hose->sg_pci = NULL;
 	__direct_map_base = 0;
 	__direct_map_size = 0xffffffff;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/alpha/math-emu/math.c linux/arch/alpha/math-emu/math.c
--- v2.3.99-pre2/linux/arch/alpha/math-emu/math.c	Tue Dec  7 09:32:40 1999
+++ linux/arch/alpha/math-emu/math.c	Tue Mar 21 10:47:06 2000
@@ -160,8 +160,7 @@
 	FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
 
 	unsigned long fa, fb, fc, func, mode, src;
-	unsigned long fpcw = current->thread.flags;
-	unsigned long res, va, vb, vc, fpcr;
+	unsigned long res, va, vb, vc, swcr, fpcr;
 	__u32 insn;
 
 	MOD_INC_USE_COUNT;
@@ -175,10 +174,11 @@
 	mode   = (insn >> 11) & 0x3;
 	
 	fpcr = rdfpcr();
+	swcr = swcr_update_status(current->thread.flags, fpcr);
 
 	if (mode == 3) {
-	    /* Dynamic -- get rounding mode from fpcr.  */
-	    mode = (fpcr >> FPCR_DYN_SHIFT) & 3;
+		/* Dynamic -- get rounding mode from fpcr.  */
+		mode = (fpcr >> FPCR_DYN_SHIFT) & 3;
 	}
 
 	switch (src) {
@@ -231,9 +231,14 @@
 			}
 			FP_CMP_D(res, DA, DB, 3);
 			vc = 0x4000000000000000;
-			/* CMPTEQ, CMPTUN don't trap on QNaN, while CMPTLT and CMPTLE do */
-			if (res == 3 && ((func & 3) >= 2 || FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
+			/* CMPTEQ, CMPTUN don't trap on QNaN,
+			   while CMPTLT and CMPTLE do */
+			if (res == 3
+			    && ((func & 3) >= 2
+				|| FP_ISSIGNAN_D(DA)
+				|| FP_ISSIGNAN_D(DB))) {
 				FP_SET_EXCEPTION(FP_EX_INVALID);
+			}
 			switch (func) {
 			case FOP_FNC_CMPxUN: if (res != 3) vc = 0; break;
 			case FOP_FNC_CMPxEQ: if (res) vc = 0; break;
@@ -285,9 +290,11 @@
 			}
 
 		case FOP_FNC_CVTxQ:
-			if (DB_c == FP_CLS_NAN && (_FP_FRAC_HIGH_RAW_D(DB) & _FP_QNANBIT_D))
-				vc = 0; /* AAHB Table B-2 sais QNaN should not trigger INV */
-			else
+			if (DB_c == FP_CLS_NAN
+			    && (_FP_FRAC_HIGH_RAW_D(DB) & _FP_QNANBIT_D)) {
+			  /* AAHB Table B-2 says QNaN should not trigger INV */
+				vc = 0;
+			} else
 				FP_TO_INT_ROUND_D(vc, DB, 64, 2);
 			goto done_d;
 		}
@@ -321,11 +328,15 @@
 
 pack_s:
 	FP_PACK_SP(&vc, SR);
+	if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ))
+		vc = 0;
 	alpha_write_fp_reg_s(fc, vc);
 	goto done;
 
 pack_d:
 	FP_PACK_DP(&vc, DR);
+	if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ))
+		vc = 0;
 done_d:
 	alpha_write_fp_reg(fc, vc);
 	goto done;
@@ -345,16 +356,16 @@
 done:
 	if (_fex) {
 		/* Record exceptions in software control word.  */
-		current->thread.flags
-		  = fpcw |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
+		swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
+		current->thread.flags |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
 
-		/* Update hardware control register */
+		/* Update hardware control register.  */
 		fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
-		fpcr |= ieee_swcr_to_fpcr(fpcw);
+		fpcr |= ieee_swcr_to_fpcr(swcr);
 		wrfpcr(fpcr);
 
 		/* Do we generate a signal?  */
-		if (_fex & fpcw & IEEE_TRAP_ENABLE_MASK) {
+		if (_fex & swcr & IEEE_TRAP_ENABLE_MASK) {
 			MOD_DEC_USE_COUNT;
 			return 0;
 		}
@@ -378,7 +389,7 @@
 alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
 {
 	unsigned long trigger_pc = regs->pc - 4;
-	unsigned long insn, opcode, rc;
+	unsigned long insn, opcode, rc, no_signal = 0;
 
 	MOD_INC_USE_COUNT;
 
@@ -402,15 +413,13 @@
 		      case OPC_PAL:
 		      case OPC_JSR:
 		      case 0x30 ... 0x3f:	/* branches */
-			MOD_DEC_USE_COUNT;
-			return 0;
+			goto egress;
 
 		      case OPC_MISC:
 			switch (insn & 0xffff) {
 			      case MISC_TRAPB:
 			      case MISC_EXCB:
-				MOD_DEC_USE_COUNT;
-				return 0;
+				goto egress;
 
 			      default:
 				break;
@@ -432,16 +441,15 @@
 			break;
 		}
 		if (!write_mask) {
-			if (alpha_fp_emul(trigger_pc)) {
-				/* re-execute insns in trap-shadow: */
-				regs->pc = trigger_pc + 4;
-				MOD_DEC_USE_COUNT;
-				return 1;
-			}
-			break;
+			/* Re-execute insns in the trap-shadow.  */
+			regs->pc = trigger_pc + 4;
+			no_signal = alpha_fp_emul(trigger_pc);
+			goto egress;
 		}
 		trigger_pc -= 4;
 	}
+
+egress:
 	MOD_DEC_USE_COUNT;
-	return 0;
+	return no_signal;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.3.99-pre2/linux/arch/arm/config.in	Sun Mar 19 18:35:30 2000
+++ linux/arch/arm/config.in	Thu Mar 23 14:45:04 2000
@@ -119,6 +119,8 @@
      "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
    define_bool CONFIG_PCI y
    source drivers/pci/Config.in
+else
+   define_bool CONFIG_PCI n
 fi
 
 #
@@ -127,8 +129,10 @@
 if [ "$CONFIG_ARCH_CATS" = "y" -o \
      "$CONFIG_ARCH_SHARK" = "y" -o \
      "$CONFIG_ARCH_NETWINDER" = "y" ]; then
+   define_bool CONFIG_ISA y
    define_bool CONFIG_ISA_DMA y
 else
+   define_bool CONFIG_ISA n
    define_bool CONFIG_ISA_DMA n
 fi
 
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/arm/defconfig linux/arch/arm/defconfig
--- v2.3.99-pre2/linux/arch/arm/defconfig	Tue Mar 14 19:10:38 2000
+++ linux/arch/arm/defconfig	Thu Mar 23 14:45:04 2000
@@ -2,6 +2,7 @@
 # Automatically generated make config: don't edit
 #
 CONFIG_ARM=y
+CONFIG_UID16=y
 
 #
 # Code maturity level options
@@ -19,14 +20,17 @@
 CONFIG_HOST_FOOTBRIDGE=y
 # CONFIG_ADDIN_FOOTBRIDGE is not set
 CONFIG_ARCH_EBSA285=y
-# CONFIG_CATS is not set
+# CONFIG_ARCH_CATS is not set
 CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_PERSONAL_SERVER is not set
 # CONFIG_ARCH_ACORN is not set
 CONFIG_CPU_32=y
 # CONFIG_CPU_26 is not set
 CONFIG_CPU_32v4=y
 CONFIG_CPU_SA110=y
 CONFIG_PCI=y
+CONFIG_PCI_NAMES=y
+CONFIG_ISA=y
 CONFIG_ISA_DMA=y
 # CONFIG_ALIGNMENT_TRAP is not set
 
@@ -51,10 +55,14 @@
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_ARTHUR is not set
+
+#
+# Parallel port support
+#
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
 CONFIG_PARPORT_PC_FIFO=y
-# CONFIG_PARPORT_PC_PCMCIA is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
 # CONFIG_PARPORT_ARC is not set
 # CONFIG_PARPORT_AMIGA is not set
 # CONFIG_PARPORT_MFC3 is not set
@@ -68,6 +76,11 @@
 # CONFIG_LEDS_CPU is not set
 
 #
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
 # I2O device support
 #
 # CONFIG_I2O is not set
@@ -87,24 +100,9 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_CPQ_DA is not set
-
-#
-# Additional Block Devices
-#
-CONFIG_BLK_DEV_LOOP=m
-CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_MD=y
-CONFIG_MD_LINEAR=m
-CONFIG_MD_STRIPED=m
-CONFIG_MD_MIRRORING=m
-CONFIG_MD_RAID5=m
-CONFIG_BLK_DEV_RAM=y
-# CONFIG_BLK_DEV_INITRD is not set
 # CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-CONFIG_PARIDE_PARPORT=y
 CONFIG_PARIDE=m
+CONFIG_PARIDE_PARPORT=y
 
 #
 # Parallel IDE high-level drivers
@@ -132,6 +130,19 @@
 CONFIG_PARIDE_KTTI=m
 CONFIG_PARIDE_ON20=m
 CONFIG_PARIDE_ON26=m
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_INITRD is not set
 
 #
 # Character devices
@@ -149,6 +160,11 @@
 # CONFIG_PPDEV is not set
 
 #
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
 # Mice
 #
 # CONFIG_BUSMOUSE is not set
@@ -172,6 +188,7 @@
 CONFIG_SOFT_WATCHDOG=y
 # CONFIG_PCWATCHDOG is not set
 # CONFIG_ACQUIRE_WDT is not set
+# CONFIG_MIXCOMWD is not set
 # CONFIG_21285_WATCHDOG is not set
 CONFIG_977_WATCHDOG=m
 CONFIG_DS1620=y
@@ -188,29 +205,32 @@
 # CONFIG_I2C_PARPORT is not set
 
 #
-# Radio/Video Adapters
+# Radio Adapters
 #
 # CONFIG_RADIO_CADET is not set
 # CONFIG_RADIO_RTRACK is not set
 # CONFIG_RADIO_RTRACK2 is not set
 # CONFIG_RADIO_AZTECH is not set
-# CONFIG_VIDEO_BT848 is not set
 # CONFIG_RADIO_GEMTEK is not set
-# CONFIG_VIDEO_PMS is not set
 # CONFIG_RADIO_MIROPCM20 is not set
-# CONFIG_VIDEO_BWQCAM is not set
-# CONFIG_VIDEO_CQCAM is not set
-# CONFIG_VIDEO_SAA5249 is not set
 # CONFIG_RADIO_SF16FMI is not set
-# CONFIG_VIDEO_STRADIS is not set
 # CONFIG_RADIO_TERRATEC is not set
 # CONFIG_RADIO_TRUST is not set
 # CONFIG_RADIO_TYPHOON is not set
 # CONFIG_RADIO_ZOLTRIX is not set
+
+#
+# Video Adapters
+#
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+# CONFIG_VIDEO_STRADIS is not set
 # CONFIG_VIDEO_ZORAN is not set
 # CONFIG_VIDEO_BUZ is not set
 # CONFIG_VIDEO_ZR36120 is not set
-CONFIG_VIDEO_CYBERPRO=m
 # CONFIG_DTLK is not set
 # CONFIG_R3964 is not set
 # CONFIG_APPLICOM is not set
@@ -221,15 +241,10 @@
 # CONFIG_FTAPE is not set
 # CONFIG_DRM is not set
 # CONFIG_DRM_TDFX is not set
-
-#
-# PCMCIA character device support
-#
-# CONFIG_PCMCIA_SERIAL_CS is not set
 # CONFIG_AGP is not set
 
 #
-# Support for USB
+# USB support
 #
 CONFIG_USB=m
 
@@ -237,32 +252,49 @@
 # USB Controllers
 #
 # CONFIG_USB_UHCI is not set
+# CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=m
-CONFIG_USB_OHCI_DEBUG=y
-CONFIG_USB_OHCI_HCD=m
-CONFIG_USB_OHCI_VROOTHUB=y
 
 #
 # Miscellaneous USB options
 #
-# CONFIG_USB_DEBUG_ISOC is not set
-CONFIG_USB_PROC=y
-# CONFIG_USB_EZUSB is not set
+CONFIG_USB_DEVICEFS=y
 
 #
 # USB Devices
 #
-CONFIG_USB_MOUSE=m
-# CONFIG_USB_HP_SCANNER is not set
-CONFIG_USB_KBD=m
+CONFIG_USB_PRINTER=m
+# CONFIG_USB_SCANNER is not set
 CONFIG_USB_AUDIO=m
 CONFIG_USB_ACM=m
-CONFIG_USB_PRINTER=m
 # CONFIG_USB_SERIAL is not set
 # CONFIG_USB_CPIA is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_OV511 is not set
 # CONFIG_USB_DC2XX is not set
-# CONFIG_USB_SCSI is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_STORAGE is not set
 # CONFIG_USB_USS720 is not set
+# CONFIG_USB_DABUSB is not set
+# CONFIG_USB_PLUSB is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_DSBR is not set
+
+#
+# USB HID
+#
+# CONFIG_USB_HID is not set
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_WMFORCE is not set
+CONFIG_INPUT_KEYBDEV=m
+CONFIG_INPUT_MOUSEDEV=m
+CONFIG_INPUT_MOUSEDEV_MIX=y
+# CONFIG_INPUT_MOUSEDEV_DIGITIZER is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
 
 #
 # Console drivers
@@ -300,6 +332,7 @@
 # CONFIG_FBCON_MAC is not set
 # CONFIG_FBCON_VGA_PLANES is not set
 CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_HGA is not set
 # CONFIG_FBCON_FONTWIDTH8_ONLY is not set
 CONFIG_FBCON_FONTS=y
 CONFIG_FONT_8x8=y
@@ -379,6 +412,7 @@
 #
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_NET_SB1000 is not set
 
@@ -398,30 +432,30 @@
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
-# CONFIG_RTL8139 is not set
-# CONFIG_DM9102 is not set
 # CONFIG_AT1700 is not set
 # CONFIG_DEPCA is not set
 # CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
+CONFIG_NET_PCI=y
 # CONFIG_PCNET32 is not set
 # CONFIG_ADAPTEC_STARFIRE is not set
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
 # CONFIG_DE4X5 is not set
-CONFIG_DEC_ELCP=m
+CONFIG_TULIP=m
 # CONFIG_DGRS is not set
-# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_DM9102 is not set
+# CONFIG_EEPRO100 is not set
 # CONFIG_LNE390 is not set
 # CONFIG_NE3210 is not set
 CONFIG_NE2K_PCI=y
+# CONFIG_RTL8129 is not set
+# CONFIG_8139TOO is not set
 # CONFIG_SIS900 is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
 # CONFIG_ES3210 is not set
 # CONFIG_EPIC100 is not set
-# CONFIG_ZNET is not set
 # CONFIG_NET_POCKET is not set
 
 #
@@ -449,7 +483,7 @@
 # CONFIG_NET_RADIO is not set
 
 #
-# Token Ring driver support
+# Token Ring devices
 #
 # CONFIG_TR is not set
 # CONFIG_NET_FC is not set
@@ -462,22 +496,23 @@
 # CONFIG_WAN is not set
 
 #
-# PCMCIA network device support
+# ATA/IDE/MFM/RLL support
 #
-# CONFIG_NET_PCMCIA is not set
+CONFIG_IDE=y
 
 #
-# ATA/IDE/MFM/RLL support
+# IDE, ATA and ATAPI Block devices
 #
-CONFIG_IDE=y
 CONFIG_BLK_DEV_IDE=y
 
 #
 # Please see Documentation/ide.txt for help/info on IDE drives
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_IDEDISK_MULTI_MODE=y
+# CONFIG_BLK_DEV_IDECS is not set
 # CONFIG_BLK_DEV_IDECD is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
@@ -487,32 +522,44 @@
 # IDE chipset support/bugfixes
 #
 # CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA_PCI=y
 # CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_BLK_DEV_OFFBOARD=y
 CONFIG_IDEDMA_PCI_AUTO=y
-CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
+CONFIG_BLK_DEV_IDEDMA=y
 CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
-CONFIG_BLK_DEV_OFFBOARD=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
 # CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
 # CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
 # CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_CMD64X_RAID is not set
 CONFIG_BLK_DEV_CY82C693=y
+# CONFIG_BLK_DEV_CS5530 is not set
 # CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
 # CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
 CONFIG_BLK_DEV_PDC202XX=y
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
 CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
 # CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
 CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
@@ -533,8 +580,12 @@
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
 CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_TRACEINIT is not set
+# CONFIG_SOUND_DMAP is not set
 # CONFIG_SOUND_AD1816 is not set
 # CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_ACI_MIXER is not set
 # CONFIG_SOUND_CS4232 is not set
 # CONFIG_SOUND_SSCAPE is not set
 # CONFIG_SOUND_GUS is not set
@@ -545,9 +596,11 @@
 # CONFIG_SOUND_NM256 is not set
 # CONFIG_SOUND_MAD16 is not set
 # CONFIG_SOUND_PAS is not set
+# CONFIG_PAS_JOYSTICK is not set
 # CONFIG_SOUND_PSS is not set
 # CONFIG_SOUND_SOFTOSS is not set
 CONFIG_SOUND_SB=m
+# CONFIG_SOUND_AWE32_SYNTH is not set
 # CONFIG_SOUND_WAVEFRONT is not set
 # CONFIG_SOUND_MAUI is not set
 # CONFIG_SOUND_VIA82CXXX is not set
@@ -555,24 +608,18 @@
 # CONFIG_SOUND_OPL3SA1 is not set
 # CONFIG_SOUND_OPL3SA2 is not set
 # CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_AEDSP16 is not set
 # CONFIG_SOUND_VIDC is not set
 CONFIG_SOUND_WAVEARTIST=m
-CONFIG_WAVEARTIST_BASE=250
-CONFIG_WAVEARTIST_IRQ=12
-CONFIG_WAVEARTIST_DMA=3
-CONFIG_WAVEARTIST_DMA2=7
 
 #
-# Additional low level sound drivers
-#
-# CONFIG_LOWLEVEL_SOUND is not set
-
-#
-# Filesystems
+# File systems
 #
 # CONFIG_QUOTA is not set
 CONFIG_AUTOFS_FS=y
+# CONFIG_AUTOFS4_FS is not set
 CONFIG_ADFS_FS=y
+# CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
@@ -581,12 +628,15 @@
 # CONFIG_UMSDOS_FS is not set
 CONFIG_VFAT_FS=m
 # CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
 CONFIG_ISO9660_FS=m
 CONFIG_JOLIET=y
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
 CONFIG_DEVPTS_FS=y
 # CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
@@ -612,7 +662,14 @@
 # Partition Types
 #
 CONFIG_PARTITION_ADVANCED=y
+CONFIG_ACORN_PARTITION=y
+# CONFIG_ACORN_PARTITION_ICS is not set
+CONFIG_ACORN_PARTITION_ADFS=y
+# CONFIG_ACORN_PARTITION_POWERTEC is not set
+# CONFIG_ACORN_PARTITION_RISCIX is not set
 # CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
 # CONFIG_MAC_PARTITION is not set
 CONFIG_MSDOS_PARTITION=y
 # CONFIG_BSD_DISKLABEL is not set
@@ -620,13 +677,6 @@
 # CONFIG_UNIXWARE_DISKLABEL is not set
 # CONFIG_SGI_PARTITION is not set
 # CONFIG_SUN_PARTITION is not set
-# CONFIG_AMIGA_PARTITION is not set
-# CONFIG_ATARI_PARTITION is not set
-CONFIG_ACORN_PARTITION=y
-CONFIG_ACORN_PARTITION_ADFS=y
-# CONFIG_ACORN_PARTITION_ICS is not set
-# CONFIG_ACORN_PARTITION_POWERTEC is not set
-# CONFIG_ACORN_PARTITION_RISCIX is not set
 CONFIG_NLS=y
 
 #
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v2.3.99-pre2/linux/arch/i386/Makefile	Sun Mar 19 18:35:30 2000
+++ linux/arch/i386/Makefile	Wed Mar 22 22:23:54 2000
@@ -30,56 +30,42 @@
 CFLAGS += $(shell if $(CC) -mpreferred-stack-boundary=2 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mpreferred-stack-boundary=2"; fi)
 
 ifdef CONFIG_M386
-CFLAGS := $(CFLAGS) -DCPU=386
 CFLAGS += $(shell if $(CC) -march=i386 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i386"; else echo "-m386"; fi)
-AFLAGS := $(AFLAGS) -DCPU=386
 endif
 
 ifdef CONFIG_M486
-CFLAGS := $(CFLAGS) -DCPU=486
 CFLAGS += $(shell if $(CC) -march=i486 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i486"; else echo "-m486"; fi)
-AFLAGS := $(AFLAGS) -DCPU=486
 endif
 
 ifdef CONFIG_M586
-CFLAGS := $(CFLAGS) -DCPU=586
 CFLAGS += $(shell if $(CC) -march=i586 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i586"; fi)
-AFLAGS := $(AFLAGS) -DCPU=586
 endif
 
 ifdef CONFIG_M586TSC
-CFLAGS := $(CFLAGS) -DCPU=586
 CFLAGS += $(shell if $(CC) -march=i586 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i586"; fi)
-AFLAGS := $(AFLAGS) -DCPU=586
 endif
 
 ifdef CONFIG_M686
-CFLAGS := $(CFLAGS) -DCPU=686
 CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
-AFLAGS := $(AFLAGS) -DCPU=686
 endif
 
 ifdef CONFIG_MK6
-CFLAGS := $(CFLAGS) -DCPU=586
 CFLAGS += $(shell if $(CC) -march=k6 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=k6"; fi)
-AFLAGS := $(AFLAGS) -DCPU=586
 endif
 
 ifdef CONFIG_MK7
-CFLAGS := $(CFLAGS) -malign-functions=4 -fschedule-insns2 -mwide-multiply -fexpensive-optimizations -DCPU=686
-CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
-AFLAGS := $(AFLAGS) -DCPU=686
+CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi) -malign-functions=4 -fschedule-insns2 -mwide-multiply -fexpensive-optimizations
 endif
 
 HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
 
-SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib
+SUBDIRS += arch/i386/kernel arch/i386/mm arch/i386/lib
 CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES)
 LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a
 
 ifdef CONFIG_MATH_EMULATION
-SUBDIRS := $(SUBDIRS) arch/i386/math-emu
-DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a
+SUBDIRS += arch/i386/math-emu
+DRIVERS += arch/i386/math-emu/math.a
 endif
 
 arch/i386/kernel: dummy
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c
--- v2.3.99-pre2/linux/arch/i386/boot/compressed/misc.c	Tue Nov 23 22:42:20 1999
+++ linux/arch/i386/boot/compressed/misc.c	Mon Mar 20 08:24:54 2000
@@ -145,7 +145,7 @@
 	free_mem_ptr = (long) *ptr;
 }
  
-static void scroll()
+static void scroll(void)
 {
 	int i;
 
@@ -197,6 +197,7 @@
 	char *ss = (char*)s;
 
 	for (i=0;i<n;i++) ss[i] = c;
+	return s;
 }
 
 void* memcpy(void* __dest, __const void* __src,
@@ -206,13 +207,14 @@
 	char *d = (char *)__dest, *s = (char *)__src;
 
 	for (i=0;i<__n;i++) d[i] = s[i];
+	return __dest;
 }
 
 /* ===========================================================================
  * Fill the input buffer. This is called only when the buffer is empty
  * and at least one byte is really needed.
  */
-static int fill_inbuf()
+static int fill_inbuf(void)
 {
 	if (insize != 0) {
 		error("ran out of input data\n");
@@ -228,7 +230,7 @@
  * Write the output window window[0..outcnt-1] and update crc and bytes_out.
  * (Used for the decompressed data only.)
  */
-static void flush_window_low()
+static void flush_window_low(void)
 {
     ulg c = crc;         /* temporary variable */
     unsigned n;
@@ -246,7 +248,7 @@
     outcnt = 0;
 }
 
-static void flush_window_high()
+static void flush_window_high(void)
 {
     ulg c = crc;         /* temporary variable */
     unsigned n;
@@ -262,7 +264,7 @@
     outcnt = 0;
 }
 
-static void flush_window()
+static void flush_window(void)
 {
 	if (high_loaded) flush_window_high();
 	else flush_window_low();
@@ -286,7 +288,7 @@
 	short b;
 	} stack_start = { & user_stack [STACK_SIZE] , __KERNEL_DS };
 
-void setup_normal_output_buffer()
+void setup_normal_output_buffer(void)
 {
 #ifdef STANDARD_MEMORY_BIOS_CALL
 	if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n");
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.3.99-pre2/linux/arch/i386/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/i386/config.in	Wed Mar 22 22:23:54 2000
@@ -30,32 +30,44 @@
 if [ "$CONFIG_M386" != "y" ]; then
    define_bool CONFIG_X86_WP_WORKS_OK y
    define_bool CONFIG_X86_INVLPG y
+   define_bool CONFIG_X86_CMPXCHG y
    define_bool CONFIG_X86_BSWAP y
    define_bool CONFIG_X86_POPAD_OK y
 fi
-if [ "$CONFIG_M686" = "y" -o "$CONFIG_M586TSC" = "y" ]; then
+if [ "$CONFIG_M386" = "y" -o "$CONFIG_M486" = "y" ]; then
+   define_int CONFIG_X86_L1_CACHE_BYTES 16
+else
+   define_int CONFIG_X86_L1_CACHE_BYTES 32
+fi
+if [ "$CONFIG_M486" = "y" -o "$CONFIG_M586" = "y" ]; then
+   define_bool CONFIG_X86_USE_STRING_486 y
+   define_bool CONFIG_X86_ALIGNMENT_16 y
+fi
+if [ "$CONFIG_M586TSC" = "y" -o "$CONFIG_MK6" = "y" ]; then
+   define_bool CONFIG_X86_USE_STRING_486 y
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_TSC y
 fi
 if [ "$CONFIG_M686" = "y" ]; then
+   define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_PGE y
-fi
-if [ "$CONFIG_MK6" = "y" ]; then
-   define_bool CONFIG_X86_TSC y
+   define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
 if [ "$CONFIG_MK7" = "y" ]; then
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_USE_3DNOW y
    define_bool CONFIG_X86_PGE y
+   define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
 
 tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE
 
 choice 'High Memory Support' \
 	"off    CONFIG_NOHIGHMEM \
-	4GB    CONFIG_HIGHMEM4G \
-	64GB   CONFIG_HIGHMEM64G" off
+	 4GB    CONFIG_HIGHMEM4G \
+	 64GB   CONFIG_HIGHMEM64G" off
 if [ "$CONFIG_HIGHMEM4G" = "y" ]; then
    define_bool CONFIG_HIGHMEM y
 fi
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.99-pre2/linux/arch/i386/defconfig	Sun Mar 19 18:35:30 2000
+++ linux/arch/i386/defconfig	Mon Mar 20 08:36:56 2000
@@ -324,6 +324,7 @@
 # CONFIG_NET_ISA is not set
 CONFIG_NET_PCI=y
 # CONFIG_PCNET32 is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
 # CONFIG_DE4X5 is not set
@@ -375,6 +376,7 @@
 # CONFIG_PCMCIA_XIRC2PS is not set
 # CONFIG_ARCNET_COM20020_CS is not set
 # CONFIG_PCMCIA_3C575 is not set
+# CONFIG_PCMCIA_XIRTULIP is not set
 CONFIG_NET_PCMCIA_RADIO=y
 CONFIG_PCMCIA_RAYCS=y
 # CONFIG_PCMCIA_NETWAVE is not set
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.3.99-pre2/linux/arch/i386/kernel/entry.S	Tue Mar 14 19:10:39 2000
+++ linux/arch/i386/kernel/entry.S	Thu Mar 23 08:15:02 2000
@@ -174,9 +174,7 @@
 	jmp ret_from_sys_call
 
 
-	ALIGN
-	.globl	ret_from_fork
-ret_from_fork:
+ENTRY(ret_from_fork)
 	pushl %ebx
 	call SYMBOL_NAME(schedule_tail)
 	addl $4, %esp
@@ -202,10 +200,7 @@
 	jne tracesys
 	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
 	movl %eax,EAX(%esp)		# save the return value
-	ALIGN
-	.globl ret_from_sys_call
-	.globl ret_from_intr
-ret_from_sys_call:
+ENTRY(ret_from_sys_call)
 #ifdef __SMP__
 	movl processor(%ebx),%eax
 	shll $5,%eax
@@ -273,8 +268,7 @@
 #endif
 	jne   handle_softirq
 
-	ALIGN
-ret_from_intr:
+ENTRY(ret_from_intr)
 	GET_CURRENT(%ebx)
 	movl EFLAGS(%esp),%eax		# mix EFLAGS and CS
 	movb CS(%esp),%al
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/kernel/i8259.c linux/arch/i386/kernel/i8259.c
--- v2.3.99-pre2/linux/arch/i386/kernel/i8259.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/i386/kernel/i8259.c	Thu Mar 23 08:34:11 2000
@@ -292,12 +292,12 @@
 	if (irq & 8) {
 		inb(0xA1);		/* DUMMY - (do we need this?) */
 		outb(cached_A1,0xA1);
+		outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */
 		outb(0x62,0x20);	/* 'Specific EOI' to master-IRQ2 */
-		outb(0x20,0xA0);	/* 'generic EOI' to slave */
 	} else {
 		inb(0x21);		/* DUMMY - (do we need this?) */
 		outb(cached_21,0x21);
-		outb(0x20,0x20);	/* 'generic EOI' to master */
+		outb(0x60+irq,0x20);	/* 'Specific EOI' to master */
 	}
 	spin_unlock_irqrestore(&i8259A_lock, flags);
 	return;
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.3.99-pre2/linux/arch/i386/kernel/io_apic.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/i386/kernel/io_apic.c	Mon Mar 20 07:53:51 2000
@@ -190,13 +190,13 @@
 		pirq_entries[i] = -1;
 
 	pirqs_enabled = 1;
-	printk("PIRQ redirection, working around broken MP-BIOS.\n");
+	printk(KERN_INFO "PIRQ redirection, working around broken MP-BIOS.\n");
 	max = MAX_PIRQS;
 	if (ints[0] < MAX_PIRQS)
 		max = ints[0];
 
 	for (i = 0; i < max; i++) {
-		printk("... PIRQ%d -> IRQ %d\n", i, ints[i+1]);
+		printk(KERN_DEBUG "... PIRQ%d -> IRQ %d\n", i, ints[i+1]);
 		/*
 		 * PIRQs are mapped upside down, usually.
 		 */
@@ -289,7 +289,7 @@
 		unsigned int port = 0x4d0 + (irq >> 3);
 		return (inb(port) >> (irq & 7)) & 1;
 	}
-	printk("Broken MPtable reports ISA irq %d\n", irq);
+	printk(KERN_INFO "Broken MPtable reports ISA irq %d\n", irq);
 	return 0;
 }
 
@@ -338,7 +338,7 @@
 				}
 				default:
 				{
-					printk("broken BIOS!!\n");
+					printk(KERN_WARNING "broken BIOS!!\n");
 					polarity = 1;
 					break;
 				}
@@ -352,7 +352,7 @@
 		}
 		case 2: /* reserved */
 		{
-			printk("broken BIOS!!\n");
+			printk(KERN_WARNING "broken BIOS!!\n");
 			polarity = 1;
 			break;
 		}
@@ -363,7 +363,7 @@
 		}
 		default: /* invalid */
 		{
-			printk("broken BIOS!!\n");
+			printk(KERN_WARNING "broken BIOS!!\n");
 			polarity = 1;
 			break;
 		}
@@ -402,7 +402,7 @@
 				}
 				default:
 				{
-					printk("broken BIOS!!\n");
+					printk(KERN_WARNING "broken BIOS!!\n");
 					trigger = 1;
 					break;
 				}
@@ -416,7 +416,7 @@
 		}
 		case 2: /* reserved */
 		{
-			printk("broken BIOS!!\n");
+			printk(KERN_WARNING "broken BIOS!!\n");
 			trigger = 1;
 			break;
 		}
@@ -427,7 +427,7 @@
 		}
 		default: /* invalid */
 		{
-			printk("broken BIOS!!\n");
+			printk(KERN_WARNING "broken BIOS!!\n");
 			trigger = 0;
 			break;
 		}
@@ -454,7 +454,7 @@
 	 * Debugging check, we are in big trouble if this message pops up!
 	 */
 	if (mp_irqs[idx].mpc_dstirq != pin)
-		printk("broken BIOS or MPTABLE parser, ayiee!!\n");
+		printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n");
 
 	switch (mp_bus_id_to_type[bus])
 	{
@@ -477,7 +477,7 @@
 		}
 		default:
 		{
-			printk("unknown bus type %d.\n",bus); 
+			printk(KERN_ERR "unknown bus type %d.\n",bus); 
 			irq = 0;
 			break;
 		}
@@ -489,10 +489,10 @@
 	if ((pin >= 16) && (pin <= 23)) {
 		if (pirq_entries[pin-16] != -1) {
 			if (!pirq_entries[pin-16]) {
-				printk("disabling PIRQ%d\n", pin-16);
+				printk(KERN_DEBUG "disabling PIRQ%d\n", pin-16);
 			} else {
 				irq = pirq_entries[pin-16];
-				printk("using PIRQ%d -> IRQ %d\n",
+				printk(KERN_DEBUG "using PIRQ%d -> IRQ %d\n",
 						pin-16, irq);
 			}
 		}
@@ -549,7 +549,7 @@
 	struct IO_APIC_route_entry entry;
 	int apic, pin, idx, irq, first_notcon = 1, vector;
 
-	printk("init IO_APIC IRQs\n");
+	printk(KERN_DEBUG "init IO_APIC IRQs\n");
 
 	for (apic = 0; apic < nr_ioapics; apic++) {
 	for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
@@ -567,7 +567,7 @@
 		idx = find_irq_entry(apic,pin,mp_INT);
 		if (idx == -1) {
 			if (first_notcon) {
-				printk(" IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin);
+				printk(KERN_DEBUG " IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin);
 				first_notcon = 0;
 			} else
 				printk(", %d-%d", mp_ioapics[apic].mpc_apicid, pin);
@@ -658,8 +658,8 @@
 
 void __init UNEXPECTED_IO_APIC(void)
 {
-	printk(" WARNING: unexpected IO-APIC, please mail\n");
-	printk("          to linux-smp@vger.rutgers.edu\n");
+	printk(KERN_WARNING " WARNING: unexpected IO-APIC, please mail\n");
+	printk(KERN_WARNING "          to linux-smp@vger.rutgers.edu\n");
 }
 
 void __init print_IO_APIC(void)
@@ -669,15 +669,16 @@
 	struct IO_APIC_reg_01 reg_01;
 	struct IO_APIC_reg_02 reg_02;
 
- 	printk("number of MP IRQ sources: %d.\n", mp_irq_entries);
+ 	printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
 	for (i = 0; i < nr_ioapics; i++)
-		printk("number of IO-APIC #%d registers: %d.\n", mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]);
+		printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n",
+		       mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]);
 
 	/*
 	 * We are a bit conservative about what we expect.  We have to
 	 * know about every hardware change ASAP.
 	 */
-	printk("testing the IO APIC.......................\n");
+	printk(KERN_INFO "testing the IO APIC.......................\n");
 
 	for (apic = 0; apic < nr_ioapics; apic++) {
 
@@ -686,14 +687,15 @@
 	if (reg_01.version >= 0x10)
 		*(int *)&reg_02 = io_apic_read(apic, 2);
 
-	printk("\nIO APIC #%d......\n", mp_ioapics[apic].mpc_apicid);
-	printk(".... register #00: %08X\n", *(int *)&reg_00);
-	printk(".......    : physical APIC id: %02X\n", reg_00.ID);
+	printk("\n");
+	printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid);
+	printk(KERN_DEBUG ".... register #00: %08X\n", *(int *)&reg_00);
+	printk(KERN_DEBUG ".......    : physical APIC id: %02X\n", reg_00.ID);
 	if (reg_00.__reserved_1 || reg_00.__reserved_2)
 		UNEXPECTED_IO_APIC();
 
-	printk(".... register #01: %08X\n", *(int *)&reg_01);
-	printk(".......     : max redirection entries: %04X\n", reg_01.entries);
+	printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)&reg_01);
+	printk(KERN_DEBUG ".......     : max redirection entries: %04X\n", reg_01.entries);
 	if (	(reg_01.entries != 0x0f) && /* older (Neptune) boards */
 		(reg_01.entries != 0x17) && /* typical ISA+PCI boards */
 		(reg_01.entries != 0x1b) && /* Compaq Proliant boards */
@@ -704,7 +706,7 @@
 	)
 		UNEXPECTED_IO_APIC();
 
-	printk(".......     : IO APIC version: %04X\n", reg_01.version);
+	printk(KERN_DEBUG ".......     : IO APIC version: %04X\n", reg_01.version);
 	if (	(reg_01.version != 0x01) && /* 82489DX IO-APICs */
 		(reg_01.version != 0x10) && /* oldest IO-APICs */
 		(reg_01.version != 0x11) && /* Pentium/Pro IO-APICs */
@@ -715,16 +717,16 @@
 		UNEXPECTED_IO_APIC();
 
 	if (reg_01.version >= 0x10) {
-		printk(".... register #02: %08X\n", *(int *)&reg_02);
-		printk(".......     : arbitration: %02X\n", reg_02.arbitration);
+		printk(KERN_DEBUG ".... register #02: %08X\n", *(int *)&reg_02);
+		printk(KERN_DEBUG ".......     : arbitration: %02X\n", reg_02.arbitration);
 		if (reg_02.__reserved_1 || reg_02.__reserved_2)
 			UNEXPECTED_IO_APIC();
 	}
 
-	printk(".... IRQ redirection table:\n");
+	printk(KERN_DEBUG ".... IRQ redirection table:\n");
 
-	printk(" NR Log Phy ");
-	printk("Mask Trig IRR Pol Stat Dest Deli Vect:   \n");
+	printk(KERN_DEBUG " NR Log Phy ");
+	printk(KERN_DEBUG "Mask Trig IRR Pol Stat Dest Deli Vect:   \n");
 
 	for (i = 0; i <= reg_01.entries; i++) {
 		struct IO_APIC_route_entry entry;
@@ -732,7 +734,7 @@
 		*(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2);
 		*(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2);
 
-		printk(" %02x %03X %02X  ",
+		printk(KERN_DEBUG " %02x %03X %02X  ",
 			i,
 			entry.dest.logical.logical_dest,
 			entry.dest.physical.physical_dest
@@ -765,7 +767,7 @@
 		printk("\n");
 	}
 
-	printk(".................................... done.\n");
+	printk(KERN_INFO ".................................... done.\n");
 
 	return;
 }
@@ -775,7 +777,7 @@
 	unsigned int v;
 	int i, j;
 
-	printk("0123456789abcdef0123456789abcdef\n");
+	printk(KERN_DEBUG "0123456789abcdef0123456789abcdef\n" KERN_DEBUG);
 	for (i = 0; i < 8; i++) {
 		v = apic_read(base + i*0x10);
 		for (j = 0; j < 32; j++) {
@@ -792,40 +794,40 @@
 {
 	unsigned int v, ver, maxlvt;
 
-	printk("\nprinting local APIC contents on CPU#%d/%d:\n",
+	printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
 		smp_processor_id(), hard_smp_processor_id());
 	v = apic_read(APIC_ID);
-	printk("... APIC ID:      %08x (%01x)\n", v, GET_APIC_ID(v));
+	printk(KERN_INFO "... APIC ID:      %08x (%01x)\n", v, GET_APIC_ID(v));
 	v = apic_read(APIC_LVR);
-	printk("... APIC VERSION: %08x\n", v);
+	printk(KERN_INFO "... APIC VERSION: %08x\n", v);
 	ver = GET_APIC_VERSION(v);
 	maxlvt = get_maxlvt();
 
 	v = apic_read(APIC_TASKPRI);
-	printk("... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK);
+	printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK);
 
 	if (APIC_INTEGRATED(ver)) {			/* !82489DX */
 		v = apic_read(APIC_ARBPRI);
-		printk("... APIC ARBPRI: %08x (%02x)\n", v,
+		printk(KERN_DEBUG "... APIC ARBPRI: %08x (%02x)\n", v,
 			v & APIC_ARBPRI_MASK);
 		v = apic_read(APIC_PROCPRI);
-		printk("... APIC PROCPRI: %08x\n", v);
+		printk(KERN_DEBUG "... APIC PROCPRI: %08x\n", v);
 	}
 
 	v = apic_read(APIC_EOI);
-	printk("... APIC EOI: %08x\n", v);
+	printk(KERN_DEBUG "... APIC EOI: %08x\n", v);
 	v = apic_read(APIC_LDR);
-	printk("... APIC LDR: %08x\n", v);
+	printk(KERN_DEBUG "... APIC LDR: %08x\n", v);
 	v = apic_read(APIC_DFR);
-	printk("... APIC DFR: %08x\n", v);
+	printk(KERN_DEBUG "... APIC DFR: %08x\n", v);
 	v = apic_read(APIC_SPIV);
-	printk("... APIC SPIV: %08x\n", v);
+	printk(KERN_DEBUG "... APIC SPIV: %08x\n", v);
 
-	printk("... APIC ISR field:\n");
+	printk(KERN_DEBUG "... APIC ISR field:\n");
 	print_APIC_bitfield(APIC_ISR);
-	printk("... APIC TMR field:\n");
+	printk(KERN_DEBUG "... APIC TMR field:\n");
 	print_APIC_bitfield(APIC_TMR);
-	printk("... APIC IRR field:\n");
+	printk(KERN_DEBUG "... APIC IRR field:\n");
 	print_APIC_bitfield(APIC_IRR);
 
 	if (APIC_INTEGRATED(ver)) {		/* !82489DX */
@@ -837,37 +839,37 @@
 			apic_write(APIC_ESR, 0);
 		}
 		v = apic_read(APIC_ESR);
-		printk("... APIC ESR: %08x\n", v);
+		printk(KERN_DEBUG "... APIC ESR: %08x\n", v);
 	}
 
 	v = apic_read(APIC_ICR);
-	printk("... APIC ICR: %08x\n", v);
+	printk(KERN_DEBUG "... APIC ICR: %08x\n", v);
 	v = apic_read(APIC_ICR2);
-	printk("... APIC ICR2: %08x\n", v);
+	printk(KERN_DEBUG "... APIC ICR2: %08x\n", v);
 
 	v = apic_read(APIC_LVTT);
-	printk("... APIC LVTT: %08x\n", v);
+	printk(KERN_DEBUG "... APIC LVTT: %08x\n", v);
 
 	if (maxlvt > 3) {                       /* PC is LVT#4. */
 		v = apic_read(APIC_LVTPC);
-		printk("... APIC LVTPC: %08x\n", v);
+		printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v);
 	}
 	v = apic_read(APIC_LVT0);
-	printk("... APIC LVT0: %08x\n", v);
+	printk(KERN_DEBUG "... APIC LVT0: %08x\n", v);
 	v = apic_read(APIC_LVT1);
-	printk("... APIC LVT1: %08x\n", v);
+	printk(KERN_DEBUG "... APIC LVT1: %08x\n", v);
 
 	if (maxlvt > 2) {			/* ERR is LVT#3. */
 		v = apic_read(APIC_LVTERR);
-		printk("... APIC LVTERR: %08x\n", v);
+		printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v);
 	}
 
 	v = apic_read(APIC_TMICT);
-	printk("... APIC TMICT: %08x\n", v);
+	printk(KERN_DEBUG "... APIC TMICT: %08x\n", v);
 	v = apic_read(APIC_TMCCT);
-	printk("... APIC TMCCT: %08x\n", v);
+	printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v);
 	v = apic_read(APIC_TDCR);
-	printk("... APIC TDCR: %08x\n", v);
+	printk(KERN_DEBUG "... APIC TDCR: %08x\n", v);
 	printk("\n");
 }
 
@@ -958,7 +960,7 @@
 		 * Read the right value from the MPC table and
 		 * write it into the ID register.
 	 	 */
-		printk("...changing IO-APIC physical APIC ID to %d ...",
+		printk(KERN_INFO "...changing IO-APIC physical APIC ID to %d ...",
 					mp_ioapics[apic].mpc_apicid);
 
 		/*
@@ -1061,7 +1063,7 @@
 	for (j = 0; j < smp_num_cpus; j++) {
 		cpu = cpu_logical_map(j);
 		if (atomic_read(&nmi_counter(cpu)) - atomic_read(&tmp[cpu].__nmi_counter) <= 3) {
-			printk("CPU#%d NMI appears to be stuck.\n", cpu);
+			printk(KERN_WARNING "CPU#%d NMI appears to be stuck.\n", cpu);
 			return 0;
 		}
 	}
@@ -1270,7 +1272,7 @@
 	 * is from Maciej W. Rozycki - so we do not have to EOI from
 	 * the NMI handler or the timer interrupt.
 	 */ 
-	printk("activating NMI Watchdog ...");
+	printk(KERN_INFO "activating NMI Watchdog ...");
 
 	smp_call_function(enable_NMI_through_LVT0, NULL, 1, 1);
 	enable_NMI_through_LVT0(NULL);
@@ -1299,7 +1301,7 @@
 	pin1 = find_timer_pin(mp_INT);
 	pin2 = find_timer_pin(mp_ExtINT);
 
-	printk("..TIMER: vector=%d pin1=%d pin2=%d\n", vector, pin1, pin2);
+	printk(KERN_INFO "..TIMER: vector=%d pin1=%d pin2=%d\n", vector, pin1, pin2);
 
 	/*
 	 * Ok, does IRQ0 through the IOAPIC work?
@@ -1317,11 +1319,11 @@
 	}
 
 	if (pin1 != -1) {
-		printk("..MP-BIOS bug: 8254 timer not connected to IO-APIC\n");
+		printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n");
 		clear_IO_APIC_pin(0, pin1);
 	}
 
-	printk("...trying to set up timer (IRQ0) through the 8259A ... ");
+	printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... ");
 	if (pin2 != -1) {
 		printk("\n..... (found pin %d) ...", pin2);
 		/*
@@ -1345,11 +1347,11 @@
 	printk(" failed.\n");
 
 	if (nmi_watchdog) {
-		printk("timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n");
+		printk(KERN_WARNING "timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n");
 		nmi_watchdog = 0;
 	}
 
-	printk("...trying to set up timer as Virtual Wire IRQ...");
+	printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
 
 	disable_8259A_irq(0);
 	irq_desc[0].handler = &lapic_irq_type;
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.3.99-pre2/linux/arch/i386/kernel/signal.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/i386/kernel/signal.c	Thu Mar 23 08:15:01 2000
@@ -597,10 +597,6 @@
  * Note that 'init' is a special process: it doesn't get signals it doesn't
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
- *
- * Note that we go through the signals twice: once to check the signals that
- * the kernel can handle, and then we build all the user-level signal handling
- * stack-frames in one go after that.
  */
 int do_signal(struct pt_regs *regs, sigset_t *oldset)
 {
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/i386/lib/checksum.S linux/arch/i386/lib/checksum.S
--- v2.3.99-pre2/linux/arch/i386/lib/checksum.S	Sun May 16 13:17:22 1999
+++ linux/arch/i386/lib/checksum.S	Wed Mar 22 22:23:54 2000
@@ -25,6 +25,7 @@
  *		2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h>
 #include <asm/errno.h>
 				
 /*
@@ -39,7 +40,7 @@
 .align 4
 .globl csum_partial								
 		
-#if CPU!=686
+#ifndef CONFIG_X86_USE_PPRO_CHECKSUM
 
 	  /*		
 	   * Experiments with Ethernet and SLIP connections show that buff
@@ -114,7 +115,9 @@
 	popl %esi
 	ret
 
-#else /* CPU==686 */
+#else
+
+/* Version for PentiumII/PPro */
 
 csum_partial:
 	pushl %esi
@@ -210,7 +213,7 @@
 	popl %esi
 	ret
 				
-#endif /* CPU==686 */ 
+#endif
 
 /*
 unsigned int csum_partial_copy_generic (const char *src, char *dst,
@@ -243,7 +246,7 @@
 .align 4
 .globl csum_partial_copy_generic
 				
-#if CPU!=686
+#ifndef CONFIG_X86_USE_PPRO_CHECKSUM
 
 #define ARGBASE 16		
 #define FP		12
@@ -448,4 +451,4 @@
 #undef ROUND
 #undef ROUND1		
 		
-#endif	/* CPU==i686 */ 
+#endif
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/ia64/config.in linux/arch/ia64/config.in
--- v2.3.99-pre2/linux/arch/ia64/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/ia64/config.in	Thu Mar 23 14:45:05 2000
@@ -19,11 +19,11 @@
 
 if [ "$CONFIG_IA64_DIG" = "y" ]; then
 	bool '  Enable Itanium A-step specific code' CONFIG_ITANIUM_ASTEP_SPECIFIC
-	bool '  Enable SoftSDV hacks' CONFIG_IA64_SOFTSDV_HACKS n
-	bool '  Enable BigSur hacks' CONFIG_IA64_BIGSUR_HACKS y
-	bool '  Enable Lion hacks' CONFIG_IA64_LION_HACKS n
-	bool '  Emulate PAL/SAL/EFI firmware' CONFIG_IA64_FW_EMU n
-	bool '  Get PCI IRQ routing from firmware/ACPI'	CONFIG_IA64_IRQ_ACPI y
+	bool '  Enable SoftSDV hacks' CONFIG_IA64_SOFTSDV_HACKS
+	bool '  Enable BigSur hacks' CONFIG_IA64_BIGSUR_HACKS
+	bool '  Enable Lion hacks' CONFIG_IA64_LION_HACKS
+	bool '  Emulate PAL/SAL/EFI firmware' CONFIG_IA64_FW_EMU
+	bool '  Get PCI IRQ routing from firmware/ACPI'	CONFIG_IA64_IRQ_ACPI
 fi
 
 if [ "$CONFIG_IA64_GENERIC" = "y" ]; then
@@ -37,33 +37,32 @@
 
 define_bool CONFIG_KCORE_ELF y	# On IA-64, we always want an ELF /proc/kcore.
 
-bool 'SMP support' CONFIG_SMP n
-bool 'Performance monitor support' CONFIG_PERFMON n
+bool 'SMP support' CONFIG_SMP
+bool 'Performance monitor support' CONFIG_PERFMON
 
-bool 'Networking support' CONFIG_NET n
-bool 'System V IPC' CONFIG_SYSVIPC n
-bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT n
-bool 'Sysctl support' CONFIG_SYSCTL n
+bool 'Networking support' CONFIG_NET
+bool 'System V IPC' CONFIG_SYSVIPC
+bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
-bool 'PCI support' CONFIG_PCI n
+bool 'PCI support' CONFIG_PCI
 source drivers/pci/Config.in
 
 source drivers/pcmcia/Config.in
 
 mainmenu_option next_comment
  comment 'Code maturity level options'
- bool 'Prompt for development and/or incomplete code/drivers' \
-	CONFIG_EXPERIMENTAL n
+ bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
 endmenu
 
 mainmenu_option next_comment
  comment 'Loadable module support'
- bool 'Enable loadable module support' CONFIG_MODULES n
+ bool 'Enable loadable module support' CONFIG_MODULES
  if [ "$CONFIG_MODULES" = "y" ]; then
-  bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
-  bool 'Kernel module loader' CONFIG_KMOD n
+  bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
+  bool 'Kernel module loader' CONFIG_KMOD
  fi
 endmenu
 
@@ -99,7 +98,7 @@
 
 if [ "$CONFIG_SCSI" != "n" ]; then
   source drivers/scsi/Config.in
-  bool 'Simulated SCSI disk' CONFIG_SCSI_SIM n
+  bool 'Simulated SCSI disk' CONFIG_SCSI_SIM
 fi
 endmenu
 
@@ -107,7 +106,7 @@
   mainmenu_option next_comment
   comment 'Network device support'
 
-  bool 'Network device support' CONFIG_NETDEVICES n
+  bool 'Network device support' CONFIG_NETDEVICES
   if [ "$CONFIG_NETDEVICES" = "y" ]; then
     source drivers/net/Config.in
   fi
@@ -128,7 +127,7 @@
 mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
-bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI n
+bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI
 if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
   source drivers/cdrom/Config.in
 fi
@@ -145,11 +144,11 @@
 if [ "$CONFIG_VT" = "y" ]; then
   mainmenu_option next_comment
   comment 'Console drivers'
-  bool 'VGA text console' CONFIG_VGA_CONSOLE n
+  bool 'VGA text console' CONFIG_VGA_CONSOLE
+  source drivers/video/Config.in
   if [ "$CONFIG_FB" = "y" ]; then
     define_bool CONFIG_PCI_CONSOLE y
   fi
-  source drivers/video/Config.in
   endmenu
 fi
 
@@ -173,11 +172,11 @@
   define_bool CONFIG_MATHEMU y
 fi
 
-bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ n
-bool 'Early printk support (requires VGA!)' CONFIG_IA64_EARLY_PRINTK n
-bool 'Turn on compare-and-exchange bug checking (slow!)' CONFIG_IA64_DEBUG_CMPXCHG n
-bool 'Turn on irq debug checks (slow!)' CONFIG_IA64_DEBUG_IRQ n
-bool 'Print possible IA64 hazards to console' CONFIG_IA64_PRINT_HAZARDS n
+bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+bool 'Early printk support (requires VGA!)' CONFIG_IA64_EARLY_PRINTK
+bool 'Turn on compare-and-exchange bug checking (slow!)' CONFIG_IA64_DEBUG_CMPXCHG
+bool 'Turn on irq debug checks (slow!)' CONFIG_IA64_DEBUG_IRQ
+bool 'Print possible IA64 hazards to console' CONFIG_IA64_PRINT_HAZARDS
 bool 'Built-in Kernel Debugger support' CONFIG_KDB
 if [ "$CONFIG_KDB" = "y" ]; then
   bool 'Compile the kernel with frame pointers' CONFIG_KDB_FRAMEPTR
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/ia64/defconfig linux/arch/ia64/defconfig
--- v2.3.99-pre2/linux/arch/ia64/defconfig	Tue Mar 14 19:10:39 2000
+++ linux/arch/ia64/defconfig	Thu Mar 23 14:45:05 2000
@@ -3,6 +3,35 @@
 #
 
 #
+# General setup
+#
+CONFIG_IA64=y
+# CONFIG_IA64_GENERIC is not set
+CONFIG_IA64_HP_SIM=y
+# CONFIG_IA64_SGI_SN1_SIM is not set
+# CONFIG_IA64_DIG is not set
+# CONFIG_IA64_PAGE_SIZE_4KB is not set
+# CONFIG_IA64_PAGE_SIZE_8KB is not set
+CONFIG_IA64_PAGE_SIZE_16KB=y
+# CONFIG_IA64_PAGE_SIZE_64KB is not set
+CONFIG_KCORE_ELF=y
+# CONFIG_SMP is not set
+# CONFIG_PERFMON is not set
+# CONFIG_NET is not set
+# CONFIG_SYSVIPC is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_SYSCTL is not set
+# CONFIG_BINFMT_ELF is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_PCI=y
+CONFIG_PCI_NAMES=y
+
+#
+# PCMCIA/CardBus support
+#
+# CONFIG_PCMCIA is not set
+
+#
 # Code maturity level options
 #
 CONFIG_EXPERIMENTAL=y
@@ -13,53 +42,56 @@
 # CONFIG_MODULES is not set
 
 #
-# General setup
+# Parallel port support
 #
-CONFIG_IA64_SIM=y
-CONFIG_PCI=y
-# CONFIG_PCI_QUIRKS is not set
-CONFIG_PCI_OLD_PROC=y
-# CONFIG_NET is not set
-# CONFIG_SYSVIPC is not set
-# CONFIG_BSD_PROCESS_ACCT is not set
-# CONFIG_SYSCTL is not set
-# CONFIG_BINFMT_ELF is not set
-# CONFIG_BINFMT_MISC is not set
-# CONFIG_BINFMT_JAVA is not set
-# CONFIG_BINFMT_EM86 is not set
 # CONFIG_PARPORT is not set
 
 #
-# Plug and Play support
+# Plug and Play configuration
 #
 # CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
 
 #
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
 
 #
 # Additional Block Devices
 #
 # CONFIG_BLK_DEV_LOOP is not set
-# CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-CONFIG_PARIDE_PARPORT=y
-# CONFIG_PARIDE is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_PCI is not set
+# CONFIG_I2O_BLOCK is not set
+# CONFIG_I2O_SCSI is not set
+# CONFIG_I2O_PROC is not set
 
 #
 # ATA/IDE/MFM/RLL support
 #
 CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
 CONFIG_BLK_DEV_IDE=y
 
 #
 # Please see Documentation/ide.txt for help/info on IDE drives
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_BLK_DEV_IDECS is not set
@@ -78,24 +110,44 @@
 CONFIG_BLK_DEV_IDEPCI=y
 CONFIG_IDEPCI_SHARE_IRQ=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
 CONFIG_IDEDMA_PCI_AUTO=y
-# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+CONFIG_BLK_DEV_IDEDMA=y
 CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
-# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_CMD64X_RAID is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
 CONFIG_BLK_DEV_PIIX=y
 CONFIG_PIIX_TUNING=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
 # CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
 CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
 #
 # CONFIG_SCSI is not set
-# CONFIG_SCSI_G_NCR5380_PORT is not set
-# CONFIG_SCSI_G_NCR5380_MEM is not set
 
 #
 # Amateur Radio support
@@ -120,51 +172,91 @@
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 # CONFIG_UNIX98_PTYS is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
 # CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
 # CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
 # CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 CONFIG_EFI_RTC=y
+
+#
+# Video For Linux
+#
 # CONFIG_VIDEO_DEV is not set
-# CONFIG_NVRAM is not set
-# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
 
 #
 # Ftape, the floppy tape device driver
 #
 # CONFIG_FTAPE is not set
-# CONFIG_FT_NORMAL_DEBUG is not set
-# CONFIG_FT_FULL_DEBUG is not set
-# CONFIG_FT_NO_TRACE is not set
-# CONFIG_FT_NO_TRACE_AT_ALL is not set
-# CONFIG_FT_STD_FDC is not set
-# CONFIG_FT_MACH2 is not set
-# CONFIG_FT_PROBE_FC10 is not set
-# CONFIG_FT_ALT_FDC is not set
+# CONFIG_DRM is not set
+# CONFIG_DRM_TDFX is not set
+# CONFIG_AGP is not set
 
 #
-# Filesystems
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# File systems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_EXT2_FS is not set
-# CONFIG_ISO9660_FS is not set
-# CONFIG_FAT_FS is not set
-# CONFIG_PROC_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_PROC_FS is not set
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS is not set
+# CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
 # CONFIG_UFS_FS is not set
-# CONFIG_BSD_DISKLABEL is not set
-# CONFIG_SMD_DISKLABEL is not set
-# CONFIG_SOLARIS_X86_PARTITION is not set
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_MAC_PARTITION is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
 # CONFIG_NLS is not set
 
 #
@@ -175,5 +267,11 @@
 #
 # Kernel hacking
 #
+# CONFIG_IA32_SUPPORT is not set
 # CONFIG_MATHEMU is not set
 # CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_IA64_EARLY_PRINTK is not set
+# CONFIG_IA64_DEBUG_CMPXCHG is not set
+# CONFIG_IA64_DEBUG_IRQ is not set
+# CONFIG_IA64_PRINT_HAZARDS is not set
+# CONFIG_KDB is not set
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/m68k/Makefile linux/arch/m68k/Makefile
--- v2.3.99-pre2/linux/arch/m68k/Makefile	Tue Sep  7 12:14:06 1999
+++ linux/arch/m68k/Makefile	Thu Mar 23 08:47:44 2000
@@ -19,10 +19,6 @@
 # override top level makefile
 AS += -m68020
 LD += -m m68kelf
-ifneq ($(COMPILE_ARCH),$(ARCH))
-	# prefix for cross-compiling binaries
-	CROSS_COMPILE = m68k-linux-
-endif
 
 ifndef CONFIG_SUN3
 LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux.lds
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.3.99-pre2/linux/arch/m68k/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/m68k/config.in	Thu Mar 23 14:45:05 2000
@@ -15,6 +15,7 @@
 mainmenu_option next_comment
 comment 'Platform dependent setup'
 
+define_bool CONFIG_ISA n
 bool 'Amiga support' CONFIG_AMIGA
 bool 'Atari support' CONFIG_ATARI
 if [ "$CONFIG_ATARI" = "y" ]; then
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.3.99-pre2/linux/arch/m68k/defconfig	Tue Mar 14 19:10:39 2000
+++ linux/arch/m68k/defconfig	Thu Mar 23 14:45:05 2000
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
 #
+CONFIG_UID16=y
 
 #
 # Code maturity level options
@@ -8,18 +9,19 @@
 CONFIG_EXPERIMENTAL=y
 
 #
-# Loadable module support
-#
-# CONFIG_MODULES is not set
-# CONFIG_MODVERSIONS is not set
-# CONFIG_KMOD is not set
-
-#
-# Platform-dependent setup
+# Platform dependent setup
 #
+# CONFIG_ISA is not set
 CONFIG_AMIGA=y
 # CONFIG_ATARI is not set
+# CONFIG_PCI is not set
 # CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_SUN3 is not set
+# CONFIG_Q40 is not set
 
 #
 # Processor type
@@ -28,42 +30,48 @@
 CONFIG_M68030=y
 CONFIG_M68040=y
 # CONFIG_M68060 is not set
+# CONFIG_M68KFPU_EMU is not set
 # CONFIG_ADVANCED is not set
-# CONFIG_SINGLE_MEMORY_CHUNK is not set
-# CONFIG_RMW_INSNS is not set
 
 #
 # General setup
 #
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_SYSCTL=y
 CONFIG_KCORE_ELF=y
 # CONFIG_KCORE_AOUT is not set
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
 CONFIG_ZORRO=y
-# CONFIG_AMIGA_GSP is not set
-# CONFIG_GSP_RESOLVER is not set
-# CONFIG_GSP_A2410 is not set
 # CONFIG_AMIGA_PCMCIA is not set
 # CONFIG_HEARTBEAT is not set
 CONFIG_PROC_HARDWARE=y
+# CONFIG_PARPORT is not set
+# CONFIG_PRINTER is not set
 
 #
-# Block device driver configuration
+# Loadable module support
 #
+# CONFIG_MODULES is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
 CONFIG_AMIGA_FLOPPY=y
-CONFIG_ATARI_FLOPPY=y
-# CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_AMIGA_Z2RAM is not set
-# CONFIG_ATARI_ACSI is not set
-# CONFIG_ACSI_MULTI_LUN is not set
-# CONFIG_ATARI_SLM
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+
+#
+# Additional Block Devices
+#
 # CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
-# CONFIG_MD_LINEAR is not set
-# CONFIG_MD_STRIPED is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 
@@ -71,8 +79,9 @@
 # Networking options
 #
 CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
 # CONFIG_NETLINK is not set
-# CONFIG_FIREWALL is not set
+# CONFIG_NETFILTER is not set
 # CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
@@ -88,16 +97,17 @@
 #
 # (it is safe to leave these untouched)
 #
-# CONFIG_INET_RARP is not set
-CONFIG_IP_NOSR=y
 # CONFIG_SKB_LARGE is not set
 # CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
 
 #
 #  
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
@@ -106,7 +116,6 @@
 # CONFIG_WAN_ROUTER is not set
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
-# CONFIG_CPU_IS_SLOW is not set
 
 #
 # QoS and/or fair queueing
@@ -117,6 +126,8 @@
 # ATA/IDE/MFM/RLL support
 #
 # CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
@@ -124,12 +135,15 @@
 CONFIG_SCSI=y
 
 #
-# SCSI support type (disk, tape, CDrom)
+# SCSI support type (disk, tape, CD-ROM)
 #
 CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
 CONFIG_CHR_DEV_ST=y
+CONFIG_ST_EXTRA_DEVS=2
 CONFIG_BLK_DEV_SR=y
 # CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_SR_EXTRA_DEVS=2
 # CONFIG_CHR_DEV_SG is not set
 
 #
@@ -143,6 +157,7 @@
 # SCSI low-level drivers
 #
 CONFIG_A3000_SCSI=y
+# CONFIG_A4000T_SCSI is not set
 CONFIG_A2091_SCSI=y
 CONFIG_GVP11_SCSI=y
 # CONFIG_CYBERSTORM_SCSI is not set
@@ -150,13 +165,10 @@
 # CONFIG_BLZ2060_SCSI is not set
 # CONFIG_BLZ1230_SCSI is not set
 # CONFIG_FASTLANE_SCSI is not set
-# CONFIG_A4000T_SCSI is not set
 # CONFIG_A4091_SCSI is not set
 # CONFIG_WARPENGINE_SCSI is not set
 # CONFIG_BLZ603EPLUS_SCSI is not set
-CONFIG_ATARI_SCSI=y
-# CONFIG_ATARI_SCSI_TOSHIBA_DELAY is not set
-# CONFIG_ATARI_SCSI_RESET_BOOT is not set
+# CONFIG_OKTAGON_SCSI is not set
 
 #
 # Network device support
@@ -165,80 +177,150 @@
 # CONFIG_DUMMY is not set
 # CONFIG_SLIP is not set
 # CONFIG_PPP is not set
+# CONFIG_EQUALIZER is not set
 # CONFIG_ARIADNE is not set
 # CONFIG_ARIADNE2 is not set
 # CONFIG_A2065 is not set
 # CONFIG_HYDRA is not set
-# CONFIG_APNE is not set
-# CONFIG_ATARILANCE is not set
-# CONFIG_ATARI_BIONET is not set
-# CONFIG_ATARI_PAMSNET is not set
 
 #
-# Filesystems
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_AMIGAMOUSE=y
+CONFIG_BUSMOUSE=y
+CONFIG_AMIGA_BUILTIN_SERIAL=y
+# CONFIG_GVPIOEXT is not set
+# CONFIG_GVPIOEXT_LP is not set
+# CONFIG_GVPIOEXT_PLIP is not set
+# CONFIG_MULTIFACE_III_TTY is not set
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_USERIAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# Sound support
+#
+# CONFIG_SOUND is not set
+
+#
+# File systems
 #
 # CONFIG_QUOTA is not set
-CONFIG_MINIX_FS=y
-CONFIG_EXT2_FS=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
 CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
-# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
-# CONFIG_MSDOS_PARTITION is not set
-CONFIG_PROC_FS=y
-CONFIG_NFS_FS=y
-# CONFIG_ROOT_NFS is not set
-# CONFIG_SMB_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
 # CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+CONFIG_MINIX_FS=y
+# CONFIG_NTFS_FS is not set
 # CONFIG_HPFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
 # CONFIG_UFS_FS is not set
-# CONFIG_CRAMFS is not set
 
 #
-# Frame buffer devices
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_AMIGA_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
 #
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Console drivers
+#
+CONFIG_FB=y
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
 CONFIG_FB_AMIGA=y
 CONFIG_FB_AMIGA_OCS=y
 CONFIG_FB_AMIGA_ECS=y
 CONFIG_FB_AMIGA_AGA=y
 # CONFIG_FB_CYBER is not set
 # CONFIG_FB_VIRGE is not set
-# CONFIG_FB_CVPPC is not set
 # CONFIG_FB_RETINAZ3 is not set
-# CONFIG_FB_CLGEN is not set
-# CONFIG_FB_ATARI is not set
+# CONFIG_FB_FM2 is not set
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
-
-CONFIG_NLS_CODEPAGE_437=y
-
-#
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-# CONFIG_M68K_PRINTER is not set
-CONFIG_AMIGAMOUSE=y
-CONFIG_ATARIMOUSE=y
-CONFIG_AMIGA_BUILTIN_SERIAL=y
-# CONFIG_GVPIOEXT is not set
-# CONFIG_GVPIOEXT_LP is not set
-# CONFIG_GVPIOEXT_PLIP is not set
-# CONFIG_MULTIFACE_III_TTY is not set
-# CONFIG_USERIAL is not set
-# CONFIG_WATCHDOG is not set
-# CONFIG_UMISC is not set
-
-#
-# Sound
-#
-# CONFIG_SOUND is not set
-# CONFIG_DMASOUND is not set
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_AFB=y
+CONFIG_FBCON_ILBM=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+# CONFIG_FBCON_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_FONT_PEARL_8x8=y
 
 #
 # Kernel hacking
 #
-CONFIG_SCSI_CONSTANTS=y
+# CONFIG_MAGIC_SYSRQ is not set
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.3.99-pre2/linux/arch/mips/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/mips/config.in	Thu Mar 23 14:45:05 2000
@@ -26,34 +26,37 @@
 #
 # Select some configuration options automatically for certain systems.
 #
-unset CONFIG_PCI
-unset CONFIG_ISA
 unset CONFIG_MIPS_JAZZ
 unset CONFIG_VIDEO_G364
 
-if [ "$CONFIG_ALGOR_P4032" = "y" ]; then
+if [ "$CONFIG_ALGOR_P4032" = "y" -o "$CONFIG_SNI_RM200_PCI" = "y" -o \
+     "$CONFIG_DDB5074" = "y" ]; then
    define_bool CONFIG_PCI y
+else
+   define_bool CONFIG_PCI n
 fi
+if [ "$CONFIG_MIPS_MAGNUM_4000" = "y" -o "$CONFIG_OLIVETTI_M700" = "y" -o \
+     "$CONFIG_SNI_RM200_PCI" = "y" -o "$CONFIG_ACER_PICA_61" = "y" ]; then
+   define_bool CONFIG_ISA y
+else
+   define_bool CONFIG_ISA n
+fi
+
 if [ "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \
      "$CONFIG_OLIVETTI_M700" = "y" ]; then
-   define_bool CONFIG_ISA y
    define_bool CONFIG_HAVE_IO_PORTS y
    define_bool CONFIG_MIPS_JAZZ y
    define_bool CONFIG_FB y
    define_bool CONFIG_FB_G364 y	
 fi
 if [ "$CONFIG_ACER_PICA_61" = "y" ]; then
-   define_bool CONFIG_ISA y
    define_bool CONFIG_HAVE_IO_PORTS y
    define_bool CONFIG_MIPS_JAZZ y
 fi
 if [ "$CONFIG_SNI_RM200_PCI" = "y" ]; then
-   define_bool CONFIG_ISA y
    define_bool CONFIG_HAVE_IO_PORTS y
-   define_bool CONFIG_PCI y
 fi
 if [ "$CONFIG_DDB5074" = "y" ]; then
-   define_bool CONFIG_PCI y
    define_bool CONFIG_HAVE_IO_PORTS y
 fi
 endmenu
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/mips/defconfig linux/arch/mips/defconfig
--- v2.3.99-pre2/linux/arch/mips/defconfig	Tue Mar 14 19:10:39 2000
+++ linux/arch/mips/defconfig	Thu Mar 23 14:45:05 2000
@@ -19,6 +19,8 @@
 # CONFIG_OLIVETTI_M700 is not set
 CONFIG_SGI_IP22=y
 # CONFIG_SNI_RM200_PCI is not set
+# CONFIG_PCI is not set
+# CONFIG_ISA is not set
 
 #
 # CPU selection
@@ -73,6 +75,8 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 
 #
 # Additional Block Devices
@@ -81,8 +85,6 @@
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
 
 #
 # Networking options
@@ -134,15 +136,11 @@
 # CONFIG_NET_SCHED is not set
 
 #
-# Telephony Support
-#
-# CONFIG_PHONE is not set
-# CONFIG_PHONE_IXJ is not set
-
-#
 # ATA/IDE/MFM/RLL support
 #
 # CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
@@ -155,7 +153,6 @@
 CONFIG_BLK_DEV_SD=y
 CONFIG_SD_EXTRA_DEVS=40
 CONFIG_CHR_DEV_ST=y
-CONFIG_ST_EXTRA_DEVS=2
 CONFIG_BLK_DEV_SR=y
 # CONFIG_BLK_DEV_SR_VENDOR is not set
 CONFIG_SR_EXTRA_DEVS=2
@@ -207,26 +204,80 @@
 # CONFIG_SCSI_U14_34F is not set
 # CONFIG_SCSI_ULTRASTOR is not set
 # CONFIG_SCSI_DEBUG is not set
-# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 
 #
-# I2O device support
+# Network device support
 #
-# CONFIG_I2O is not set
-# CONFIG_I2O_PCI is not set
-# CONFIG_I2O_BLOCK is not set
-# CONFIG_I2O_LAN is not set
-# CONFIG_I2O_SCSI is not set
-# CONFIG_I2O_PROC is not set
+CONFIG_NETDEVICES=y
 
 #
-# Network device support
+# ARCnet devices
 #
-CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
-# CONFIG_SLIP is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+# CONFIG_NET_ETHERNET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
 # CONFIG_PPP is not set
-CONFIG_SGISEEQ=y
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# AX.25 network device drivers
+#
+# CONFIG_MKISS is not set
+# CONFIG_6PACK is not set
+# CONFIG_BPQETHER is not set
+# CONFIG_DMASCC is not set
+# CONFIG_SCC is not set
+# CONFIG_BAYCOM_SER_FDX is not set
+# CONFIG_BAYCOM_SER_HDX is not set
+# CONFIG_BAYCOM_PAR is not set
+# CONFIG_BAYCOM_EPP is not set
+# CONFIG_SOUNDMODEM is not set
+# CONFIG_YAM is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
 
 #
 # Character devices
@@ -336,8 +387,15 @@
 #
 # Partition Types
 #
-# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
 CONFIG_SGI_PARTITION=y
+# CONFIG_SUN_PARTITION is not set
 # CONFIG_NLS is not set
 
 #
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/mips/kernel/irixelf.c linux/arch/mips/kernel/irixelf.c
--- v2.3.99-pre2/linux/arch/mips/kernel/irixelf.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/mips/kernel/irixelf.c	Wed Mar 22 22:15:56 2000
@@ -230,15 +230,13 @@
  * an ELF header.
  */
 static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex,
-				     struct dentry * interpreter_dentry,
+				     struct file * interpreter,
 				     unsigned int *interp_load_addr)
 {
-	struct file * file;
 	struct elf_phdr *elf_phdata  =  NULL;
 	struct elf_phdr *eppnt;
 	unsigned int len;
 	unsigned int load_addr;
-	int elf_exec_fileno;
 	int elf_bss;
 	int retval;
 	unsigned int last_bss;
@@ -258,8 +256,7 @@
 	if((interp_elf_ex->e_type != ET_EXEC &&
 	    interp_elf_ex->e_type != ET_DYN) ||
 	    !elf_check_arch(interp_elf_ex->e_machine) ||
-	   (!interpreter_dentry->d_inode->i_fop ||
-	    !interpreter_dentry->d_inode->i_fop->mmap)) {
+	    !interpreter->f_op->mmap) {
 		printk("IRIX interp has bad e_type %d\n", interp_elf_ex->e_type);
 		return 0xffffffff;
 	}
@@ -290,23 +287,14 @@
 		return 0xffffffff;
 	}
 
-	retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff,
+	retval = kernel_read(interpreter, interp_elf_ex->e_phoff,
 			   (char *) elf_phdata,
-			   sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1);
+			   sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
 
 #ifdef DEBUG_ELF
 	dump_phdrs(elf_phdata, interp_elf_ex->e_phnum);
 #endif
 
-	elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY);
-	if (elf_exec_fileno < 0) {
-		printk("Could not open IRIX interp inode.\n");
-		kfree(elf_phdata);
-		return 0xffffffff;
-	}
-
-	file = fget(elf_exec_fileno);
-
 	eppnt = elf_phdata;
 	for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
 	  if(eppnt->p_type == PT_LOAD) {
@@ -321,12 +309,12 @@
 
 #ifdef DEBUG_ELF
 	    printk("INTERP do_mmap(%p, %08lx, %08lx, %08lx, %08lx, %08lx) ",
-		   file, vaddr,
+		   interpreter, vaddr,
 		   (unsigned long) (eppnt->p_filesz + (eppnt->p_vaddr & 0xfff)),
 		   (unsigned long) elf_prot, (unsigned long) elf_type,
 		   (unsigned long) (eppnt->p_offset & 0xfffff000));
 #endif
-	    error = do_mmap(file, vaddr,
+	    error = do_mmap(interpreter, vaddr,
 			    eppnt->p_filesz + (eppnt->p_vaddr & 0xfff),
 			    elf_prot, elf_type,
 			    eppnt->p_offset & 0xfffff000);
@@ -363,8 +351,6 @@
 	}
 
 	/* Now use mmap to map the library into memory. */
-	fput(file);
-	sys_close(elf_exec_fileno);
 	if(error < 0 && error > -1024) {
 #ifdef DEBUG_ELF
 		printk("got error %d\n", error);
@@ -407,9 +393,7 @@
 
 	/* First of all, some simple consistency checks */
 	if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) || 
-	    !elf_check_arch(ehp->e_machine) ||
-	   (!bprm->dentry->d_inode->i_fop ||
-	    !bprm->dentry->d_inode->i_fop->mmap)) {
+	    !elf_check_arch(ehp->e_machine) || !bprm->file->f_op->mmap) {
 		return -ENOEXEC;
 	}
 
@@ -435,7 +419,7 @@
 
 /* Look for an IRIX ELF interpreter. */
 static inline int look_for_irix_interpreter(char **name,
-					    struct dentry **interpreter_dentry,
+					    struct file **interpreter,
 					    struct elfhdr *interp_elf_ex,
 					    struct elf_phdr *epp,
 					    struct linux_binprm *bprm, int pnum)
@@ -443,7 +427,7 @@
 	mm_segment_t old_fs;
 	int i;
 	int retval = -EINVAL;
-	struct dentry *dentry = NULL;
+	struct file *file = NULL;
 
 	*name = NULL;
 	for(i = 0; i < pnum; i++, epp++) {
@@ -461,29 +445,27 @@
 			return -ENOMEM;
 
 		strcpy(*name, IRIX_INTERP_PREFIX);
-		retval = read_exec(bprm->dentry, epp->p_offset, (*name + 16),
-				   epp->p_filesz, 1);
+		retval = kernel_read(bprm->file, epp->p_offset, (*name + 16),
+				   epp->p_filesz);
 		if(retval < 0)
 			goto out;
 
-		old_fs = get_fs(); set_fs(get_ds());
-		dentry = namei(*name);
-		set_fs(old_fs);
-		if(IS_ERR(dentry)) {
-			retval = PTR_ERR(dentry);
+		file = open_exec(*name);
+		if(IS_ERR(file)) {
+			retval = PTR_ERR(file);
 			goto out;
 		}
-		retval = read_exec(dentry, 0, bprm->buf, 128, 1);
+		retval = kernel_read(file, 0, bprm->buf, 128);
 		if(retval < 0)
 			goto dput_and_out;
 
 		*interp_elf_ex = *((struct elfhdr *) bprm->buf);
 	}
-	*interpreter_dentry = dentry;
+	*interpreter = file;
 	return 0;
 
 dput_and_out:
-	dput(dentry);
+	fput(file);
 out:
 	kfree(*name);
 	return retval;
@@ -544,7 +526,7 @@
 }
 
 static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp,
-				  struct dentry *identry, unsigned int *iladdr,
+				  struct file *interp, unsigned int *iladdr,
 				  int pnum, mm_segment_t old_fs,
 				  unsigned int *eentry)
 {
@@ -560,11 +542,11 @@
 			return -1;
 
 		set_fs(old_fs);
-		*eentry = load_irix_interp(ihp, identry, iladdr);
+		*eentry = load_irix_interp(ihp, interp, iladdr);
 		old_fs = get_fs();
 		set_fs(get_ds());
 
-		dput(identry);
+		fput(interp);
 
 		if(*eentry == 0xffffffff)
 			return -1;
@@ -603,14 +585,13 @@
 static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct elfhdr elf_ex, interp_elf_ex;
-	struct dentry *interpreter_dentry;
+	struct file *interpreter;
 	struct elf_phdr *elf_phdata, *elf_ihdr, *elf_ephdr;
 	unsigned int load_addr, elf_bss, elf_brk;
 	unsigned int elf_entry, interp_load_addr = 0;
 	unsigned int start_code, end_code, end_data, elf_stack;
-	int elf_exec_fileno, retval, has_interp, has_ephdr, size, i;
+	int retval, has_interp, has_ephdr, size, i;
 	char *elf_interpreter;
-	struct file *file;
 	mm_segment_t old_fs;
 	
 	load_addr = 0;
@@ -636,8 +617,7 @@
 		goto out;
 	}
 
-	retval = read_exec(bprm->dentry, elf_ex.e_phoff,
-	                   (char *) elf_phdata, size, 1);
+	retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *)elf_phdata, size);
 	if (retval < 0)
 		goto out_free_ph;
 
@@ -664,10 +644,6 @@
 
 	elf_bss = 0;
 	elf_brk = 0;
-	retval = open_dentry(bprm->dentry, O_RDONLY);
-	if (retval < 0)
-		goto out_free_ph;
-	file = fget(elf_exec_fileno = retval);
 
 	elf_stack = 0xffffffff;
 	elf_interpreter = NULL;
@@ -676,7 +652,7 @@
 	end_data = 0;
 
 	retval = look_for_irix_interpreter(&elf_interpreter,
-	                                   &interpreter_dentry,
+	                                   &interpreter,
 					   &interp_elf_ex, elf_phdata, bprm,
 					   elf_ex.e_phnum);
 	if(retval)
@@ -720,13 +696,13 @@
 	old_fs = get_fs();
 	set_fs(get_ds());
 
-	map_executable(file, elf_phdata, elf_ex.e_phnum, &elf_stack,
+	map_executable(bprm->file, elf_phdata, elf_ex.e_phnum, &elf_stack,
 	               &load_addr, &start_code, &elf_bss, &end_code,
 	               &end_data, &elf_brk);
 
 	if(elf_interpreter) {
 		retval = map_interpreter(elf_phdata, &interp_elf_ex,
-					 interpreter_dentry, &interp_load_addr,
+					 interpreter, &interp_load_addr,
 					 elf_ex.e_phnum, old_fs, &elf_entry);
 		kfree(elf_interpreter);
 		if(retval) {
@@ -741,8 +717,6 @@
 	set_fs(old_fs);
 
 	kfree(elf_phdata);
-	fput(file);
-	sys_close(elf_exec_fileno);
 	current->personality = PER_IRIX32;
 
 	put_exec_domain(current->exec_domain);
@@ -805,13 +779,11 @@
 	return retval;
 
 out_free_dentry:
-	dput(interpreter_dentry);
+	fput(interpreter);
 out_free_interp:
 	if (elf_interpreter)
 		kfree(elf_interpreter);
 out_free_file:
-	fput(file);
-	sys_close(elf_exec_fileno);
 out_free_ph:
 	kfree (elf_phdata);
 	goto out;
@@ -824,31 +796,14 @@
 {
 	struct elfhdr elf_ex;
 	struct elf_phdr *elf_phdata  =  NULL;
-	struct dentry *dentry;
-	struct inode *inode;
-	unsigned int len;
-	int elf_bss;
+	unsigned int len = 0;
+	int elf_bss = 0;
 	int retval;
 	unsigned int bss;
 	int error;
 	int i,j, k;
 
-	len = 0;
-	dentry = file->f_dentry;
-	inode = dentry->d_inode;
-	elf_bss = 0;
-	
-	/* Seek to the beginning of the file. */
-	if (file->f_op->llseek) {
-		if ((error = file->f_op->llseek(file, 0, 0)) != 0)
-			return -ENOEXEC;
-	} else
-		file->f_pos = 0;
-
-	set_fs(KERNEL_DS);
-	error = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex),
-	                         &file->f_pos);
-	set_fs(USER_DS);
+	error = kernel_read(file, 0, (char *) &elf_ex, sizeof(elf_ex));
 	if (error != sizeof(elf_ex))
 		return -ENOEXEC;
 
@@ -857,9 +812,7 @@
 
 	/* First of all, some simple consistency checks. */
 	if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
-	   !elf_check_arch(elf_ex.e_machine) ||
-	   (!dentry->d_inode->i_fop ||
-	    !dentry->d_inode->i_fop->mmap))
+	   !elf_check_arch(elf_ex.e_machine) || !file->f_op->mmap)
 		return -ENOEXEC;
 	
 	/* Now read in all of the header information. */
@@ -871,8 +824,8 @@
 	if (elf_phdata == NULL)
 		return -ENOMEM;
 	
-	retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
-			   sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
+	retval = kernel_read(file, elf_ex.e_phoff, (char *) elf_phdata,
+			   sizeof(struct elf_phdr) * elf_ex.e_phnum);
 	
 	j = 0;
 	for(i=0; i<elf_ex.e_phnum; i++)
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/mips64/config.in linux/arch/mips64/config.in
--- v2.3.99-pre2/linux/arch/mips64/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/mips64/config.in	Thu Mar 23 14:45:05 2000
@@ -35,7 +35,8 @@
 unset CONFIG_BOARD_SCACHE
 unset CONFIG_COHERENT_IO
 unset CONFIG_BINFMT_ELF32
-unset CONFIG_PCI
+
+define_bool CONFIG_ISA n
 
 if [ "$CONFIG_SGI_IP22" = "y" ]; then
    define_bool CONFIG_BOOT_ELF32 y
@@ -50,6 +51,8 @@
    define_bool CONFIG_COHERENT_IO y
    define_bool CONFIG_PCI y
    define_bool CONFIG_QL_ISP_A64 y
+else
+   define_bool CONFIG_PCI n
 fi
 
 mainmenu_option next_comment
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/mips64/defconfig linux/arch/mips64/defconfig
--- v2.3.99-pre2/linux/arch/mips64/defconfig	Tue Mar 14 19:10:39 2000
+++ linux/arch/mips64/defconfig	Thu Mar 23 14:45:05 2000
@@ -14,6 +14,7 @@
 CONFIG_SGI_IP27=y
 # CONFIG_SGI_SN0_N_MODE is not set
 # CONFIG_DISCONTIGMEM is not set
+# CONFIG_ISA is not set
 CONFIG_BOOT_ELF64=y
 CONFIG_ARC64=y
 CONFIG_COHERENT_IO=y
@@ -60,7 +61,10 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
 
 #
 # Additional Block Devices
@@ -69,9 +73,6 @@
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
 
 #
 # Networking options
@@ -103,6 +104,7 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
 
 #
 # Telephony Support
@@ -114,6 +116,8 @@
 # ATA/IDE/MFM/RLL support
 #
 # CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
@@ -126,7 +130,6 @@
 CONFIG_BLK_DEV_SD=y
 CONFIG_SD_EXTRA_DEVS=40
 # CONFIG_CHR_DEV_ST is not set
-CONFIG_ST_EXTRA_DEVS=2
 # CONFIG_BLK_DEV_SR is not set
 # CONFIG_CHR_DEV_SG is not set
 
@@ -141,6 +144,7 @@
 #
 # SCSI low-level drivers
 #
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
@@ -204,6 +208,7 @@
 #
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_NET_SB1000 is not set
 
@@ -218,7 +223,7 @@
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_DEPCA is not set
 # CONFIG_NET_ISA is not set
-# CONFIG_NET_EISA is not set
+# CONFIG_NET_PCI is not set
 # CONFIG_NET_POCKET is not set
 
 #
@@ -237,7 +242,7 @@
 # CONFIG_NET_RADIO is not set
 
 #
-# Token Ring driver support
+# Token Ring devices
 #
 # CONFIG_TR is not set
 # CONFIG_NET_FC is not set
@@ -324,7 +329,7 @@
 # CONFIG_USB is not set
 
 #
-# Filesystems
+# File systems
 #
 # CONFIG_QUOTA is not set
 # CONFIG_AUTOFS_FS is not set
@@ -343,20 +348,17 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
 # CONFIG_DEVPTS_FS is not set
 # CONFIG_QNX4FS_FS is not set
-# CONFIG_QNX4FS_RW is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_EXT2_FS=y
 # CONFIG_SYSV_FS is not set
-# CONFIG_SYSV_FS_WRITE is not set
 # CONFIG_UDF_FS is not set
-# CONFIG_UDF_RW is not set
 # CONFIG_UFS_FS is not set
-# CONFIG_UFS_FS_WRITE is not set
 
 #
 # Network File Systems
@@ -365,7 +367,6 @@
 CONFIG_NFS_FS=y
 CONFIG_ROOT_NFS=y
 # CONFIG_NFSD is not set
-# CONFIG_NFSD_V3 is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
@@ -374,8 +375,15 @@
 #
 # Partition Types
 #
-# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
 CONFIG_SGI_PARTITION=y
+# CONFIG_SUN_PARTITION is not set
 # CONFIG_NLS is not set
 CONFIG_KCORE_ELF=y
 
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/mips64/sgi-ip22/ip22-sc.c linux/arch/mips64/sgi-ip22/ip22-sc.c
--- v2.3.99-pre2/linux/arch/mips64/sgi-ip22/ip22-sc.c	Sat Feb 26 22:31:41 2000
+++ linux/arch/mips64/sgi-ip22/ip22-sc.c	Tue Mar 21 13:06:20 2000
@@ -9,7 +9,6 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
-#include <linux/autoconf.h>
 
 #include <asm/bcache.h>
 #include <asm/sgi/sgimc.h>
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.3.99-pre2/linux/arch/ppc/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/ppc/config.in	Thu Mar 23 14:45:05 2000
@@ -19,7 +19,7 @@
 	 4xx			CONFIG_4xx	\
 	 630/Power3(64-Bit)	CONFIG_PPC64	\
 	 82xx			CONFIG_82xx	\
-	 8xx			CONFIG_8xx"	6xx/7xx
+	 8xx			CONFIG_8xx"	6xx
 
 if [ "$CONFIG_4xx" = "y" ]; then
     choice 'Machine Type'			\
@@ -82,20 +82,18 @@
 mainmenu_option next_comment
 comment 'General setup'
 
-if [ "$CONFIG_APUS" = "y" ]; then
-  define_bool CONFIG_PCI n
-fi
-if [ "$CONFIG_OAK" = "y" ]; then
-  define_bool CONFIG_PCI n
-fi
-if [ "$CONFIG_8xx" = "y" ]; then
-    bool 'QSpan PCI' CONFIG_PCI
-fi
-if [ "$CONFIG_6xx" = "y" -a "$CONFIG_APUS" != "y" ]; then
-    define_bool CONFIG_PCI y
-fi
-if [ "$CONFIG_PREP" = "y" -o "$CONFIG_PMAC" = "y" -o "$CONFIG_CHRP" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
-    define_bool CONFIG_PCI y
+define_bool CONFIG_ISA n
+
+if [ "$CONFIG_APUS" = "y" -o "$CONFIG_4xx" = "y" -o \
+     "$CONFIG_82xx" = "y" ]; then
+   define_bool CONFIG_PCI n
+else
+   if [ "$CONFIG_6xx" = "y" -o CONFIG_PPC64 ]; then
+      define_bool CONFIG_PCI y
+   else
+   # CONFIG_8xx
+      bool 'QSpan PCI' CONFIG_PCI
+   fi
 fi
 
 bool 'Networking support' CONFIG_NET
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/ppc/configs/common_defconfig linux/arch/ppc/configs/common_defconfig
--- v2.3.99-pre2/linux/arch/ppc/configs/common_defconfig	Fri Mar 10 16:40:40 2000
+++ linux/arch/ppc/configs/common_defconfig	Tue Mar 21 10:49:32 2000
@@ -1,5 +1,5 @@
 #
-# Automatically generated by make menuconfig: don't edit
+# Automatically generated make config: don't edit
 #
 # CONFIG_UID16 is not set
 
@@ -33,6 +33,7 @@
 #
 # General setup
 #
+# CONFIG_PCI is not set
 CONFIG_PCI=y
 CONFIG_PCI=y
 CONFIG_NET=y
@@ -56,7 +57,6 @@
 CONFIG_PMAC_PBOOK=y
 CONFIG_MAC_FLOPPY=y
 CONFIG_MAC_SERIAL=y
-# CONFIG_SERIAL_CONSOLE is not set
 CONFIG_ADB=y
 CONFIG_ADB_CUDA=y
 CONFIG_ADB_MACIO=y
@@ -73,49 +73,23 @@
 # Plug and Play configuration
 #
 # CONFIG_PNP is not set
-# CONFIG_ISAPNP is not set
 
 #
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_BLK_DEV_IDECS is not set
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-CONFIG_BLK_DEV_IDEFLOPPY=y
-CONFIG_BLK_DEV_IDESCSI=y
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-# CONFIG_IDEPCI_SHARE_IRQ is not set
-# CONFIG_BLK_DEV_IDEDMA_PCI is not set
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDE_PMAC=y
-CONFIG_BLK_DEV_IDEDMA_PMAC=y
-CONFIG_IDEDMA_PMAC_AUTO=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_BLK_DEV_XD is not set
 # CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+
+#
+# Additional Block Devices
+#
 CONFIG_BLK_DEV_LOOP=y
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Networking options
@@ -138,10 +112,18 @@
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=y
 CONFIG_SYN_COOKIES=y
+
+#
+# (it is safe to leave these untouched)
+#
 CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
+
+#
+#  
+#
 # CONFIG_IPX is not set
 CONFIG_ATALK=m
 # CONFIG_DECNET is not set
@@ -160,17 +142,71 @@
 # CONFIG_NET_SCHED is not set
 
 #
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+CONFIG_BLK_DEV_IDESCSI=y
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_IDEDMA_PMAC_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+CONFIG_BLK_DEV_IDE_MODES=y
+
+#
 # SCSI support
 #
 CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
 CONFIG_BLK_DEV_SD=y
 CONFIG_SD_EXTRA_DEVS=40
 CONFIG_CHR_DEV_ST=y
-CONFIG_ST_EXTRA_DEVS=2
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
 CONFIG_SR_EXTRA_DEVS=2
 CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
 # CONFIG_SCSI_DEBUG_QUEUES is not set
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
@@ -254,9 +290,7 @@
 #
 # Appletalk devices
 #
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-# CONFIG_IPDDP is not set
+# CONFIG_APPLETALK is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
@@ -449,17 +483,28 @@
 #
 # CONFIG_FTAPE is not set
 # CONFIG_DRM is not set
-# CONFIG_DRM_TDFX is not set
 # CONFIG_AGP is not set
 
 #
 # USB support
 #
 CONFIG_USB=y
+
+#
+# USB Controllers
+#
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
+
+#
+# Miscellaneous USB options
+#
 # CONFIG_USB_DEVICEFS is not set
+
+#
+# USB Devices
+#
 # CONFIG_USB_PRINTER is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_AUDIO is not set
@@ -470,11 +515,15 @@
 # CONFIG_USB_OV511 is not set
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_STORAGE is not set
-# CONFIG_USB_USS720 is not set
 # CONFIG_USB_DABUSB is not set
 # CONFIG_USB_PLUSB is not set
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RIO500 is not set
+# CONFIG_USB_DSBR is not set
+
+#
+# USB HID
+#
 # CONFIG_USB_HID is not set
 CONFIG_USB_KBD=y
 CONFIG_USB_MOUSE=y
@@ -510,7 +559,6 @@
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
-# CONFIG_DEVFS_DEBUG is not set
 CONFIG_DEVPTS_FS=y
 # CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
@@ -524,7 +572,6 @@
 #
 # CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
-# CONFIG_ROOT_NFS is not set
 CONFIG_NFSD=y
 # CONFIG_NFSD_V3 is not set
 CONFIG_SUNRPC=y
@@ -596,8 +643,12 @@
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
 CONFIG_SOUND_OSS=y
+# CONFIG_SOUND_TRACEINIT is not set
+# CONFIG_SOUND_DMAP is not set
 # CONFIG_SOUND_AD1816 is not set
 # CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_ACI_MIXER is not set
 CONFIG_SOUND_CS4232=m
 # CONFIG_SOUND_SSCAPE is not set
 # CONFIG_SOUND_GUS is not set
@@ -608,11 +659,10 @@
 # CONFIG_SOUND_NM256 is not set
 # CONFIG_SOUND_MAD16 is not set
 # CONFIG_SOUND_PAS is not set
-# CONFIG_PAS_JOYSTICK is not set
 # CONFIG_SOUND_PSS is not set
-# CONFIG_PSS_HAVE_BOOT is not set
 # CONFIG_SOUND_SOFTOSS is not set
 # CONFIG_SOUND_SB is not set
+# CONFIG_SOUND_AWE32_SYNTH is not set
 # CONFIG_SOUND_WAVEFRONT is not set
 # CONFIG_SOUND_MAUI is not set
 # CONFIG_SOUND_VIA82CXXX is not set
@@ -620,11 +670,7 @@
 # CONFIG_SOUND_OPL3SA1 is not set
 # CONFIG_SOUND_OPL3SA2 is not set
 # CONFIG_SOUND_UART6850 is not set
-
-#
-# Additional low level sound drivers
-#
-# CONFIG_LOWLEVEL_SOUND is not set
+# CONFIG_SOUND_AEDSP16 is not set
 
 #
 # Kernel hacking
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.3.99-pre2/linux/arch/ppc/defconfig	Tue Mar 14 19:10:39 2000
+++ linux/arch/ppc/defconfig	Thu Mar 23 14:45:05 2000
@@ -1,5 +1,5 @@
 #
-# Automatically generated by make menuconfig: don't edit
+# Automatically generated make config: don't edit
 #
 # CONFIG_UID16 is not set
 
@@ -33,7 +33,7 @@
 #
 # General setup
 #
-CONFIG_PCI=y
+# CONFIG_ISA is not set
 CONFIG_PCI=y
 CONFIG_NET=y
 CONFIG_SYSCTL=y
@@ -79,15 +79,19 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+
+#
+# Additional Block Devices
+#
 CONFIG_BLK_DEV_LOOP=y
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
 
 #
 # Networking options
@@ -110,10 +114,18 @@
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=y
 CONFIG_SYN_COOKIES=y
+
+#
+# (it is safe to leave these untouched)
+#
 CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
+
+#
+#  
+#
 # CONFIG_IPX is not set
 CONFIG_ATALK=m
 # CONFIG_DECNET is not set
@@ -135,48 +147,91 @@
 # ATA/IDE/MFM/RLL support
 #
 CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
 CONFIG_BLK_DEV_IDE=y
 
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
 # CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_BLK_DEV_IDECS is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
-CONFIG_BLK_DEV_IDEFLOPPY=y
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
 CONFIG_BLK_DEV_IDESCSI=y
+
+#
+# IDE chipset support/bugfixes
+#
 # CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
 # CONFIG_IDEPCI_SHARE_IRQ is not set
 # CONFIG_BLK_DEV_IDEDMA_PCI is not set
 # CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
 # CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
 # CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_CMD64X_RAID is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
 # CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
 CONFIG_BLK_DEV_IDE_PMAC=y
 CONFIG_BLK_DEV_IDEDMA_PMAC=y
 CONFIG_IDEDMA_PMAC_AUTO=y
 CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
 # CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
 CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
 #
 CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
 CONFIG_BLK_DEV_SD=y
 CONFIG_SD_EXTRA_DEVS=40
 CONFIG_CHR_DEV_ST=y
-CONFIG_ST_EXTRA_DEVS=2
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
 CONFIG_SR_EXTRA_DEVS=2
 CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
 # CONFIG_SCSI_DEBUG_QUEUES is not set
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
@@ -260,9 +315,7 @@
 #
 # Appletalk devices
 #
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-# CONFIG_IPDDP is not set
+# CONFIG_APPLETALK is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
@@ -462,10 +515,22 @@
 # USB support
 #
 CONFIG_USB=y
+
+#
+# USB Controllers
+#
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
+
+#
+# Miscellaneous USB options
+#
 # CONFIG_USB_DEVICEFS is not set
+
+#
+# USB Devices
+#
 # CONFIG_USB_PRINTER is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_AUDIO is not set
@@ -475,12 +540,18 @@
 # CONFIG_USB_IBMCAM is not set
 # CONFIG_USB_OV511 is not set
 # CONFIG_USB_DC2XX is not set
+# CONFIG_USB_MDC800 is not set
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_USS720 is not set
 # CONFIG_USB_DABUSB is not set
 # CONFIG_USB_PLUSB is not set
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RIO500 is not set
+# CONFIG_USB_DSBR is not set
+
+#
+# USB HID
+#
 # CONFIG_USB_HID is not set
 CONFIG_USB_KBD=y
 CONFIG_USB_MOUSE=y
@@ -602,8 +673,12 @@
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
 CONFIG_SOUND_OSS=y
+# CONFIG_SOUND_TRACEINIT is not set
+# CONFIG_SOUND_DMAP is not set
 # CONFIG_SOUND_AD1816 is not set
 # CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_ACI_MIXER is not set
 CONFIG_SOUND_CS4232=m
 # CONFIG_SOUND_SSCAPE is not set
 # CONFIG_SOUND_GUS is not set
@@ -616,9 +691,9 @@
 # CONFIG_SOUND_PAS is not set
 # CONFIG_PAS_JOYSTICK is not set
 # CONFIG_SOUND_PSS is not set
-# CONFIG_PSS_HAVE_BOOT is not set
 # CONFIG_SOUND_SOFTOSS is not set
 # CONFIG_SOUND_SB is not set
+# CONFIG_SOUND_AWE32_SYNTH is not set
 # CONFIG_SOUND_WAVEFRONT is not set
 # CONFIG_SOUND_MAUI is not set
 # CONFIG_SOUND_VIA82CXXX is not set
@@ -626,11 +701,7 @@
 # CONFIG_SOUND_OPL3SA1 is not set
 # CONFIG_SOUND_OPL3SA2 is not set
 # CONFIG_SOUND_UART6850 is not set
-
-#
-# Additional low level sound drivers
-#
-# CONFIG_LOWLEVEL_SOUND is not set
+# CONFIG_SOUND_AEDSP16 is not set
 
 #
 # Kernel hacking
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sh/config.in linux/arch/sh/config.in
--- v2.3.99-pre2/linux/arch/sh/config.in	Tue Mar 14 19:10:39 2000
+++ linux/arch/sh/config.in	Thu Mar 23 14:45:05 2000
@@ -47,6 +47,8 @@
 mainmenu_option next_comment
 comment 'General setup'
 
+define_bool CONFIG_ISA n
+
 bool 'Networking support' CONFIG_NET
 
 bool 'Directy Connected Compact Flash support' CONFIG_CF_ENABLER
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sh/defconfig linux/arch/sh/defconfig
--- v2.3.99-pre2/linux/arch/sh/defconfig	Tue Mar 14 19:10:39 2000
+++ linux/arch/sh/defconfig	Thu Mar 23 14:45:05 2000
@@ -28,6 +28,7 @@
 #
 # General setup
 #
+# CONFIG_ISA is not set
 # CONFIG_NET is not set
 CONFIG_CF_ENABLER=y
 # CONFIG_PCI is not set
@@ -39,12 +40,18 @@
 # CONFIG_KCORE_AOUT is not set
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
+
+#
+# Parallel port support
+#
 # CONFIG_PARPORT is not set
 
 #
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 
 #
 # Additional Block Devices
@@ -53,21 +60,25 @@
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
 
 #
 # ATA/IDE/MFM/RLL support
 #
 CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
 CONFIG_BLK_DEV_IDE=y
 
 #
 # Please see Documentation/ide.txt for help/info on IDE drives
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
 # CONFIG_BLK_DEV_IDECD is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
@@ -77,9 +88,11 @@
 # IDE chipset support/bugfixes
 #
 # CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
 # CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
@@ -101,13 +114,20 @@
 # CONFIG_UNIX98_PTYS is not set
 
 #
-# Filesystems
+# File systems
 #
 # CONFIG_QUOTA is not set
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
 # CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
 # CONFIG_CRAMFS is not set
 # CONFIG_ISO9660_FS is not set
 # CONFIG_JOLIET is not set
@@ -115,6 +135,10 @@
 # CONFIG_NTFS_FS is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS is not set
+# CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_EXT2_FS=y
 # CONFIG_SYSV_FS is not set
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc/Makefile linux/arch/sparc/Makefile
--- v2.3.99-pre2/linux/arch/sparc/Makefile	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc/Makefile	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.45 2000/01/29 01:08:48 anton Exp $
+# $Id: Makefile,v 1.46 2000/03/21 06:12:26 davem Exp $
 # sparc/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.3.99-pre2/linux/arch/sparc/config.in	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc/config.in	Thu Mar 23 14:45:05 2000
@@ -36,6 +36,8 @@
 if [ "$CONFIG_SUN4" != "y" ]; then
    bool 'Support for PCI and PS/2 keyboard/mouse' CONFIG_PCI
    source drivers/pci/Config.in
+else
+   define_bool CONFIG_PCI n
 fi
 
 tristate 'Openprom tree appears in /proc/openprom' CONFIG_SUN_OPENPROMFS
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.3.99-pre2/linux/arch/sparc64/Makefile	Fri Mar 10 16:40:41 2000
+++ linux/arch/sparc64/Makefile	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.42 2000/03/09 05:56:43 jj Exp $
+# $Id: Makefile,v 1.43 2000/03/21 06:12:28 davem Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -17,6 +17,8 @@
 NEW_GCC := $(shell if $(CC) -m64 -mcmodel=medlow -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo y; else echo n; fi; )
 NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
 UNDECLARED_REGS := $(shell if $(CC) -c -x assembler /dev/null -Wa,--help | grep undeclared-regs > /dev/null; then echo y; else echo n; fi; )
+
+export NEW_GCC
 
 ifneq ($(NEW_GAS),y)
 AS		= sparc64-linux-as
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.3.99-pre2/linux/arch/sparc64/config.in	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc64/config.in	Thu Mar 23 23:36:10 2000
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.104 2000/03/15 15:02:28 jj Exp $
+# $Id: config.in,v 1.105 2000/03/24 00:34:11 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -29,6 +29,7 @@
 define_bool CONFIG_SUN_CONSOLE y
 define_bool CONFIG_SUN_AUXIO y
 define_bool CONFIG_SUN_IO y
+define_bool CONFIG_ISA n
 bool 'PCI support' CONFIG_PCI
 source drivers/pci/Config.in
 
@@ -230,6 +231,7 @@
 	 tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
 	 tristate 'RealTek RTL-8139 support' CONFIG_8139TOO
 	 tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
+	 tristate 'VIA Rhine support' CONFIG_VIA_RHINE
 	 tristate 'EtherExpressPro/100 support' CONFIG_EEPRO100
 	 tristate 'Adaptec Starfire support' CONFIG_ADAPTEC_STARFIRE
       fi
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.3.99-pre2/linux/arch/sparc64/defconfig	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc64/defconfig	Thu Mar 23 23:36:10 2000
@@ -24,6 +24,7 @@
 CONFIG_SUN_CONSOLE=y
 CONFIG_SUN_AUXIO=y
 CONFIG_SUN_IO=y
+# CONFIG_ISA is not set
 CONFIG_PCI=y
 CONFIG_PCI_NAMES=y
 CONFIG_SUN_OPENPROMFS=m
@@ -218,7 +219,6 @@
 # CONFIG_BLK_DEV_OFFBOARD is not set
 CONFIG_IDEDMA_PCI_AUTO=y
 CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
 CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
 # CONFIG_IDEDMA_PCI_WIP is not set
 # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
@@ -245,6 +245,7 @@
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
 # CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
 CONFIG_BLK_DEV_IDE_MODES=y
 
 #
@@ -336,6 +337,7 @@
 CONFIG_VORTEX=m
 CONFIG_8139TOO=m
 CONFIG_NE2K_PCI=m
+CONFIG_VIA_RHINE=m
 CONFIG_EEPRO100=m
 CONFIG_ADAPTEC_STARFIRE=m
 
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- v2.3.99-pre2/linux/arch/sparc64/kernel/Makefile	Thu Feb 10 17:11:06 2000
+++ linux/arch/sparc64/kernel/Makefile	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.51 2000/02/08 05:11:31 jj Exp $
+# $Id: Makefile,v 1.52 2000/03/19 07:00:29 ecd Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -61,7 +61,7 @@
 #
 binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c
 
-ifneq ($(IS_EGCS),y)
+ifneq ($(NEW_GCC),y)
   CMODEL_CFLAG := -mmedlow
 else
   CMODEL_CFLAG := -mcmodel=medlow
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c
--- v2.3.99-pre2/linux/arch/sparc64/kernel/binfmt_aout32.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc64/kernel/binfmt_aout32.c	Wed Mar 22 22:15:56 2000
@@ -198,7 +198,6 @@
 static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct exec ex;
-	struct file * file;
 	int fd;
 	unsigned long error;
 	unsigned long fd_offset;
@@ -209,7 +208,7 @@
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
 	     N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
 	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
-	    bprm->dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+	    bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
 		return -ENOEXEC;
 	}
 
@@ -245,21 +244,23 @@
 	compute_creds(bprm);
  	current->flags &= ~PF_FORKNOEXEC;
 	if (N_MAGIC(ex) == NMAGIC) {
+		loff_t pos = fd_offset;
 		/* Fuck me plenty... */
 		error = do_brk(N_TXTADDR(ex), ex.a_text);
-		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
-			  ex.a_text, 0);
+		bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
+			  ex.a_text, &pos);
 		error = do_brk(N_DATADDR(ex), ex.a_data);
-		read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
-			  ex.a_data, 0);
+		bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
+			  ex.a_data, &pos);
 		goto beyond_if;
 	}
 
 	if (N_MAGIC(ex) == OMAGIC) {
+		loff_t pos = fd_offset;
 		do_brk(N_TXTADDR(ex) & PAGE_MASK,
 			ex.a_text+ex.a_data + PAGE_SIZE - 1);
-		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
-			  ex.a_text+ex.a_data, 0);
+		bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
+			  ex.a_text+ex.a_data, &pos);
 	} else {
 		static unsigned long error_time;
 		if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
@@ -269,37 +270,36 @@
 			error_time = jiffies;
 		}
 
-		fd = open_dentry(bprm->dentry, O_RDONLY);
+		fd = get_unused_fd();
 		if (fd < 0)
 			return fd;
-		file = fget(fd);
+		get_file(bprm->file);
+		fd_install(fd, bprm->file);
 
-		if (!file->f_op || !file->f_op->mmap) {
-			fput(file);
+		if (!bprm->file->f_op->mmap) {
+			loff_t pos = fd_offset;
 			sys_close(fd);
 			do_brk(0, ex.a_text+ex.a_data);
-			read_exec(bprm->dentry, fd_offset,
-				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
+			bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
+				  ex.a_text+ex.a_data, &pos);
 			goto beyond_if;
 		}
 
-		error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
+		error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
 			PROT_READ | PROT_EXEC,
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 			fd_offset);
 
 		if (error != N_TXTADDR(ex)) {
-			fput(file);
 			sys_close(fd);
 			send_sig(SIGKILL, current, 0);
 			return error;
 		}
 
- 		error = do_mmap(file, N_DATADDR(ex), ex.a_data,
+ 		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
 				PROT_READ | PROT_WRITE | PROT_EXEC,
 				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 				fd_offset + ex.a_text);
-		fput(file);
 		sys_close(fd);
 		if (error != N_DATADDR(ex)) {
 			send_sig(SIGKILL, current, 0);
@@ -349,16 +349,12 @@
 	unsigned long bss, start_addr, len;
 	unsigned long error;
 	int retval;
-	loff_t offset = 0;
 	struct exec ex;
 
 	inode = file->f_dentry->d_inode;
 
 	retval = -ENOEXEC;
-	/* N.B. Save current fs? */
-	set_fs(KERNEL_DS);
-	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset);
-	set_fs(USER_DS);
+	error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
 	if (error != sizeof(ex))
 		goto out;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.3.99-pre2/linux/arch/sparc64/kernel/ioctl32.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc64/kernel/ioctl32.c	Wed Mar 22 21:50:29 2000
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.83 2000/03/14 07:31:25 jj Exp $
+/* $Id: ioctl32.c,v 1.85 2000/03/23 05:25:41 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -1594,7 +1594,7 @@
 {
 	mm_segment_t old_fs = get_fs();
 	struct loop_info l;
-	int err = 0;
+	int err = -EINVAL;
 
 	switch(cmd) {
 	case LOOP_SET_STATUS:
@@ -1604,11 +1604,13 @@
 		err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice);
 		err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset,
 					   8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset);
-		if (err)
-			return -EFAULT;
-		set_fs (KERNEL_DS);
-		err = sys_ioctl (fd, cmd, (unsigned long)&l);
-		set_fs (old_fs);
+		if (err) {
+			err = -EFAULT;
+		} else {
+			set_fs (KERNEL_DS);
+			err = sys_ioctl (fd, cmd, (unsigned long)&l);
+			set_fs (old_fs);
+		}
 		break;
 	case LOOP_GET_STATUS:
 		set_fs (KERNEL_DS);
@@ -1621,10 +1623,19 @@
 			err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice);
 			err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset,
 					   (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset);
+			if (err)
+				err = -EFAULT;
 		}
 		break;
+	default: {
+		static int count = 0;
+		if (++count <= 20)
+			printk("%s: Unknown loop ioctl cmd, fd(%d) "
+			       "cmd(%08x) arg(%08lx)\n",
+			       __FUNCTION__, fd, cmd, arg);
 	}
-	return err ? -EFAULT : 0;
+	}
+	return err;
 }
 
 extern int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg);
@@ -1803,6 +1814,8 @@
 #define ATM_SETESIF32     _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32)
 #define ATM_GETSTAT32     _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32)
 #define ATM_GETSTATZ32    _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32)
+#define ATM_GETLOOP32	  _IOW('a', ATMIOC_SARCOM+2, struct atmif_sioc32)
+#define ATM_SETLOOP32	  _IOW('a', ATMIOC_SARCOM+3, struct atmif_sioc32)
 
 static struct {
         unsigned int cmd32;
@@ -1821,7 +1834,9 @@
 	{ ATM_SETESI32,      ATM_SETESI },
 	{ ATM_SETESIF32,     ATM_SETESIF },
 	{ ATM_GETSTAT32,     ATM_GETSTAT },
-	{ ATM_GETSTATZ32,    ATM_GETSTATZ }
+	{ ATM_GETSTATZ32,    ATM_GETSTATZ },
+	{ ATM_GETLOOP32,     ATM_GETLOOP },
+	{ ATM_SETLOOP32,     ATM_SETLOOP }
 };
 
 #define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0]))
@@ -1941,8 +1956,6 @@
         unsigned int cmd = 0;
         
 	switch (cmd32) {
-	case SUNI_GETLOOP:
-	case SUNI_SETLOOP:
 	case SONET_GETSTAT:
 	case SONET_GETSTATZ:
 	case SONET_GETDIAG:
@@ -1954,7 +1967,6 @@
 		return do_atmif_sioc(fd, cmd32, arg);
 	}
 
-	if (cmd == 0) {
 		for (i = 0; i < NR_ATM_IOCTL; i++) {
 			if (cmd32 == atm_ioctl_map[i].cmd32) {
 				cmd = atm_ioctl_map[i].cmd;
@@ -1964,7 +1976,6 @@
 	        if (i == NR_ATM_IOCTL) {
 	        return -EINVAL;
 	        }
-	}
         
         switch (cmd) {
 	case ATM_GETNAMES:
@@ -1983,6 +1994,8 @@
 	case ATM_SETESIF:
 	case ATM_GETSTAT:
 	case ATM_GETSTATZ:
+	case ATM_GETLOOP:
+	case ATM_SETLOOP:
                 return do_atmif_sioc(fd, cmd, arg);
         }
 
@@ -3076,8 +3089,8 @@
 HANDLE_IOCTL(ATM_SETESIF32, do_atm_ioctl)
 HANDLE_IOCTL(ATM_GETSTAT32, do_atm_ioctl)
 HANDLE_IOCTL(ATM_GETSTATZ32, do_atm_ioctl)
-HANDLE_IOCTL(SUNI_GETLOOP, do_atm_ioctl)
-HANDLE_IOCTL(SUNI_SETLOOP, do_atm_ioctl)
+HANDLE_IOCTL(ATM_GETLOOP32, do_atm_ioctl)
+HANDLE_IOCTL(ATM_SETLOOP32, do_atm_ioctl)
 HANDLE_IOCTL(SONET_GETSTAT, do_atm_ioctl)
 HANDLE_IOCTL(SONET_GETSTATZ, do_atm_ioctl)
 HANDLE_IOCTL(SONET_GETDIAG, do_atm_ioctl)
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.3.99-pre2/linux/arch/sparc64/kernel/sys_sparc32.c	Sun Mar 19 18:35:30 2000
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Thu Mar 23 23:36:10 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.139 2000/03/16 20:37:57 davem Exp $
+/* $Id: sys_sparc32.c,v 1.141 2000/03/24 01:31:30 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -3051,7 +3051,7 @@
 do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs)
 {
 	struct linux_binprm bprm;
-	struct dentry * dentry;
+	struct file * file;
 	int retval;
 	int i;
 
@@ -3059,28 +3059,24 @@
 	memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0]));
 
 	lock_kernel();
-	dentry = open_namei(filename);
+	file = open_exec(filename);
 	unlock_kernel();
 
-	retval = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
+	retval = PTR_ERR(file);
+	if (IS_ERR(file))
 		return retval;
 
-	bprm.dentry = dentry;
+	bprm.file = file;
 	bprm.filename = filename;
 	bprm.sh_bang = 0;
 	bprm.loader = 0;
 	bprm.exec = 0;
 	if ((bprm.argc = count32(argv)) < 0) {
-		lock_kernel();
-		dput(dentry);
-		unlock_kernel();
+		fput(file);
 		return bprm.argc;
 	}
 	if ((bprm.envc = count32(envp)) < 0) {
-		lock_kernel();
-		dput(dentry);
-		unlock_kernel();
+		fput(file);
 		return bprm.envc;
 	}
 
@@ -3108,11 +3104,8 @@
 
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
-	if (bprm.dentry) {
-		lock_kernel();
-		dput(bprm.dentry);
-		unlock_kernel();
-	}
+	if (bprm.file)
+		fput(bprm.file);
 
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
 		if (bprm.page[i])
@@ -3634,7 +3627,7 @@
 
 union nfsctl_res32 {
 	__u8			cr32_getfh[NFS_FHSIZE];
-	u32			cr32_debug;
+	struct knfsd_fh		cr32_getfs;
 };
 
 static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
@@ -3760,15 +3753,12 @@
 	return err;
 }
 
+/* This really doesn't need translations, we are only passing
+ * back a union which contains opaque nfs file handle data.
+ */
 static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32)
 {
-	int err;
-	
-	err = copy_to_user(&res32->cr32_getfh,
-			&kres->cr_getfh,
-			sizeof(res32->cr32_getfh));
-	err |= __put_user(kres->cr_debug, &res32->cr32_debug);
-	return err;
+	return copy_to_user(res32, kres, sizeof(*res32));
 }
 
 extern asmlinkage int sys_nfsservctl(int cmd, void *arg, void *resp);
diff -u --recursive --new-file v2.3.99-pre2/linux/arch/sparc64/lib/memcmp.S linux/arch/sparc64/lib/memcmp.S
--- v2.3.99-pre2/linux/arch/sparc64/lib/memcmp.S	Mon Apr 14 16:28:10 1997
+++ linux/arch/sparc64/lib/memcmp.S	Thu Mar 23 23:36:10 2000
@@ -1,29 +1,28 @@
-/* $Id: memcmp.S,v 1.2 1997/04/01 03:43:18 davem Exp $
+/* $Id: memcmp.S,v 1.3 2000/03/23 07:51:08 davem Exp $
  * Sparc64 optimized memcmp code.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
  */
 
 	.text
-	.align 4
-	.globl __memcmp, memcmp
+	.align	32
+	.globl	__memcmp, memcmp
 __memcmp:
 memcmp:
-	brlez,pn %o2, 2f
-	 sub	%g0, %o2, %o3
-	add	%o0, %o2, %o0
-	add	%o1, %o2, %o1
-	ldub	[%o0 + %o3], %o4
-1:
-	ldub	[%o1 + %o3], %o5
-	sub	%o4, %o5, %o4
-	brnz,pn	%o4, 3f
-	 addcc	%o3, 1, %o3
-	bne,a,pt %xcc, 1b
-	 ldub	[%o0 + %o3], %o4
-2:
-	retl
-	 clr	%o0
-3:
-	retl
-	 mov	%o4, %o0
+	cmp	%o2, 0		! IEU1	Group
+loop:	be,pn	%icc, ret_0	! CTI
+	 nop			! IEU0
+	ldub	[%o0], %g5	! LSU	Group
+	ldub	[%o1], %g3	! LSU	Group
+	sub	%o2, 1, %o2	! IEU0
+	add	%o0, 1, %o0	! IEU1
+	add	%o1, 1, %o1	! IEU0	Group
+	subcc	%g5, %g3, %g3	! IEU1	Group
+	be,pt	%icc, loop	! CTI
+	 cmp	%o2, 0		! IEU1	Group
+
+ret_n0:	retl
+	 mov	%g3, %o0
+ret_0:	retl
+	 mov	0, %o0
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/acorn/block/Makefile linux/drivers/acorn/block/Makefile
--- v2.3.99-pre2/linux/drivers/acorn/block/Makefile	Thu Mar  2 14:36:22 2000
+++ linux/drivers/acorn/block/Makefile	Wed Mar 22 09:39:11 2000
@@ -51,9 +51,6 @@
 
 include $(TOPDIR)/Rules.make
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
-
 fd1772_mod.o: $(FLOPPY)
 	$(LD) -r -o $@ $(FLOPPY)
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/acorn/scsi/Makefile linux/drivers/acorn/scsi/Makefile
--- v2.3.99-pre2/linux/drivers/acorn/scsi/Makefile	Thu Mar  2 14:36:22 2000
+++ linux/drivers/acorn/scsi/Makefile	Wed Mar 22 09:39:11 2000
@@ -49,8 +49,5 @@
 
 include $(TOPDIR)/Rules.make
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
-
 acornscsi_mod.o: acornscsi.o acornscsi-io.o
 	$(LD) $(LD_RFLAG) -r -o $@ acornscsi.o acornscsi-io.o
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/Makefile linux/drivers/atm/Makefile
--- v2.3.99-pre2/linux/drivers/atm/Makefile	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/Makefile	Tue Mar 21 23:38:26 2000
@@ -3,15 +3,15 @@
 # Makefile for the Linux network (ATM) device drivers.
 #
 
-L_TARGET := atm.a
-L_OBJS	 := atmdev_init.o
+O_TARGET := atm.o
+O_OBJS	 := atmdev_init.o
 M_OBJS	 :=
 MOD_LIST_NAME := ATM_MODULES
 
 include ../../.config
 
 ifeq ($(CONFIG_ATM_ENI),y)
-L_OBJS += eni.o
+O_OBJS += eni.o
 NEED_SUNI_LX = suni.o
 else
   ifeq ($(CONFIG_ATM_ENI),m)
@@ -21,8 +21,8 @@
 endif
 
 ifeq ($(CONFIG_ATM_ZATM),y)
-L_OBJS += zatm.o
-LX_OBJS += uPD98402.o
+O_OBJS += zatm.o
+OX_OBJS += uPD98402.o
 else
   ifeq ($(CONFIG_ATM_ZATM),m)
   M_OBJS += zatm.o
@@ -31,11 +31,11 @@
 endif
 
 ifeq ($(CONFIG_ATM_TNETA1570),y)
-L_OBJS += tneta1570.o suni.o
+O_OBJS += tneta1570.o suni.o
 endif
 
 ifeq ($(CONFIG_ATM_NICSTAR),y)
-L_OBJS += nicstar.o
+O_OBJS += nicstar.o
   ifeq ($(CONFIG_ATM_NICSTAR_USE_SUNI),y)
   NEED_SUNI_LX = suni.o
   endif
@@ -55,7 +55,7 @@
 endif
 
 ifeq ($(CONFIG_ATM_HORIZON),y)
-L_OBJS += horizon.o
+O_OBJS += horizon.o
 else
   ifeq ($(CONFIG_ATM_HORIZON),m)
   M_OBJS += horizon.o
@@ -63,7 +63,7 @@
 endif
 
 ifeq ($(CONFIG_ATM_AMBASSADOR),y)
-L_OBJS += ambassador.o
+O_OBJS += ambassador.o
 else
   ifeq ($(CONFIG_ATM_AMBASSADOR),m)
   M_OBJS += ambassador.o
@@ -71,7 +71,7 @@
 endif
 
 ifeq ($(CONFIG_ATM_TCP),y)
-L_OBJS += atmtcp.o
+O_OBJS += atmtcp.o
 else
   ifeq ($(CONFIG_ATM_TCP),m)
   M_OBJS += atmtcp.o
@@ -79,7 +79,7 @@
 endif
 
 ifeq ($(CONFIG_ATM_IA),y)
-L_OBJS += iphase.o
+O_OBJS += iphase.o
 NEED_SUNI_LX = suni.o
 else
 ifeq ($(CONFIG_ATM_IA),m)
@@ -91,13 +91,13 @@
 ifeq ($(NEED_SUNI_LX),)
   MX_OBJS += $(NEED_SUNI_MX)
 else
-  LX_OBJS += $(NEED_SUNI_LX)
+  OX_OBJS += $(NEED_SUNI_LX)
 endif
 
 ifeq ($(NEED_IDT77105_LX),)
   MX_OBJS += $(NEED_IDT77105_MX)
 else
-  LX_OBJS += $(NEED_IDT77105_LX)
+  OX_OBJS += $(NEED_IDT77105_LX)
 endif
 
 ifeq ($(CONFIG_ATM_FORE200E_PCA),y)
@@ -114,7 +114,7 @@
   endif
 endif
 ifeq ($(CONFIG_ATM_FORE200E),y)
-L_OBJS += fore200e.o $(FORE200E_FW_OBJS)
+O_OBJS += fore200e.o $(FORE200E_FW_OBJS)
 else
   ifeq ($(CONFIG_ATM_FORE200E),m)
   M_OBJS += fore_200e.o
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/ambassador.c linux/drivers/atm/ambassador.c
--- v2.3.99-pre2/linux/drivers/atm/ambassador.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/ambassador.c	Tue Mar 21 23:38:26 2000
@@ -32,6 +32,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 
+#include <asm/atomic.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -39,7 +40,7 @@
 
 #define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
 #define description_string "Madge ATM Ambassador driver"
-#define version_string "1.2"
+#define version_string "1.2.4"
 
 static inline void __init show_version (void) {
   printk ("%s version %s\n", description_string, version_string);
@@ -97,8 +98,8 @@
   
   The adapter is quite intelligent (fast) and has a simple interface
   (few features). VPI is always zero, 1024 VCIs are supported. There
-  is limited cell rate support. UBR channels can be kept and ABR
-  (explicit rate, bu not EFCI) is supported. There is no CBR or VBR
+  is limited cell rate support. UBR channels can be capped and ABR
+  (explicit rate, but not EFCI) is supported. There is no CBR or VBR
   support.
   
   1. Driver <-> Adapter Communication
@@ -321,8 +322,29 @@
 static unsigned int rx_lats = 7;
 static unsigned char pci_lat = 0;
 
+static const unsigned long onegigmask = -1 << 30;
+
 /********** access to adapter **********/
 
+static inline void wr_plain (const amb_dev * dev, const u32 * addr, u32 data) {
+  PRINTD (DBG_FLOW|DBG_REGS, "wr: %p <- %08x", addr, data);
+#ifdef AMB_MMIO
+  dev->membase[addr - (u32 *) 0] = data;
+#else
+  outl (data, dev->iobase + (addr - (u32 *) 0) * sizeof(u32));
+#endif
+}
+
+static inline u32 rd_plain (const amb_dev * dev, const u32 * addr) {
+#ifdef AMB_MMIO
+  u32 data = dev->membase[addr - (u32 *) 0];
+#else
+  u32 data = inl (dev->iobase + (addr - (u32 *) 0) * sizeof(u32));
+#endif
+  PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x", addr, data);
+  return data;
+}
+
 static const amb_mem * const mem = 0;
 
 static inline void wr_mem (const amb_dev * dev, const u32 * addr, u32 data) {
@@ -342,7 +364,7 @@
   u32 be = inl (dev->iobase + (addr - (u32 *) 0) * sizeof(u32));
 #endif
   u32 data = be32_to_cpu (be);
-  PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x b[%08x]", addr, data, be);  
+  PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x b[%08x]", addr, data, be);
   return data;
 }
 
@@ -350,14 +372,18 @@
 
 static inline void dump_registers (const amb_dev * dev) {
 #ifdef DEBUG_AMBASSADOR
-  // u32 * i;
-  // PRINTD (DBG_REGS, "mailboxes: ");
-  // for (i = (u32 *) 0x40; i < (u32 *) 0x60; ++i)
-  // PRINTD (DBG_REGS, "%08x ", rd_mem (dev, i));
-  PRINTD (DBG_REGS, "doorb %08x", rd_mem (dev, (u32 *) 0x60));
-  PRINTD (DBG_REGS, "irqev %08x", rd_mem (dev, (u32 *) 0x64));
-  PRINTD (DBG_REGS, "irqen %08x", rd_mem (dev, (u32 *) 0x68));
-  PRINTD (DBG_REGS, "reset %08x", rd_mem (dev, (u32 *) 0x6c));
+  if (debug & DBG_REGS) {
+    u32 * i;
+    PRINTD (DBG_REGS, "reading PLX control: ");
+    for (i = (u32 *) 0x00; i < (u32 *) 0x30; ++i)
+      rd_mem (dev, i);
+    PRINTD (DBG_REGS, "reading mailboxes: ");
+    for (i = (u32 *) 0x40; i < (u32 *) 0x60; ++i)
+      rd_mem (dev, i);
+    PRINTD (DBG_REGS, "reading doorb irqev irqen reset:");
+    for (i = (u32 *) 0x60; i < (u32 *) 0x70; ++i)
+      rd_mem (dev, i);
+  }
 #else
   (void) dev;
 #endif
@@ -414,8 +440,8 @@
 
 static inline int check_area (void * start, size_t length) {
   // assumes length > 0
-  const u32 fourmegmask = (-1)<<22;
-  const u32 twofivesixmask = (-1)<<8;
+  const u32 fourmegmask = -1 << 22;
+  const u32 twofivesixmask = -1 << 8;
   const u32 starthole = 0xE0000000;
   u32 startaddress = virt_to_bus (start);
   u32 lastaddress = startaddress+length-1;
@@ -435,7 +461,7 @@
   if (ATM_SKB(skb)->vcc->pop) {
     ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
   } else {
-    dev_kfree_skb (skb);
+    dev_kfree_skb_any (skb);
   }
 }
 
@@ -448,7 +474,7 @@
   PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx);
   
   // VC layer stats
-  ATM_SKB(skb)->vcc->stats->tx++;
+  atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
   
   // free the descriptor
   kfree (tx_descr);
@@ -489,7 +515,7 @@
 	  dump_skb ("<<<", vc, skb);
 	  
 	  // VC layer stats
-	  atm_vcc->stats->rx++;
+	  atomic_inc(&atm_vcc->stats->rx);
 	  skb->stamp = xtime;
 	  // end of our responsability
 	  atm_vcc->push (atm_vcc, skb);
@@ -504,7 +530,7 @@
       } else {
       	PRINTK (KERN_INFO, "dropped over-size frame");
 	// should we count this?
-	atm_vcc->stats->rx_drop++;
+	atomic_inc(&atm_vcc->stats->rx_drop);
       }
       
     } else {
@@ -524,7 +550,7 @@
       dev->stats.rx.unused++;
   }
   
-  dev_kfree_skb (skb);
+  dev_kfree_skb_any (skb);
   return;
 }
 
@@ -548,7 +574,8 @@
 // sometimes does 16-bit accesses (yuk yuk yuk)
 
 static int command_do (amb_dev * dev, command * cmd) {
-  volatile amb_cq * cq = &dev->cq;
+  amb_cq * cq = &dev->cq;
+  volatile amb_cq_ptrs * ptrs = &cq->ptrs;
   command * my_slot;
   unsigned long timeout;
   
@@ -562,18 +589,18 @@
   // if not full...
   if (cq->pending < cq->maximum) {
     // remember my slot for later
-    my_slot = cq->in;
+    my_slot = ptrs->in;
     PRINTD (DBG_CMD, "command in slot %p", my_slot);
     
     dump_command (cmd);
     
     // copy command in
-    *cq->in = *cmd;
+    *ptrs->in = *cmd;
     cq->pending++;
-    cq->in = NEXTQ (cq->in, cq->start, cq->limit);
+    ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit);
     
     // mail the command
-    wr_mem (dev, &mem->mb.adapter.cmd_address, virt_to_bus (cq->in));
+    wr_mem (dev, &mem->mb.adapter.cmd_address, virt_to_bus (ptrs->in));
     
     // prepare to wait for cq->pending milliseconds
     // effectively one centisecond on i386
@@ -591,13 +618,13 @@
     }
     
     // wait for my slot to be reached (all waiters are here or above, until...)
-    while (cq->out != my_slot) {
-      PRINTD (DBG_CMD, "wait: command slot (now at %p)", cq->out);
+    while (ptrs->out != my_slot) {
+      PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out);
       schedule();
     }
     
     // wait on my slot (... one gets to its slot, and... )
-    while (cq->out->request != cpu_to_be32 (SRB_COMPLETE)) {
+    while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) {
       PRINTD (DBG_CMD, "wait: command slot completion");
       schedule();
     }
@@ -607,12 +634,13 @@
     spin_lock (&cq->lock);
     cq->pending--;
     // copy command out
-    *cmd = *cq->out;
-    cq->out = NEXTQ (cq->out, cq->start, cq->limit);
+    *cmd = *ptrs->out;
+    ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit);
     spin_unlock (&cq->lock);
     
     return 0;
   } else {
+    cq->filled++;
     spin_unlock (&cq->lock);
     return -EAGAIN;
   }
@@ -796,7 +824,7 @@
       return;
     }
     if (check_area (skb->data, skb->truesize)) {
-      dev_kfree_skb (skb);
+      dev_kfree_skb_any (skb);
       return;
     }
     // cast needed as there is no %? for pointer differences
@@ -805,14 +833,14 @@
     rx.handle = virt_to_bus (skb);
     rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
     if (rx_give (dev, &rx, pool))
-      dev_kfree_skb (skb);
+      dev_kfree_skb_any (skb);
     
   }
   
   return;
 }
 
-// top up all RX pools (also called as a bottom half)
+// top up all RX pools (can also be called as a bottom half)
 static void fill_rx_pools (amb_dev * dev) {
   unsigned char pool;
   
@@ -827,25 +855,23 @@
 /********** enable host interrupts **********/
 
 static inline void interrupts_on (amb_dev * dev) {
-  wr_mem (dev, &mem->interrupt_control,
-	  rd_mem (dev, &mem->interrupt_control)
-	  | AMB_INTERRUPT_BITS);
+  wr_plain (dev, &mem->interrupt_control,
+	    rd_plain (dev, &mem->interrupt_control)
+	    | AMB_INTERRUPT_BITS);
 }
 
 /********** disable host interrupts **********/
 
 static inline void interrupts_off (amb_dev * dev) {
-  wr_mem (dev, &mem->interrupt_control,
-	  rd_mem (dev, &mem->interrupt_control)
-	  &~ AMB_INTERRUPT_BITS);
+  wr_plain (dev, &mem->interrupt_control,
+	    rd_plain (dev, &mem->interrupt_control)
+	    &~ AMB_INTERRUPT_BITS);
 }
 
 /********** interrupt handling **********/
 
 static void interrupt_handler (int irq, void * dev_id, struct pt_regs * pt_regs) {
   amb_dev * dev = amb_devs;
-  unsigned int irq_ok;
-  unsigned int irq_ok_old;
   (void) pt_regs;
   
   PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id);
@@ -860,50 +886,56 @@
       break;
     dev = dev->prev;
   }
+  // impossible - unless we add the device to our list after both
+  // registering the IRQ handler for it and enabling interrupts, AND
+  // the card generates an IRQ at startup - should not happen again
   if (!dev) {
-    PRINTD (DBG_IRQ, "irq not for me: %d", irq);
+    PRINTD (DBG_IRQ, "irq for unknown device: %d", irq);
     return;
   }
+  // impossible - unless we have memory corruption of dev or kernel
   if (irq != dev->irq) {
     PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq);
     return;
   }
   
-  // definitely for us
-  irq_ok = 0;
-  irq_ok_old = -1;
+  {
+    u32 interrupt = rd_plain (dev, &mem->interrupt);
   
-  // perhaps disable interrupts? (disabled at PIC by Linux)
-  // interrupts_off (dev);
+    // for us or someone else sharing the same interrupt
+    if (!interrupt) {
+      PRINTD (DBG_IRQ, "irq not for me: %d", irq);
+      return;
+    }
+    
+    // definitely for us
+    PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt);
+    wr_plain (dev, &mem->interrupt, -1);
+  }
   
-  while (irq_ok_old != irq_ok && irq_ok < 100) {
+  {
+    unsigned int irq_work = 0;
     unsigned char pool;
-    PRINTD (DBG_IRQ, "FYI: interrupt was %08x, work %u",
-	    rd_mem (dev, &mem->interrupt), irq_ok);
-    wr_mem (dev, &mem->interrupt, -1);
-    irq_ok_old = irq_ok;
     for (pool = 0; pool < NUM_RX_POOLS; ++pool)
       while (!rx_take (dev, pool))
-	++irq_ok;
+	++irq_work;
     while (!tx_take (dev))
-      ++irq_ok;
-  }
+      ++irq_work;
   
-  if (irq_ok) {
-#if 0
-    queue_task (&dev->bh, &tq_immediate);
-    mark_bh (IMMEDIATE_BH);
+    if (irq_work) {
+#ifdef FILL_RX_POOLS_IN_BH
+      queue_task (&dev->bh, &tq_immediate);
+      mark_bh (IMMEDIATE_BH);
 #else
-    fill_rx_pools (dev);
+      fill_rx_pools (dev);
 #endif
 
-    PRINTD (DBG_IRQ, "work done: %u", irq_ok);
-  } else {
-    PRINTD (DBG_IRQ|DBG_WARN, "no work done");
+      PRINTD (DBG_IRQ, "work done: %u", irq_work);
+    } else {
+      PRINTD (DBG_IRQ|DBG_WARN, "no work done");
+    }
   }
   
-  // perhaps re-enable interrupts? (re-enabled at PIC by Linux)
-  // interrupts_on (dev);
   PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
   return;
 }
@@ -913,6 +945,7 @@
 #ifdef DEBUG_AMBASSADOR
 static void dont_panic (amb_dev * dev) {
   amb_cq * cq = &dev->cq;
+  volatile amb_cq_ptrs * ptrs = &cq->ptrs;
   amb_txq * txq;
   amb_rxq * rxq;
   command * cmd;
@@ -926,10 +959,11 @@
   cli();
   
   PRINTK (KERN_INFO, "don't panic - putting adapter into reset");
-  wr_mem (dev, &mem->reset_control, rd_mem (dev, &mem->reset_control) | AMB_RESET);
+  wr_plain (dev, &mem->reset_control,
+	    rd_plain (dev, &mem->reset_control) | AMB_RESET_BITS);
   
   PRINTK (KERN_INFO, "marking all commands complete");
-  for (cmd = cq->start; cmd < cq->limit; ++cmd)
+  for (cmd = ptrs->start; cmd < ptrs->limit; ++cmd)
     cmd->request = cpu_to_be32 (SRB_COMPLETE);
 
   PRINTK (KERN_INFO, "completing all TXs");
@@ -952,7 +986,7 @@
       if (rx == rxq->in.start)
 	rx = rxq->in.limit;
       --rx;
-      dev_kfree_skb (bus_to_virt (rx->handle));
+      dev_kfree_skb_any (bus_to_virt (rx->handle));
     }
   }
   
@@ -972,18 +1006,19 @@
   
   PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate);
   
-  // rates in cells per second, ITU format (nasty 16bit fp)
+  // rates in cells per second, ITU format (nasty 16-bit floating-point)
   // given 5-bit e and 9-bit m:
   // rate = EITHER (1+m/2^9)*2^e    OR 0
   // bits = EITHER 1<<14 | e<<9 | m OR 0
   // (bit 15 is "reserved", bit 14 "non-zero")
   // smallest rate is 0 (special representation)
   // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1)
+  // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0)
   // simple algorithm:
   // find position of top bit, this gives e
   // remove top bit and shift (rounding if feeling clever) by 9-e
   
-  // ucode bug: please don't set bit 14! 0 not representable
+  // ucode bug: please don't set bit 14! so 0 rate not representable
   
   if (rate > 0xffc00000U) {
     // larger than largest representable rate
@@ -1228,7 +1263,7 @@
   
   // we are not really "immediately before allocating the connection
   // identifier in hardware", but it will just have to do!
-  atm_vcc->flags |= ATM_VF_ADDR;
+  set_bit(ATM_VF_ADDR,&atm_vcc->flags);
   
   if (txtp->traffic_class != ATM_NONE) {
     command cmd;
@@ -1303,7 +1338,7 @@
   atm_vcc->vci = vci;
   
   // indicate readiness
-  atm_vcc->flags |= ATM_VF_READY;
+  set_bit(ATM_VF_READY,&atm_vcc->flags);
   
   MOD_INC_USE_COUNT;
   return 0;
@@ -1319,7 +1354,7 @@
   PRINTD (DBG_VCC|DBG_FLOW, "amb_close");
   
   // indicate unreadiness
-  atm_vcc->flags &= ~ATM_VF_READY;
+  clear_bit(ATM_VF_READY,&atm_vcc->flags);
   
   // disable TXing
   if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
@@ -1384,7 +1419,7 @@
   kfree (vcc);
   
   // say the VPI/VCI is free again
-  atm_vcc->flags &= ~ATM_VF_ADDR;
+  clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
   MOD_DEC_USE_COUNT;
 }
 
@@ -1394,6 +1429,8 @@
 static int amb_ioctl (struct atm_dev * dev, unsigned int cmd, void * arg) {
   unsigned short newdebug;
   if (cmd == AMB_SETDEBUG) {
+    if (!capable(CAP_NET_ADMIN))
+      return -EPERM;
     if (copy_from_user (&newdebug, arg, sizeof(newdebug))) {
       // moan
       return -EFAULT;
@@ -1402,6 +1439,8 @@
       return 0;
     }
   } else if (cmd == AMB_DONTPANIC) {
+    if (!capable(CAP_NET_ADMIN))
+      return -EPERM;
     dont_panic (dev);
   } else {
     // moan
@@ -1453,7 +1492,7 @@
   }
   
   if (check_area (skb->data, skb->len)) {
-    atm_vcc->stats->tx_err++;
+    atomic_inc(&atm_vcc->stats->tx_err);
     return -ENOMEM; // ?
   }
   
@@ -1564,7 +1603,7 @@
   // at. However, I note that the ATM layer calls kfree_skb rather
   // than dev_kfree_skb at this point so we are least covered as far
   // as buffer locking goes. There may be bugs if pcap clones RX skbs.
-  
+
   PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)",
 	  skb, atm_vcc, vcc);
   
@@ -1582,7 +1621,7 @@
   }
   
   // just do what the ATM layer would have done
-  kfree_skb (skb);
+  dev_kfree_skb_any (skb);
   
   return;
 }
@@ -1733,12 +1772,12 @@
     cq->high = 0;
     cq->maximum = cmds - 1;
     
-    cq->start = cmd;
-    cq->in = cmd;
-    cq->out = cmd;
-    cq->limit = cmd + cmds;
+    cq->ptrs.start = cmd;
+    cq->ptrs.in = cmd;
+    cq->ptrs.out = cmd;
+    cq->ptrs.limit = cmd + cmds;
     
-    memory = cq->limit;
+    memory = cq->ptrs.limit;
   }
   
   PRINTD (DBG_TX, "TX queue pair at %p", memory);
@@ -1810,7 +1849,7 @@
 
 static void destroy_queues (amb_dev * dev) {
   // all queues assumed empty
-  void * memory = dev->cq.start;
+  void * memory = dev->cq.ptrs.start;
   // includes txq.in, txq.out, rxq[].in and rxq[].out
   
   PRINTD (DBG_FLOW, "destroy_queues %p", dev);
@@ -1823,9 +1862,8 @@
 
 /********** basic loader commands and error handling **********/
 
-static int __init do_loader_command (const amb_dev * dev, loader_command cmd,
-				     volatile loader_block * lb) {
-  
+static int __init do_loader_command (volatile loader_block * lb,
+				     const amb_dev * dev, loader_command cmd) {
   // centisecond timeouts - guessing away here
   unsigned int command_timeouts [] = {
     [host_memory_test]     = 15,
@@ -1939,8 +1977,9 @@
   lb->result = 0;
   lb->command = cpu_to_be32 (cmd);
   lb->valid = cpu_to_be32 (DMA_VALID);
+  // dump_registers (dev);
   // dump_loader_block (lb);
-  wr_mem (dev, &mem->doorbell, virt_to_bus (lb));
+  wr_mem (dev, &mem->doorbell, virt_to_bus (lb) & ~onegigmask);
   
   timeout = command_timeouts[cmd] * HZ/100;
   
@@ -1957,7 +1996,7 @@
   if (cmd == adapter_start) {
     // wait for start command to acknowledge...
     timeout = HZ/10;
-    while (rd_mem (dev, &mem->doorbell))
+    while (rd_plain (dev, &mem->doorbell))
       if (timeout) {
 	timeout = schedule_timeout (timeout);
       } else {
@@ -1975,27 +2014,27 @@
 
 /* loader: determine loader version */
 
-static int __init get_loader_version (const amb_dev * dev, u32 * version) {
-  loader_block lb;
+static int __init get_loader_version (loader_block * lb,
+				      const amb_dev * dev, u32 * version) {
   int res;
   
   PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version");
   
-  res = do_loader_command (dev, get_version_number, &lb);
+  res = do_loader_command (lb, dev, get_version_number);
   if (res)
     return res;
   if (version)
-    *version = be32_to_cpu (lb.payload.version);
+    *version = be32_to_cpu (lb->payload.version);
   return 0;
 }
 
-/* loader: read or verify memory data blocks */
+/* loader: write memory data blocks */
 
-static int __init loader_write (const amb_dev * dev, const u32 * data,
+static int __init loader_write (loader_block * lb,
+				const amb_dev * dev, const u32 * data,
 				u32 address, unsigned int count) {
   unsigned int i;
-  loader_block lb;
-  transfer_block * tb = &lb.payload.transfer;
+  transfer_block * tb = &lb->payload.transfer;
   
   PRINTD (DBG_FLOW|DBG_LOAD, "loader_write");
   
@@ -2005,14 +2044,16 @@
   tb->count = cpu_to_be32 (count);
   for (i = 0; i < count; ++i)
     tb->data[i] = cpu_to_be32 (data[i]);
-  return do_loader_command (dev, write_adapter_memory, &lb);
+  return do_loader_command (lb, dev, write_adapter_memory);
 }
 
-static int __init loader_verify (const amb_dev * dev, const u32 * data,
+/* loader: verify memory data blocks */
+
+static int __init loader_verify (loader_block * lb,
+				 const amb_dev * dev, const u32 * data,
 				 u32 address, unsigned int count) {
   unsigned int i;
-  loader_block lb;
-  transfer_block * tb = &lb.payload.transfer;
+  transfer_block * tb = &lb->payload.transfer;
   int res;
   
   PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify");
@@ -2021,7 +2062,7 @@
     return -EINVAL;
   tb->address = cpu_to_be32 (address);
   tb->count = cpu_to_be32 (count);
-  res = do_loader_command (dev, read_adapter_memory, &lb);
+  res = do_loader_command (lb, dev, read_adapter_memory);
   if (!res)
     for (i = 0; i < count; ++i)
       if (tb->data[i] != cpu_to_be32 (data[i])) {
@@ -2031,13 +2072,14 @@
   return res;
 }
 
-static int __init loader_start (const amb_dev * dev, u32 address) {
-  loader_block lb;
-  
+/* loader: start microcode */
+
+static int __init loader_start (loader_block * lb,
+				const amb_dev * dev, u32 address) {
   PRINTD (DBG_FLOW|DBG_LOAD, "loader_start");
   
-  lb.payload.start = cpu_to_be32 (address);
-  return do_loader_command (dev, adapter_start, &lb);
+  lb->payload.start = cpu_to_be32 (address);
+  return do_loader_command (lb, dev, adapter_start);
 }
 
 /********** reset card **********/
@@ -2047,19 +2089,21 @@
   
   PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset");
   
-  word = rd_mem (dev, &mem->reset_control);
-#if 0
-  // clear all interrupts just in case
-  wr_mem (dev, &mem->interrupt, -1);
-#endif
+  word = rd_plain (dev, &mem->reset_control);
   // put card into reset state
-  wr_mem (dev, &mem->reset_control, word | AMB_RESET);
+  wr_plain (dev, &mem->reset_control, word | AMB_RESET_BITS);
   // wait a short while
   udelay (10);
+#if 1
+  // put card into known good state
+  wr_plain (dev, &mem->interrupt_control, AMB_DOORBELL_BITS);
+  // clear all interrupts just in case
+  wr_plain (dev, &mem->interrupt, -1);
+#endif
   // clear self-test done flag
-  wr_mem (dev, &mem->mb.loader.ready, 0);
+  wr_plain (dev, &mem->mb.loader.ready, 0);
   // take card out of reset state
-  wr_mem (dev, &mem->reset_control, word &~ AMB_RESET);
+  wr_plain (dev, &mem->reset_control, word &~ AMB_RESET_BITS);
   
   if (diags) { 
     unsigned long timeout;
@@ -2069,7 +2113,7 @@
       timeout = schedule_timeout (timeout);
     // half second time-out
     timeout = HZ/2;
-    while (!rd_mem (dev, &mem->mb.loader.ready))
+    while (!rd_plain (dev, &mem->mb.loader.ready))
       if (timeout) {
 	timeout = schedule_timeout (timeout);
       } else {
@@ -2078,6 +2122,7 @@
       }
     
     // get results of self-test
+    // XXX double check byte-order
     word = rd_mem (dev, &mem->mb.loader.result);
     if (word & SELF_TEST_FAILURE) {
       void sf (const char * msg) {
@@ -2105,7 +2150,7 @@
 
 /********** transfer and start the microcode **********/
 
-static int __init ucode_init (amb_dev * dev) {
+static int __init ucode_init (loader_block * lb, amb_dev * dev) {
   unsigned int i = 0;
   unsigned int total = 0;
   const u32 * pointer = ucode_data;
@@ -2125,10 +2170,10 @@
       else
 	words = MAX_TRANSFER_DATA;
       total += words;
-      res = loader_write (dev, pointer, address, words);
+      res = loader_write (lb, dev, pointer, address, words);
       if (res)
 	return res;
-      res = loader_verify (dev, pointer, address, words);
+      res = loader_verify (lb, dev, pointer, address, words);
       if (res)
 	return res;
       count -= words;
@@ -2138,7 +2183,7 @@
     i += 1;
   }
   if (*pointer == 0xdeadbeef) {
-    return loader_start (dev, ucode_start);
+    return loader_start (lb, dev, ucode_start);
   } else {
     // cast needed as there is no %? for pointer differnces
     PRINTD (DBG_LOAD|DBG_ERR,
@@ -2156,14 +2201,14 @@
   unsigned char pool;
   unsigned long timeout;
   
-  static inline u32 x (void * addr) {
+  u32 x (void * addr) {
     return cpu_to_be32 (virt_to_bus (addr));
   }
   
   PRINTD (DBG_FLOW, "amb_talk %p", dev);
   
-  a.command_start = x (dev->cq.start);
-  a.command_end   = x (dev->cq.limit);
+  a.command_start = x (dev->cq.ptrs.start);
+  a.command_end   = x (dev->cq.ptrs.limit);
   a.tx_start      = x (dev->txq.in.start);
   a.tx_end        = x (dev->txq.in.limit);
   a.txcom_start   = x (dev->txq.out.start);
@@ -2192,7 +2237,7 @@
     timeout = schedule_timeout (timeout);
   // give the adapter another half second?
   timeout = HZ/2;
-  while (rd_mem (dev, &mem->doorbell))
+  while (rd_plain (dev, &mem->doorbell))
     if (timeout) {
       timeout = schedule_timeout (timeout);
     } else {
@@ -2259,38 +2304,55 @@
 }
 
 static int __init amb_init (amb_dev * dev) {
-  u32 version;
+  loader_block lb;
   
-  /* enable adapter doorbell */
-  wr_mem (dev, &mem->interrupt_control,
-	  rd_mem (dev, &mem->interrupt_control)
-	  | AMB_DOORBELL_BITS);
+  void fixup_plx_window (void) {
+    // fix up the PLX-mapped window base address to match the block
+    unsigned long blb;
+    u32 mapreg;
+    blb = virt_to_bus (&lb);
+    // the kernel stack had better not ever cross a 1Gb boundary!
+    mapreg = rd_plain (dev, &mem->stuff[10]);
+    mapreg &= ~onegigmask;
+    mapreg |= blb & onegigmask;
+    wr_plain (dev, &mem->stuff[10], mapreg);
+    return;
+  }
+  
+  u32 version;
   
   if (amb_reset (dev, 1)) {
     PRINTK (KERN_ERR, "card reset failed!");
-  } else if (get_loader_version (dev, &version)) {
-    PRINTK (KERN_INFO, "failed to get loader version");
   } else {
-    PRINTK (KERN_INFO, "loader version is %08x", version);
+    fixup_plx_window ();
     
-    if (ucode_init (dev)) {
-      PRINTK (KERN_ERR, "microcode failure");
-    } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
-      PRINTK (KERN_ERR, "failed to get memory for queues");
+    if (get_loader_version (&lb, dev, &version)) {
+      PRINTK (KERN_INFO, "failed to get loader version");
     } else {
+      PRINTK (KERN_INFO, "loader version is %08x", version);
       
-      if (amb_talk (dev)) {
-	PRINTK (KERN_ERR, "adapter did not accept queues");
+      if (ucode_init (&lb, dev)) {
+	PRINTK (KERN_ERR, "microcode failure");
+      } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
+	PRINTK (KERN_ERR, "failed to get memory for queues");
       } else {
 	
-	amb_ucode_version (dev);
-	return 0;
-      } /* amb_talk */
+	if (amb_talk (dev)) {
+	  PRINTK (KERN_ERR, "adapter did not accept queues");
+	} else {
+	  
+	  amb_ucode_version (dev);
+	  return 0;
+	  
+	} /* amb_talk */
+	
+	destroy_queues (dev);
+      } /* create_queues, ucode_init */
       
-      destroy_queues (dev);
-    } /* create_queues, ucode_init */
+      amb_reset (dev, 0);
+    } /* get_loader_version */
     
-  } /* get_loader_version, amb_reset */
+  } /* amb_reset */
   
   return -1;
 }
@@ -2303,81 +2365,57 @@
     amb_dev * dev;
     
     // read resources from PCI configuration space
-    u32 * membase = bus_to_virt
-      (pci_dev->resource[0].start);
-    u32 iobase = pci_dev->resource[1].start;
     u8 irq = pci_dev->irq;
+    u32 * membase = bus_to_virt (pci_dev->resource[0].start);
+    u32 iobase = pci_dev->resource[1].start;
     
-    // check IO region
-    if (check_region (iobase, AMB_EXTENT)) {
-      PRINTK (KERN_ERR, "IO range already in use!");
-      return;
-    }
-    
-    dev = kmalloc (sizeof(amb_dev), GFP_KERNEL);
-    if (!dev) {
-      // perhaps we should be nice: deregister all adapters and abort?
-      PRINTK (KERN_ERR, "out of memory!");
-      return;
-    }
-    memset (dev, 0, sizeof(amb_dev));    
-    
-    // set up known dev items straight away
-    dev->pci_dev = pci_dev; 
-    
-    dev->iobase = iobase;
-    dev->irq = irq; 
-    dev->membase = membase;
-    
-    // flags (currently only dead)
-    dev->flags = 0;
-    
-    // Allocate cell rates (fibre)
-    // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
-    // to be really pedantic, this should be ATM_OC3c_PCR
-    dev->tx_avail = ATM_OC3_PCR;
-    dev->rx_avail = ATM_OC3_PCR;
-    
-#if 0
-    // initialise bottom half
-    dev->bh.next = 0;
-    dev->bh.sync = 0;
-    dev->bh.routine = (void (*)(void *)) fill_rx_pools;
-    dev->bh.data = dev;
-#endif
-    
-    // semaphore for txer/rxer modifications - we cannot use a
-    // spinlock as the critical region needs to switch processes
-    init_MUTEX (&dev->vcc_sf);
-    // queue manipulation spinlocks; we want atomic reads and
-    // writes to the queue descriptors (handles IRQ and SMP)
-    // consider replacing "int pending" -> "atomic_t available"
-    // => problem related to who gets to move queue pointers
-    spin_lock_init (&dev->cq.lock);
-    spin_lock_init (&dev->txq.lock);
-    {
+    void setup_dev (void) {
       unsigned char pool;
+      memset (dev, 0, sizeof(amb_dev));
+      
+      // set up known dev items straight away
+      dev->pci_dev = pci_dev; 
+      
+      dev->iobase = iobase;
+      dev->irq = irq; 
+      dev->membase = membase;
+      
+      // flags (currently only dead)
+      dev->flags = 0;
+      
+      // Allocate cell rates (fibre)
+      // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
+      // to be really pedantic, this should be ATM_OC3c_PCR
+      dev->tx_avail = ATM_OC3_PCR;
+      dev->rx_avail = ATM_OC3_PCR;
+      
+#ifdef FILL_RX_POOLS_IN_BH
+      // initialise bottom half
+      dev->bh.next = 0;
+      dev->bh.sync = 0;
+      dev->bh.routine = (void (*)(void *)) fill_rx_pools;
+      dev->bh.data = dev;
+#endif
+      
+      // semaphore for txer/rxer modifications - we cannot use a
+      // spinlock as the critical region needs to switch processes
+      init_MUTEX (&dev->vcc_sf);
+      // queue manipulation spinlocks; we want atomic reads and
+      // writes to the queue descriptors (handles IRQ and SMP)
+      // consider replacing "int pending" -> "atomic_t available"
+      // => problem related to who gets to move queue pointers
+      spin_lock_init (&dev->cq.lock);
+      spin_lock_init (&dev->txq.lock);
       for (pool = 0; pool < NUM_RX_POOLS; ++pool)
 	spin_lock_init (&dev->rxq[pool].lock);
     }
     
-    // grab (but share) IRQ and install handler
-    if (request_irq (irq, interrupt_handler, SA_SHIRQ, DEV_LABEL, dev)) {
-      PRINTK (KERN_ERR, "request IRQ failed!");
-      // free_irq is at "endif"
-    } else {
-      
+    void setup_pci_dev (void) {
       unsigned char lat;
       
-      // reserve IO region
-      request_region (iobase, AMB_EXTENT, DEV_LABEL);
-      
-      PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at IO %x, IRQ %u, MEM %p",
-	      iobase, irq, membase);
-      
       // enable bus master accesses
       pci_set_master (pci_dev);
-
+      
       // frobnicate latency (upwards, usually)
       pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat);
       if (pci_lat) {
@@ -2389,43 +2427,78 @@
 		"increasing", lat, MIN_PCI_LATENCY);
 	pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY);
       }
+    }
+    
+    PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
+	    " IO %x, IRQ %u, MEM %p", iobase, irq, membase);
+    
+    // check IO region
+    if (check_region (iobase, AMB_EXTENT)) {
+      PRINTK (KERN_ERR, "IO range already in use!");
+      return;
+    }
+    
+    dev = kmalloc (sizeof(amb_dev), GFP_KERNEL);
+    if (!dev) {
+      // perhaps we should be nice: deregister all adapters and abort?
+      PRINTK (KERN_ERR, "out of memory!");
+      return;
+    }
+    
+    setup_dev();
+    
+    if (amb_init (dev)) {
+      PRINTK (KERN_ERR, "adapter initialisation failure");
+    } else {
+      
+      setup_pci_dev();
       
-      if (amb_init (dev)) {
-	PRINTK (KERN_ERR, "adapter initialisation failure");
-      } else if (!(dev->atm_dev = atm_dev_register (DEV_LABEL, &amb_ops, -1, 0))) {
-	PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
+      // grab (but share) IRQ and install handler
+      if (request_irq (irq, interrupt_handler, SA_SHIRQ, DEV_LABEL, dev)) {
+	PRINTK (KERN_ERR, "request IRQ failed!");
+	// free_irq is at "endif"
       } else {
 	
-	PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
-		dev->atm_dev->number, dev, dev->atm_dev);
-	dev->atm_dev->dev_data = (void *) dev;
-	
-	// register our address
-	amb_esi (dev, dev->atm_dev->esi);
+	// reserve IO region
+	request_region (iobase, AMB_EXTENT, DEV_LABEL);
 	
-	// 0 bits for vpi, 10 bits for vci
-	dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
-	dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
-	
-	fill_rx_pools (dev);
-	
-	/* enable host interrupts */
-	interrupts_on (dev);
-	
-        // update count and linked list
-	++devs;
-	dev->prev = amb_devs;
-	amb_devs = dev;
-	// success
-	return;
+	dev->atm_dev = atm_dev_register (DEV_LABEL, &amb_ops, -1, NULL);
+	if (!dev->atm_dev) {
+	  PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
+	} else {
+	  
+	  PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
+		  dev->atm_dev->number, dev, dev->atm_dev);
+	  dev->atm_dev->dev_data = (void *) dev;
+	  
+	  // register our address
+	  amb_esi (dev, dev->atm_dev->esi);
+	  
+	  // 0 bits for vpi, 10 bits for vci
+	  dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
+	  dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
+	  
+	  // update count and linked list
+	  ++devs;
+	  dev->prev = amb_devs;
+	  amb_devs = dev;
+	  
+	  // enable host interrupts
+	  interrupts_on (dev);
+	  
+	  // success
+	  return;
+	  
+	  // not currently reached
+	  atm_dev_deregister (dev->atm_dev);
+	} /* atm_dev_register */
 	
-	// not currently reached
-	atm_dev_deregister (dev->atm_dev);
-      } /* atm_dev_register, amb_init */
+	release_region (iobase, AMB_EXTENT);
+	free_irq (irq, dev);
+      } /* request_region, request_irq */
       
-      release_region (iobase, AMB_EXTENT);
-      free_irq (irq, dev);
-    } /* request_region, request_irq */
+      amb_reset (dev, 0);
+    } /* amb_init */
     
     kfree (dev);
   } /* kmalloc, end-of-fn */
@@ -2529,7 +2602,6 @@
   
   show_version();
   
-  // check arguments
   amb_check_args();
   
   // get the juice
@@ -2566,8 +2638,8 @@
     PRINTD (DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
     // the drain should not be necessary
     drain_rx_pools (dev);
-    amb_reset (dev, 0);
     interrupts_off (dev);
+    amb_reset (dev, 0);
     destroy_queues (dev);
     atm_dev_deregister (dev->atm_dev);
     free_irq (dev->irq, dev);
@@ -2594,7 +2666,6 @@
   
   show_version();
   
-  // check arguments
   amb_check_args();
   
   // get the juice
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/ambassador.h linux/drivers/atm/ambassador.h
--- v2.3.99-pre2/linux/drivers/atm/ambassador.h	Fri Sep 10 23:57:29 1999
+++ linux/drivers/atm/ambassador.h	Tue Mar 21 23:38:26 2000
@@ -237,8 +237,6 @@
 #define FP_155_RATE	0x24b1
 #define FP_25_RATE	0x1f9d
 
-#define AMB_RESET       0x40
-
 /* #define VERSION_NUMBER 0x01000000 // initial release */
 /* #define VERSION_NUMBER 0x01010000 // fixed startup probs PLX MB0 not cleared */
 /* #define VERSION_NUMBER 0x01020000 // changed SUNI reset timings; allowed r/w onchip */
@@ -333,9 +331,10 @@
   u32 reset_control;
 } amb_mem;
 
-/* IRQ (card to host) and doorbell (host to card) enable bits */
-#define AMB_INTERRUPT_BITS 0x00030000
-#define AMB_DOORBELL_BITS  0x00000300
+/* RESET bit, IRQ (card to host) and doorbell (host to card) enable bits */
+#define AMB_RESET_BITS	   0x40000000
+#define AMB_INTERRUPT_BITS 0x00000300
+#define AMB_DOORBELL_BITS  0x00030000
 
 /* loader commands */
 
@@ -543,14 +542,19 @@
   ( (current)+1 < (limit) ? (current)+1 : (start) ) 
 
 typedef struct {
-  spinlock_t lock;
-  unsigned int pending;
-  unsigned int high;
-  unsigned int maximum; // size - 1 (q implementation)
   command * start;
   command * in;
   command * out;
   command * limit;
+} amb_cq_ptrs;
+
+typedef struct {
+  spinlock_t lock;
+  unsigned int pending;
+  unsigned int high;
+  unsigned int filled;
+  unsigned int maximum; // size - 1 (q implementation)
+  amb_cq_ptrs ptrs;
 } amb_cq;
 
 typedef struct {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/atmdev_init.c linux/drivers/atm/atmdev_init.c
--- v2.3.99-pre2/linux/drivers/atm/atmdev_init.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/atmdev_init.c	Tue Mar 21 23:38:26 2000
@@ -39,7 +39,7 @@
 
 	devs = 0;
 #ifdef CONFIG_ATM_ENI
-	devs += eni_detect();
+//	devs += eni_detect();
 #endif
 #ifdef CONFIG_ATM_ZATM
 	devs += zatm_detect();
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/atmtcp.c linux/drivers/atm/atmtcp.c
--- v2.3.99-pre2/linux/drivers/atm/atmtcp.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/atmtcp.c	Tue Mar 21 23:38:26 2000
@@ -7,7 +7,9 @@
 #include <linux/wait.h>
 #include <linux/atmdev.h>
 #include <linux/atm_tcp.h>
+#include <linux/bitops.h>
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 
 
 extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */
@@ -36,12 +38,14 @@
 
 
 static int atmtcp_send_control(struct atm_vcc *vcc,int type,
-    const struct atmtcp_control *msg,unsigned short flag)
+    const struct atmtcp_control *msg,int flag)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct atm_vcc *out_vcc;
 	struct sk_buff *skb;
 	struct atmtcp_control *new_msg;
-	unsigned short old_flags;
+	int old_test;
+	int error = 0;
 
 	out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL;
 	if (!out_vcc) return -EUNATCH;
@@ -60,16 +64,21 @@
 	new_msg->type = type;
 	memset(&new_msg->vcc,0,sizeof(atm_kptr_t));
 	*(struct atm_vcc **) &new_msg->vcc = vcc;
-	old_flags = vcc->flags;
+	old_test = test_bit(flag,&vcc->flags);
 	out_vcc->push(out_vcc,skb);
-	while (!((vcc->flags ^ old_flags) & flag)) {
+	add_wait_queue(&vcc->sleep,&wait);
+	while (test_bit(flag,&vcc->flags) == old_test) {
 		mb();
 		out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL;
-		if (!out_vcc) return -EUNATCH;
-		sleep_on(&vcc->sleep);
-		
+		if (!out_vcc) {
+			error = -EUNATCH;
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
 	}
-	return 0;
+	remove_wait_queue(&vcc->sleep,&wait);
+	return error;
 }
 
 
@@ -83,10 +92,10 @@
 	vcc->reply = msg->result;
 	switch (msg->type) {
 	    case ATMTCP_CTRL_OPEN:
-		vcc->flags ^= ATM_VF_READY;
+		change_bit(ATM_VF_READY,&vcc->flags);
 		break;
 	    case ATMTCP_CTRL_CLOSE:
-		vcc->flags ^= ATM_VF_ADDR;
+		change_bit(ATM_VF_ADDR,&vcc->flags);
 		break;
 	    default:
 		printk(KERN_ERR "atmtcp_recv_control: unknown type %d\n",
@@ -120,8 +129,8 @@
 	if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0;
 	msg.type = ATMTCP_CTRL_OPEN;
 	msg.qos = vcc->qos;
-	vcc->flags |= ATM_VF_ADDR;
-	vcc->flags &= ~ATM_VF_READY; /* just in case ... */
+	set_bit(ATM_VF_ADDR,&vcc->flags);
+	clear_bit(ATM_VF_READY,&vcc->flags); /* just in case ... */
 	error = atmtcp_send_control(vcc,ATMTCP_CTRL_OPEN,&msg,ATM_VF_READY);
 	if (error) return error;
 	return vcc->reply;
@@ -136,7 +145,7 @@
 	msg.addr.sap_family = AF_ATMPVC;
 	msg.addr.sap_addr.vpi = vcc->vpi;
 	msg.addr.sap_addr.vci = vcc->vci;
-	vcc->flags &= ~ATM_VF_READY;
+	clear_bit(ATM_VF_READY,&vcc->flags);
 	(void) atmtcp_send_control(vcc,ATMTCP_CTRL_CLOSE,&msg,ATM_VF_ADDR);
 }
 
@@ -168,13 +177,18 @@
 	struct atmtcp_hdr *hdr;
 	int size;
 
+	if (vcc->qos.txtp.traffic_class == ATM_NONE) {
+		if (vcc->pop) vcc->pop(vcc,skb);
+		else dev_kfree_skb(skb);
+		return -EINVAL;
+	}
 	dev_data = PRIV(vcc->dev);
 	if (dev_data) out_vcc = dev_data->vcc;
 	if (!dev_data || !out_vcc) {
 		if (vcc->pop) vcc->pop(vcc,skb);
 		else dev_kfree_skb(skb);
 		if (dev_data) return 0;
-		vcc->stats->tx_err++;
+		atomic_inc(&vcc->stats->tx_err);
 		return -ENOLINK;
 	}
 	size = skb->len+sizeof(struct atmtcp_hdr);
@@ -182,7 +196,7 @@
 	if (!new_skb) {
 		if (vcc->pop) vcc->pop(vcc,skb);
 		else dev_kfree_skb(skb);
-		vcc->stats->tx_err++;
+		atomic_inc(&vcc->stats->tx_err);
 		return -ENOBUFS;
 	}
 	hdr = (void *) skb_put(new_skb,sizeof(struct atmtcp_hdr));
@@ -193,8 +207,8 @@
 	if (vcc->pop) vcc->pop(vcc,skb);
 	else dev_kfree_skb(skb);
 	out_vcc->push(out_vcc,new_skb);
-	vcc->stats->tx++;
-	out_vcc->stats->rx++;
+	atomic_inc(&vcc->stats->tx);
+	atomic_inc(&out_vcc->stats->rx);
 	return 0;
 }
 
@@ -251,7 +265,7 @@
 		    out_vcc->qos.rxtp.traffic_class != ATM_NONE)
 			break;
 	if (!out_vcc) {
-		vcc->stats->tx_err++;
+		atomic_inc(&vcc->stats->tx_err);
 		goto done;
 	}
 	skb_pull(skb,sizeof(struct atmtcp_hdr));
@@ -263,8 +277,8 @@
 	new_skb->stamp = xtime;
 	memcpy(skb_put(new_skb,skb->len),skb->data,skb->len);
 	out_vcc->push(out_vcc,new_skb);
-	vcc->stats->tx++;
-	out_vcc->stats->rx++;
+	atomic_inc(&vcc->stats->tx);
+	atomic_inc(&out_vcc->stats->rx);
 done:
 	if (vcc->pop) vcc->pop(vcc,skb);
 	else dev_kfree_skb(skb);
@@ -305,7 +319,7 @@
 	999,		/* dummy device number */
 	NULL,NULL,	/* pretend not to have any VCCs */
 	NULL,NULL,	/* no data */
-	0,		/* no flags */
+	{ 0 },		/* no flags */
 	NULL,		/* no local address */
 	{ 0 }		/* no ESI, no statistics */
 };
@@ -318,7 +332,7 @@
 
 	dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL);
 	if (!dev_data) return -ENOMEM;
-	dev = atm_dev_register(DEV_LABEL,&atmtcp_v_dev_ops,itf,0);
+	dev = atm_dev_register(DEV_LABEL,&atmtcp_v_dev_ops,itf,NULL);
 	if (!dev) {
 		kfree(dev_data);
 		return itf == -1 ? -ENOMEM : -EBUSY;
@@ -352,7 +366,8 @@
 	}
 	PRIV(dev)->vcc = vcc;
 	bind_vcc(vcc,&atmtcp_control_dev);
-	vcc->flags |= ATM_VF_READY | ATM_VF_META;
+	set_bit(ATM_VF_META,&vcc->flags);
+	set_bit(ATM_VF_READY,&vcc->flags);
 	vcc->dev_data = dev;
 	(void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */
 	vcc->stats = &atmtcp_control_dev.stats.aal5;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/eni.c linux/drivers/atm/eni.c
--- v2.3.99-pre2/linux/drivers/atm/eni.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/eni.c	Tue Mar 21 23:38:26 2000
@@ -19,8 +19,10 @@
 #include <linux/uio.h>
 #include <linux/init.h>
 #include <linux/atm_eni.h>
+#include <linux/bitops.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/string.h>
 #include <asm/byteorder.h>
@@ -526,7 +528,7 @@
 		DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
 		    vcc->dev->number);
 		length = 0;
-		vcc->stats->rx_err++;
+		atomic_inc(&vcc->stats->rx_err);
 	}
 	else {
 		length = ATM_CELL_SIZE-1; /* no HEC */
@@ -581,7 +583,7 @@
 			    size);
 		}
 		eff = length = 0;
-		vcc->stats->rx_err++;
+		atomic_inc(&vcc->stats->rx_err);
 	}
 	else {
 		size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
@@ -598,7 +600,7 @@
 			    "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n",
 			    vcc->dev->number,vcc->vci,length,size << 2,descr);
 			length = eff = 0;
-			vcc->stats->rx_err++;
+			atomic_inc(&vcc->stats->rx_err);
 		}
 	}
 	skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL;
@@ -663,6 +665,7 @@
 		if (rx_vcc(curr)) return;
 		eni_dev->fast = ENI_VCC(curr)->next;
 		ENI_VCC(curr)->next = ENI_VCC_NOS;
+		barrier();
 		ENI_VCC(curr)->servicing--;
 	}
 	while ((curr = eni_dev->slow)) {
@@ -670,6 +673,7 @@
 		if (rx_vcc(curr)) return;
 		eni_dev->slow = ENI_VCC(curr)->next;
 		ENI_VCC(curr)->next = ENI_VCC_NOS;
+		barrier();
 		ENI_VCC(curr)->servicing--;
 	}
 }
@@ -768,7 +772,7 @@
 			vcc->push(vcc,skb);
 			pushed++;
 		}
-		vcc->stats->rx++;
+		atomic_inc(&vcc->stats->rx);
 	}
 	wake_up(&eni_dev->rx_wait);
 }
@@ -836,10 +840,10 @@
 
 static void close_rx(struct atm_vcc *vcc)
 {
-	unsigned long here,flags;
+	DECLARE_WAITQUEUE(wait,current);
+	unsigned long here;
 	struct eni_dev *eni_dev;
 	struct eni_vcc *eni_vcc;
-	u32 tmp;
 
 	eni_vcc = ENI_VCC(vcc);
 	if (!eni_vcc->rx) return;
@@ -858,25 +862,41 @@
 		/* wait for RX queue to drain */
 		DPRINTK("eni_close: waiting for RX ...\n");
 		EVENT("RX closing\n",0,0);
-		save_flags(flags);
-		cli();
-		while (eni_vcc->rxing || eni_vcc->servicing) {
+		add_wait_queue(&eni_dev->rx_wait,&wait);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		barrier();
+		for (;;) {
+			/* transition service->rx: rxing++, servicing-- */
+			if (!eni_vcc->servicing) {
+				barrier();
+				if (!eni_vcc->rxing) break;
+			}
 			EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing,
 			    eni_vcc->servicing);
 			printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing,
 			    eni_vcc->rxing);
-			sleep_on(&eni_dev->rx_wait);
+			schedule();
+			set_current_state(TASK_UNINTERRUPTIBLE);
 		}
-		while (eni_vcc->rx_pos != (tmp =
-		     readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ)>>
-		     MID_VCI_READ_SHIFT) {
+		for (;;) {
+			unsigned long flags;
+			int at_end;
+			u32 tmp;
+
+			spin_lock_irqsave(&eni_dev->lock,flags);
+			tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ;
+			at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT;
+			spin_unlock_irqrestore(&eni_dev->lock,flags);
+			if (at_end) break;
 			EVENT("drain discard (host 0x%lx, nic 0x%lx)\n",
 			    eni_vcc->rx_pos,tmp);
 			printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n",
 			    eni_vcc->rx_pos,tmp);
-			sleep_on(&eni_dev->rx_wait);
+			schedule();
+			set_current_state(TASK_UNINTERRUPTIBLE);
 		}
-		restore_flags(flags);
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&eni_dev->rx_wait,&wait);
 	}
 	eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2);
 	eni_vcc->rx = NULL;
@@ -1054,8 +1074,9 @@
 	 * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-)
 	 */
 
+	aal5 = vcc->qos.aal == ATM_AAL5;
 	/* check space in buffer */
-	if (!(aal5 = vcc->qos.aal == ATM_AAL5))
+	if (!aal5)
 		size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE;
 			/* cell without HEC plus segmentation header (includes
 			   four-byte cell header) */
@@ -1207,7 +1228,7 @@
 		    PCI_DMA_TODEVICE);
 		if (vcc->pop) vcc->pop(vcc,skb);
 		else dev_kfree_skb_irq(skb);
-		vcc->stats->tx++;
+		atomic_inc(&vcc->stats->tx);
 		wake_up(&eni_dev->tx_wait);
 dma_complete++;
 	}
@@ -1366,34 +1387,40 @@
 
 static void close_tx(struct atm_vcc *vcc)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct eni_dev *eni_dev;
 	struct eni_vcc *eni_vcc;
-	unsigned long flags;
 
 	eni_vcc = ENI_VCC(vcc);
 	if (!eni_vcc->tx) return;
 	eni_dev = ENI_DEV(vcc->dev);
 	/* wait for TX queue to drain */
 	DPRINTK("eni_close: waiting for TX ...\n");
-	save_flags(flags);
-	cli();
-	while (skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing) {
+	add_wait_queue(&eni_dev->tx_wait,&wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	for (;;) {
+		unsigned long flags;
+		int txing;
+
+		spin_lock_irqsave(&eni_dev->lock,flags);
+		txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing;
+		spin_unlock_irqrestore(&eni_dev->lock,flags);
+		if (!txing) break;
 		DPRINTK("%d TX left\n",eni_vcc->txing);
-		sleep_on(&eni_dev->tx_wait);
-	}
-	/*
-	 * Looping a few times in here is probably far cheaper than keeping
-	 * track of TX completions all the time, so let's poll a bit ...
-	 */
-	while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) !=
-	    eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index)))
 		schedule();
-	restore_flags(flags);
-#if 0
-	if (skb_peek(&eni_vcc->tx->backlog))
-		printk(KERN_CRIT DEV_LABEL "SKBs in BACKLOG !!!\n");
-#endif
+		set_current_state(TASK_UNINTERRUPTIBLE);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&eni_dev->tx_wait,&wait);
 	if (eni_vcc->tx != eni_dev->ubr) {
+		/*
+		 * Looping a few times in here is probably far cheaper than
+		 * keeping track of TX completions all the time, so let's poll
+		 * a bit ...
+		 */
+		while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) !=
+		    eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index)))
+			schedule();
 		eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2);
 		eni_vcc->tx->send = 0;
 		eni_dev->tx_bw += eni_vcc->tx->reserved;
@@ -1496,28 +1523,36 @@
 		if (reason & MID_RX_DMA_COMPLETE) {
 			EVENT("INT: RX DMA complete, starting dequeue_rx\n",
 			    0,0);
+			spin_lock(&eni_dev->lock);
 			dequeue_rx(dev);
 			EVENT("dequeue_rx done, starting poll_rx\n",0,0);
 			poll_rx(dev);
+			spin_unlock(&eni_dev->lock);
 			EVENT("poll_rx done\n",0,0);
 			/* poll_tx ? */
 		}
 		if (reason & MID_SERVICE) {
 			EVENT("INT: service, starting get_service\n",0,0);
+			spin_lock(&eni_dev->lock);
 			get_service(dev);
 			EVENT("get_service done, starting poll_rx\n",0,0);
 			poll_rx(dev);
+			spin_unlock(&eni_dev->lock);
 			EVENT("poll_rx done\n",0,0);
 		}
  		if (reason & MID_TX_DMA_COMPLETE) {
 			EVENT("INT: TX DMA COMPLETE\n",0,0);
+			spin_lock(&eni_dev->lock);
 			dequeue_tx(dev);
+			spin_unlock(&eni_dev->lock);
 		}
 		if (reason & MID_TX_COMPLETE) {
 			EVENT("INT: TX COMPLETE\n",0,0);
 tx_complete++;
-			wake_up(&eni_dev->tx_wait);
+			spin_lock(&eni_dev->lock);
 			poll_tx(dev);
+			spin_unlock(&eni_dev->lock);
+			wake_up(&eni_dev->tx_wait);
 			/* poll_rx ? */
 		}
 		if (reason & (MID_STAT_OVFL | MID_SUNI_INT | MID_DMA_ERR_ACK |
@@ -1532,7 +1567,7 @@
 /*--------------------------------- entries ---------------------------------*/
 
 
-static const char *media_name[] __initdata = {
+static const char *media_name[] __devinitdata = {
     "MMF", "SMF", "MMF", "03?", /*  0- 3 */
     "UTP", "05?", "06?", "07?", /*  4- 7 */
     "TAXI","09?", "10?", "11?", /*  8-11 */
@@ -1556,7 +1591,7 @@
   } })
 
 
-static int __init get_esi_asic(struct atm_dev *dev)
+static int __devinit get_esi_asic(struct atm_dev *dev)
 {
 	struct eni_dev *eni_dev;
 	unsigned char tonga;
@@ -1648,7 +1683,7 @@
 #undef GET_SEPROM
 
 
-static int __init get_esi_fpga(struct atm_dev *dev,unsigned long base)
+static int __devinit get_esi_fpga(struct atm_dev *dev,unsigned long base)
 {
 	unsigned long mac_base;
 	int i;
@@ -1659,7 +1694,7 @@
 }
 
 
-static int __init eni_init(struct atm_dev *dev)
+static int __devinit eni_do_init(struct atm_dev *dev)
 {
 	struct midway_eprom *eprom;
 	struct eni_dev *eni_dev;
@@ -1748,7 +1783,7 @@
 }
 
 
-static int __init eni_start(struct atm_dev *dev)
+static int __devinit eni_start(struct atm_dev *dev)
 {
 	struct eni_dev *eni_dev;
 	unsigned long buf,buffer_mem;
@@ -1788,6 +1823,7 @@
 	DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n",
 	     eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma,
 	     eni_dev->service,buf);
+	spin_lock_init(&eni_dev->lock);
 	/* initialize memory management */
 	buffer_mem = eni_dev->mem-(buf-eni_dev->ram);
 	eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2;
@@ -1830,14 +1866,14 @@
 {
 	DPRINTK(">eni_close\n");
 	if (!ENI_VCC(vcc)) return;
-	vcc->flags &= ~ATM_VF_READY;
+	clear_bit(ATM_VF_READY,&vcc->flags);
 	close_rx(vcc);
 	close_tx(vcc);
 	DPRINTK("eni_close: done waiting\n");
 	/* deallocate memory */
 	kfree(ENI_VCC(vcc));
 	ENI_VCC(vcc) = NULL;
-	vcc->flags &= ~ATM_VF_ADDR;
+	clear_bit(ATM_VF_ADDR,&vcc->flags);
 	/*foo();*/
 }
 
@@ -1855,8 +1891,8 @@
 			if (vcc->qos.txtp.traffic_class != ATM_NONE) {
 				for (walk = vcc->dev->vccs; walk;
 				    walk = walk->next)
-					if ((walk->flags & ATM_VF_ADDR) &&
-					    walk->vci == *vci &&
+					if (test_bit(ATM_VF_ADDR,&walk->flags)
+					    && walk->vci == *vci &&
 					    walk->qos.txtp.traffic_class !=
 					    ATM_NONE)
 						break;
@@ -1872,7 +1908,7 @@
 		return -EADDRINUSE;
 	if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
 	for (walk = vcc->dev->vccs; walk; walk = walk->next)
-		if ((walk->flags & ATM_VF_ADDR) && walk->vci == *vci &&
+		if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vci == *vci &&
 		    walk->qos.txtp.traffic_class != ATM_NONE)
 			return -EADDRINUSE;
 	return 0;
@@ -1887,19 +1923,19 @@
 
 	DPRINTK(">eni_open\n");
 	EVENT("eni_open\n",0,0);
-	if (!(vcc->flags & ATM_VF_PARTIAL)) ENI_VCC(vcc) = NULL;
+	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) ENI_VCC(vcc) = NULL;
 	eni_dev = ENI_DEV(vcc->dev);
 	error = get_ci(vcc,&vpi,&vci);
 	if (error) return error;
 	vcc->vpi = vpi;
 	vcc->vci = vci;
 	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
-		vcc->flags |= ATM_VF_ADDR;
+		set_bit(ATM_VF_ADDR,&vcc->flags);
 	if (vcc->qos.aal != ATM_AAL0 && vcc->qos.aal != ATM_AAL5)
 		return -EINVAL;
 	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
 	    vcc->vci);
-	if (!(vcc->flags & ATM_VF_PARTIAL)) {
+	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
 		eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL);
 		if (!eni_vcc) return -ENOMEM;
 		ENI_VCC(vcc) = eni_vcc;
@@ -1922,7 +1958,7 @@
 		eni_close(vcc);
 		return error;
 	}
-	vcc->flags |= ATM_VF_READY;
+	set_bit(ATM_VF_READY,&vcc->flags);
 	/* should power down SUNI while !ref_count @@@ */
 	return 0;
 }
@@ -1953,8 +1989,7 @@
 	 * Walk through the send buffer and patch the rate information in all
 	 * segmentation buffer descriptors of this VCC.
 	 */
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&eni_dev->lock,flags);
 	for (skb = eni_dev->tx_queue.next; skb !=
 	    (struct sk_buff *) &eni_dev->tx_queue; skb = skb->next) {
 		unsigned long dsc;
@@ -1965,7 +2000,7 @@
 		    (tx->prescaler << MID_SEG_PR_SHIFT) |
 		    (tx->resolution << MID_SEG_RATE_SHIFT), dsc);
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&eni_dev->lock,flags);
 	return 0;
 }
 
@@ -2049,14 +2084,13 @@
 	}
 submitted++;
 	ATM_SKB(skb)->vcc = vcc;
-	save_flags(flags);
-	cli(); /* brute force */
+	spin_lock_irqsave(&ENI_DEV(vcc->dev)->lock,flags); /* brute force */
 	if (skb_peek(&ENI_VCC(vcc)->tx->backlog) || do_tx(skb)) {
 		skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb);
 		ENI_VCC(vcc)->tx->backlog_len++;
 backlogged++;
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&ENI_DEV(vcc->dev)->lock,flags);
 	return 0;
 }
 
@@ -2206,79 +2240,86 @@
 };
 
 
-int __init eni_detect(void)
+static int __devinit eni_init_one(struct pci_dev *pci_dev,
+    const struct pci_device_id *ent)
 {
 	struct atm_dev *dev;
 	struct eni_dev *eni_dev;
-	int devs,type;
-	struct sk_buff *skb;
+	int error = -ENOMEM;
 
-	DPRINTK("eni_detect\n");
-	if (sizeof(skb->cb) < sizeof(struct eni_skb_prv)) {
-		printk(KERN_ERR "eni_detect: skb->cb is too small (%d < %d)\n",
-		    sizeof(skb->cb),sizeof(struct eni_skb_prv));
-		return 0;
-	}
-	eni_dev = (struct eni_dev *) kmalloc(sizeof(struct eni_dev),
-	    GFP_KERNEL);
+	DPRINTK("eni_init_one\n");
+	eni_dev = (struct eni_dev *) kmalloc(sizeof(struct eni_dev),GFP_KERNEL);
 	if (!eni_dev) return -ENOMEM;
-	devs = 0;
-	for (type = 0; type < 2; type++) {
-		struct pci_dev *pci_dev;
-
-		pci_dev = NULL;
-		while ((pci_dev = pci_find_device(PCI_VENDOR_ID_EF,type ?
-		    PCI_DEVICE_ID_EF_ATM_ASIC : PCI_DEVICE_ID_EF_ATM_FPGA,
-		    pci_dev))) {
-			if (!devs) {
-				cpu_zeroes = pci_alloc_consistent(pci_dev,
-				    ENI_ZEROES_SIZE,&zeroes);
-				if (!cpu_zeroes) {
-					kfree(eni_dev);
-					return -ENOMEM;
-				}
-			}
-			dev = atm_dev_register(DEV_LABEL,&ops,-1,0);
-			if (!dev) break;
-			eni_dev->pci_dev = pci_dev;
-			ENI_DEV(dev) = eni_dev;
-			eni_dev->asic = type;
-			if (eni_init(dev) || eni_start(dev)) {
-				atm_dev_deregister(dev);
-				break;
-			}
-			eni_dev->more = eni_boards;
-			eni_boards = dev;
-			devs++;
-			eni_dev = (struct eni_dev *) kmalloc(sizeof(struct
-			    eni_dev),GFP_KERNEL);
-			if (!eni_dev) break;
-		}
-	}
-	if (!devs && cpu_zeroes) {
-		pci_free_consistent(eni_dev->pci_dev,ENI_ZEROES_SIZE,
-		    cpu_zeroes,zeroes);
-		cpu_zeroes = NULL;
-	}
+	if (!cpu_zeroes) {
+		cpu_zeroes = pci_alloc_consistent(pci_dev,ENI_ZEROES_SIZE,
+		    &zeroes);
+		if (!cpu_zeroes) goto out1;
+	}
+	dev = atm_dev_register(DEV_LABEL,&ops,-1,NULL);
+	if (!dev) goto out2;
+	pci_dev->driver_data = dev;
+	eni_dev->pci_dev = pci_dev;
+	ENI_DEV(dev) = eni_dev;
+	eni_dev->asic = ent->driver_data;
+	error = eni_do_init(dev);
+	if (error) goto out3;
+	error = eni_start(dev);
+	if (error) goto out3;
+	eni_dev->more = eni_boards;
+	eni_boards = dev;
+	MOD_INC_USE_COUNT; /* @@@ we don't support unloading yet */
+	return 0;
+out3:
+	atm_dev_deregister(dev);
+out2:
+	pci_free_consistent(eni_dev->pci_dev,ENI_ZEROES_SIZE,cpu_zeroes,zeroes);
+	cpu_zeroes = NULL;
+out1:
 	kfree(eni_dev);
-	return devs;
+	return error;
 }
 
 
-#ifdef MODULE
+static struct pci_device_id eni_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_EF, PCI_DEVICE_ID_EF_ATM_FPGA, PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, 0 /* FPGA */ },
+	{ PCI_VENDOR_ID_EF, PCI_DEVICE_ID_EF_ATM_ASIC, PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, 1 /* ASIC */ },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci,eni_pci_tbl);
+
 
-int init_module(void)
+static void __devexit eni_remove_one(struct pci_dev *pci_dev)
 {
-	if (!eni_detect()) {
-		printk(KERN_ERR DEV_LABEL ": no adapter found\n");
-		return -ENXIO;
+	/* grrr */
+}
+
+
+static struct pci_driver eni_driver = {
+	name:		DEV_LABEL,
+	id_table:	eni_pci_tbl,
+	probe:		eni_init_one,
+	remove:		eni_remove_one,
+};
+
+
+static int __init eni_init(void)
+{
+	struct sk_buff *skb; /* dummy for sizeof */
+
+	if (sizeof(skb->cb) < sizeof(struct eni_skb_prv)) {
+		printk(KERN_ERR "eni_detect: skb->cb is too small (%d < %d)\n",
+		    sizeof(skb->cb),sizeof(struct eni_skb_prv));
+		return -EIO;
 	}
-	MOD_INC_USE_COUNT;
-	return 0;
+	if (pci_register_driver(&eni_driver) > 0) return 0;
+	pci_unregister_driver (&eni_driver);
+	return -ENODEV;
 }
 
 
-void cleanup_module(void)
+static void __exit eni_cleanup(void)
 {
 	/*
 	 * Well, there's no way to get rid of the driver yet, so we don't
@@ -2286,4 +2327,6 @@
 	 */
 }
 
-#endif
+
+module_init(eni_init);
+module_exit(eni_cleanup);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/eni.h linux/drivers/atm/eni.h
--- v2.3.99-pre2/linux/drivers/atm/eni.h	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/eni.h	Tue Mar 21 23:38:26 2000
@@ -12,6 +12,7 @@
 #include <linux/skbuff.h>
 #include <linux/time.h>
 #include <linux/pci.h>
+#include <linux/spinlock.h>
 
 #include "midway.h"
 
@@ -65,6 +66,8 @@
 };
 
 struct eni_dev {
+	/*-------------------------------- spinlock */
+	spinlock_t lock;		/* sync with interrupt */
 	/*-------------------------------- base pointers into Midway address
 					   space */
 	unsigned long phy;		/* PHY interface chip registers */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/fore200e.c linux/drivers/atm/fore200e.c
--- v2.3.99-pre2/linux/drivers/atm/fore200e.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/fore200e.c	Tue Mar 21 23:38:26 2000
@@ -1,5 +1,5 @@
 /*
-  $Id: fore200e.c,v 1.1 2000/02/21 16:04:31 davem Exp $
+  $Id: fore200e.c,v 1.2 2000/03/21 21:19:24 davem Exp $
 
   A FORE Systems 200E-series driver for ATM on Linux.
   Christophe Lizzi (lizzi@cnam.fr), October 1999-February 2000.
@@ -35,6 +35,7 @@
 #include <linux/atmdev.h>
 #include <linux/sonet.h>
 #include <linux/atm_suni.h>
+#include <linux/bitops.h>
 #include <asm/io.h>
 #include <asm/string.h>
 #include <asm/segment.h>
@@ -43,6 +44,7 @@
 #include <asm/dma.h>
 #include <asm/byteorder.h>
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 
 #ifdef CONFIG_ATM_FORE200E_PCA
 #include <linux/pci.h>
@@ -67,7 +69,7 @@
 #define FORE200E_52BYTE_AAL0_SDU
 #endif
 
-#define FORE200E_VERSION "0.2a"
+#define FORE200E_VERSION "0.2b"
 
 
 #define FORE200E         "fore200e: "
@@ -187,10 +189,10 @@
 
 
 /* allocate and align a chunk of memory intended to hold the data behing exchanged
-   between the driver and the adapter (using streaming DVMA on SBUS hosts) */
+   between the driver and the adapter (using streaming DVMA) */
 
 static int
-fore200e_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, int size, int alignment)
+fore200e_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, int size, int alignment, int direction)
 {
     unsigned long offset = 0;
 
@@ -199,6 +201,7 @@
 
     chunk->alloc_size = size + alignment;
     chunk->align_size = size;
+    chunk->direction  = direction;
 
     chunk->alloc_addr = fore200e_kmalloc(chunk->alloc_size, GFP_KERNEL | GFP_DMA);
     if (chunk->alloc_addr == NULL)
@@ -209,7 +212,7 @@
     
     chunk->align_addr = chunk->alloc_addr + offset;
 
-    chunk->dma_addr = fore200e->bus->dma_map(fore200e, chunk->align_addr, chunk->align_size);
+    chunk->dma_addr = fore200e->bus->dma_map(fore200e, chunk->align_addr, chunk->align_size, direction);
     
     return 0;
 }
@@ -220,7 +223,7 @@
 static void
 fore200e_chunk_free(struct fore200e* fore200e, struct chunk* chunk)
 {
-    fore200e->bus->dma_unmap(fore200e, chunk->dma_addr, chunk->dma_size);
+    fore200e->bus->dma_unmap(fore200e, chunk->dma_addr, chunk->dma_size, chunk->direction);
 
     fore200e_kfree(chunk->alloc_addr);
 }
@@ -463,34 +466,32 @@
 
 
 static u32
-fore200e_pca_dma_map(struct fore200e* fore200e, void* virt_addr, int size)
+fore200e_pca_dma_map(struct fore200e* fore200e, void* virt_addr, int size, int direction)
 {
-    u32 dma_addr = pci_map_single((struct pci_dev*)fore200e->bus_dev, virt_addr, size, PCI_DMA_BIDIRECTIONAL);
+    u32 dma_addr = pci_map_single((struct pci_dev*)fore200e->bus_dev, virt_addr, size, direction);
 
-    DPRINTK(3, "PCI DVMA mapping: virt_addr = 0x%p, size = %d --> dma_addr = 0x%08x\n",
-	    virt_addr, size, dma_addr);
+    DPRINTK(3, "PCI DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d,  --> dma_addr = 0x%08x\n",
+	    virt_addr, size, direction, dma_addr);
     
     return dma_addr;
 }
 
 
 static void
-fore200e_pca_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size)
+fore200e_pca_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
 {
-    DPRINTK(3, "PCI DVMA unmapping: dma_addr = 0x%08x, size = %d\n", dma_addr, size);
+    DPRINTK(3, "PCI DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
 
-    pci_unmap_single((struct pci_dev*)fore200e->bus_dev, dma_addr, size,
-      PCI_DMA_BIDIRECTIONAL);
+    pci_unmap_single((struct pci_dev*)fore200e->bus_dev, dma_addr, size, direction);
 }
 
 
 static void
-fore200e_pca_dma_sync(struct fore200e* fore200e, u32 dma_addr, int size)
+fore200e_pca_dma_sync(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
 {
-    DPRINTK(3, "PCI DVMA sync: dma_addr = 0x%08x, size = %d\n", dma_addr, size);
+    DPRINTK(3, "PCI DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
 
-    pci_dma_sync_single((struct pci_dev*)fore200e->bus_dev, dma_addr, size,
-      PCI_DMA_BIDIRECTIONAL);
+    pci_dma_sync_single((struct pci_dev*)fore200e->bus_dev, dma_addr, size, direction);
 }
 
 
@@ -511,10 +512,8 @@
 
     chunk->align_addr = chunk->alloc_addr;
 #else
-    if (fore200e_chunk_alloc(fore200e, chunk, size * nbr, alignment) < 0)
+    if (fore200e_chunk_alloc(fore200e, chunk, size * nbr, alignment, FORE200E_DMA_BIDIRECTIONAL) < 0)
 	return -ENOMEM;
-    
-    chunk->dma_addr = fore200e_pca_dma_map(fore200e, chunk->align_addr, chunk->align_size);
 #endif
     
     return 0;
@@ -532,8 +531,6 @@
 			chunk->alloc_addr,
 			chunk->dma_addr);
 #else
-    fore200e_pca_dma_unmap(fore200e, chunk->dma_addr, chunk->dma_size);
-    
     fore200e_chunk_free(fore200e, chunk);
 #endif
 }
@@ -685,7 +682,7 @@
     opcode.opcode = OPCODE_GET_PROM;
     opcode.pad    = 0;
 
-    prom_dma = fore200e->bus->dma_map(fore200e, prom, sizeof(struct prom_data));
+    prom_dma = fore200e->bus->dma_map(fore200e, prom, sizeof(struct prom_data), FORE200E_DMA_FROMDEVICE);
 
     fore200e->bus->write(prom_dma, &entry->cp_entry->cmd.prom_block.prom_haddr);
     
@@ -697,7 +694,7 @@
 
     *entry->status = STATUS_FREE;
 
-    fore200e->bus->dma_unmap(fore200e, prom_dma, sizeof(struct prom_data));
+    fore200e->bus->dma_unmap(fore200e, prom_dma, sizeof(struct prom_data), FORE200E_DMA_FROMDEVICE);
 
     if (ok == 0) {
 	printk(FORE200E "unable to get PROM data from device %s\n", fore200e->name);
@@ -748,31 +745,32 @@
 
 
 static u32
-fore200e_sba_dma_map(struct fore200e* fore200e, void* virt_addr, int size)
+fore200e_sba_dma_map(struct fore200e* fore200e, void* virt_addr, int size, int direction)
 {
-    u32 dma_addr = sbus_map_single((struct sbus_dev*)fore200e->bus_dev, virt_addr, size);
+    u32 dma_addr = sbus_map_single((struct sbus_dev*)fore200e->bus_dev, virt_addr, size, direction);
 
-    DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d --> dma_addr = 0x%08x\n", virt_addr, size, dma_addr);
+    DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d --> dma_addr = 0x%08x\n",
+	    virt_addr, size, direction, dma_addr);
     
     return dma_addr;
 }
 
 
 static void
-fore200e_sba_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size)
+fore200e_sba_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
 {
-    DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d\n", dma_addr, size);
+    DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n", dma_addr, size, direction);
 
-    sbus_unmap_single((struct sbus_dev*)fore200e->bus_dev, dma_addr, size);
+    sbus_unmap_single((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction);
 }
 
 
 static void
-fore200e_sba_dma_sync(struct fore200e* fore200e, u32 dma_addr, int size)
+fore200e_sba_dma_sync(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
 {
-    DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d\n", dma_addr, size);
+    DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
     
-    sbus_dma_sync_single((struct sbus_dev*)fore200e->bus_dev, dma_addr, size);
+    sbus_dma_sync_single((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction);
 }
 
 
@@ -1002,7 +1000,8 @@
 		kfree(entry->data);
 
 	    /* remove DMA mapping */
-	    fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length);
+	    fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
+				     FORE200E_DMA_TODEVICE);
 
 	    /* notify tx completion */
 	    if (entry->vcc->pop)
@@ -1012,9 +1011,9 @@
 
 	    /* check error condition */
 	    if (*entry->status & STATUS_ERROR)
-		entry->vcc->stats->tx_err++;
+		atomic_inc(&entry->vcc->stats->tx_err);
 	    else
-		entry->vcc->stats->tx++;
+		atomic_inc(&entry->vcc->stats->tx);
 
 	    *entry->status = STATUS_FREE;
 	    
@@ -1127,7 +1126,7 @@
     if (skb == NULL) {
 	
 	printk(FORE200E "unable to alloc new skb, rx PDU length = %d\n", pdu_len);
-	vcc->stats->rx_drop++;
+	atomic_inc(&vcc->stats->rx_drop);
 	return;
     } 
 
@@ -1146,7 +1145,7 @@
 	buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle);
 	
 	/* ensure DMA synchronisation */
-	    fore200e->bus->dma_sync(fore200e, buffer->data.dma_addr, rpd->rsd[ i ].length);
+	fore200e->bus->dma_sync(fore200e, buffer->data.dma_addr, rpd->rsd[ i ].length, FORE200E_DMA_FROMDEVICE);
 	
 	memcpy(skb_put(skb, rpd->rsd[ i ].length), buffer->data.align_addr, rpd->rsd[ i ].length);
     }
@@ -1169,7 +1168,7 @@
     }
 
     vcc->push(vcc, skb);
-    vcc->stats->rx++;
+    atomic_inc(&vcc->stats->rx);
 }
 
 
@@ -1404,7 +1403,7 @@
     if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC)
 	return 0;
 
-    vcc->flags |= ATM_VF_ADDR;
+    set_bit(ATM_VF_ADDR,&vcc->flags);
     vcc->itf    = vcc->dev->number;
 
     DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
@@ -1467,7 +1466,7 @@
     fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = 65536;
     fore200e_vcc->tx_max_pdu = fore200e_vcc->rx_max_pdu = 0;
     
-    vcc->flags |= ATM_VF_READY;
+    clear_bit(ATM_VF_READY,&vcc->flags);
     return 0;
 }
 
@@ -1552,11 +1551,14 @@
 	    if(--retry > 0)
 		goto retry_here;
 	    
-	    vcc->stats->tx_err++;
+	    atomic_inc(&vcc->stats->tx_err);
 	    
 	    printk(FORE200E "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n",
 		   fore200e->name, fore200e->cp_queues->heartbeat);
-	    
+	    if (vcc->pop)
+		vcc->pop(vcc, skb);
+	    else
+		dev_kfree_skb(skb);
 	    return -EIO;
 	}
     }
@@ -1584,6 +1586,10 @@
 	if (entry->data == NULL) {
 	    
 	    spin_unlock_irqrestore(&fore200e->tx_lock, flags);
+	    if (vcc->pop)
+		vcc->pop(vcc, skb);
+	    else
+		dev_kfree_skb(skb);
 	    return -ENOMEM;
 	}
 
@@ -1591,11 +1597,11 @@
 	if (skb_len < tx_len)
 	    memset(entry->data + skb_len, 0x00, tx_len - skb_len);
 	
-	tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, entry->data, tx_len);
+	tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, entry->data, tx_len, FORE200E_DMA_TODEVICE);
     }
     else {
 	entry->data = NULL;
-	tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, skb_data, tx_len);
+	tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, skb_data, tx_len, FORE200E_DMA_TODEVICE);
     }
 
     tpd->tsd[ 0 ].length = tx_len;
@@ -1606,7 +1612,7 @@
     spin_unlock_irqrestore(&fore200e->tx_lock, flags);
     
     /* ensure DMA synchronisation */
-	fore200e->bus->dma_sync(fore200e, tpd->tsd[ 0 ].buffer, tpd->tsd[ 0 ].length);
+	fore200e->bus->dma_sync(fore200e, tpd->tsd[ 0 ].buffer, tpd->tsd[ 0 ].length, FORE200E_DMA_TODEVICE);
     
     DPRINTK(3, "tx on %d.%d.%d:%d, len = %u (%u)\n", 
 	    vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
@@ -1661,27 +1667,29 @@
     {
 	int ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 10);
 
-	fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length);
+	fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
+				 FORE200E_DMA_TODEVICE);
 	
-	if (ok == 0) {
-	    printk(FORE200E "synchronous tx on %d:%d:%d failed\n", vcc->itf, vcc->vpi, vcc->vci);
-
-	    entry->vcc->stats->tx_err++;
-	    return -EIO;
-	}
-	entry->vcc->stats->tx++;
-
-	DPRINTK(3, "synchronous tx on %d:%d:%d succeeded\n", vcc->itf, vcc->vpi, vcc->vci);
-
 	/* free tmp copy of misaligned data */
 	if (entry->data)
 	    kfree(entry->data);
-	
+
 	/* notify tx completion */
 	if (vcc->pop)
 	    vcc->pop(vcc, skb);
 	else
 	    dev_kfree_skb(skb);
+
+	if (ok == 0) {
+	    printk(FORE200E "synchronous tx on %d:%d:%d failed\n", vcc->itf, vcc->vpi, vcc->vci);
+
+	    atomic_inc(&entry->vcc->stats->tx_err);
+	    return -EIO;
+	}
+	atomic_inc(&entry->vcc->stats->tx);
+
+	DPRINTK(3, "synchronous tx on %d:%d:%d succeeded\n", vcc->itf, vcc->vpi, vcc->vci);
+
     }
 #endif
 
@@ -1704,7 +1712,7 @@
 	    return -ENOMEM;
     }
     
-    stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats, sizeof(struct stats));
+    stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats, sizeof(struct stats), FORE200E_DMA_FROMDEVICE);
     
     FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
 
@@ -1721,7 +1729,7 @@
 
     *entry->status = STATUS_FREE;
 
-    fore200e->bus->dma_unmap(fore200e, stats_dma_addr, sizeof(struct stats));
+    fore200e->bus->dma_unmap(fore200e, stats_dma_addr, sizeof(struct stats), FORE200E_DMA_FROMDEVICE);
     
     if (ok == 0) {
 	printk(FORE200E "unable to get statistics from device %s\n", fore200e->name);
@@ -1766,7 +1774,7 @@
     int                     ok;
     u32                     oc3_regs_dma_addr;
 
-    oc3_regs_dma_addr = fore200e->bus->dma_map(fore200e, regs, sizeof(struct oc3_regs));
+    oc3_regs_dma_addr = fore200e->bus->dma_map(fore200e, regs, sizeof(struct oc3_regs), FORE200E_DMA_FROMDEVICE);
 
     FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
 
@@ -1785,7 +1793,7 @@
 
     *entry->status = STATUS_FREE;
 
-    fore200e->bus_dma_unmap(fore200e, oc3_regs_dma_addr, sizeof(struct oc3_regs));
+    fore200e->bus->dma_unmap(fore200e, oc3_regs_dma_addr, sizeof(struct oc3_regs), FORE200E_DMA_FROMDEVICE);
     
     if (ok == 0) {
 	printk(FORE200E "unable to get OC-3 regs of device %s\n", fore200e->name);
@@ -1842,16 +1850,16 @@
     
     switch (loop_mode) {
 
-    case SUNI_LM_NONE:
+    case ATM_LM_NONE:
 	mct_value = 0; 
 	mct_mask  = SUNI_MCT_DLE | SUNI_MCT_LLE;
 	break;
 	
-    case SUNI_LM_DIAG:
+    case ATM_LM_LOC_PHY:
 	mct_value = mct_mask = SUNI_MCT_DLE;
 	break;
 
-    case SUNI_LM_LOOP:
+    case ATM_LM_RMT_PHY:
 	mct_value = mct_mask = SUNI_MCT_LLE;
 	break;
 
@@ -1921,12 +1929,16 @@
 
     case SONET_GETDIAG:
 	return put_user(0, (int*)arg) ? -EFAULT : 0;
-	
-    case SUNI_SETLOOP:
+
+    case ATM_SETLOOP:
 	return fore200e_setloop(fore200e, (int)(unsigned long)arg);
 
-    case SUNI_GETLOOP:
+    case ATM_GETLOOP:
 	return put_user(fore200e->loop_mode, (int*)arg) ? -EFAULT : 0;
+
+    case ATM_QUERYLOOP:
+	return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY, (int*)arg) ?
+	    -EFAULT : 0;
     }
 
     return -ENOSYS; /* not implemented */
@@ -1967,7 +1979,7 @@
 	/* update rate control parameters */
 	fore200e_rate_ctrl(qos, &fore200e_vcc->rate);
 
-	vcc->flags |= ATM_VF_HASQOS;
+	set_bit(ATM_VF_HASQOS,&vcc->flags);
 	return 0;
     }
     
@@ -2051,7 +2063,8 @@
 
 		/* allocate the receive buffer body */
 		if (fore200e_chunk_alloc(fore200e,
-					 &buffer[ i ].data, size, fore200e->bus->buffer_alignment) < 0) {
+					 &buffer[ i ].data, size, fore200e->bus->buffer_alignment,
+					 FORE200E_DMA_FROMDEVICE) < 0) {
 		    
 		    while (i > 0)
 			fore200e_chunk_free(fore200e, &buffer[ --i ].data);
@@ -2485,7 +2498,8 @@
 
     DPRINTK(2, "device %s being registered\n", fore200e->name);
 
-    atm_dev = atm_dev_register(fore200e->bus->proc_name, &fore200e_ops, -1, 0); 
+    atm_dev = atm_dev_register(fore200e->bus->proc_name, &fore200e_ops, -1,
+      NULL); 
     if (atm_dev == NULL) {
 	printk(FORE200E "unable to register device %s\n", fore200e->name);
 	return -ENODEV;
@@ -2690,17 +2704,29 @@
 	static const char* oc3_mode[] = {
 	    "normal operation",
 	    "diagnostic loopback",
-	    "line loopback"
+	    "line loopback",
+	    "unknown"
 	};
 
 	u32 fw_release     = fore200e->bus->read(&fore200e->cp_queues->fw_release);
 	u32 mon960_release = fore200e->bus->read(&fore200e->cp_queues->mon960_release);
 	u32 oc3_revision   = fore200e->bus->read(&fore200e->cp_queues->oc3_revision);
 	u32 media_index    = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type));
+	u32 oc3_index;
 
 	if (media_index < 0 || media_index > 4)
 	    media_index = 5;
 	
+	switch(fore200e->loop_mode) {
+	    case ATM_LM_NONE:    oc3_index = 0;
+		                 break;
+	    case ATM_LM_LOC_PHY: oc3_index = 1;
+		                 break;
+	    case ATM_LM_RMT_PHY: oc3_index = 2;
+		                 break;
+	    default:             oc3_index = 3;
+	}
+
 	return sprintf(page,
 		       "   firmware release:\t\t%d.%d.%d\n"
 		       "   monitor release:\t\t%d.%d\n"
@@ -2711,7 +2737,7 @@
 		       mon960_release >> 16, mon960_release << 16 >> 16,
 		       media_name[ media_index ],
 		       oc3_revision,
-		       oc3_mode[ fore200e->loop_mode ]);
+		       oc3_mode[ oc3_index ]);
     }
 
     if (!left--) {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/fore200e.h linux/drivers/atm/fore200e.h
--- v2.3.99-pre2/linux/drivers/atm/fore200e.h	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/fore200e.h	Wed Mar 22 22:22:05 2000
@@ -2,6 +2,7 @@
 #define _FORE200E_H
 
 #ifdef __KERNEL__
+#include <linux/config.h>
 
 /* rx buffer sizes */
 
@@ -559,6 +560,7 @@
     void* alloc_addr;    /* base address of allocated chunk */
     void* align_addr;    /* base address of aligned chunk   */
     u32   dma_addr;      /* DMA address of aligned chunk    */
+    int   direction;     /* direction of DMA mapping        */
     u32   alloc_size;    /* length of allocated chunk       */
     u32   align_size;    /* length of aligned chunk         */
 } chunk_t;
@@ -796,9 +798,9 @@
     const unsigned int*  fw_size;             /* address of firmware data size          */
     u32                  (*read)(volatile u32*);
     void                 (*write)(u32, volatile u32*);
-    u32                  (*dma_map)(struct fore200e*, void*, int);
-    void                 (*dma_unmap)(struct fore200e*, u32, int);
-    void                 (*dma_sync)(struct fore200e*, u32, int);
+    u32                  (*dma_map)(struct fore200e*, void*, int, int);
+    void                 (*dma_unmap)(struct fore200e*, u32, int, int);
+    void                 (*dma_sync)(struct fore200e*, u32, int, int);
     int                  (*dma_chunk_alloc)(struct fore200e*, struct chunk*, int, int, int);
     void                 (*dma_chunk_free)(struct fore200e*, struct chunk*);
     struct fore200e*     (*detect)(const struct fore200e_bus*, int);
@@ -812,6 +814,31 @@
     void                 (*irq_ack)(struct fore200e*);
     int                  (*proc_read)(struct fore200e*, char*);
 } fore200e_bus_t;
+
+
+#if defined(CONFIG_ATM_FORE200E_SBA)
+#  if defined(CONFIG_ATM_FORE200E_PCA)
+#    if (PCI_DMA_BIDIRECTIONAL == SBUS_DMA_BIDIRECTIONAL) && \
+        (PCI_DMA_TODEVICE      == SBUS_DMA_TODEVICE)      && \
+        (PCI_DMA_FROMDEVICE    == SBUS_DMA_FROMDEVICE)
+#      define FORE200E_DMA_BIDIRECTIONAL PCI_DMA_BIDIRECTIONAL
+#      define FORE200E_DMA_TODEVICE      PCI_DMA_TODEVICE
+#      define FORE200E_DMA_FROMDEVICE    PCI_DMA_FROMDEVICE
+#    else
+       /* in that case, we'll need to add an extra indirection, e.g.
+	  fore200e->bus->dma_direction[ fore200e_dma_direction ] */
+#      error PCI and SBUS DMA direction flags differ!
+#    endif
+#  else
+#    define FORE200E_DMA_BIDIRECTIONAL SBA_DMA_BIDIRECTIONAL
+#    define FORE200E_DMA_TODEVICE      SBA_DMA_TODEVICE
+#    define FORE200E_DMA_FROMDEVICE    SBA_DMA_FROMDEVICE
+#  endif
+#else
+#  define FORE200E_DMA_BIDIRECTIONAL PCI_DMA_BIDIRECTIONAL
+#  define FORE200E_DMA_TODEVICE      PCI_DMA_TODEVICE
+#  define FORE200E_DMA_FROMDEVICE    PCI_DMA_FROMDEVICE
+#endif
 
 
 /* per-device data */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/horizon.c linux/drivers/atm/horizon.c
--- v2.3.99-pre2/linux/drivers/atm/horizon.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/horizon.c	Tue Mar 21 23:38:26 2000
@@ -42,6 +42,7 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/string.h>
 #include <asm/byteorder.h>
@@ -50,7 +51,7 @@
 
 #define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
 #define description_string "Madge ATM Horizon [Ultra] driver"
-#define version_string "1.2"
+#define version_string "1.2.1"
 
 static inline void __init show_version (void) {
   printk ("%s version %s\n", description_string, version_string);
@@ -246,7 +247,7 @@
   Atomic test and set tx_busy until we succeed; we should implement
   some sort of timeout so that tx_busy will never be stuck at true.
   
-  If no TX channel is setup for this VC we wait for an idle one (if
+  If no TX channel is set up for this VC we wait for an idle one (if
   necessary) and set it up.
   
   At this point we have a TX channel ready for use. We wait for enough
@@ -276,7 +277,7 @@
   available handler is locked out over the same period.
   
   Data available on the card triggers an interrupt. If the data is not
-  suitable for out existing RX channels or we cannot allocate a buffer
+  suitable for our existing RX channels or we cannot allocate a buffer
   it is flushed. Otherwise an RX receive is scheduled. Multiple RX
   transfers may be scheduled for the same frame.
   
@@ -321,7 +322,7 @@
   and the frame continues to be received.
   
   The solution is to make sure any received frames are flushed when
-  ready. This is currently done just before the solution to 3.
+  ready. This is currently done just before the solution to 2.
   
   4. PCI bus (original Horizon only, fixed in Ultra)
   
@@ -608,7 +609,7 @@
   u32 pre;
   
   // local fn to build the timer bits
-  inline int set_cr (void) {
+  int set_cr (void) {
     // paranoia
     if (div > CR_MAXD || (!pre) || pre > 1<<CR_MAXPEXP) {
       PRINTD (DBG_QOS, "set_cr internal failure: d=%u p=%u",
@@ -813,7 +814,7 @@
   if (ATM_SKB(skb)->vcc->pop) {
     ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
   } else {
-    dev_kfree_skb (skb);
+    dev_kfree_skb_any (skb);
   }
 }
 
@@ -961,12 +962,11 @@
 static void rx_schedule (hrz_dev * dev, int irq) {
   unsigned int rx_bytes;
   
-  int pio_instead;
+  int pio_instead = 0;
 #ifndef TAILRECURSIONWORKS
-  do {
+  pio_instead = 1;
+  while (pio_instead) {
 #endif
-    pio_instead = 0;
-    
     // bytes waiting for RX transfer
     rx_bytes = dev->rx_bytes;
     
@@ -1047,7 +1047,7 @@
 	{
 	  struct atm_vcc * vcc = ATM_SKB(skb)->vcc;
 	  // VC layer stats
-	  vcc->stats->rx++;
+	  atomic_inc(&vcc->stats->rx);
 	  skb->stamp = xtime;
 	  // end of our responsability
 	  vcc->push (vcc, skb);
@@ -1078,12 +1078,12 @@
 #ifdef TAILRECURSIONWORKS
     // and we all bless optimised tail calls
     if (pio_instead)
-      rx_schedule (dev, 0);
+      return rx_schedule (dev, 0);
     return;
 #else
     // grrrrrrr!
     irq = 0;
-  } while (pio_instead);
+  }
   return;
 #endif
 }
@@ -1130,11 +1130,11 @@
   
   int append_desc = 0;
   
-  int pio_instead;
+  int pio_instead = 0;
 #ifndef TAILRECURSIONWORKS
-  do {
+  pio_instead = 1;
+  while (pio_instead) {
 #endif
-    pio_instead = 0;
     // bytes in current region waiting for TX transfer
     tx_bytes = dev->tx_bytes;
     
@@ -1201,7 +1201,7 @@
 	dev->tx_iovec = 0;
 	
 	// VC layer stats
-	ATM_SKB(skb)->vcc->stats->tx++;
+	atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
 	
 	// free the skb
 	hrz_kfree_skb (skb);
@@ -1236,12 +1236,12 @@
 #ifdef TAILRECURSIONWORKS
     // and we all bless optimised tail calls
     if (pio_instead)
-      tx_schedule (dev, 0);
+      return tx_schedule (dev, 0);
     return;
 #else
     // grrrrrrr!
     irq = 0;
-  } while (pio_instead);
+  }
   return;
 #endif
 }
@@ -1340,37 +1340,33 @@
       if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
 	
 	if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {
-	  struct sk_buff *skb = atm_alloc_charge(atm_vcc,rx_len,GFP_ATOMIC);
-
-	  // If everyone has to call atm_pdu2... why isn't it part of
-	  // atm_charge? B'cos some people already have skb->truesize!
-	  // WA: well. even if they think they do, they might not ... :-)
-
-	    if (skb) {
-	      // remember this so we can push it later
-	      dev->rx_skb = skb;
-	      // remember this so we can flush it later
-	      dev->rx_channel = rx_channel;
-	      
-	      // prepare socket buffer
-	      skb_put (skb, rx_len);
-	      ATM_SKB(skb)->vcc = atm_vcc;
-	      
-	      // simple transfer
-	      // dev->rx_regions = 0;
-	      // dev->rx_iovec = 0;
-	      dev->rx_bytes = rx_len;
-	      dev->rx_addr = skb->data;
-	      PRINTD (DBG_RX, "RX start simple transfer (addr %p, len %d)",
-		      skb->data, rx_len);
-	      
-	      // do the business
-	      rx_schedule (dev, 0);
-	      return;
-	      
-	    } else {
-	      PRINTD (DBG_INFO, "failed to get skb");
-	    }
+	    
+	  struct sk_buff * skb = atm_alloc_charge (atm_vcc, rx_len, GFP_ATOMIC);
+	  if (skb) {
+	    // remember this so we can push it later
+	    dev->rx_skb = skb;
+	    // remember this so we can flush it later
+	    dev->rx_channel = rx_channel;
+	    
+	    // prepare socket buffer
+	    skb_put (skb, rx_len);
+	    ATM_SKB(skb)->vcc = atm_vcc;
+	    
+	    // simple transfer
+	    // dev->rx_regions = 0;
+	    // dev->rx_iovec = 0;
+	    dev->rx_bytes = rx_len;
+	    dev->rx_addr = skb->data;
+	    PRINTD (DBG_RX, "RX start simple transfer (addr %p, len %d)",
+		    skb->data, rx_len);
+	    
+	    // do the business
+	    rx_schedule (dev, 0);
+	    return;
+	    
+	  } else {
+	    PRINTD (DBG_SKB|DBG_WARN, "failed to get skb");
+	  }
 	  
 	} else {
 	  PRINTK (KERN_INFO, "frame received on TX-only VC %x", rx_channel);
@@ -1662,6 +1658,7 @@
   
   if (!channel) {
     PRINTD (DBG_ERR|DBG_TX, "attempt to transmit on zero (rx_)channel");
+    hrz_kfree_skb (skb);
     return -EIO;
   }
   
@@ -1699,9 +1696,11 @@
 #endif
   
   // wait until TX is free and grab lock
-  if (tx_hold (dev))
+  if (tx_hold (dev)) {
+    hrz_kfree_skb (skb);
     return -ERESTARTSYS;
-  
+  }
+ 
   // Wait for enough space to be available in transmit buffer memory.
   
   // should be number of cells needed + 2 (according to hardware docs)
@@ -1722,6 +1721,7 @@
       PRINTD (DBG_TX|DBG_ERR, "spun out waiting for tx buffers, got %d of %d",
 	      free_buffers, buffers_required);
       tx_release (dev);
+      hrz_kfree_skb (skb);
       return -ERESTARTSYS;
     }
   }
@@ -1820,12 +1820,12 @@
   
   u32 ctrl = rd_regl (dev, CONTROL_0_REG);
   
-  inline void WRITE_IT_WAIT (void) {
+  void WRITE_IT_WAIT (void) {
     wr_regl (dev, CONTROL_0_REG, ctrl);
     udelay (5);
   }
   
-  inline void CLOCK_IT (void) {
+  void CLOCK_IT (void) {
     // DI must be valid around rising SK edge
     ctrl &= ~SEEPROM_SK;
     WRITE_IT_WAIT();
@@ -2530,7 +2530,7 @@
   
   // this is "immediately before allocating the connection identifier
   // in hardware" - so long as the next call does not fail :)
-  atm_vcc->flags |= ATM_VF_ADDR;
+  set_bit(ATM_VF_ADDR,&atm_vcc->flags);
   
   // any errors here are very serious and should never occur
   
@@ -2554,7 +2554,7 @@
   atm_vcc->dev_data = (void *) vccp;
   
   // indicate readiness
-  atm_vcc->flags |= ATM_VF_READY;
+  set_bit(ATM_VF_READY,&atm_vcc->flags);
   
   MOD_INC_USE_COUNT;
   return 0;
@@ -2569,7 +2569,7 @@
   PRINTD (DBG_VCC|DBG_FLOW, "hrz_close");
   
   // indicate unreadiness
-  atm_vcc->flags &= ~ATM_VF_READY;
+  clear_bit(ATM_VF_READY,&atm_vcc->flags);
 
   if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
     unsigned int i;
@@ -2611,7 +2611,7 @@
   // free our structure
   kfree (vcc);
   // say the VPI/VCI is free again
-  atm_vcc->flags &= ~ATM_VF_ADDR;
+  clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
   MOD_DEC_USE_COUNT;
 }
 
@@ -2758,13 +2758,13 @@
   devs = 0;
   pci_dev = NULL;
   while ((pci_dev = pci_find_device
-          (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, pci_dev)
-          )) {
+	  (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, pci_dev)
+	  )) {
     hrz_dev * dev;
     
     // adapter slot free, read resources from PCI configuration space
     u32 iobase = pci_dev->resource[0].start;
-    u32 * membase = bus_to_virt(pci_dev->resource[1].start);
+    u32 * membase = bus_to_virt (pci_dev->resource[1].start);
     u8 irq = pci_dev->irq;
     
     // check IO region
@@ -2795,7 +2795,7 @@
       PRINTD (DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p",
 	      iobase, irq, membase);
       
-      dev->atm_dev = atm_dev_register (DEV_LABEL, &hrz_ops, -1, 0);
+      dev->atm_dev = atm_dev_register (DEV_LABEL, &hrz_ops, -1, NULL);
       if (!(dev->atm_dev)) {
 	PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
       } else {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/horizon.h linux/drivers/atm/horizon.h
--- v2.3.99-pre2/linux/drivers/atm/horizon.h	Fri Sep 10 23:57:29 1999
+++ linux/drivers/atm/horizon.h	Tue Mar 21 23:38:26 2000
@@ -31,9 +31,7 @@
 #define DRIVER_ATM_HORIZON_H
 
 #include <linux/config.h>
-
 #include <linux/version.h>
-
 
 #ifdef CONFIG_ATM_HORIZON_DEBUG
 #define DEBUG_HORIZON
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/idt77105.c linux/drivers/atm/idt77105.c
--- v2.3.99-pre2/linux/drivers/atm/idt77105.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/idt77105.c	Tue Mar 21 23:38:26 2000
@@ -157,36 +157,54 @@
 }
 
 
+static int set_loopback(struct atm_dev *dev,int mode)
+{
+	int diag;
+
+	diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK;
+	switch (mode) {
+		case ATM_LM_NONE:
+			break;
+		case ATM_LM_LOC_ATM:
+			diag |= IDT77105_DIAG_LC_PHY_LOOPBACK;
+			break;
+		case ATM_LM_RMT_ATM:
+			diag |= IDT77105_DIAG_LC_LINE_LOOPBACK;
+			break;
+		default:
+			return -EINVAL;
+	}
+	PUT(diag,DIAG);
+	printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type,
+	    dev->number,
+	    (mode == ATM_LM_NONE ? "NONE" : 
+	      (mode == ATM_LM_LOC_ATM ? "DIAG (local)" :
+		(mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" :
+		  "unknown")))
+		    );
+	PRIV(dev)->loop_mode = mode;
+	return 0;
+}
+
 
 static int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
 {
         printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number);
 	switch (cmd) {
 		case IDT77105_GETSTATZ:
+			if (!capable(CAP_NET_ADMIN)) return -EPERM;
+			/* fall through */
 		case IDT77105_GETSTAT:
 			return fetch_stats(dev,(struct idt77105_stats *) arg,
 			    cmd == IDT77105_GETSTATZ);
-		case IDT77105_SETLOOP:
-			if (!capable(CAP_NET_ADMIN)) return -EPERM;
-			if ((int) arg < 0 || (int) arg > IDT77105_LM_LOOP)
-				return -EINVAL;
-			PUT((GET(DIAG) & ~IDT77105_DIAG_LCMASK) |
-			    ((int) arg == IDT77105_LM_NONE ? IDT77105_DIAG_LC_NORMAL : 0) |
-			    ((int) arg == IDT77105_LM_DIAG ? IDT77105_DIAG_LC_PHY_LOOPBACK : 0) |
-			    ((int) arg == IDT77105_LM_LOOP ? IDT77105_DIAG_LC_LINE_LOOPBACK : 0),
-                            DIAG);
-                        printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n",
-                            dev->type, dev->number,
-                            ((int) arg == IDT77105_LM_NONE ? "NONE" : 
-                                ((int) arg == IDT77105_LM_DIAG ? "DIAG (local)" :
-			            ((int) arg == IDT77105_LM_LOOP ? "LOOP (remote)" :
-                                        "unknown")))
-                            );
-			PRIV(dev)->loop_mode = (int) arg;
-			return 0;
-		case IDT77105_GETLOOP:
+		case ATM_SETLOOP:
+			return set_loopback(dev,(int) (long) arg);
+		case ATM_GETLOOP:
 			return put_user(PRIV(dev)->loop_mode,(int *) arg) ?
-			    -EFAULT : sizeof(int);
+			    -EFAULT : 0;
+		case ATM_QUERYLOOP:
+			return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM,
+			    (int *) arg) ? -EFAULT : 0;
 		default:
 			return -ENOIOCTLCMD;
 	}
@@ -266,13 +284,13 @@
         /* initialise loop mode from hardware */
         switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) {
         case IDT77105_DIAG_LC_NORMAL:
-            PRIV(dev)->loop_mode = IDT77105_LM_NONE;
+            PRIV(dev)->loop_mode = ATM_LM_NONE;
             break;
         case IDT77105_DIAG_LC_PHY_LOOPBACK:
-            PRIV(dev)->loop_mode = IDT77105_LM_DIAG;
+            PRIV(dev)->loop_mode = ATM_LM_LOC_ATM;
             break;
         case IDT77105_DIAG_LC_LINE_LOOPBACK:
-            PRIV(dev)->loop_mode = IDT77105_LM_LOOP;
+            PRIV(dev)->loop_mode = ATM_LM_RMT_ATM;
             break;
         }
         
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/iphase.c linux/drivers/atm/iphase.c
--- v2.3.99-pre2/linux/drivers/atm/iphase.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/iphase.c	Tue Mar 21 23:38:26 2000
@@ -59,6 +59,7 @@
 #include <linux/init.h>  
 #include <asm/system.h>  
 #include <asm/io.h>  
+#include <asm/atomic.h>  
 #include <asm/uaccess.h>  
 #include <asm/string.h>  
 #include <asm/byteorder.h>  
@@ -68,7 +69,7 @@
 #include "suni.h"		  
 #define swap(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8))  
 struct suni_priv {
-        struct sonet_stats sonet_stats; /* link diagnostics */
+        struct k_sonet_stats sonet_stats; /* link diagnostics */
         unsigned char loop_mode;        /* loopback mode */
         struct atm_dev *dev;            /* device back-pointer */
         struct suni_priv *next;         /* next SUNI */
@@ -625,12 +626,12 @@
    num_desc = ia_avail_descs(iadev);
    while (num_desc && (skb = skb_dequeue(&iadev->tx_backlog))) {
       if (!(vcc = ATM_SKB(skb)->vcc)) {
-         dev_kfree_skb(skb);
+         dev_kfree_skb_any(skb);
          printk("ia_que_tx: Null vcc\n");
          break;
       }
-      if ((vcc->flags & ATM_VF_READY) == 0 ) {
-         dev_kfree_skb(skb);
+      if (!test_bit(ATM_VF_READY,&vcc->flags)) {
+         dev_kfree_skb_any(skb);
          printk("Free the SKB on closed vci %d \n", vcc->vci);
          break;
       }
@@ -658,14 +659,14 @@
        vcc = ATM_SKB(skb)->vcc;
        if (!vcc) {
            printk("ia_tx_poll: vcc is null\n");
-           dev_kfree_skb(skb);
+           dev_kfree_skb_any(skb);
            return;
        }
 
        iavcc = INPH_IA_VCC(vcc);
        if (!iavcc) {
            printk("ia_tx_poll: iavcc is null\n");
-           dev_kfree_skb(skb);
+           dev_kfree_skb_any(skb);
            return;
        }
 
@@ -682,7 +683,7 @@
                                                           (long)skb1);)
           }
           else 
-             dev_kfree_skb(skb1);
+             dev_kfree_skb_any(skb1);
           skb1 = skb_dequeue(&iavcc->txing_skb);
        }                                                        
        if (!skb1) {
@@ -696,7 +697,7 @@
           IF_EVENT(printk("Tx Done - skb 0x%lx return\n",(long)skb);)
        }
        else 
-          dev_kfree_skb(skb);
+          dev_kfree_skb_any(skb);
        kfree(rtne);
     }
     ia_que_tx(iadev);
@@ -1128,7 +1129,7 @@
 	status = (u_short) (buf_desc_ptr->desc_mode);  
 	if (status & (RX_CER | RX_PTE | RX_OFL))  
 	{  
-                vcc->stats->rx_err++;
+                atomic_inc(&vcc->stats->rx_err);
 		IF_ERR(printk("IA: bad packet, dropping it");)  
                 if (status & RX_CER) { 
                     IF_ERR(printk(" cause: packet CRC error\n");)
@@ -1152,7 +1153,7 @@
 	len = dma_addr - buf_addr;  
         if (len > iadev->rx_buf_sz) {
            printk("Over %d bytes sdu received, dropped!!!\n", iadev->rx_buf_sz);
-           vcc->stats->rx_err++;
+           atomic_inc(&vcc->stats->rx_err);
            free_desc(dev, desc); 
            return 0;
         }
@@ -1166,7 +1167,7 @@
 	   if (!skb)  
 	   {  
               IF_ERR(printk("can't allocate memory for recv, drop pkt!\n");)  
-              vcc->stats->rx_drop++;
+              atomic_inc(&vcc->stats->rx_drop);
               atm_return(vcc, atm_pdu2truesize(len));
               free_desc(dev, desc); 
 	      return 0;  
@@ -1297,7 +1298,7 @@
       if (!skb->len)  
       {  
           printk("rx_dle_intr: skb len 0\n");  
-	  dev_kfree_skb(skb);  
+	  dev_kfree_skb_any(skb);  
       }  
       else  
       {  
@@ -1308,15 +1309,15 @@
           vcc = ATM_SKB(skb)->vcc;
 	  if (!vcc) {
 	      printk("IA: null vcc\n");  
-              vcc->stats->rx_err++;
-              dev_kfree_skb(skb);
+              atomic_inc(&vcc->stats->rx_err);
+              dev_kfree_skb_any(skb);
               goto INCR_DLE;
           }
           ia_vcc = INPH_IA_VCC(vcc);
           if (ia_vcc == NULL)
           {
-             vcc->stats->rx_err++;
-             dev_kfree_skb(skb);
+             atomic_inc(&vcc->stats->rx_err);
+             dev_kfree_skb_any(skb);
 #if LINUX_VERSION_CODE >= 0x20312
              atm_return(vcc, atm_guess_pdu2truesize(skb->len));
 #else
@@ -1331,8 +1332,8 @@
           if ((length > iadev->rx_buf_sz) || (length > 
                               (skb->len - sizeof(struct cpcs_trailer))))
           {
-             vcc->stats->rx_err++;
-             dev_kfree_skb(skb);
+             atomic_inc(&vcc->stats->rx_err);
+             dev_kfree_skb_any(skb);
              IF_ERR(printk("rx_dle_intr: Bad  AAL5 trailer %d (skb len %d)", 
                                                             length, skb->len);)
 #if LINUX_VERSION_CODE >= 0x20312
@@ -1351,7 +1352,7 @@
 
 	  IF_RX(printk("rx_dle_intr: skb push");)  
 	  vcc->push(vcc,skb);  
-	  vcc->stats->rx++;   
+	  atomic_inc(&vcc->stats->rx);
           iadev->rx_pkt_cnt++;
       }  
 INCR_DLE:
@@ -1710,13 +1711,13 @@
             vcc = ATM_SKB(skb)->vcc;
             if (!vcc) {
                   printk("tx_dle_intr: vcc is null\n");
-                  dev_kfree_skb(skb);
+                  dev_kfree_skb_any(skb);
                   return;
             }
             iavcc = INPH_IA_VCC(vcc);
             if (!iavcc) {
                   printk("tx_dle_intr: iavcc is null\n");
-                  dev_kfree_skb(skb);
+                  dev_kfree_skb_any(skb);
                   return;
             }
             if (vcc->qos.txtp.pcr >= iadev->rate_limit) {
@@ -1725,7 +1726,7 @@
                  vcc->pop(vcc, skb);
                } 
                else {
-                 dev_kfree_skb(skb);
+                 dev_kfree_skb_any(skb);
                }
             }
             else { /* Hold the rate-limited skb for flow control */
@@ -2601,7 +2602,7 @@
 
         IF_EVENT(printk("ia_close: ia_vcc->vc_desc_cnt = %d  vci = %d\n", 
                                               ia_vcc->vc_desc_cnt,vcc->vci);)
-	vcc->flags &= ~ATM_VF_READY;  
+	clear_bit(ATM_VF_READY,&vcc->flags);
         skb_queue_head_init (&tmp_tx_backlog);
         skb_queue_head_init (&tmp_vcc_backlog); 
         if (vcc->qos.txtp.traffic_class != ATM_NONE) {
@@ -2611,7 +2612,7 @@
            while((skb = skb_dequeue(&iadev->tx_backlog))) {
               if (ATM_SKB(skb)->vcc == vcc){ 
                  if (vcc->pop) vcc->pop(vcc, skb);
-                 else dev_kfree_skb(skb);
+                 else dev_kfree_skb_any(skb);
               }
               else 
                  skb_queue_tail(&tmp_tx_backlog, skb);
@@ -2669,7 +2670,7 @@
 	kfree(INPH_IA_VCC(vcc));  
         ia_vcc = NULL;
         INPH_IA_VCC(vcc) = NULL;  
-        vcc->flags &= ~ATM_VF_ADDR;  
+        clear_bit(ATM_VF_ADDR,&vcc->flags);
         return;        
 }  
   
@@ -2678,7 +2679,7 @@
 	IADEV *iadev;  
 	struct ia_vcc *ia_vcc;  
 	int error;  
-	if (!(vcc->flags & ATM_VF_PARTIAL))  
+	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))  
 	{  
 		IF_EVENT(printk("ia: not partially allocated resources\n");)  
 		INPH_IA_VCC(vcc) = NULL;  
@@ -2695,7 +2696,7 @@
 	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)  
 	{  
 		IF_EVENT(printk("iphase open: unspec part\n");)  
-		vcc->flags |= ATM_VF_ADDR;  
+		set_bit(ATM_VF_ADDR,&vcc->flags);
 	}  
 	if (vcc->qos.aal != ATM_AAL5)  
 		return -EINVAL;  
@@ -2721,7 +2722,7 @@
 		return error;  
 	}  
   
-	vcc->flags |= ATM_VF_READY;  
+	set_bit(ATM_VF_READY,&vcc->flags);
 
 #ifndef MODULE
         {
@@ -2749,7 +2750,7 @@
   
 static int ia_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg)  
 {  
-   PIA_CMDBUF ia_cmds;
+   IA_CMDBUF ia_cmds;
    IADEV *iadev;
    int i, board;
    u16 *tmps;
@@ -2757,34 +2758,37 @@
    if (cmd != IA_CMD) {
       if (!dev->phy->ioctl) return -EINVAL;
       return dev->phy->ioctl(dev,cmd,arg);
-   } 
-   ia_cmds	= (PIA_CMDBUF)arg;
-   board = ia_cmds->status;
+   }
+   if (copy_from_user(&ia_cmds, arg, sizeof ia_cmds)) return -EFAULT; 
+   board = ia_cmds.status;
    if ((board < 0) || (board > iadev_count))
          board = 0;    
    iadev = ia_dev[board];
-   switch (ia_cmds->cmd) {
+   switch (ia_cmds.cmd) {
    case MEMDUMP:
    {
-	switch (ia_cmds->sub_cmd) {
+	switch (ia_cmds.sub_cmd) {
        	  case MEMDUMP_DEV:     
-             memcpy((char*)ia_cmds->buf, (char*)iadev,
-                          			sizeof(IADEV));
-             ia_cmds->status = 0;
+	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	     if (copy_to_user(ia_cmds.buf, iadev, sizeof(IADEV)))
+                return -EFAULT;
+             ia_cmds.status = 0;
              break;
           case MEMDUMP_SEGREG:
-             tmps = (u16 *)ia_cmds->buf;
+	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
+             tmps = (u16 *)ia_cmds.buf;
              for(i=0; i<0x80; i+=2, tmps++)
-                *tmps = *(u16*)(iadev->seg_reg+i);
-             ia_cmds->status = 0;
-             ia_cmds->len = 0x80;
+                if(put_user(*(u16*)(iadev->seg_reg+i), tmps)) return -EFAULT;
+             ia_cmds.status = 0;
+             ia_cmds.len = 0x80;
              break;
           case MEMDUMP_REASSREG:
-             tmps = (u16 *)ia_cmds->buf;
+	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
+             tmps = (u16 *)ia_cmds.buf;
              for(i=0; i<0x80; i+=2, tmps++)
-                *tmps = *(u16*)(iadev->reass_reg+i);
-             ia_cmds->status = 0;
-             ia_cmds->len = 0x80;
+                if(put_user(*(u16*)(iadev->reass_reg+i), tmps)) return -EFAULT;
+             ia_cmds.status = 0;
+             ia_cmds.len = 0x80;
              break;
           case MEMDUMP_FFL:
           {  
@@ -2792,6 +2796,7 @@
              ffredn_t        *ffL = &regs_local.ffredn;
              rfredn_t        *rfL = &regs_local.rfredn;
                      
+	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
              /* Copy real rfred registers into the local copy */
  	     for (i=0; i<(sizeof (rfredn_t))/4; i++)
                 ((u_int *)rfL)[i] = ((u_int *)iadev->reass_reg)[i] & 0xffff;
@@ -2799,63 +2804,67 @@
 	     for (i=0; i<(sizeof (ffredn_t))/4; i++)
                 ((u_int *)ffL)[i] = ((u_int *)iadev->seg_reg)[i] & 0xffff;
 
-             memcpy((char*)ia_cmds->buf,(char*)&regs_local,sizeof(ia_regs_t));
+             if (copy_to_user(ia_cmds.buf, &regs_local,sizeof(ia_regs_t)))
+                return -EFAULT;
              printk("Board %d registers dumped\n", board);
-             ia_cmds->status = 0;                  
+             ia_cmds.status = 0;                  
 	 }	
     	     break;        
          case READ_REG:
          {  
+	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
              desc_dbg(iadev); 
-             ia_cmds->status = 0; 
+             ia_cmds.status = 0; 
          }
              break;
          case 0x6:
          {  
-             ia_cmds->status = 0; 
+             ia_cmds.status = 0; 
              printk("skb = 0x%lx\n", (long)skb_peek(&iadev->tx_backlog));
              printk("rtn_q: 0x%lx\n",(long)ia_deque_rtn_q(&iadev->tx_return_q));
          }
              break;
          case 0x8:
          {
-             struct sonet_stats *stats;
+             struct k_sonet_stats *stats;
              stats = &PRIV(_ia_dev[board])->sonet_stats;
-             printk("section_bip: %d\n", stats->section_bip);
-             printk("line_bip   : %d\n", stats->line_bip);
-             printk("path_bip   : %d\n", stats->path_bip);
-             printk("line_febe  : %d\n", stats->line_febe);
-             printk("path_febe  : %d\n", stats->path_febe);
-             printk("corr_hcs   : %d\n", stats->corr_hcs);
-             printk("uncorr_hcs : %d\n", stats->uncorr_hcs);
-             printk("tx_cells   : %d\n", stats->tx_cells);
-             printk("rx_cells   : %d\n", stats->rx_cells);
+             printk("section_bip: %d\n", atomic_read(&stats->section_bip));
+             printk("line_bip   : %d\n", atomic_read(&stats->line_bip));
+             printk("path_bip   : %d\n", atomic_read(&stats->path_bip));
+             printk("line_febe  : %d\n", atomic_read(&stats->line_febe));
+             printk("path_febe  : %d\n", atomic_read(&stats->path_febe));
+             printk("corr_hcs   : %d\n", atomic_read(&stats->corr_hcs));
+             printk("uncorr_hcs : %d\n", atomic_read(&stats->uncorr_hcs));
+             printk("tx_cells   : %d\n", atomic_read(&stats->tx_cells));
+             printk("rx_cells   : %d\n", atomic_read(&stats->rx_cells));
          }
-            ia_cmds->status = 0;
+            ia_cmds.status = 0;
             break;
          case 0x9:
+	    if (!capable(CAP_NET_ADMIN)) return -EPERM;
             for (i = 1; i <= iadev->num_rx_desc; i++)
                free_desc(_ia_dev[board], i);
             writew( ~(RX_FREEQ_EMPT | RX_EXCP_RCVD), 
                                             iadev->reass_reg+REASS_MASK_REG);
             iadev->rxing = 1;
             
-            ia_cmds->status = 0;
+            ia_cmds.status = 0;
             break;
 
          case 0xb:
+	    if (!capable(CAP_NET_ADMIN)) return -EPERM;
             IaFrontEndIntr(iadev);
             break;
          case 0xa:
+	    if (!capable(CAP_NET_ADMIN)) return -EPERM;
          {  
-             ia_cmds->status = 0; 
-             IADebugFlag = ia_cmds->maddr;
+             ia_cmds.status = 0; 
+             IADebugFlag = ia_cmds.maddr;
              printk("New debug option loaded\n");
          }
              break;
          default:
-             memcpy((char*)ia_cmds->buf, (char*)ia_cmds->maddr, ia_cmds->len);
-             ia_cmds->status = 0;
+             ia_cmds.status = 0;
              break;
       }	
    }
@@ -2896,7 +2905,7 @@
         if (!iavcc->txing) {
            printk("discard packet on closed VC\n");
            if (vcc->pop) vcc->pop(vcc, skb);
-           else dev_kfree_skb(skb);
+           else dev_kfree_skb_any(skb);
         }
 
         if (skb->len > iadev->tx_buf_sz - 8) {
@@ -2904,7 +2913,7 @@
            if (vcc->pop)
                  vcc->pop(vcc, skb);
            else
-                 dev_kfree_skb(skb);
+                 dev_kfree_skb_any(skb);
           return 0;
         }
         if ((u32)skb->data & 3) {
@@ -2912,7 +2921,7 @@
            if (vcc->pop)
                  vcc->pop(vcc, skb);
            else
-                 dev_kfree_skb(skb);
+                 dev_kfree_skb_any(skb);
            return 0;
         }       
 	/* Get a descriptor number from our free descriptor queue  
@@ -2929,11 +2938,11 @@
 	if ((desc == 0) || (desc > iadev->num_tx_desc))  
 	{  
 		IF_ERR(printk(DEV_LABEL "invalid desc for send: %d\n", desc);) 
-                vcc->stats->tx++;
+                atomic_inc(&vcc->stats->tx);
 		if (vcc->pop)   
 		    vcc->pop(vcc, skb);   
 		else  
-		    dev_kfree_skb(skb);
+		    dev_kfree_skb_any(skb);
 		return 0;   /* return SUCCESS */
 	}  
   
@@ -3038,14 +3047,14 @@
         ATM_DESC(skb) = vcc->vci;
         skb_queue_tail(&iadev->tx_dma_q, skb);
 
-        vcc->stats->tx++;
+        atomic_inc(&vcc->stats->tx);
         iadev->tx_pkt_cnt++;
 	/* Increment transaction counter */  
 	writel(2, iadev->dma+IPHASE5575_TX_COUNTER);  
         
 #if 0        
         /* add flow control logic */ 
-        if (vcc->stats->tx % 20 == 0) {
+        if (atomic_read(&vcc->stats->tx) % 20 == 0) {
           if (iavcc->vc_desc_cnt > 10) {
              vcc->tx_quota =  vcc->tx_quota * 3 / 4;
             printk("Tx1:  vcc->tx_quota = %d \n", (u32)vcc->tx_quota );
@@ -3074,12 +3083,12 @@
         {
             if (!skb)
                 printk(KERN_CRIT "null skb in ia_send\n");
-            else dev_kfree_skb(skb);
+            else dev_kfree_skb_any(skb);
             return -EINVAL;
         }                         
         spin_lock_irqsave(&iadev->tx_lock, flags); 
-        if ((vcc->flags & ATM_VF_READY) == 0){ 
-            dev_kfree_skb(skb);
+        if (!test_bit(ATM_VF_READY,&vcc->flags)){ 
+            dev_kfree_skb_any(skb);
             spin_unlock_irqrestore(&iadev->tx_lock, flags);
             return -EINVAL; 
         }
@@ -3197,7 +3206,7 @@
 		IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n",
                      iadev->pci->bus->number, PCI_SLOT(iadev->pci->devfn), 
                                                  PCI_FUNC(iadev->pci->devfn));)  
-		dev = atm_dev_register(DEV_LABEL, &ops, -1, 0);   
+		dev = atm_dev_register(DEV_LABEL, &ops, -1, NULL);
 		if (!dev) break;  
 		IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n", 
                                                              dev->number);)  
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/nicstar.c linux/drivers/atm/nicstar.c
--- v2.3.99-pre2/linux/drivers/atm/nicstar.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/nicstar.c	Tue Mar 21 23:38:26 2000
@@ -37,8 +37,10 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/interrupt.h>
+#include <linux/bitops.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 #include "nicstar.h"
 #include "nicstarmac.h"
 #ifdef CONFIG_ATM_NICSTAR_USE_SUNI
@@ -284,7 +286,7 @@
       card = cards[i];
 
 #ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
-      if (card->max_pcr == IDT_25_PCR) {
+      if (card->max_pcr == ATM_25_PCR) {
         idt77105_stop(card->atmdev);
       }
 #endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */
@@ -311,7 +313,7 @@
       PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
       while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
       {
-         kfree_skb(hb);
+         dev_kfree_skb_any(hb);
 	 j++;
       }
       PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
@@ -319,14 +321,14 @@
       PRINTK("nicstar%d: freeing %d iovec buffers.\n", i, card->iovpool.count);
       while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
       {
-         kfree_skb(iovb);
+         dev_kfree_skb_any(iovb);
          j++;
       }
       PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
       while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
-         kfree_skb(lb);
+         dev_kfree_skb_any(lb);
       while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
-         kfree_skb(sb);
+         dev_kfree_skb_any(sb);
       free_scq(card->scq0, NULL);
       for (j = 0; j < NS_FRSCD_NUM; j++)
       {
@@ -547,7 +549,7 @@
    switch(data) {
       case 0x00000009:
          printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
-         card->max_pcr = IDT_25_PCR;
+         card->max_pcr = ATM_25_PCR;
          while(CMD_BUSY(card));
          writel(0x00000008, card->membase + DR0);
          writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
@@ -857,7 +859,7 @@
    card->efbie = 1;
 
    /* Register device */
-   card->atmdev = atm_dev_register("nicstar", &atm_ops, -1, 0UL);
+   card->atmdev = atm_dev_register("nicstar", &atm_ops, -1, NULL);
    if (card->atmdev == NULL)
    {
       printk("nicstar%d: can't register device.\n", i);
@@ -891,7 +893,7 @@
 #endif /* CONFIG_ATM_NICSTAR_USE_SUNI */
 
 #ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
-   if (card->max_pcr == IDT_25_PCR) {
+   if (card->max_pcr == ATM_25_PCR) {
       idt77105_init(card->atmdev);
       /* Note that for the IDT77105 PHY we don't need the awful
        * module count hack that the SUNI needs because we can
@@ -936,26 +938,26 @@
    {
       struct sk_buff *iovb;
       while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
-         kfree_skb(iovb);
+         dev_kfree_skb_any(iovb);
    }
    if (error >= 15)
    {
       struct sk_buff *sb;
       while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
-         kfree_skb(sb);
+         dev_kfree_skb_any(sb);
       free_scq(card->scq0, NULL);
    }
    if (error >= 14)
    {
       struct sk_buff *lb;
       while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
-         kfree_skb(lb);
+         dev_kfree_skb_any(lb);
    }
    if (error >= 13)
    {
       struct sk_buff *hb;
       while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
-         kfree_skb(hb);
+         dev_kfree_skb_any(hb);
    }
    if (error >= 12)
    {
@@ -1039,7 +1041,7 @@
             if (vcc->pop != NULL)
 	       vcc->pop(vcc, scq->skb[i]);
 	    else
-               dev_kfree_skb(scq->skb[i]);
+               dev_kfree_skb_any(scq->skb[i]);
          }
       }
    else /* vcc must be != NULL */
@@ -1048,7 +1050,7 @@
       {
          printk("nicstar: free_scq() called with vcc == NULL for fixed rate scq.");
          for (i = 0; i < scq->num_entries; i++)
-            dev_kfree_skb(scq->skb[i]);
+            dev_kfree_skb_any(scq->skb[i]);
       }
       else
          for (i = 0; i < scq->num_entries; i++)
@@ -1058,7 +1060,7 @@
                if (vcc->pop != NULL)
                   vcc->pop(vcc, scq->skb[i]);
                else
-                  dev_kfree_skb(scq->skb[i]);
+                  dev_kfree_skb_any(scq->skb[i]);
             }
          }
    }
@@ -1130,9 +1132,9 @@
          if (card->sbfqc >= card->sbnr.max)
          {
             skb_unlink((struct sk_buff *) handle1);
-            kfree_skb((struct sk_buff *) handle1);
+            dev_kfree_skb_any((struct sk_buff *) handle1);
             skb_unlink((struct sk_buff *) handle2);
-            kfree_skb((struct sk_buff *) handle2);
+            dev_kfree_skb_any((struct sk_buff *) handle2);
             return;
          }
 	 else
@@ -1143,9 +1145,9 @@
          if (card->lbfqc >= card->lbnr.max)
          {
             skb_unlink((struct sk_buff *) handle1);
-            kfree_skb((struct sk_buff *) handle1);
+            dev_kfree_skb_any((struct sk_buff *) handle1);
             skb_unlink((struct sk_buff *) handle2);
-            kfree_skb((struct sk_buff *) handle2);
+            dev_kfree_skb_any((struct sk_buff *) handle2);
             return;
          }
          else
@@ -1420,16 +1422,16 @@
       return -EINVAL;
    }
 
-   vcc->flags |= ATM_VF_ADDR;
+   set_bit(ATM_VF_ADDR,&vcc->flags);
 
    /* NOTE: You are not allowed to modify an open connection's QOS. To change
       that, remove the ATM_VF_PARTIAL flag checking. There may be other changes
       needed to do that. */
-   if (!(vcc->flags & ATM_VF_PARTIAL))
+   if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
    {
       scq_info *scq;
       
-      vcc->flags |= ATM_VF_PARTIAL;
+      set_bit(ATM_VF_PARTIAL,&vcc->flags);
       if (vcc->qos.txtp.traffic_class == ATM_CBR)
       {
          /* Check requested cell rate and availability of SCD */
@@ -1438,7 +1440,8 @@
          {
             PRINTK("nicstar%d: trying to open a CBR vc with cell rate = 0 \n",
 	           card->index);
-            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
+	    clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	    clear_bit(ATM_VF_ADDR,&vcc->flags);
             return -EINVAL;
          }
 
@@ -1461,7 +1464,8 @@
             if ((n = (card->tst_free_entries - NS_TST_RESERVED)) <= 0)
 	    {
                PRINTK("nicstar%d: no CBR bandwidth free.\n", card->index);
-	       vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
+	       clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	       clear_bit(ATM_VF_ADDR,&vcc->flags);
                return -EINVAL;
             }
          }
@@ -1469,14 +1473,16 @@
          if (n == 0)
          {
             printk("nicstar%d: selected bandwidth < granularity.\n", card->index);
-            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
+	    clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	    clear_bit(ATM_VF_ADDR,&vcc->flags);
             return -EINVAL;
          }
 
          if (n > (card->tst_free_entries - NS_TST_RESERVED))
          {
             PRINTK("nicstar%d: not enough free CBR bandwidth.\n", card->index);
-	    vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
+	    clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	    clear_bit(ATM_VF_ADDR,&vcc->flags);
             return -EINVAL;
          }
          else
@@ -1495,7 +1501,8 @@
          {
             PRINTK("nicstar%d: no SCD available for CBR channel.\n", card->index);
             card->tst_free_entries += n;
-            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
+	    clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	    clear_bit(ATM_VF_ADDR,&vcc->flags);
 	    return -EBUSY;
          }
 
@@ -1507,7 +1514,8 @@
             PRINTK("nicstar%d: can't get fixed rate SCQ.\n", card->index);
             card->scd2vc[frscdi] = NULL;
             card->tst_free_entries += n;
-            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
+	    clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	    clear_bit(ATM_VF_ADDR,&vcc->flags);
             return -ENOMEM;
          }
 	 vc->scq = scq;
@@ -1553,7 +1561,7 @@
       
    }
    
-   vcc->flags |= ATM_VF_READY;
+   set_bit(ATM_VF_READY,&vcc->flags);
    MOD_INC_USE_COUNT;
    return 0;
 }
@@ -1572,7 +1580,7 @@
    PRINTK("nicstar%d: closing vpi.vci %d.%d \n", card->index,
           (int) vcc->vpi, vcc->vci);
 
-   vcc->flags &= ~(ATM_VF_READY);
+   clear_bit(ATM_VF_READY,&vcc->flags);
    
    if (vcc->qos.rxtp.traffic_class != ATM_NONE)
    {
@@ -1681,7 +1689,8 @@
    }
 
    vcc->dev_data = NULL;
-   vcc->flags &= ~(ATM_VF_PARTIAL | ATM_VF_ADDR);
+   clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+   clear_bit(ATM_VF_ADDR,&vcc->flags);
    MOD_DEC_USE_COUNT;
 
 #ifdef RX_DEBUG
@@ -1778,32 +1787,32 @@
    if ((vc = (vc_map *) vcc->dev_data) == NULL)
    {
       printk("nicstar%d: vcc->dev_data == NULL on ns_send().\n", card->index);
-      vcc->stats->tx_err++;
-      dev_kfree_skb(skb);
+      atomic_inc(&vcc->stats->tx_err);
+      dev_kfree_skb_any(skb);
       return -EINVAL;
    }
    
    if (!vc->tx)
    {
       printk("nicstar%d: Trying to transmit on a non-tx VC.\n", card->index);
-      vcc->stats->tx_err++;
-      dev_kfree_skb(skb);
+      atomic_inc(&vcc->stats->tx_err);
+      dev_kfree_skb_any(skb);
       return -EINVAL;
    }
    
    if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0)
    {
       printk("nicstar%d: Only AAL0 and AAL5 are supported.\n", card->index);
-      vcc->stats->tx_err++;
-      dev_kfree_skb(skb);
+      atomic_inc(&vcc->stats->tx_err);
+      dev_kfree_skb_any(skb);
       return -EINVAL;
    }
    
    if (ATM_SKB(skb)->iovcnt != 0)
    {
       printk("nicstar%d: No scatter-gather yet.\n", card->index);
-      vcc->stats->tx_err++;
-      dev_kfree_skb(skb);
+      atomic_inc(&vcc->stats->tx_err);
+      dev_kfree_skb_any(skb);
       return -EINVAL;
    }
    
@@ -1847,11 +1856,11 @@
 
    if (push_scqe(card, vc, scq, &scqe, skb) != 0)
    {
-      vcc->stats->tx_err++;
-      dev_kfree_skb(skb);
+      atomic_inc(&vcc->stats->tx_err);
+      dev_kfree_skb_any(skb);
       return -EIO;
    }
-   vcc->stats->tx++;
+   atomic_inc(&vcc->stats->tx);
 
    return 0;
 }
@@ -2071,7 +2080,7 @@
 	 if (vcc->pop != NULL)
 	    vcc->pop(vcc, skb);
 	 else
-	    dev_kfree_skb(skb);
+	    dev_kfree_skb_any(skb);
 	 scq->skb[i] = NULL;
       }
       if (++i == scq->num_entries)
@@ -2155,15 +2164,15 @@
          {
             printk("nicstar%d: Can't allocate buffers for aal0.\n",
                    card->index);
-            vcc->stats->rx_drop += i;
+            atomic_add(i,&vcc->stats->rx_drop);
             break;
          }
          if (!atm_charge(vcc, sb->truesize))
          {
             RXPRINTK("nicstar%d: atm_charge() dropped aal0 packets.\n",
                      card->index);
-            vcc->stats->rx_drop += i - 1;	/* already increased by 1 */
-            kfree_skb(sb);
+            atomic_add(i-1,&vcc->stats->rx_drop); /* already increased by 1 */
+            dev_kfree_skb_any(sb);
             break;
          }
          /* Rebuild the header */
@@ -2177,7 +2186,7 @@
          ATM_SKB(sb)->vcc = vcc;
          sb->stamp = xtime;
          vcc->push(vcc, sb);
-         vcc->stats->rx++;
+         atomic_inc(&vcc->stats->rx);
          cell += ATM_CELL_PAYLOAD;
       }
 
@@ -2196,7 +2205,7 @@
 	 if (iovb == NULL)
 	 {
 	    printk("nicstar%d: Out of iovec buffers.\n", card->index);
-            vcc->stats->rx_drop++;
+            atomic_inc(&vcc->stats->rx_drop);
             recycle_rx_buf(card, skb);
             return;
 	 }
@@ -2223,7 +2232,7 @@
    else if (ATM_SKB(iovb)->iovcnt >= NS_MAX_IOVECS)
    {
       printk("nicstar%d: received too big AAL5 SDU.\n", card->index);
-      vcc->stats->rx_err++;
+      atomic_inc(&vcc->stats->rx_err);
       recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS);
       ATM_SKB(iovb)->iovcnt = 0;
       iovb->len = 0;
@@ -2242,7 +2251,7 @@
          printk("nicstar%d: Expected a small buffer, and this is not one.\n",
 	        card->index);
          which_list(card, skb);
-         vcc->stats->rx_err++;
+         atomic_inc(&vcc->stats->rx_err);
          recycle_rx_buf(card, skb);
          vc->rx_iov = NULL;
          recycle_iov_buf(card, iovb);
@@ -2256,7 +2265,7 @@
          printk("nicstar%d: Expected a large buffer, and this is not one.\n",
 	        card->index);
          which_list(card, skb);
-         vcc->stats->rx_err++;
+         atomic_inc(&vcc->stats->rx_err);
          recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data,
 	                       ATM_SKB(iovb)->iovcnt);
          vc->rx_iov = NULL;
@@ -2280,7 +2289,7 @@
             printk(" - PDU size mismatch.\n");
          else
             printk(".\n");
-         vcc->stats->rx_err++;
+         atomic_inc(&vcc->stats->rx_err);
          recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data,
 	   ATM_SKB(iovb)->iovcnt);
 	 vc->rx_iov = NULL;
@@ -2308,7 +2317,7 @@
             ATM_SKB(skb)->vcc = vcc;
             skb->stamp = xtime;
             vcc->push(vcc, skb);
-            vcc->stats->rx++;
+            atomic_inc(&vcc->stats->rx);
          }
       }
       else if (ATM_SKB(iovb)->iovcnt == 2)	/* One small plus one large buffer */
@@ -2335,7 +2344,7 @@
                ATM_SKB(sb)->vcc = vcc;
                sb->stamp = xtime;
                vcc->push(vcc, sb);
-               vcc->stats->rx++;
+               atomic_inc(&vcc->stats->rx);
             }
 
             push_rxbufs(card, BUF_LG, (u32) skb,
@@ -2361,7 +2370,7 @@
                ATM_SKB(skb)->vcc = vcc;
                skb->stamp = xtime;
                vcc->push(vcc, skb);
-               vcc->stats->rx++;
+               atomic_inc(&vcc->stats->rx);
             }
 
             push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data),
@@ -2384,7 +2393,7 @@
             if (hb == NULL)
             {
                printk("nicstar%d: Out of huge buffers.\n", card->index);
-               vcc->stats->rx_drop++;
+               atomic_inc(&vcc->stats->rx_drop);
                recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data,
 	                             ATM_SKB(iovb)->iovcnt);
                vc->rx_iov = NULL;
@@ -2431,7 +2440,7 @@
                card->hbpool.count++;
             }
 	    else
-	       kfree_skb(hb);
+	       dev_kfree_skb_any(hb);
          }
          else
 	 {
@@ -2467,7 +2476,7 @@
 #endif /* NS_USE_DESTRUCTORS */
             hb->stamp = xtime;
             vcc->push(vcc, hb);
-            vcc->stats->rx++;
+            atomic_inc(&vcc->stats->rx);
          }
       }
 
@@ -2556,7 +2565,7 @@
    else
    {
       printk("nicstar%d: What kind of rx buffer is this?\n", card->index);
-      kfree_skb(skb);
+      dev_kfree_skb_any(skb);
    }
 }
 
@@ -2578,7 +2587,7 @@
       else
       {
          printk("nicstar%d: What kind of rx buffer is this?\n", card->index);
-         kfree_skb(skb);
+         dev_kfree_skb_any(skb);
       }
    }
 }
@@ -2593,7 +2602,7 @@
       card->iovpool.count++;
    }
    else
-      kfree_skb(iovb);
+      dev_kfree_skb_any(iovb);
 }
 
 
@@ -2700,7 +2709,7 @@
    /* Dump 25.6 Mbps PHY registers */
    /* Now there's a 25.6 Mbps PHY driver this code isn't needed. I left it
       here just in case it's needed for debugging. */
-   if (card->max_pcr == IDT_25_PCR && !left--)
+   if (card->max_pcr == ATM_25_PCR && !left--)
    {
       u32 phy_regs[4];
       u32 i;
@@ -2787,7 +2796,7 @@
 	    return -EFAULT;
 
       case NS_SETBUFLEV:
-         if (!suser())
+         if (!capable(CAP_NET_ADMIN))
 	    return -EPERM;
          if (copy_from_user(&pl, (pool_levels *) arg, sizeof(pl)))
 	    return -EFAULT;
@@ -2836,7 +2845,7 @@
          return 0;
 
       case NS_ADJBUFLEV:
-         if (!suser())
+         if (!capable(CAP_NET_ADMIN))
 	    return -EPERM;
          btype = (int) arg;	/* an int is the same size as a pointer */
          switch (btype)
@@ -2882,7 +2891,7 @@
 		     printk("nicstar%d: huge buffer count inconsistent.\n",
 		            card->index);
                   else
-		     kfree_skb(hb);
+		     dev_kfree_skb_any(hb);
 		  
 	       }
                while (card->hbpool.count < card->hbnr.init)
@@ -2912,7 +2921,7 @@
 		     printk("nicstar%d: iovec buffer count inconsistent.\n",
 		            card->index);
                   else
-		     kfree_skb(iovb);
+		     dev_kfree_skb_any(iovb);
 
 	       }
                while (card->iovpool.count < card->iovnr.init)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/nicstar.h linux/drivers/atm/nicstar.h
--- v2.3.99-pre2/linux/drivers/atm/nicstar.h	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/nicstar.h	Tue Mar 21 23:38:26 2000
@@ -100,8 +100,6 @@
 
 #define NS_IOREMAP_SIZE 4096
 
-#define IDT_25_PCR ((25600000 / 8 - 8000) / 54)
-
 #define BUF_SM 0x00000000	/* These two are used for push_rxbufs() */
 #define BUF_LG 0x00000001       /* CMD, Write_FreeBufQ, LBUF bit */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/suni.c linux/drivers/atm/suni.c
--- v2.3.99-pre2/linux/drivers/atm/suni.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/suni.c	Tue Mar 21 23:38:26 2000
@@ -1,4 +1,4 @@
-/* drivers/atm/suni.c - PMC SUNI (PHY) driver */
+/* drivers/atm/suni.c - PMC PM5346 SUNI (PHY) driver */
  
 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
@@ -18,6 +18,7 @@
 #include <asm/system.h>
 #include <asm/param.h>
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 
 #include "suni.h"
 
@@ -30,8 +31,8 @@
 
 
 struct suni_priv {
-	struct sonet_stats sonet_stats; /* link diagnostics */
-	unsigned char loop_mode;        /* loopback mode */
+	struct k_sonet_stats sonet_stats; /* link diagnostics */
+	int loop_mode;			/* loopback mode */
 	struct atm_dev *dev;		/* device back-pointer */
 	struct suni_priv *next;		/* next SUNI */
 };
@@ -46,69 +47,62 @@
 
 
 static struct timer_list poll_timer;
-static int start_timer = 1;
 static struct suni_priv *sunis = NULL;
+static spinlock_t sunis_lock = SPIN_LOCK_UNLOCKED;
 
 
-static void suni_hz(unsigned long dummy)
+#define ADD_LIMITED(s,v) \
+    atomic_add((v),&stats->s); \
+    if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX);
+
+
+static void suni_hz(unsigned long from_timer)
 {
 	struct suni_priv *walk;
 	struct atm_dev *dev;
-	struct sonet_stats *stats;
+	struct k_sonet_stats *stats;
 
 	for (walk = sunis; walk; walk = walk->next) {
 		dev = walk->dev;
 		stats = &walk->sonet_stats;
 		PUT(0,MRI); /* latch counters */
 		udelay(1);
-		stats->section_bip += (GET(RSOP_SBL) & 0xff) |
-		    ((GET(RSOP_SBM) & 0xff) << 8);
-		if (stats->section_bip < 0) stats->section_bip = LONG_MAX;
-		stats->line_bip += (GET(RLOP_LBL) & 0xff) |
+		ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) |
+		    ((GET(RSOP_SBM) & 0xff) << 8));
+		ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) |
 		    ((GET(RLOP_LB) & 0xff) << 8) |
-		    ((GET(RLOP_LBM) & 0xf) << 16);
-		if (stats->line_bip < 0) stats->line_bip = LONG_MAX;
-		stats->path_bip += (GET(RPOP_PBL) & 0xff) |
-		    ((GET(RPOP_PBM) & 0xff) << 8);
-		if (stats->path_bip < 0) stats->path_bip = LONG_MAX;
-		stats->line_febe += (GET(RLOP_LFL) & 0xff) |
+		    ((GET(RLOP_LBM) & 0xf) << 16));
+		ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) |
+		    ((GET(RPOP_PBM) & 0xff) << 8));
+		ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) |
 		    ((GET(RLOP_LF) & 0xff) << 8) |
-		    ((GET(RLOP_LFM) & 0xf) << 16);
-		if (stats->line_febe < 0) stats->line_febe = LONG_MAX;
-		stats->path_febe += (GET(RPOP_PFL) & 0xff) |
-		    ((GET(RPOP_PFM) & 0xff) << 8);
-		if (stats->path_febe < 0) stats->path_febe = LONG_MAX;
-		stats->corr_hcs += GET(RACP_CHEC) & 0xff;
-		if (stats->corr_hcs < 0) stats->corr_hcs = LONG_MAX;
-		stats->uncorr_hcs += GET(RACP_UHEC) & 0xff;
-		if (stats->uncorr_hcs < 0) stats->uncorr_hcs = LONG_MAX;
-		stats->rx_cells += (GET(RACP_RCCL) & 0xff) |
+		    ((GET(RLOP_LFM) & 0xf) << 16));
+		ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) |
+		    ((GET(RPOP_PFM) & 0xff) << 8));
+		ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff);
+		ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff);
+		ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) |
 		    ((GET(RACP_RCC) & 0xff) << 8) |
-		    ((GET(RACP_RCCM) & 7) << 16);
-		if (stats->rx_cells < 0) stats->rx_cells = LONG_MAX;
-		stats->tx_cells += (GET(TACP_TCCL) & 0xff) |
+		    ((GET(RACP_RCCM) & 7) << 16));
+		ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) |
 		    ((GET(TACP_TCC) & 0xff) << 8) |
-		    ((GET(TACP_TCCM) & 7) << 16);
-		if (stats->tx_cells < 0) stats->tx_cells = LONG_MAX;
+		    ((GET(TACP_TCCM) & 7) << 16));
 	}
-	if (!start_timer) mod_timer(&poll_timer,jiffies+HZ);
+	if (from_timer) mod_timer(&poll_timer,jiffies+HZ);
 }
 
 
+#undef ADD_LIMITED
+
+
 static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
 {
-	unsigned long flags;
-	int error;
+	struct sonet_stats tmp;
+	int error = 0;
 
-	error = 0;
-	save_flags(flags);
-	cli();
-	if (arg)
-		error = copy_to_user(arg,&PRIV(dev)->sonet_stats,
-		    sizeof(struct sonet_stats));
-	if (zero && !error)
-		memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
-	restore_flags(flags);
+	sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
+	if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
+	if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
 	return error ? -EFAULT : 0;
 }
 
@@ -158,6 +152,29 @@
 }
 
 
+static int set_loopback(struct atm_dev *dev,int mode)
+{
+	unsigned char control;
+
+	control = GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE);
+	switch (mode) {
+		case ATM_LM_NONE:
+			break;
+		case ATM_LM_LOC_PHY:
+			control |= SUNI_MCT_DLE;
+			break;
+		case ATM_LM_RMT_PHY:
+			control |= SUNI_MCT_LLE;
+			break;
+		default:
+			return -EINVAL;
+	}
+	PUT(control,MCT);
+	PRIV(dev)->loop_mode = mode;
+	return 0;
+}
+
+
 static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
 {
 	switch (cmd) {
@@ -172,7 +189,6 @@
 		case SONET_GETDIAG:
 			return get_diag(dev,arg);
 		case SONET_SETFRAMING:
-			if (!capable(CAP_NET_ADMIN)) return -EPERM;
 			if (arg != SONET_FRAME_SONET) return -EINVAL;
 			return 0;
 		case SONET_GETFRAMING:
@@ -180,23 +196,14 @@
 			    -EFAULT : 0;
 		case SONET_GETFRSENSE:
 			return -EINVAL;
-		case SUNI_SETLOOP:
-			{
-				int int_arg = (int) (long) arg;
-
-				if (!capable(CAP_NET_ADMIN)) return -EPERM;
-				if (int_arg < 0 || int_arg > SUNI_LM_LOOP)
-					return -EINVAL;
-				PUT((GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE))
-				    | (int_arg == SUNI_LM_DIAG ? SUNI_MCT_DLE :
-				    0) | (int_arg == SUNI_LM_LOOP ?
-				    SUNI_MCT_LLE : 0),MCT);
-				PRIV(dev)->loop_mode = int_arg;
-				return 0;
-			}
-		case SUNI_GETLOOP:
+		case ATM_SETLOOP:
+			return set_loopback(dev,(int) (long) arg);
+		case ATM_GETLOOP:
 			return put_user(PRIV(dev)->loop_mode,(int *) arg) ?
 			    -EFAULT : 0;
+		case ATM_QUERYLOOP:
+			return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY,
+			    (int *) arg) ? -EFAULT : 0;
 		default:
 			return -ENOIOCTLCMD;
 	}
@@ -221,33 +228,31 @@
 static int suni_start(struct atm_dev *dev)
 {
 	unsigned long flags;
+	int first;
 
 	if (!(PRIV(dev) = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
 		return -ENOMEM;
 	PRIV(dev)->dev = dev;
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&sunis_lock,flags);
+	first = !sunis;
 	PRIV(dev)->next = sunis;
 	sunis = PRIV(dev);
-	restore_flags(flags);
-	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
+	spin_unlock_irqrestore(&sunis_lock,flags);
+	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
 	PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
 		/* interrupt on loss of signal */
 	poll_los(dev); /* ... and clear SUNI interrupts */
 	if (dev->signal == ATM_PHY_SIG_LOST)
 		printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
 		    dev->number);
-	PRIV(dev)->loop_mode = SUNI_LM_NONE;
+	PRIV(dev)->loop_mode = ATM_LM_NONE;
 	suni_hz(0); /* clear SUNI counters */
 	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
-	cli();
-	if (!start_timer) restore_flags(flags);
-	else {
-		start_timer = 0;
-		restore_flags(flags);
+	if (first) {
 		init_timer(&poll_timer);
 		poll_timer.expires = jiffies+HZ;
 		poll_timer.function = suni_hz;
+		poll_timer.data = 1;
 #if 0
 printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.prev,
     (unsigned long) poll_timer.next);
@@ -258,10 +263,28 @@
 }
 
 
+static int suni_stop(struct atm_dev *dev)
+{
+	struct suni_priv **walk;
+	unsigned long flags;
+
+	/* let SAR driver worry about stopping interrupts */
+	spin_lock_irqsave(&sunis_lock,flags);
+	for (walk = &sunis; *walk != PRIV(dev);
+	    walk = &PRIV((*walk)->dev)->next);
+	*walk = PRIV((*walk)->dev)->next;
+	if (!sunis) del_timer_sync(&poll_timer);
+	spin_unlock_irqrestore(&sunis_lock,flags);
+	kfree(PRIV(dev));
+	return 0;
+}
+
+
 static const struct atmphy_ops suni_ops = {
-	suni_start,
-	suni_ioctl,
-	suni_int
+	start:		suni_start,
+	ioctl:		suni_ioctl,
+	interrupt:	suni_int,
+	stop:		suni_stop,
 };
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/suni.h linux/drivers/atm/suni.h
--- v2.3.99-pre2/linux/drivers/atm/suni.h	Thu Aug 26 13:05:34 1999
+++ linux/drivers/atm/suni.h	Tue Mar 21 23:38:26 2000
@@ -1,6 +1,6 @@
-/* drivers/atm/suni.h - PMC SUNI (PHY) declarations */
+/* drivers/atm/suni.h - PMC PM5346 SUNI (PHY) declarations */
  
-/* Written 1995,1998 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
  
 
 #ifndef DRIVER_ATM_SUNI_H
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/uPD98402.c linux/drivers/atm/uPD98402.c
--- v2.3.99-pre2/linux/drivers/atm/uPD98402.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/atm/uPD98402.c	Tue Mar 21 23:38:26 2000
@@ -11,6 +11,7 @@
 #include <linux/sonet.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 
 #include "uPD98402.h"
 
@@ -23,8 +24,9 @@
 
 
 struct uPD98402_priv {
-	struct sonet_stats sonet_stats;	/* link diagnostics */
+	struct k_sonet_stats sonet_stats;/* link diagnostics */
 	unsigned char framing;		/* SONET/SDH framing */
+	int loop_mode;			/* loopback mode */
 };
 
 
@@ -36,23 +38,18 @@
 
 static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
 {
-	unsigned long flags;
- 	int error;
+	struct sonet_stats tmp;
+ 	int error = 0;
 
-	error = 0;
-	save_flags(flags);
-	cli();
-	PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
-	if (arg)
-		error = copy_to_user(arg,&PRIV(dev)->sonet_stats,
-		    sizeof(struct sonet_stats));
+	atomic_add(GET(HECCT),&PRIV(dev)->sonet_stats.uncorr_hcs);
+	sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
+	if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
 	if (zero && !error) {
-		memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
-		PRIV(dev)->sonet_stats.corr_hcs = -1;
-		PRIV(dev)->sonet_stats.tx_cells = -1;
-		PRIV(dev)->sonet_stats.rx_cells = -1;
+		/* unused fields are reported as -1, but we must not "adjust"
+		   them */
+		tmp.corr_hcs = tmp.tx_cells = tmp.rx_cells = 0;
+		sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
 	}
-	restore_flags(flags);
 	return error ? -EFAULT : 0;
 }
 
@@ -102,6 +99,39 @@
 }
 
 
+static int set_loopback(struct atm_dev *dev,int mode)
+{
+	unsigned char mode_reg;
+
+	mode_reg = GET(MDR) & ~(uPD98402_MDR_TPLP | uPD98402_MDR_ALP |
+	    uPD98402_MDR_RPLP);
+	switch (__ATM_LM_XTLOC(mode)) {
+		case __ATM_LM_NONE:
+			break;
+		case __ATM_LM_PHY:
+			mode_reg |= uPD98402_MDR_TPLP;
+			break;
+		case __ATM_LM_ATM:
+			mode_reg |= uPD98402_MDR_ALP;
+			break;
+		default:
+			return -EINVAL;
+	}
+	switch (__ATM_LM_XTRMT(mode)) {
+		case __ATM_LM_NONE:
+			break;
+		case __ATM_LM_PHY:
+			mode_reg |= uPD98402_MDR_RPLP;
+			break;
+		default:
+			return -EINVAL;
+	}
+	PUT(mode_reg,MDR);
+	PRIV(dev)->loop_mode = mode;
+	return 0;
+}
+
+
 static int uPD98402_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
 {
 	switch (cmd) {
@@ -117,35 +147,42 @@
 			    -EFAULT : 0;
 		case SONET_GETFRSENSE:
 			return get_sense(dev,arg);
+		case ATM_SETLOOP:
+			return set_loopback(dev,(int) (long) arg);
+		case ATM_GETLOOP:
+			return put_user(PRIV(dev)->loop_mode,(int *) arg) ?
+			    -EFAULT : 0;
+		case ATM_QUERYLOOP:
+			return put_user(ATM_LM_LOC_PHY | ATM_LM_LOC_ATM |
+			    ATM_LM_RMT_PHY,(int *) arg) ? -EFAULT : 0;
 		default:
-		    return -ENOIOCTLCMD;
+			return -ENOIOCTLCMD;
 	}
 }
 
 
+#define ADD_LIMITED(s,v) \
+    { atomic_add(GET(v),&PRIV(dev)->sonet_stats.s); \
+    if (atomic_read(&PRIV(dev)->sonet_stats.s) < 0) \
+	atomic_set(&PRIV(dev)->sonet_stats.s,INT_MAX); }
+
+
 static void stat_event(struct atm_dev *dev)
 {
 	unsigned char events;
 
 	events = GET(PCR);
-	if (events & uPD98402_PFM_PFEB)
-		if ((PRIV(dev)->sonet_stats.path_febe += GET(PFECB)) < 0)
-			PRIV(dev)->sonet_stats.path_febe = LONG_MAX;
-	if (events & uPD98402_PFM_LFEB)
-		if ((PRIV(dev)->sonet_stats.line_febe += GET(LECCT)) < 0)
-			PRIV(dev)->sonet_stats.line_febe = LONG_MAX;
-	if (events & uPD98402_PFM_B3E)
-		if ((PRIV(dev)->sonet_stats.path_bip += GET(B3ECT)) < 0)
-			PRIV(dev)->sonet_stats.path_bip = LONG_MAX;
-	if (events & uPD98402_PFM_B2E)
-		if ((PRIV(dev)->sonet_stats.line_bip += GET(B2ECT)) < 0)
-			PRIV(dev)->sonet_stats.line_bip = LONG_MAX;
-	if (events & uPD98402_PFM_B1E)
-		if ((PRIV(dev)->sonet_stats.section_bip += GET(B1ECT)) < 0)
-			PRIV(dev)->sonet_stats.section_bip = LONG_MAX;
+	if (events & uPD98402_PFM_PFEB) ADD_LIMITED(path_febe,PFECB);
+	if (events & uPD98402_PFM_LFEB) ADD_LIMITED(line_febe,LECCT);
+	if (events & uPD98402_PFM_B3E) ADD_LIMITED(path_bip,B3ECT);
+	if (events & uPD98402_PFM_B2E) ADD_LIMITED(line_bip,B2ECT);
+	if (events & uPD98402_PFM_B1E) ADD_LIMITED(section_bip,B1ECT);
 }
 
 
+#undef ADD_LIMITED
+
+
 static void uPD98402_int(struct atm_dev *dev)
 {
 	static unsigned long silence = 0;
@@ -158,7 +195,8 @@
 		if (reason & uPD98402_INT_PFM) stat_event(dev);
 		if (reason & uPD98402_INT_PCO) {
 			(void) GET(PCOCR); /* clear interrupt cause */
-			PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
+			atomic_add(GET(HECCT),
+			    &PRIV(dev)->sonet_stats.uncorr_hcs);
 		}
 		if ((reason & uPD98402_INT_RFO) && 
 		    (time_after(jiffies, silence) || silence == 0)) {
@@ -172,10 +210,10 @@
 
 static int uPD98402_start(struct atm_dev *dev)
 {
-DPRINTK("phy_start\n");
+	DPRINTK("phy_start\n");
 	if (!(PRIV(dev) = kmalloc(sizeof(struct uPD98402_priv),GFP_KERNEL)))
 		return -ENOMEM;
-	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
+	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
 	(void) GET(PCR); /* clear performance events */
 	PUT(uPD98402_PFM_FJ,PCMR); /* ignore frequency adj */
 	(void) GET(PCOCR); /* clear overflows */
@@ -184,15 +222,26 @@
 	PUT(~(uPD98402_INT_PFM | uPD98402_INT_ALM | uPD98402_INT_RFO |
 	  uPD98402_INT_LOS),PIMR); /* enable them */
 	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
+	atomic_set(&PRIV(dev)->sonet_stats.corr_hcs,-1);
+	atomic_set(&PRIV(dev)->sonet_stats.tx_cells,-1);
+	atomic_set(&PRIV(dev)->sonet_stats.rx_cells,-1);
 	return 0;
 }
 
 
+static int uPD98402_stop(struct atm_dev *dev)
+{
+	/* let SAR driver worry about stopping interrupts */
+	kfree(PRIV(dev));
+	return 0;
+}
+
 
 static const struct atmphy_ops uPD98402_ops = {
-	uPD98402_start,
-	uPD98402_ioctl,	/* no ioctl yet */
-	uPD98402_int
+	start:		uPD98402_start,
+	ioctl:		uPD98402_ioctl,
+	interrupt:	uPD98402_int,
+	stop:		uPD98402_stop,
 };
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/zatm.c linux/drivers/atm/zatm.c
--- v2.3.99-pre2/linux/drivers/atm/zatm.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/atm/zatm.c	Tue Mar 21 23:38:26 2000
@@ -21,10 +21,12 @@
 #include <linux/init.h>
 #include <linux/atm_zatm.h>
 #include <linux/capability.h>
+#include <linux/bitops.h>
 #include <asm/byteorder.h>
 #include <asm/system.h>
 #include <asm/string.h>
 #include <asm/io.h>
+#include <asm/atomic.h>
 #include <asm/uaccess.h>
 
 #include "uPD98401.h"
@@ -637,7 +639,7 @@
 		}
 		if (!size) {
 			dev_kfree_skb_irq(skb);
-			if (vcc) vcc->stats->rx_err++;
+			if (vcc) atomic_inc(&vcc->stats->rx_err);
 			continue;
 		}
 		if (!atm_charge(vcc,skb->truesize)) {
@@ -647,7 +649,7 @@
 		skb->len = size;
 		ATM_SKB(skb)->vcc = vcc;
 		vcc->push(vcc,skb);
-		vcc->stats->rx++;
+		atomic_inc(&vcc->stats->rx);
 	}
 	zout(pos & 0xffff,MTA(mbx));
 #if 0 /* probably a stupid idea */
@@ -914,7 +916,7 @@
 			skb_queue_head(&zatm_vcc->backlog,skb);
 			break;
 		}
-	vcc->stats->tx++;
+	atomic_inc(&vcc->stats->tx);
 	wake_up(&zatm_vcc->tx_wait);
 }
 
@@ -1537,7 +1539,7 @@
 {
         DPRINTK(">zatm_close\n");
         if (!ZATM_VCC(vcc)) return;
-	vcc->flags &= ~ATM_VF_READY;
+	clear_bit(ATM_VF_READY,&vcc->flags);
         close_rx(vcc);
 	EVENT("close_tx\n",0,0);
         close_tx(vcc);
@@ -1545,7 +1547,7 @@
         /* deallocate memory */
         kfree(ZATM_VCC(vcc));
         ZATM_VCC(vcc) = NULL;
-	vcc->flags &= ~ATM_VF_ADDR;
+	clear_bit(ATM_VF_ADDR,&vcc->flags);
 }
 
 
@@ -1557,20 +1559,20 @@
 
 	DPRINTK(">zatm_open\n");
 	zatm_dev = ZATM_DEV(vcc->dev);
-	if (!(vcc->flags & ATM_VF_PARTIAL)) ZATM_VCC(vcc) = NULL;
+	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) ZATM_VCC(vcc) = NULL;
 	error = atm_find_ci(vcc,&vpi,&vci);
 	if (error) return error;
 	vcc->vpi = vpi;
 	vcc->vci = vci;
 	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
-		vcc->flags |= ATM_VF_ADDR;
+		set_bit(ATM_VF_ADDR,&vcc->flags);
 	if (vcc->qos.aal != ATM_AAL5) return -EINVAL; /* @@@ AAL0 */
 	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
 	    vcc->vci);
-	if (!(vcc->flags & ATM_VF_PARTIAL)) {
+	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
 		zatm_vcc = kmalloc(sizeof(struct zatm_vcc),GFP_KERNEL);
 		if (!zatm_vcc) {
-			vcc->flags &= ~ATM_VF_ADDR;
+			clear_bit(ATM_VF_ADDR,&vcc->flags);
 			return -ENOMEM;
 		}
 		ZATM_VCC(vcc) = zatm_vcc;
@@ -1593,7 +1595,7 @@
 		zatm_close(vcc);
 		return error;
         }
-	vcc->flags |= ATM_VF_READY;
+	set_bit(ATM_VF_READY,&vcc->flags);
         return 0;
 }
 
@@ -1733,7 +1735,7 @@
 	int error;
 
 	EVENT(">zatm_send 0x%lx\n",(unsigned long) skb,0);
-	if (!ZATM_VCC(vcc)->tx_chan || !(vcc->flags & ATM_VF_READY)) {
+	if (!ZATM_VCC(vcc)->tx_chan || !test_bit(ATM_VF_READY,&vcc->flags)) {
 		if (vcc->pop) vcc->pop(vcc,skb);
 		else dev_kfree_skb(skb);
 		return -EINVAL;
@@ -1809,7 +1811,7 @@
 		while ((pci_dev = pci_find_device(PCI_VENDOR_ID_ZEITNET,type ?
 		    PCI_DEVICE_ID_ZEITNET_1225 : PCI_DEVICE_ID_ZEITNET_1221,
 		    pci_dev))) {
-			dev = atm_dev_register(DEV_LABEL,&ops,-1,0);
+			dev = atm_dev_register(DEV_LABEL,&ops,-1,NULL);
 			if (!dev) break;
 			zatm_dev->pci_dev = pci_dev;
 			ZATM_DEV(dev) = zatm_dev;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.3.99-pre2/linux/drivers/block/loop.c	Sun Feb 20 21:12:38 2000
+++ linux/drivers/block/loop.c	Tue Mar 21 11:30:08 2000
@@ -190,7 +190,7 @@
 		page = grab_cache_page(mapping, index);
 		if (!page)
 			goto fail;
-		if (aops->prepare_write(page, offset, offset+size))
+		if (aops->prepare_write(file, page, offset, offset+size))
 			goto unlock;
 		kaddr = (char*)page_address(page);
 		if ((lo->transfer)(lo, WRITE, kaddr+offset, data, size, IV))
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/block/lvm.c linux/drivers/block/lvm.c
--- v2.3.99-pre2/linux/drivers/block/lvm.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/block/lvm.c	Thu Mar 23 08:13:35 2000
@@ -2527,6 +2527,15 @@
  *
  * Is this the real thing?
  *
+ *	No, it's bollocks. md.c tries to do a bit different thing that might
+ * _somewhat_ work eons ago. Neither does any good these days. mount() couldn't
+ * care less for icache (it cares only for ->s_root->d_count and if we want
+ * loopback mounts even that will stop). BTW, with the form used here mount()
+ * would have to scan the _whole_ icache to detect the attempt - how on the
+ * Earth could it guess the i_ino of your dummy inode? Official line on the
+ * exclusion between mount()/swapon()/open()/etc. is Just Don't Do It(tm).
+ * If you can convince Linus that it's worth changing - fine, then you'll need
+ * to do blkdev_get()/blkdev_put(). Until then...
  */
 struct inode *lvm_get_inode(int dev)
 {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.3.99-pre2/linux/drivers/block/md.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/md.c	Thu Mar 23 08:13:35 2000
@@ -1098,7 +1098,7 @@
 	}
 	memset(rdev, 0, sizeof(*rdev));
 
-	if (!fs_may_mount(newdev)) {
+	if (get_super(newdev)) {
 		printk("md: can not import %s, has active inodes!\n",
 			partition_name(newdev));
 		err = -EBUSY;
@@ -1722,7 +1722,7 @@
 	int err = 0, resync_interrupted = 0;
 	kdev_t dev = mddev_to_kdev(mddev);
  
-	if (!ro && !fs_may_mount (dev)) {
+	if (!ro && get_super(dev)) {
 		printk (STILL_MOUNTED, mdidx(mddev));
 		OUT(-EBUSY);
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c
--- v2.3.99-pre2/linux/drivers/block/paride/paride.c	Mon Oct  4 15:49:29 1999
+++ linux/drivers/block/paride/paride.c	Thu Mar 23 08:47:04 2000
@@ -20,6 +20,7 @@
 
 #include <linux/module.h>
 #include <linux/config.h>
+#include <linux/kmod.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
@@ -335,6 +336,9 @@
 	int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0};
 
 	s = protocol; e = s+1;
+
+	if (!protocols[0])
+		request_module ("paride_protocol");
 
 	if (autoprobe) {
 		s = 0; 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.3.99-pre2/linux/drivers/cdrom/cm206.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/cdrom/cm206.c	Wed Mar 22 22:23:55 2000
@@ -1483,6 +1483,6 @@
 #endif /* !MODULE */
 /*
  * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -m486 -DCPU=486 -D__SMP__ -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o cm206.o cm206.c"
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D__SMP__ -pipe -fno-strength-reduce -m486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o cm206.o cm206.c"
  * End:
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/acquirewdt.c linux/drivers/char/acquirewdt.c
--- v2.3.99-pre2/linux/drivers/char/acquirewdt.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/char/acquirewdt.c	Tue Mar 21 16:57:09 2000
@@ -37,8 +37,10 @@
 #include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
+#include <linux/spinlock.h>
 
 static int acq_is_open=0;
+static spinlock_t acq_lock;
 
 /*
  *	You must set these - there is no sane way to probe for this board.
@@ -117,8 +119,12 @@
 	switch(MINOR(inode->i_rdev))
 	{
 		case WATCHDOG_MINOR:
+			spin_lock(&acq_lock);
 			if(acq_is_open)
+			{
+				spin_unlock(&acq_lock);
 				return -EBUSY;
+			}
 			MOD_INC_USE_COUNT;
 			/*
 			 *	Activate 
@@ -126,6 +132,7 @@
 	 
 			acq_is_open=1;
 			inb_p(WDT_START);      
+			spin_unlock(&acq_lock);
 			return 0;
 		default:
 			return -ENODEV;
@@ -136,10 +143,12 @@
 {
 	if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
 	{
+		spin_lock(&acq_lock);
 #ifndef CONFIG_WATCHDOG_NOWAYOUT	
 		inb_p(WDT_STOP);
 #endif		
 		acq_is_open=0;
+		spin_unlock(&acq_lock);
 	}
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -211,6 +220,7 @@
 {
 	printk("WDT driver for Acquire single board computer initialising.\n");
 
+	spin_lock_init(&acq_lock);
 	misc_register(&acq_miscdev);
 	request_region(WDT_STOP, 1, "Acquire WDT");
 	request_region(WDT_START, 1, "Acquire WDT");
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/amigamouse.c linux/drivers/char/amigamouse.c
--- v2.3.99-pre2/linux/drivers/char/amigamouse.c	Sun Nov  7 16:37:34 1999
+++ linux/drivers/char/amigamouse.c	Tue Mar 21 14:43:39 2000
@@ -162,6 +162,10 @@
 
 static int open_mouse(struct inode * inode, struct file * file)
 {
+	/* Lock module first - request_irq might sleep */
+	
+	MOD_INC_USE_COUNT;
+	
 	/*
 	 *  use VBL to poll mouse deltas
 	 */
@@ -169,10 +173,10 @@
 	if(request_irq(IRQ_AMIGA_VERTB, mouse_interrupt, 0,
 	               "Amiga mouse", mouse_interrupt)) {
 		printk(KERN_INFO "Installing Amiga mouse failed.\n");
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
-	MOD_INC_USE_COUNT;
 #if AMIGA_OLD_INT
 	AMI_MSE_INT_ON();
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/amiserial.c linux/drivers/char/amiserial.c
--- v2.3.99-pre2/linux/drivers/char/amiserial.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/char/amiserial.c	Wed Mar 22 22:23:55 2000
@@ -2259,6 +2259,6 @@
 
 /*
   Local variables:
-  compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce  -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -DEXPORT_SYMTAB -c amiserial.c"
+  compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce  -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -DEXPORT_SYMTAB -c amiserial.c"
   End:
 */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/applicom.c linux/drivers/char/applicom.c
--- v2.3.99-pre2/linux/drivers/char/applicom.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/char/applicom.c	Tue Mar 21 14:43:39 2000
@@ -14,7 +14,7 @@
 /* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter  */
 /* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3    */
 /* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes       */
-/* adresses de base des cartes, IOCTL 6 plus complet                         */ 
+/* adresses de base des cartes, IOCTL 6 plus complet                         */
 /* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification  */
 /* de code autre que le texte V2.6.1 en V2.8.0                               */
 /*****************************************************************************/
@@ -37,108 +37,102 @@
 #undef DEBUG
 #define DEVPRIO PZERO+8
 #define FALSE 0
-#define TRUE  ~FALSE 
-#define MAX_BOARD 8                 /* maximum of pc board possible */
+#define TRUE  ~FALSE
+#define MAX_BOARD 8		/* maximum of pc board possible */
 #define MAX_ISA_BOARD 4
 #define LEN_RAM_IO 0x800
 #define AC_MINOR 157
 
 #ifndef PCI_VENDOR_ID_APPLICOM
-#define PCI_VENDOR_ID_APPLICOM                0x1389 
+#define PCI_VENDOR_ID_APPLICOM                0x1389
 #define PCI_DEVICE_ID_APPLICOM_PCIGENERIC     0x0001
 #define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002
 #define PCI_DEVICE_ID_APPLICOM_PCI2000PFB     0x0003
 #define MAX_PCI_DEVICE_NUM 3
 #endif
 
-static char *applicom_pci_devnames[]={
-  "PCI board", "PCI2000IBS / PCI2000CAN", "PCI2000PFB"};
+static char *applicom_pci_devnames[] = {
+	"PCI board", "PCI2000IBS / PCI2000CAN", "PCI2000PFB"
+};
 
 MODULE_AUTHOR("David Woodhouse & Applicom International");
 MODULE_DESCRIPTION("Driver for Applicom Profibus card");
 MODULE_PARM(irq, "i");
 MODULE_PARM_DESC(irq, "IRQ of the Applicom board");
-MODULE_PARM(mem,"i");
+MODULE_PARM(mem, "i");
 MODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board");
 
 MODULE_SUPPORTED_DEVICE("ac");
 
 
 struct applicom_board {
-  unsigned long PhysIO;
-  unsigned long RamIO;
+	unsigned long PhysIO;
+	unsigned long RamIO;
 #if LINUX_VERSION_CODE > 0x20300
-  wait_queue_head_t FlagSleepSend;
+	wait_queue_head_t FlagSleepSend;
 #else
-  struct wait_queue *FlagSleepSend;
+	struct wait_queue *FlagSleepSend;
 #endif
-  long irq;
+	long irq;
 } apbs[MAX_BOARD];
 
-static unsigned int irq=0;                    /* interrupt number IRQ       */
-static unsigned long mem=0;                   /* physical segment of board  */
+static unsigned int irq = 0;	/* interrupt number IRQ       */
+static unsigned long mem = 0;	/* physical segment of board  */
 
-static unsigned int numboards;                /* number of installed boards */
+static unsigned int numboards;	/* number of installed boards */
 static volatile unsigned char Dummy;
 #if LINUX_VERSION_CODE > 0x20300
-static DECLARE_WAIT_QUEUE_HEAD (FlagSleepRec);
+static DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec);
 #else
 static struct wait_queue *FlagSleepRec;
 #endif
-static unsigned int WriteErrorCount;          /* number of write error      */
-static unsigned int ReadErrorCount;           /* number of read error       */
-static unsigned int DeviceErrorCount;         /* number of device error     */
+static unsigned int WriteErrorCount;	/* number of write error      */
+static unsigned int ReadErrorCount;	/* number of read error       */
+static unsigned int DeviceErrorCount;	/* number of device error     */
 
 static loff_t ac_llseek(struct file *file, loff_t offset, int origin);
 static int ac_open(struct inode *inode, struct file *filp);
-static ssize_t ac_read (struct file *filp, char *buf, size_t count, loff_t *ptr);
-static ssize_t ac_write (struct file *file, const char *buf, size_t count, loff_t *ppos);
+static ssize_t ac_read(struct file *filp, char *buf, size_t count, loff_t * ptr);
+static ssize_t ac_write(struct file *file, const char *buf, size_t count, loff_t * ppos);
 static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
 static int ac_release(struct inode *inode, struct file *file);
 static void ac_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 
-struct file_operations ac_fops={
-	llseek:		ac_llseek,
-	read:		ac_read,
-	write:		ac_write,
-	ioctl:		ac_ioctl,
-	open:		ac_open,
-	release:	ac_release,
+struct file_operations ac_fops = {
+	llseek:ac_llseek,
+	read:ac_read,
+	write:ac_write,
+	ioctl:ac_ioctl,
+	open:ac_open,
+	release:ac_release,
 };
 
-struct miscdevice ac_miscdev={
-  AC_MINOR,
-  "ac",
-  &ac_fops
+struct miscdevice ac_miscdev = {
+	AC_MINOR,
+	"ac",
+	&ac_fops
 };
 
-int ac_register_board(unsigned long physloc, unsigned long loc, 
-		      unsigned char boardno)
+int ac_register_board(unsigned long physloc, unsigned long loc, unsigned char boardno)
 {
 	volatile unsigned char byte_reset_it;
 
-	if((readb(loc + CONF_END_TEST)     != 0x00) ||
-	   (readb(loc + CONF_END_TEST + 1) != 0x55) ||
-	   (readb(loc + CONF_END_TEST + 2) != 0xAA) ||
-	   (readb(loc + CONF_END_TEST + 3) != 0xFF))
-	  return 0;
+	if ((readb(loc + CONF_END_TEST) != 0x00) || (readb(loc + CONF_END_TEST + 1) != 0x55) || (readb(loc + CONF_END_TEST + 2) != 0xAA) || (readb(loc + CONF_END_TEST + 3) != 0xFF))
+		return 0;
 
 
 	if (!boardno)
-	  boardno = readb(loc + NUMCARD_OWNER_TO_PC);
+		boardno = readb(loc + NUMCARD_OWNER_TO_PC);
+
+	if (!boardno && boardno > MAX_BOARD) {
+		printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n", boardno, physloc, MAX_BOARD);
+		return 0;
+	}
 
-	if (!boardno && boardno > MAX_BOARD)
-	  {
-	    printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n",boardno, physloc, MAX_BOARD);
-	    return 0;
-	  }
-
-	if (apbs[boardno-1].RamIO)
-	  {
-	    printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", 
-		   boardno, physloc, boardno, apbs[boardno-1].PhysIO);
-	    return 0;
-	  }
+	if (apbs[boardno - 1].RamIO) {
+		printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", boardno, physloc, boardno, apbs[boardno - 1].PhysIO);
+		return 0;
+	}
 
 	boardno--;
 
@@ -152,7 +146,7 @@
 	byte_reset_it = readb(loc + RAM_IT_TO_PC);
 
 	numboards++;
-	return 	boardno+1;
+	return boardno + 1;
 }
 
 #ifdef MODULE
@@ -164,22 +158,21 @@
 	int i;
 
 	misc_deregister(&ac_miscdev);
-	for (i=0; i< MAX_BOARD; i++)
-	  {
-	    if (!apbs[i].RamIO)
-	      continue;
-	    iounmap((void *)apbs[i].RamIO);
-	    if (apbs[i].irq)
-	      free_irq(apbs[i].irq,&ac_open);     
-	  }
-	//	printk("Removing Applicom module\n");
+	for (i = 0; i < MAX_BOARD; i++) {
+		if (!apbs[i].RamIO)
+			continue;
+		iounmap((void *) apbs[i].RamIO);
+		if (apbs[i].irq)
+			free_irq(apbs[i].irq, &ac_open);
+	}
+	//      printk("Removing Applicom module\n");
 }
 
-#endif /* MODULE */
+#endif				/* MODULE */
 
 int __init applicom_init(void)
 {
-	int i, numisa=0;
+	int i, numisa = 0;
 	struct pci_dev *dev = NULL;
 	void *RamIO;
 	int boardno;
@@ -190,157 +183,137 @@
 #endif
 
 	printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.16 1999/08/28 15:11:50 dwmw2 Exp $\n");
-	
+
 	/* No mem and irq given - check for a PCI card */
-	
-	while ( (dev = pci_find_device(PCI_VENDOR_ID_APPLICOM, 1, dev)))
-	  {
-	    //	    mem = dev->base_address[0];
-	    // irq = dev->irq;
-
-	    RamIO = ioremap(PCI_BASE_ADDRESS(dev), LEN_RAM_IO);
-
-	    if (!RamIO) {
-	      printk(KERN_INFO "ac.o: Failed to ioremap PCI memory space at 0x%lx\n", PCI_BASE_ADDRESS(dev));
-	      return -EIO;
-	    }
-	    
-	    printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n",
-		   applicom_pci_devnames[dev->device-1], PCI_BASE_ADDRESS(dev), 
-		   dev->irq);
-
-	    if (!(boardno = ac_register_board(PCI_BASE_ADDRESS(dev),
-					      (unsigned long)RamIO,0)))
-	      {
-		printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n");
-		iounmap(RamIO);
-		continue;
-	      }
-	    
-	    if (request_irq(dev->irq, &ac_interrupt, SA_SHIRQ, "Applicom PCI", &ac_open))
-	      {
-		printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq);
-		iounmap(RamIO);
-		apbs[boardno-1].RamIO = 0;
-		continue;
-	      }
 
-	    /* Enable interrupts. */
+	while ((dev = pci_find_device(PCI_VENDOR_ID_APPLICOM, 1, dev))) {
+		//      mem = dev->base_address[0];
+		// irq = dev->irq;
 
-	    writeb(0x40, apbs[boardno-1].RamIO + RAM_IT_FROM_PC);
+		RamIO = ioremap(PCI_BASE_ADDRESS(dev), LEN_RAM_IO);
+
+		if (!RamIO) {
+			printk(KERN_INFO "ac.o: Failed to ioremap PCI memory space at 0x%lx\n", PCI_BASE_ADDRESS(dev));
+			return -EIO;
+		}
+
+		printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n", applicom_pci_devnames[dev->device - 1], PCI_BASE_ADDRESS(dev), dev->irq);
+
+		if (!(boardno = ac_register_board(PCI_BASE_ADDRESS(dev), (unsigned long) RamIO, 0))) {
+			printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n");
+			iounmap(RamIO);
+			continue;
+		}
+
+		if (request_irq(dev->irq, &ac_interrupt, SA_SHIRQ, "Applicom PCI", &ac_open)) {
+			printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq);
+			iounmap(RamIO);
+			apbs[boardno - 1].RamIO = 0;
+			continue;
+		}
+
+		/* Enable interrupts. */
+
+		writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC);
+
+		apbs[boardno - 1].irq = dev->irq;
+	}
 
-	    apbs[boardno-1].irq = dev->irq;
-	  }
-	
 	/* Finished with PCI cards. If none registered, 
 	 * and there was no mem/irq specified, exit */
 
-	if (!mem || !irq)
-	  {
-	    if (numboards)
-	      goto fin;
-	    else
-	      {
-		printk(KERN_INFO "ac.o: No PCI boards found.\n");
-		printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n");
-		return -ENXIO;
-	      }
-	  }
-	
+	if (!mem || !irq) {
+		if (numboards)
+			goto fin;
+		else {
+			printk(KERN_INFO "ac.o: No PCI boards found.\n");
+			printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n");
+			return -ENXIO;
+		}
+	}
+
 	/* Now try the specified ISA cards */
 
-	RamIO = ioremap(mem, LEN_RAM_IO * MAX_ISA_BOARD); 
+	RamIO = ioremap(mem, LEN_RAM_IO * MAX_ISA_BOARD);
 
 	if (!RamIO) {
-	  printk(KERN_INFO "ac.o: Failed to ioremap ISA memory space at 0x%lx\n",mem);
+		printk(KERN_INFO "ac.o: Failed to ioremap ISA memory space at 0x%lx\n", mem);
 	}
-	
-	for (i=0; i< MAX_ISA_BOARD; i++)
-	  {
-	    RamIO = ioremap(mem+ (LEN_RAM_IO*i), LEN_RAM_IO); 
-
-	    if (!RamIO) {
-	      printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n",i+1);
-	      continue;
-	    }
-
-	    if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i),
-					      (unsigned long)RamIO,i+1))) {
-	      iounmap(RamIO);
-	      continue;
-	    }
-	    
-	    printk("Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq);
-
-	    if (!numisa)
-	      {
-		if (request_irq(irq, &ac_interrupt, SA_SHIRQ, "Applicom ISA", &ac_open))
-		  {
-		    printk("Could not allocate IRQ %d for ISA Applicom device.\n", irq);
-		    iounmap((void *)RamIO);
-		    apbs[boardno-1].RamIO = 0;
-		  }
-		apbs[boardno-1].irq=irq;
-	      }
-	    else
-	      apbs[boardno-1].irq=0;
 
-	    numisa++;
-	  }
+	for (i = 0; i < MAX_ISA_BOARD; i++) {
+		RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO);
+
+		if (!RamIO) {
+			printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1);
+			continue;
+		}
+
+		if (!(boardno = ac_register_board((unsigned long) mem + (LEN_RAM_IO * i), (unsigned long) RamIO, i + 1))) {
+			iounmap(RamIO);
+			continue;
+		}
+
+		printk("Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO * i), irq);
+
+		if (!numisa) {
+			if (request_irq(irq, &ac_interrupt, SA_SHIRQ, "Applicom ISA", &ac_open)) {
+				printk("Could not allocate IRQ %d for ISA Applicom device.\n", irq);
+				iounmap((void *) RamIO);
+				apbs[boardno - 1].RamIO = 0;
+			}
+			apbs[boardno - 1].irq = irq;
+		} else
+			apbs[boardno - 1].irq = 0;
+
+		numisa++;
+	}
 
 	if (!numisa)
-	  printk("ac.o: No valid ISA Applicom boards found at mem 0x%lx\n",mem);
+		printk("ac.o: No valid ISA Applicom boards found at mem 0x%lx\n", mem);
 
 #if LINUX_VERSION_CODE > 0x20300
 	init_waitqueue_head(&FlagSleepRec);
 #else
-	FlagSleepRec     = NULL;
+	FlagSleepRec = NULL;
 #endif
-	WriteErrorCount  = 0;
-	ReadErrorCount   = 0;
+	WriteErrorCount = 0;
+	ReadErrorCount = 0;
 	DeviceErrorCount = 0;
 
-fin:
-	if (numboards)
-	  {
-	    misc_register (&ac_miscdev);
-	    for (i=0; i<MAX_BOARD; i++)
-	      {
-		int serial;
-		char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
-
-		if (!apbs[i].RamIO)
-		  continue;
-		
-		for(serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
-		  boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
-		boardname[serial]=0;
-		
+      fin:
+	if (numboards) {
+		misc_register(&ac_miscdev);
+		for (i = 0; i < MAX_BOARD; i++) {
+			int serial;
+			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
+
+			if (!apbs[i].RamIO)
+				continue;
+
+			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
+				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
+			boardname[serial] = 0;
+
+
+			printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d", i + 1, boardname, (int) (readb(apbs[i].RamIO + VERS) >> 4), (int) (readb(apbs[i].RamIO + VERS) & 0xF));
+
+			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 2));
+
+			if (serial != 0)
+				printk(" S/N %d\n", serial);
+			else
+				printk("\n");
+		}
+		return 0;
+	}
 
-		printk("Applicom board %d: %s, PROM V%d.%d",
-		       i+1, boardname,
-		       (int)(readb(apbs[i].RamIO + VERS) >> 4),
-		       (int)(readb(apbs[i].RamIO + VERS) & 0xF));
-
-		serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + 
-		  (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + 
-		  (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
-		
-		if (serial != 0)
-		    printk (" S/N %d\n", serial);
-		else
-		  printk("\n");
-	      }
-	    return 0;
-	  }
-	
 	else
-	  return -ENXIO;
+		return -ENXIO;
 }
 
 
 #ifndef MODULE
-__initcall (applicom_init);
+__initcall(applicom_init);
 #endif
 
 static loff_t ac_llseek(struct file *file, loff_t offset, int origin)
@@ -361,477 +334,455 @@
 }
 
 
-static ssize_t ac_write (struct file *file, const char *buf, size_t count, loff_t *ppos)
+static ssize_t ac_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
 {
-  unsigned int NumCard;                /* Board number 1 -> 8           */
-  unsigned int IndexCard;              /* Index board number 0 -> 7     */
-  unsigned char TicCard;               /* Board TIC to send             */
-  unsigned long flags;                 /* Current priority              */
-  struct st_ram_io st_loc;
-  struct mailbox tmpmailbox;
-
-  if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
-	  printk("Hmmm. write() of Applicom card, length %d != expected %d\n",count,sizeof(struct st_ram_io) + sizeof(struct mailbox));
-	  return -EINVAL;
-  }
-
-  if(copy_from_user (&st_loc, buf, sizeof(struct st_ram_io))) {
-	  return -EFAULT;
-  }
-  if(copy_from_user (&tmpmailbox, &buf[sizeof(struct st_ram_io)], sizeof(struct mailbox))) {
-	  return -EFAULT;
-  }
-
-  NumCard   = st_loc.num_card;         /* board number to send          */
-  TicCard   = st_loc.tic_des_from_pc;  /* tic number to send            */
-  IndexCard = NumCard -1;
-  if((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO)
-  {                                    /* User board number not OK      */
-    // printk("Write to invalid Applicom board %d\n", NumCard);
-    return -EINVAL;                    /* Return error code user buffer */
-  }
-	  
+	unsigned int NumCard;	/* Board number 1 -> 8           */
+	unsigned int IndexCard;	/* Index board number 0 -> 7     */
+	unsigned char TicCard;	/* Board TIC to send             */
+	unsigned long flags;	/* Current priority              */
+	struct st_ram_io st_loc;
+	struct mailbox tmpmailbox;
+
+	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
+		printk("Hmmm. write() of Applicom card, length %d != expected %d\n", count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&st_loc, buf, sizeof(struct st_ram_io))) {
+		return -EFAULT;
+	}
+	if (copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)], sizeof(struct mailbox))) {
+		return -EFAULT;
+	}
+
+	NumCard = st_loc.num_card;	/* board number to send          */
+	TicCard = st_loc.tic_des_from_pc;	/* tic number to send            */
+	IndexCard = NumCard - 1;
+	if ((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO) {	/* User board number not OK      */
+		// printk("Write to invalid Applicom board %d\n", NumCard);
+		return -EINVAL;	/* Return error code user buffer */
+	}
 #ifdef DEBUG
-  {
-    int c;
-    
-    printk("Write to applicom card #%d. struct st_ram_io follows:",NumCard);
-    
-    
-    
-    for (c=0; c< sizeof(struct st_ram_io);)
-      {
-	printk("\n%5.5X: %2.2X",c,((unsigned char *)&st_loc)[c]);
-	
-	for (c++ ; c%8 && c<sizeof(struct st_ram_io); c++)
-	  {
-	    printk(" %2.2X",((unsigned char *)&st_loc)[c]);
-	  }
-      }
+	{
+		int c;
 
-	   printk("\nstruct mailbox follows:");
-	 
-    for (c=0; c< sizeof(struct mailbox);)
-      {
-	printk("\n%5.5X: %2.2X",c,((unsigned char *)&tmpmailbox)[c]);
-	
-	for (c++ ; c%8 && c<sizeof(struct mailbox); c++)
-	  {
-	    printk(" %2.2X",((unsigned char *)&tmpmailbox)[c]);
-	  }
-      }
-    
-    printk("\n");
-  }
-           	  
-#endif
-
-  save_flags (flags);
-  cli();                               /* disable interrupt             */
-
-  if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2)  /* Test octet ready correct */
-  {
-    Dummy = readb(apbs[IndexCard].RamIO + VERS);
-    restore_flags(flags);
-    printk("APPLICOM driver write error board %d, DataFromPcReady = %d\n",
-           IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
-    DeviceErrorCount++;
-    return -EIO;
-  }
-  while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0)
-  {
-    Dummy = readb(apbs[IndexCard].RamIO + VERS);
-    restore_flags(flags);
-    interruptible_sleep_on (&apbs[IndexCard].FlagSleepSend);
-    if (signal_pending(current))
-      return -EINTR;
-    save_flags(flags);
-    cli();
-  }
-  writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
-
-  //  memcpy_toio ((void *)apbs[IndexCard].PtrRamFromPc, (void *)&tmpmailbox, sizeof(struct mailbox));
-  {
-    unsigned char *from = (unsigned char *)&tmpmailbox;
-    unsigned long to = (unsigned long)apbs[IndexCard].RamIO + RAM_FROM_PC;
-    int c;
-
-    for (c=0; c<sizeof(struct mailbox) ; c++)
-      writeb(*(from++), to++);
-  }
-  writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC);
-  writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC);
-  writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC);
-  writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
-  writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
-  writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
-  Dummy = readb(apbs[IndexCard].RamIO + VERS);
-  restore_flags (flags);
-  return 0;
+		printk("Write to applicom card #%d. struct st_ram_io follows:", NumCard);
+
+
+
+		for (c = 0; c < sizeof(struct st_ram_io);) {
+			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]);
+
+			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
+				printk(" %2.2X", ((unsigned char *) &st_loc)[c]);
+			}
+		}
+
+		printk("\nstruct mailbox follows:");
+
+		for (c = 0; c < sizeof(struct mailbox);) {
+			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]);
+
+			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
+				printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]);
+			}
+		}
+
+		printk("\n");
+	}
+
+#endif
+
+	save_flags(flags);
+	cli();			/* disable interrupt             */
+
+	if (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) {	/* Test octet ready correct */
+		Dummy = readb(apbs[IndexCard].RamIO + VERS);
+		restore_flags(flags);
+		printk("APPLICOM driver write error board %d, DataFromPcReady = %d\n", IndexCard, (int) readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
+		DeviceErrorCount++;
+		return -EIO;
+	}
+	while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) {
+		Dummy = readb(apbs[IndexCard].RamIO + VERS);
+		restore_flags(flags);
+		/*
+		 *	FIXME: Race on wakeup. Race on re-entering write
+		 *	in another thread.
+		 */
+		interruptible_sleep_on(&apbs[IndexCard].FlagSleepSend);
+		if (signal_pending(current))
+			return -EINTR;
+		save_flags(flags);
+		cli();
+	}
+	writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
+
+	//  memcpy_toio ((void *)apbs[IndexCard].PtrRamFromPc, (void *)&tmpmailbox, sizeof(struct mailbox));
+	{
+		unsigned char *from = (unsigned char *) &tmpmailbox;
+		unsigned long to = (unsigned long) apbs[IndexCard].RamIO + RAM_FROM_PC;
+		int c;
+
+		for (c = 0; c < sizeof(struct mailbox); c++)
+			writeb(*(from++), to++);
+	}
+	writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC);
+	writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC);
+	writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC);
+	writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
+	writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
+	writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
+	Dummy = readb(apbs[IndexCard].RamIO + VERS);
+	restore_flags(flags);
+	return 0;
 }
 
-static ssize_t ac_read (struct file *filp, char *buf, size_t count, loff_t *ptr)
+static ssize_t ac_read(struct file *filp, char *buf, size_t count, loff_t * ptr)
 {
-  unsigned int NumCard;                /* board number 1 -> 8         */
-  unsigned int IndexCard;              /* index board number 0 -> 7   */
-  unsigned long flags;
-  unsigned int i;
-  unsigned char tmp=0;
-  struct st_ram_io st_loc;
-  struct mailbox tmpmailbox;           /* bounce buffer - can't copy to user space with cli() */
-
-
-  if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
-	  printk("Hmmm. read() of Applicom card, length %d != expected %d\n",count,sizeof(struct st_ram_io) + sizeof(struct mailbox));
-	  return -EINVAL;
-  }
-
-  save_flags(flags);
-  cli();
-  
-  i = 0;
-
-  while (tmp != 2)
-    {
-      for (i=0; i < MAX_BOARD; i++)
-	{
-	  if (!apbs[i].RamIO)
-	    continue;
-	  
-	  tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
-	  
-	  if (tmp == 2)
-	    break;
-	  
-	  if (tmp > 2)  /* Test octet ready correct */
-	    {
-	      Dummy = readb(apbs[i].RamIO + VERS);
-	      restore_flags(flags);
-	      printk("APPLICOM driver read error board %d, DataToPcReady = %d\n",
-		     i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
-	      DeviceErrorCount++;
-	      return -EIO;
-	    }
-	  Dummy = readb(apbs[i].RamIO + VERS);
+	unsigned int NumCard;	/* board number 1 -> 8         */
+	unsigned int IndexCard;	/* index board number 0 -> 7   */
+	unsigned long flags;
+	unsigned int i;
+	unsigned char tmp = 0;
+	struct st_ram_io st_loc;
+	struct mailbox tmpmailbox;	/* bounce buffer - can't copy to user space with cli() */
+
 
+	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
+		printk("Hmmm. read() of Applicom card, length %d != expected %d\n", count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
+		return -EINVAL;
 	}
-      if (tmp != 2)
+
+	save_flags(flags);
+	cli();
+
+	i = 0;
+
+	while (tmp != 2) {
+		for (i = 0; i < MAX_BOARD; i++) {
+			if (!apbs[i].RamIO)
+				continue;
+
+			tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
+
+			if (tmp == 2)
+				break;
+
+			if (tmp > 2) {	/* Test octet ready correct */
+				Dummy = readb(apbs[i].RamIO + VERS);
+				restore_flags(flags);
+				printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n", i, (int) readb(apbs[i].RamIO + DATA_TO_PC_READY));
+				DeviceErrorCount++;
+				return -EIO;
+			}
+			Dummy = readb(apbs[i].RamIO + VERS);
+
+		}
+		if (tmp != 2) {
+			/*
+			 *	FIXME: race on wakeup. O_NDELAY not implemented
+			 *	Parallel read threads race.
+			 */
+			restore_flags(flags);
+			interruptible_sleep_on(&FlagSleepRec);
+			if (signal_pending(current))
+				return -EINTR;
+			save_flags(flags);
+			cli();
+		}
+	}
+
+	IndexCard = i;
+	NumCard = i + 1;
+	st_loc.tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC);
+	st_loc.numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
+
+
+	//  memcpy_fromio(&tmpmailbox, apbs[IndexCard].PtrRamToPc, sizeof(struct mailbox));
 	{
-	  restore_flags(flags);
-	  interruptible_sleep_on (&FlagSleepRec);
-	  if (signal_pending(current))
-	    return -EINTR;
-	  save_flags(flags);
-	  cli();
-	}
-    }
-
-  IndexCard = i;
-  NumCard   = i+1;
-  st_loc.tic_owner_to_pc     = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC);
-  st_loc.numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
-
-
-  //  memcpy_fromio(&tmpmailbox, apbs[IndexCard].PtrRamToPc, sizeof(struct mailbox));
- {
-    unsigned long from = (unsigned long)apbs[IndexCard].RamIO + RAM_TO_PC;
-    unsigned char *to = (unsigned char *)&tmpmailbox;
-    int c;
-
-    for (c=0; c<sizeof(struct mailbox) ; c++)
-      *(to++) = readb(from++);
-  }
-  writeb(1,apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
-  writeb(1,apbs[IndexCard].RamIO + TYP_ACK_FROM_PC);
-  writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
-  writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), 
-	 apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
-  writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
-  writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY);
-  writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
-  Dummy = readb(apbs[IndexCard].RamIO + VERS);
-  restore_flags(flags);
+		unsigned long from = (unsigned long) apbs[IndexCard].RamIO + RAM_TO_PC;
+		unsigned char *to = (unsigned char *) &tmpmailbox;
+		int c;
+
+		for (c = 0; c < sizeof(struct mailbox); c++)
+			*(to++) = readb(from++);
+	}
+	writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
+	writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC);
+	writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
+	writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
+	writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
+	writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY);
+	writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
+	Dummy = readb(apbs[IndexCard].RamIO + VERS);
+	restore_flags(flags);
 
 #ifdef DEBUG
-  { int c;
+	{
+		int c;
 
-    printk("Read from applicom card #%d. struct st_ram_io follows:",NumCard);
-    
-    for (c=0; c< sizeof(struct st_ram_io);)
-      {
-	printk("\n%5.5X: %2.2X",c,((unsigned char *)&st_loc)[c]);
-	
-	for (c++ ; c%8 && c<sizeof(struct st_ram_io); c++)
-	  {
-	    printk(" %2.2X",((unsigned char *)&st_loc)[c]);
-	  }
-      }
+		printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard);
 
-	   printk("\nstruct mailbox follows:");
-	 
-    for (c=0; c< sizeof(struct mailbox);)
-      {
-	printk("\n%5.5X: %2.2X",c,((unsigned char *)&tmpmailbox)[c]);
-	
-	for (c++ ; c%8 && c<sizeof(struct mailbox); c++)
-	  {
-	    printk(" %2.2X",((unsigned char *)&tmpmailbox)[c]);
-	  }
-      }
-    printk("\n");
-    
-  }
-#endif
-           	  
-
-  /* Je suis stupide. DW. */
-
-  if(copy_to_user (buf, &st_loc, sizeof(struct st_ram_io)))
-    return -EFAULT;
-  if(copy_to_user (&buf[sizeof(struct st_ram_io)], &tmpmailbox, sizeof(struct mailbox)))
-    return -EFAULT;
+		for (c = 0; c < sizeof(struct st_ram_io);) {
+			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]);
+
+			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
+				printk(" %2.2X", ((unsigned char *) &st_loc)[c]);
+			}
+		}
+
+		printk("\nstruct mailbox follows:");
+
+		for (c = 0; c < sizeof(struct mailbox);) {
+			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]);
+
+			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
+				printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]);
+			}
+		}
+		printk("\n");
+
+	}
+#endif
+
+
+	/* Je suis stupide. DW. */
+
+	if (copy_to_user(buf, &st_loc, sizeof(struct st_ram_io)))
+		return -EFAULT;
+	if (copy_to_user(&buf[sizeof(struct st_ram_io)], &tmpmailbox, sizeof(struct mailbox)))
+		return -EFAULT;
 
-  return 0;
+	return 0;
 }
 
 static void ac_interrupt(int vec, void *dev_instance, struct pt_regs *regs)
 {
-  unsigned int i;
-  unsigned int FlagInt;
-  unsigned int LoopCount;
-  //  volatile unsigned char ResetIntBoard;
-	
-  //    printk("Applicom interrupt on IRQ %d occurred\n", vec);
-  
-  LoopCount = 0;
-  // for(i=boardno;i<MAX_BOARD;i++)         /* loop for not configured board   */
-  //  if (apbs[i].RamIO)
-  //    ResetIntBoard = *apbs[i].PtrRamItToPc;    /* reset interrupt of unused boards */
-
-  do
-  {
-    FlagInt = FALSE;
-    for(i=0;i<MAX_BOARD;i++)
-    {
-      if (!apbs[i].RamIO)
-	  continue;
-
-      if(readb(apbs[i].RamIO + RAM_IT_TO_PC) != 0)
-	FlagInt = TRUE;
-      writeb(0, apbs[i].RamIO + RAM_IT_TO_PC);
-
-      if(readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2)
-	{
-        printk("APPLICOM driver interrupt err board %d, DataToPcReady = %d\n",
-               i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
-        DeviceErrorCount++;
-      }
-      if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && 
-	 (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6))
-      {
-        printk("APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n",
-             i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY));
-        DeviceErrorCount++;
-      }
-      if(readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2)         /* mailbox sent by the card ?   */
-      {
-         wake_up_interruptible(&FlagSleepRec);
-      }
-      if(readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0)       /* ram i/o free for write by pc ? */
-      {
-        if(waitqueue_active(&apbs[i].FlagSleepSend))                /* process sleep during read ?    */
-        {
-          wake_up_interruptible(&apbs[i].FlagSleepSend);
-        }
-      }
-      Dummy = readb(apbs[i].RamIO + VERS);
-
-      if(readb(apbs[i].RamIO + RAM_IT_TO_PC))
-	i--;        /* There's another int waiting on this card */
-    }
-    if(FlagInt) LoopCount = 0;
-    else        LoopCount++;
-  }
-  while(LoopCount < 2);
+	unsigned int i;
+	unsigned int FlagInt;
+	unsigned int LoopCount;
+	//  volatile unsigned char ResetIntBoard;
+
+	//    printk("Applicom interrupt on IRQ %d occurred\n", vec);
+
+	LoopCount = 0;
+	// for(i=boardno;i<MAX_BOARD;i++)         /* loop for not configured board   */
+	//  if (apbs[i].RamIO)
+	//    ResetIntBoard = *apbs[i].PtrRamItToPc;    /* reset interrupt of unused boards */
+
+	do {
+		FlagInt = FALSE;
+		for (i = 0; i < MAX_BOARD; i++) {
+			if (!apbs[i].RamIO)
+				continue;
+
+			if (readb(apbs[i].RamIO + RAM_IT_TO_PC) != 0)
+				FlagInt = TRUE;
+			writeb(0, apbs[i].RamIO + RAM_IT_TO_PC);
+
+			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) {
+				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n", i + 1, (int) readb(apbs[i].RamIO + DATA_TO_PC_READY));
+				DeviceErrorCount++;
+			}
+			if ((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) {
+				printk("APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n", i + 1, (int) readb(apbs[i].RamIO + DATA_FROM_PC_READY));
+				DeviceErrorCount++;
+			}
+			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) {	/* mailbox sent by the card ?   */
+				wake_up_interruptible(&FlagSleepRec);
+			}
+			if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) {	/* ram i/o free for write by pc ? */
+				if (waitqueue_active(&apbs[i].FlagSleepSend)) {	/* process sleep during read ?    */
+					wake_up_interruptible(&apbs[i].FlagSleepSend);
+				}
+			}
+			Dummy = readb(apbs[i].RamIO + VERS);
+
+			if (readb(apbs[i].RamIO + RAM_IT_TO_PC))
+				i--;	/* There's another int waiting on this card */
+		}
+		if (FlagInt)
+			LoopCount = 0;
+		else
+			LoopCount++;
+	}
+	while (LoopCount < 2);
 }
 
 
 static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{				/* @ ADG ou ATO selon le cas */
+	int i;
+	unsigned char IndexCard;
+	unsigned long pmem;
+	volatile unsigned char byte_reset_it;
+	struct st_ram_io adgl;
+	unsigned char TmpRamIo[sizeof(struct st_ram_io)];
 
-{                                           /* @ ADG ou ATO selon le cas */
-  int i;
-  unsigned char IndexCard;
-  unsigned long pmem ;
-  volatile unsigned char byte_reset_it;
-  struct st_ram_io adgl ;
-  unsigned char TmpRamIo[sizeof(struct st_ram_io)];
-
-
-  if (copy_from_user (&adgl, (void *)arg,sizeof(struct st_ram_io)))
-      return -EFAULT;
-
-  IndexCard = adgl.num_card-1;
-  if(cmd != 0 && cmd != 6 &&
-	((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO))
-  {
-    printk("APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1);
-    printk("apbs[%d].RamIO = %lx\n",IndexCard, apbs[IndexCard].RamIO);
-    return -EINVAL;
-  }
-
-  switch( cmd )
-  {
-    case 0 :
-      pmem = apbs[IndexCard].RamIO;
-      for(i=0;i<sizeof(struct st_ram_io);i++)TmpRamIo[i]=readb(pmem++);
-      if (copy_to_user((void *)arg, TmpRamIo, sizeof(struct st_ram_io)))
-	      return -EFAULT;
-      break;
-    case 1 :
-      pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
-      for (i=0;i<4;i++)
-	adgl.conf_end_test[i] = readb(pmem++);
-      for (i=0;i<2;i++)
-	adgl.error_code[i] = readb(pmem++);
-      for (i=0;i<4;i++)
-	adgl.parameter_error[i] = readb(pmem++);
-      pmem = apbs[IndexCard].RamIO + VERS;
-      adgl.vers = readb(pmem);
-      pmem = apbs[IndexCard].RamIO + TYPE_CARD;
-      for (i=0;i<20;i++)
-        adgl.reserv1[i] = readb(pmem++);
-      *(int *)&adgl.reserv1[20] =  
-	(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + 
-	(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + 
-	(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) );
-
-      if (copy_to_user ((void *)arg, &adgl, sizeof(struct st_ram_io)))
-	      return -EFAULT;
-      break;
-    case 2 :
-      pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
-      for (i=0;i<10;i++)
-	writeb(0xff, pmem++);
-      writeb(adgl.data_from_pc_ready, 
-	     apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
 
-      writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
+	if (copy_from_user(&adgl, (void *) arg, sizeof(struct st_ram_io)))
+		return -EFAULT;
+
+	IndexCard = adgl.num_card - 1;
+	
+	/*
+	 *	FIXME: user can flood the console using bogus ioctls
+	 */
+	 
+	if (cmd != 0 && cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
+		printk("APPLICOM driver IOCTL, bad board number %d\n", (int) IndexCard + 1);
+		printk("apbs[%d].RamIO = %lx\n", IndexCard, apbs[IndexCard].RamIO);
+		return -EINVAL;
+	}
+
+	/*
+	 *	FIXME races between ioctls with multiple clients
+	 */
+	 
+	switch (cmd) {
+	case 0:
+		pmem = apbs[IndexCard].RamIO;
+		for (i = 0; i < sizeof(struct st_ram_io); i++)
+			TmpRamIo[i] = readb(pmem++);
+		if (copy_to_user((void *) arg, TmpRamIo, sizeof(struct st_ram_io)))
+			return -EFAULT;
+		break;
+	case 1:
+		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
+		for (i = 0; i < 4; i++)
+			adgl.conf_end_test[i] = readb(pmem++);
+		for (i = 0; i < 2; i++)
+			adgl.error_code[i] = readb(pmem++);
+		for (i = 0; i < 4; i++)
+			adgl.parameter_error[i] = readb(pmem++);
+		pmem = apbs[IndexCard].RamIO + VERS;
+		adgl.vers = readb(pmem);
+		pmem = apbs[IndexCard].RamIO + TYPE_CARD;
+		for (i = 0; i < 20; i++)
+			adgl.reserv1[i] = readb(pmem++);
+		*(int *) &adgl.reserv1[20] = (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2));
+
+		if (copy_to_user((void *) arg, &adgl, sizeof(struct st_ram_io)))
+			return -EFAULT;
+		break;
+	case 2:
+		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
+		for (i = 0; i < 10; i++)
+			writeb(0xff, pmem++);
+		writeb(adgl.data_from_pc_ready, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
+
+		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
+		
+		/*
+		 *	FIXME: can trash waitqueue that is active.
+		 */
 #if LINUX_VERSION_CODE > 0x20300
-      init_waitqueue_head (&FlagSleepRec);
+		init_waitqueue_head(&FlagSleepRec);
 #else
-      FlagSleepRec     = NULL;
+		FlagSleepRec = NULL;
 #endif
-      for (i=0;i<MAX_BOARD;i++)
-      {
-	if (apbs[i].RamIO)
-	  {
+		for (i = 0; i < MAX_BOARD; i++) {
+			if (apbs[i].RamIO) {
 #if LINUX_VERSION_CODE > 0x20300
-	    init_waitqueue_head (&apbs[i].FlagSleepSend);
+				init_waitqueue_head(&apbs[i].FlagSleepSend);
 #else
-	    apbs[i].FlagSleepSend = NULL;
+				apbs[i].FlagSleepSend = NULL;
 #endif
-	    byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC);
-	  }
-      }
-      break ;
-    case 3 :
-      pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC;
-      writeb(adgl.tic_des_from_pc, pmem);
-      break;
-    case 4 :
-      pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC;
-      adgl.tic_owner_to_pc     = readb(pmem++);
-      adgl.numcard_owner_to_pc = readb(pmem);
-      if (copy_to_user ((void *)arg, &adgl,sizeof(struct st_ram_io)))
-	      return -EFAULT;
-      break;
-    case 5 :
-      writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
-      writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
-      writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
-      writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
-      writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
-      break ;
-    case 6  :
-      printk("APPLICOM driver release .... V2.8.0\n");
-      printk("Number of installed boards . %d\n",(int)numboards);
-      printk("Segment of board ........... %X\n",(int)mem);
-      printk("Interrupt IRQ number ....... %d\n",(int)irq);
-      for(i=0;i<MAX_BOARD;i++)
-      {
-	int serial;
-	char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
-
-	if (!apbs[i].RamIO)
-	  continue;
-
-	
-	for(serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
-	  boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
-	boardname[serial]=0;
-	
-	
-        printk("Prom version board %d ....... V%d.%d %s",
-               i+1,
-               (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4),
-               (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF),
-               boardname);
-
-
-	serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + 
-	  (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + 
-	  (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
-
-        if (serial != 0)
-          printk (" S/N %d\n", serial);
-        else
-          printk("\n");
-      }
-      if(DeviceErrorCount != 0)
-        printk("DeviceErrorCount ........... %d\n",DeviceErrorCount);
-      if(ReadErrorCount != 0)
-        printk("ReadErrorCount ............. %d\n",ReadErrorCount);
-      if(WriteErrorCount != 0)
-        printk("WriteErrorCount ............ %d\n",WriteErrorCount);
-      if(waitqueue_active(&FlagSleepRec))
-        printk("Process in read pending\n");
-      for(i=0;i<MAX_BOARD;i++)
-      {
-	if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
-          printk("Process in write pending board %d\n",i+1);
-      }
-      break;
-    default :
-      printk("APPLICOM driver ioctl, unknown function code %d\n",cmd) ;
-      return -EINVAL;
-      break;
-  }
-  Dummy = readb(apbs[IndexCard].RamIO + VERS);
-  return 0;
+				byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC);
+			}
+		}
+		break;
+	case 3:
+		pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC;
+		writeb(adgl.tic_des_from_pc, pmem);
+		break;
+	case 4:
+		pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC;
+		adgl.tic_owner_to_pc = readb(pmem++);
+		adgl.numcard_owner_to_pc = readb(pmem);
+		if (copy_to_user((void *) arg, &adgl, sizeof(struct st_ram_io)))
+			return -EFAULT;
+		break;
+	case 5:
+		writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
+		writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
+		writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
+		writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
+		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
+		break;
+	case 6:
+		printk(KERN_INFO "APPLICOM driver release .... V2.8.0\n");
+		printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards);
+		printk(KERN_INFO "Segment of board ........... %X\n", (int) mem);
+		printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq);
+		for (i = 0; i < MAX_BOARD; i++) {
+			int serial;
+			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
+
+			if (!apbs[i].RamIO)
+				continue;
+
+
+			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
+				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
+			boardname[serial] = 0;
+
+
+			printk(KERN_INFO "Prom version board %d ....... V%d.%d %s", i + 1, (int) (readb(apbs[IndexCard].RamIO + VERS) >> 4), (int) (readb(apbs[IndexCard].RamIO + VERS) & 0xF), boardname);
+
+
+			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 2));
+
+			if (serial != 0)
+				printk(" S/N %d\n", serial);
+			else
+				printk("\n");
+		}
+		if (DeviceErrorCount != 0)
+			printk(KERN_INFO "DeviceErrorCount ........... %d\n", DeviceErrorCount);
+		if (ReadErrorCount != 0)
+			printk(KERN_INFO "ReadErrorCount ............. %d\n", ReadErrorCount);
+		if (WriteErrorCount != 0)
+			printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount);
+		if (waitqueue_active(&FlagSleepRec))
+			printk("Process in read pending\n");
+		for (i = 0; i < MAX_BOARD; i++) {
+			if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
+				printk("Process in write pending board %d\n", i + 1);
+		}
+		break;
+	default:
+		printk("APPLICOM driver ioctl, unknown function code %d\n", cmd);
+		return -EINVAL;
+		break;
+	}
+	Dummy = readb(apbs[IndexCard].RamIO + VERS);
+	return 0;
 }
 
 #ifndef MODULE
 static int __init applicom_setup(char *str)
 {
 	int ints[4];
-	
-	(void)get_options(str, 4, ints);
+
+	(void) get_options(str, 4, ints);
 
 	if (ints[0] > 2) {
 		printk(KERN_WARNING "Too many arguments to 'applicom=', expected mem,irq only.\n");
 	}
-		
+
 	if (ints[0] < 2) {
 		printk("applicom numargs: %d\n", ints[0]);
 		return 0;
 	}
-	
-	mem=ints[1];
-	irq=ints[2];
+
+	mem = ints[1];
+	irq = ints[2];
 	return 1;
 }
+
 #if LINUX_VERSION_CODE > 0x20300
 __setup("applicom=", applicom_setup);
 #endif
-#endif /* MODULE */
-
+#endif				/* MODULE */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/atixlmouse.c linux/drivers/char/atixlmouse.c
--- v2.3.99-pre2/linux/drivers/char/atixlmouse.c	Sun Nov  7 16:37:34 1999
+++ linux/drivers/char/atixlmouse.c	Wed Mar 22 15:42:57 2000
@@ -92,10 +92,14 @@
 
 static int open_mouse(struct inode * inode, struct file * file)
 {
+	/* Lock module as request_irq may sleep */
+	MOD_INC_USE_COUNT;
 	if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt, 0, "ATIXL mouse", NULL))
+	{
+		MOD_DEC_USE_COUNT;
 		return -EBUSY;
+	}
 	ATIXL_MSE_INT_ON(); /* Interrupts are really enabled here */
-	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -107,7 +111,13 @@
 {
 	unsigned char a,b,c;
 
-	if (check_region(ATIXL_MSE_DATA_PORT, 3))
+	/*
+	 *	We must request the resource and claim it atomically
+	 *	nowdays. We can throw it away on error. Otherwise we
+	 *	may race another module load of the same I/O
+	 */
+
+	if (!request_region(ATIXL_MSE_DATA_PORT, 3, "atixlmouse"))
 		return -EIO;
 
 	a = inb( ATIXL_MSE_SIGNATURE_PORT );	/* Get signature */
@@ -116,16 +126,20 @@
 	if (( a != b ) && ( a == c ))
 		printk(KERN_INFO "\nATI Inport ");
 	else
+	{
+		release_region(ATIXL_MSE_DATA_PORT,3);
 		return -EIO;
+	}
 	outb(0x80, ATIXL_MSE_CONTROL_PORT);	/* Reset the Inport device */
 	outb(0x07, ATIXL_MSE_CONTROL_PORT);	/* Select Internal Register 7 */
 	outb(0x0a, ATIXL_MSE_DATA_PORT);	/* Data Interrupts 8+, 1=30hz, 2=50hz, 3=100hz, 4=200hz rate */
 
-	request_region(ATIXL_MSE_DATA_PORT, 3, "atixl");
-
 	msedev = register_busmouse(&atixlmouse);
 	if (msedev < 0)
+	{
 		printk("Bus mouse initialisation error.\n");
+		release_region(ATIXL_MSE_DATA_PORT,3);	/* Was missing */
+	}
 	else
 		printk("Bus mouse detected and installed.\n");
 	return msedev < 0 ? msedev : 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.3.99-pre2/linux/drivers/char/bttv.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/char/bttv.c	Sun Mar 19 19:11:37 2000
@@ -121,6 +121,7 @@
 #define I2C_GET()   (btread(BT848_I2C)&1)
 
 #define BURSTOFFSET 76
+#define BTTV_ERRORS 5
 
 
 /* ----------------------------------------------------------------------- */
@@ -133,7 +134,6 @@
 	if (card >= bttv_num) {
 		return -1;
 	}
-		
 	return bttvs[card].type;
 }
 
@@ -146,10 +146,7 @@
 	}
 	
 	btv = &bttvs[card];
-	down(&btv->lock);
 	btaor(data, ~mask, BT848_GPIO_OUT_EN);
-	up(&btv->lock);
-
 	return 0;
 }
 
@@ -167,15 +164,9 @@
 		return -ENODEV;
 	}
 
-	down(&btv->lock);
-
 /* prior setting BT848_GPIO_REG_INP is (probably) not needed 
    because we set direct input on init */
-
 	*data = btread(BT848_GPIO_DATA);
-
-	up(&btv->lock);
-
 	return 0;
 }
 
@@ -189,15 +180,9 @@
 
 	btv = &bttvs[card];
 
-	down(&btv->lock);
-
 /* prior setting BT848_GPIO_REG_INP is (probably) not needed 
    because direct input is set on init */
-
 	btaor(data & mask, ~mask, BT848_GPIO_DATA);
-
-	up(&btv->lock);
-
 	return 0;
 }
 
@@ -210,11 +195,9 @@
 	}
 
 	btv = &bttvs[card];
-
 	if (bttvs[card].shutdown) {
 		return NULL;
 	}
-
 	return &btv->gpioq;
 }
 
@@ -787,7 +770,7 @@
           4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10},0,
 	  1,1,1,1,0 },
         { "Hauppauge old",
-          3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0,
+          4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0,
 	  1,1,0,1,0 },
         { "STB",
           3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0,
@@ -815,7 +798,7 @@
           3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0},0,
 	  1,1,1,1,0 },
         { "Hauppauge new (bt878)",
-	  3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0,
+	  4, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0,
 	  1,1,0,1,0 },
         { "MIRO PCTV pro",
           3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10},0,
@@ -1213,7 +1196,7 @@
 	unsigned int *pe=(unsigned int *) btv->vbi_even;
   
 	if (debug)
-		printk("bttv%d: vbi: po=%08lx pe=%08lx\n",
+		printk("bttv%d: vbi1: po=%08lx pe=%08lx\n",
 		       btv->nr,virt_to_bus(po), virt_to_bus(pe));
         
 	*(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0;
@@ -1233,8 +1216,10 @@
 	}
 	*(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16));
 	*(pe++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
-	DEBUG(printk(KERN_DEBUG "po: 0x%lx\n",(long)po));
-	DEBUG(printk(KERN_DEBUG "pe: 0x%lx\n",(long)pe));
+
+	if (debug)
+		printk("bttv%d: vbi2: po=%08lx pe=%08lx\n",
+		       btv->nr,virt_to_bus(po), virt_to_bus(pe));
 }
 
 static int fmtbppx2[16] = {
@@ -1310,7 +1295,7 @@
 	int shift, csize;	
 
 	if (debug)
-		printk("bttv%d: prisc: ro=%08lx re=%08lx\n",
+		printk("bttv%d: prisc1: ro=%08lx re=%08lx\n",
 		       btv->nr,virt_to_bus(ro), virt_to_bus(re));
 
 	switch(fmt)
@@ -1406,6 +1391,10 @@
 	*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
 	*(re++)=cpu_to_le32(btv->bus_vbi_odd);
 	
+	if (debug)
+		printk("bttv%d: prisc2: ro=%08lx re=%08lx\n",
+		       btv->nr,virt_to_bus(ro), virt_to_bus(re));
+
 	return 0;
 }
  
@@ -1428,7 +1417,7 @@
                 return make_prisctab(btv, ro, re, vbuf, width, height, palette);
 
 	if (debug)
-		printk("bttv%d: vrisc: ro=%08lx re=%08lx\n",
+		printk("bttv%d: vrisc1: ro=%08lx re=%08lx\n",
 		       btv->nr,virt_to_bus(ro), virt_to_bus(re));
 	
 	inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
@@ -1478,6 +1467,10 @@
 	*(ro++)=cpu_to_le32(btv->bus_vbi_even);
 	*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
 	*(re++)=cpu_to_le32(btv->bus_vbi_odd);
+
+	if (debug)
+		printk("bttv%d: vrisc2: ro=%08lx re=%08lx\n",
+		       btv->nr,virt_to_bus(ro), virt_to_bus(re));
 	
 	return 0;
 }
@@ -1555,7 +1548,7 @@
 	width=btv->win.width;
 	height=btv->win.height;
 	if (debug)
-		printk("bttv%d: make_clip: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
+		printk("bttv%d: clip1: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
 		       btv->nr,btv->picture.palette,width,height,bpl,bpp);
 	if(width > 1023)
 		width = 1023;		/* sanity check */
@@ -1662,6 +1655,10 @@
 	*(ro++)=cpu_to_le32(btv->bus_vbi_even);
 	*(re++)=cpu_to_le32(BT848_RISC_JUMP);
 	*(re++)=cpu_to_le32(btv->bus_vbi_odd);
+
+	if (debug)
+		printk("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
+		       btv->nr,btv->picture.palette,width,height,bpl,bpp);
 }
 
 /*
@@ -1879,7 +1876,7 @@
 	if(btv->gbuf[mp->frame].stat != GBUFFER_UNUSED)
 		return -EBUSY;
 		
-	if(mp->height <0 || mp->width <0)
+	if(mp->height < 32 || mp->width < 32)
 		return -EINVAL;
 	if (mp->format >= PALETTEFMT_MAX)
 		return -EINVAL;
@@ -1891,12 +1888,6 @@
 		return -EINVAL;
 
 	/*
-	 *	FIXME: Check the format of the grab here. This is probably
-	 *	also less restrictive than the normal overlay grabs. Stuff
-	 *	like QCIF has meaning as a capture.
-	 */
-	 
-	/*
 	 *	Ok load up the BT848
 	 */
 	 
@@ -1906,7 +1897,8 @@
         make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format);
 
 	if (debug)
-		printk("bttv%d: cap vgrab: queue %d\n",btv->nr,mp->frame);
+		printk("bttv%d: cap vgrab: queue %d (%dx%d)\n",
+		       btv->nr,mp->frame,mp->width,mp->height);
         cli();
         btv->gbuf[mp->frame].stat    = GBUFFER_GRABBING;
 	btv->gbuf[mp->frame].fmt     = palette2fmt[mp->format];
@@ -1995,6 +1987,26 @@
 	tvnorms[2].hdelayx1     = 186  - (on?BURSTOFFSET  :0);
 }
 
+static void bt848_restart(struct bttv *btv)
+{
+	if (verbose)
+		printk("bttv%d: resetting chip\n",btv->nr);
+	btwrite(0xfffffUL, BT848_INT_STAT);
+	btand(~15, BT848_GPIO_DMA_CTL);
+	btwrite(0, BT848_SRESET);
+	btwrite(virt_to_bus(btv->risc_jmp+2),
+		BT848_RISC_STRT_ADD);
+
+	/* enforce pll reprogramming */
+	btv->pll.pll_current = 0;
+	set_pll(btv);
+
+	btv->errors = 0;
+	btv->needs_restart = 0;
+	bt848_set_geo(btv,0);
+	bt848_set_risc_jmps(btv,-1);
+}
+
 /*
  *	Open a bttv card. Right now the flags stuff is just playing
  */
@@ -2020,6 +2032,8 @@
         for (i = 0; i < gbuffers; i++)
                 btv->gbuf[i].stat = GBUFFER_UNUSED;
 
+	if (btv->needs_restart)
+		bt848_restart(btv);
         burst(0);
 	set_pll(btv);
         btv->user++;
@@ -2117,7 +2131,6 @@
 	btaor(datahi, ~1, BT848_O_CONTROL);
 }
 
-
 /*
  *	ioctl routine
  */
@@ -2126,7 +2139,7 @@
 static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
 {
 	struct bttv *btv=(struct bttv *)dev;
- 	int i,ret;
+ 	int i,ret = 0;
 
 	if (debug) printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd);
 
@@ -2281,7 +2294,7 @@
 			
 		if(copy_from_user(&vw,arg,sizeof(vw)))
 			return -EFAULT;
-				
+
 		if(vw.flags || vw.width < 16 || vw.height < 16) 
 		{
                         down(&btv->lock);
@@ -2296,6 +2309,8 @@
 			vw.width &= ~3;
 		}
 		down(&btv->lock);
+		if (btv->needs_restart)
+			bt848_restart(btv);
 		btv->win.x=vw.x;
 		btv->win.y=vw.y;
 		btv->win.width=vw.width;
@@ -2551,14 +2566,17 @@
 			return -EINVAL;
 		switch (btv->gbuf[i].stat) {
 		case GBUFFER_UNUSED:
-			return -EINVAL;
+			ret = -EINVAL;
+			break;
 		case GBUFFER_GRABBING:
 			while(btv->gbuf[i].stat==GBUFFER_GRABBING) {
 				if (debug)
 					printk("bttv%d: cap sync: sleep on %d\n",btv->nr,i);
 				interruptible_sleep_on(&btv->capq);
-				if(signal_pending(current))
-					return -EINTR;
+				if(signal_pending(current)) {
+					ret = -EINTR;
+					break;
+				}
 			}
 			/* fall throuth */
 		case GBUFFER_DONE:
@@ -2567,9 +2585,13 @@
 			if (debug)
 				printk("bttv%d: cap sync: buffer %d, retval %d\n",btv->nr,i,ret);
 			btv->gbuf[i].stat = GBUFFER_UNUSED;
-			return ret;
 		}
-		return 0;
+		if (btv->needs_restart) {
+			down(&btv->lock);
+			bt848_restart(btv);
+			up(&btv->lock);
+		}
+		return ret;
 
 	case BTTV_FIELDNR: 
 		if(copy_to_user((void *) arg, (void *) &btv->last_field, 
@@ -2742,6 +2764,11 @@
 	todo=count;
 	while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) 
 	{
+		if (btv->needs_restart) {
+			down(&btv->lock);
+			bt848_restart(btv);
+			up(&btv->lock);
+		}
 		if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
 			return -EFAULT;
 		todo-=q;
@@ -2796,6 +2823,8 @@
 	struct bttv *btv=(struct bttv *)(dev-2);
 
         down(&btv->lock);
+	if (btv->needs_restart)
+		bt848_restart(btv);
 	btv->vbip=VBIBUF_SIZE;
 	btv->vbi_on = 1;
 	bt848_set_risc_jmps(btv,-1);
@@ -2938,8 +2967,8 @@
 		if(v.tuner||btv->channel)	/* Only tuner 0 */
 			return -EINVAL;
 		strcpy(v.name, "Radio");
-		v.rangelow=(int)(87.5*16);
-		v.rangehigh=(int)(108*16);
+		v.rangelow=(int)(76*16);	/* jp: 76.0MHz - 89.9MHz  */
+		v.rangehigh=(int)(108*16);	/* eu: 87.5MHz - 108.0MHz */
 		v.flags= 0; /* XXX */
 		v.mode = 0; /* XXX */
 		if(copy_to_user(arg,&v,sizeof(v)))
@@ -3100,12 +3129,10 @@
 	/* print which board we have found */
 	printk(KERN_INFO "bttv%d: model: ",btv->nr);
 
-	sprintf(btv->video_dev.name,"BT%d",btv->id);
-        if (btv->id==848 && btv->revision==0x12) 
-                strcat(btv->video_dev.name,"A");
-        strcat(btv->video_dev.name,"(");
-        strcat(btv->video_dev.name, tvcards[btv->type].name);
-        strcat(btv->video_dev.name,")");
+	sprintf(btv->video_dev.name,"BT%d%s(%.22s)",
+		btv->id,
+		(btv->id==848 && btv->revision==0x12) ? "A" : "",
+		tvcards[btv->type].name);
 	printk("%s\n",btv->video_dev.name);
 
         /* board specific initialisations */
@@ -3357,6 +3384,9 @@
 	btv->vbibuf=0;
         btv->field=btv->last_field=0;
 
+	btv->errors=0;
+	btv->needs_restart=0;
+
 	/* i2c */
         btv->tuner_type=-1;
         init_bttv_i2c(btv);
@@ -3458,7 +3488,7 @@
 {
 	u32 stat,astat;
 	u32 dstat;
-	int count;
+	int count,i;
 	struct bttv *btv;
  
 	btv=(struct bttv *)dev_id;
@@ -3498,24 +3528,43 @@
                         btv->field++;
 		}
 		if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) {
-			printk("bttv%d: irq:%s%s risc_count=%08x\n",btv->nr,
-			       (astat&BT848_INT_SCERR) ? " SCERR" : "",
-			       (astat&BT848_INT_OCERR) ? " OCERR" : "",
-			       btread(BT848_RISC_COUNT));
-			bt848_set_risc_jmps(btv,0);
-			btwrite(0, BT848_SRESET);
-			btwrite(virt_to_bus(btv->risc_jmp),
-				BT848_RISC_STRT_ADD);
-			bt848_set_geo(btv,0);
-			bt848_set_risc_jmps(btv,-1);
-#if 0
-			wake_up_interruptible(&btv->vbiq);
-			wake_up_interruptible(&btv->capq);
-#endif
+			if (verbose)
+				printk("bttv%d: irq:%s%s risc_count=%08x\n",
+				       btv->nr,
+				       (astat&BT848_INT_SCERR) ? " SCERR" : "",
+				       (astat&BT848_INT_OCERR) ? " OCERR" : "",
+				       btread(BT848_RISC_COUNT));
+			btv->errors++;
+			if (btv->errors < BTTV_ERRORS) {
+				btand(~15, BT848_GPIO_DMA_CTL);
+				btwrite(virt_to_bus(btv->risc_jmp+2),
+					BT848_RISC_STRT_ADD);
+				bt848_set_geo(btv,0);
+				bt848_set_risc_jmps(btv,-1);
+			} else {
+				if (verbose)
+					printk("bttv%d: aiee: error loops\n",btv->nr);
+				/* cancel all outstanding grab requests */
+				btv->gq_in = 0;
+				btv->gq_out = 0;
+				btv->gq_grab = -1;
+				for (i = 0; i < gbuffers; i++)
+					if (btv->gbuf[i].stat == GBUFFER_GRABBING)
+						btv->gbuf[i].stat = GBUFFER_ERROR;
+				/* disable DMA */
+				btv->risc_cap_odd  = 0;
+				btv->risc_cap_even = 0;
+				bt848_set_risc_jmps(btv,0);
+
+				btv->needs_restart = 1;
+				wake_up_interruptible(&btv->vbiq);
+				wake_up_interruptible(&btv->capq);
+			}
 		}
 		if (astat&BT848_INT_RISCI) 
 		{
-			IDEBUG(printk ("bttv%d: IRQ_RISCI\n", btv->nr));
+			if (debug > 1)
+				printk("bttv%d: IRQ_RISCI\n",btv->nr);
 
 			/* captured VBI frame */
 			if (stat&(1<<28)) 
@@ -3527,11 +3576,12 @@
 			}
 
 			/* captured full frame */
-			if (stat&(2<<28)) 
+			if (stat&(2<<28) && btv->gq_grab != -1) 
 			{
                                 btv->last_field=btv->field;
 				if (debug)
 					printk("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab);
+				do_gettimeofday(&btv->gbuf[btv->gq_grab].tv);
 				btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE;
 				btv->gq_grab = -1;
 			        if (btv->gq_in != btv->gq_out)
@@ -3662,6 +3712,11 @@
         btv->id=dev->device;
         btv->irq=dev->irq;
         btv->bt848_adr=dev->resource[0].start;
+	if (!request_mem_region(pci_resource_start(dev,0),
+				pci_resource_len(dev,0),
+				"bttv")) {
+		return -EBUSY;
+	}
         if (btv->id >= 878)
                 btv->i2c_command = 0x83;                   
         else
@@ -3720,15 +3775,15 @@
         {
                 printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
                        bttv_num);
-                return -EINVAL;
+		goto fail;
         }
         if (result==-EBUSY)
         {
                 printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
-                return result;
+		goto fail;
         }
         if (result < 0) 
-                return result;
+		goto fail;
         
         pci_set_master(dev);
 
@@ -3744,11 +3799,16 @@
                 if (!(command&BT878_EN_TBFX)) 
                 {
                         printk("bttv: 430FX compatibility could not be enabled\n");
-                        return -1;
+			result = -1;
+			goto fail;
                 }
         }
-        
         return 0;
+	
+ fail:
+	release_mem_region(pci_resource_start(btv->dev,0),
+			   pci_resource_len(btv->dev,0));
+	return result;
 }
 
 static int find_bt848(void)
@@ -3834,6 +3894,8 @@
 		if (radio[btv->nr] && btv->radio_dev.minor != -1)
 			video_unregister_device(&btv->radio_dev);
 
+		release_mem_region(pci_resource_start(btv->dev,0),
+				   pci_resource_len(btv->dev,0));
        		/* wake up any waiting processes
 		   because shutdown flag is set, no new processes (in this queue) 
 		   are expected
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.3.99-pre2/linux/drivers/char/bttv.h	Tue Mar 14 19:10:39 2000
+++ linux/drivers/char/bttv.h	Sun Mar 19 19:11:37 2000
@@ -21,7 +21,7 @@
 #ifndef _BTTV_H_
 #define _BTTV_H_
 
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,22) 
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,24) 
 
 #include <linux/types.h>
 #include <linux/wait.h>
@@ -112,7 +112,8 @@
 #define GBUFFER_GRABBING     1
 #define GBUFFER_DONE         2
 #define GBUFFER_ERROR        3
-
+	struct timeval tv;
+	
 	u16 width;
 	u16 height;
 	u16 fmt;
@@ -122,8 +123,7 @@
 	unsigned long re;
 };
 
-struct bttv
-{
+struct bttv {
 	struct video_device video_dev;
 	struct video_device radio_dev;
 	struct video_device vbi_dev;
@@ -192,6 +192,9 @@
 	unsigned int last_field; /* number of last grabbed field */
 	int i2c_command;
 	int triton1;
+
+	int errors;
+	int needs_restart;
 
 	WAIT_QUEUE gpioq;
 	int shutdown;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c
--- v2.3.99-pre2/linux/drivers/char/busmouse.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/char/busmouse.c	Wed Mar 22 22:22:05 2000
@@ -20,7 +20,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
@@ -48,8 +47,6 @@
  */
 /*#define BROKEN_MOUSE*/
 
-extern int sun_mouse_init(void);
-
 struct busmouse_data {
 	struct miscdevice	miscdev;
 	struct busmouse		*ops;
@@ -70,7 +67,13 @@
 #define DEV_TO_MOUSE(dev)	MINOR_TO_MOUSE(MINOR(dev))
 #define MINOR_TO_MOUSE(minor)	((minor) - FIRST_MOUSE)
 
+/*
+ *	List of mice and guarding semaphore. You must take the semaphore
+ *	before you take the misc device semaphore if you need both
+ */
+ 
 static struct busmouse_data *busmouse_data[NR_MICE];
+static DECLARE_MUTEX(mouse_sem);
 
 /* a mouse driver just has to interface with these functions
  *  These are !!!OLD!!!  Do not use!!!
@@ -98,8 +101,7 @@
 /* New interface.  !!! Use this one !!!
  * These routines will most probably be called from interrupt.
  */
-void
-busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons)
+void busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons)
 {
 	struct busmouse_data *mse = busmouse_data[mousedev];
 	int changed;
@@ -141,24 +143,21 @@
 	}
 }
 
-void
-busmouse_add_movement(int mousedev, int dx, int dy)
+void busmouse_add_movement(int mousedev, int dx, int dy)
 {
 	struct busmouse_data *mse = busmouse_data[mousedev];
 
 	busmouse_add_movementbuttons(mousedev, dx, dy, mse->buttons);
 }
 
-void
-busmouse_add_buttons(int mousedev, int clear, int eor)
+void busmouse_add_buttons(int mousedev, int clear, int eor)
 {
 	struct busmouse_data *mse = busmouse_data[mousedev];
 
 	busmouse_add_movementbuttons(mousedev, 0, 0, (mse->buttons & ~clear) ^ eor);
 }
 
-static int
-busmouse_fasync(int fd, struct file *filp, int on)
+static int busmouse_fasync(int fd, struct file *filp, int on)
 {
 	struct busmouse_data *mse = (struct busmouse_data *)filp->private_data;
 	int retval;
@@ -169,8 +168,7 @@
 	return 0;
 }
 
-static int
-busmouse_release(struct inode *inode, struct file *file)
+static int busmouse_release(struct inode *inode, struct file *file)
 {
 	struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
 	int ret = 0;
@@ -190,33 +188,34 @@
 	return ret;
 }
 
-static int
-busmouse_open(struct inode *inode, struct file *file)
+static int busmouse_open(struct inode *inode, struct file *file)
 {
 	struct busmouse_data *mse;
 	unsigned long flags;
 	unsigned int mousedev;
-	int ret = 0;
+	int ret = -ENODEV;
 
 	mousedev = DEV_TO_MOUSE(inode->i_rdev);
 	if (mousedev >= NR_MICE)
 		return -EINVAL;
+	
+	down(&mouse_sem);
 	mse = busmouse_data[mousedev];
 	if (!mse)
 		/* shouldn't happen, but... */
-		return -ENODEV;
-
+		goto end;
+	
 	if (mse->ops &&
 	    mse->ops->open)
 		ret = mse->ops->open(inode, file);
 
 	if (ret)
-		return ret;
+		goto end;
 
 	file->private_data = mse;
 
 	if (mse->active++)
-		return 0;
+		goto end;
 
 	MOD_INC_USE_COUNT;
 
@@ -231,18 +230,17 @@
 		mse->buttons = 7;
 
 	spin_unlock_irqrestore(&mse->lock, flags);
-
-	return 0;
+end:
+	up(&mouse_sem);
+	return ret;
 }
 
-static ssize_t
-busmouse_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+static ssize_t busmouse_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	return -EINVAL;
 }
 
-static ssize_t
-busmouse_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+static ssize_t busmouse_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
 	DECLARE_WAITQUEUE(wait, current);
@@ -328,8 +326,7 @@
 	return count;
 }
 
-static unsigned int
-busmouse_poll(struct file *file, poll_table *wait)
+static unsigned int busmouse_poll(struct file *file, poll_table *wait)
 {
 	struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
 
@@ -351,8 +348,16 @@
 	fasync:		busmouse_fasync,
 };
 
-int
-register_busmouse(struct busmouse *ops)
+/**
+ *	register_busmouse - register a bus mouse interface
+ *	@ops: busmouse structure for the mouse
+ *
+ *	Registers a mouse with the driver. The return is mouse number on
+ *	success and a negative errno code on an error. The passed ops
+ *	structure most not be freed until the mouser is unregistered
+ */
+ 
+int register_busmouse(struct busmouse *ops)
 {
 	unsigned int msedev = MINOR_TO_MOUSE(ops->minor);
 	struct busmouse_data *mse;
@@ -364,13 +369,18 @@
 		return -EINVAL;
 	}
 
-	if (busmouse_data[msedev])
-		return -EBUSY;
-
 	mse = kmalloc(sizeof(*mse), GFP_KERNEL);
 	if (!mse)
 		return -ENOMEM;
 
+	down(&mouse_sem);
+	if (busmouse_data[msedev])
+	{
+		up(&mouse_sem);
+		kfree(mse);
+		return -EBUSY;
+	}
+
 	memset(mse, 0, sizeof(*mse));
 
 	mse->miscdev.minor = ops->minor;
@@ -385,13 +395,23 @@
 	ret = misc_register(&mse->miscdev);
 	if (!ret)
 		ret = msedev;
-
+	up(&mouse_sem);
+	
 	return ret;
 }
 
-int
-unregister_busmouse(int mousedev)
+/**
+ *	unregister_busmouse - unregister a bus mouse interface
+ *	@mousedev: Mouse number to release
+ *
+ *	Unregister a previously installed mouse handler. The mousedev
+ *	passed is the return code from a previous call to register_busmouse
+ */
+ 
+
+int unregister_busmouse(int mousedev)
 {
+	int err = -EINVAL;
 	if (mousedev < 0)
 		return 0;
 	if (mousedev >= NR_MICE) {
@@ -400,32 +420,27 @@
 		return -EINVAL;
 	}
 
+	down(&mouse_sem);
+	
 	if (!busmouse_data[mousedev]) {
 		printk(KERN_WARNING "busmouse: trying to free free mouse"
 		       " on mousedev %d\n", mousedev);
-		return -EINVAL;
+		goto fail;
 	}
 
 	if (busmouse_data[mousedev]->active) {
 		printk(KERN_ERR "busmouse: trying to free active mouse"
 		       " on mousedev %d\n", mousedev);
-		return -EINVAL;
+		goto fail;
 	}
 
-	misc_deregister(&busmouse_data[mousedev]->miscdev);
+	err=misc_deregister(&busmouse_data[mousedev]->miscdev);
 
 	kfree(busmouse_data[mousedev]);
 	busmouse_data[mousedev] = NULL;
-	return 0;
-}
-
-int __init
-bus_mouse_init(void)
-{
-#ifdef CONFIG_SUN_MOUSE
-	sun_mouse_init();
-#endif
-	return 0;
+fail:
+	up(&mouse_sem);
+	return err;
 }
 
 EXPORT_SYMBOL(busmouse_add_movementbuttons);
@@ -433,16 +448,3 @@
 EXPORT_SYMBOL(busmouse_add_buttons);
 EXPORT_SYMBOL(register_busmouse);
 EXPORT_SYMBOL(unregister_busmouse);
-
-#ifdef MODULE
-int
-init_module(void)
-{
-	return bus_mouse_init();
-}
-
-void
-cleanup_module(void)
-{
-}
-#endif
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/busmouse.h linux/drivers/char/busmouse.h
--- v2.3.99-pre2/linux/drivers/char/busmouse.h	Mon Aug  2 09:54:29 1999
+++ linux/drivers/char/busmouse.h	Tue Mar 21 23:38:25 2000
@@ -23,6 +23,4 @@
 extern int register_busmouse(struct busmouse *ops);
 extern int unregister_busmouse(int mousedev);
 
-extern int bus_mouse_init(void);
-
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.3.99-pre2/linux/drivers/char/misc.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/char/misc.c	Tue Mar 21 23:38:25 2000
@@ -57,6 +57,7 @@
  * Head entry for the doubly linked miscdevice list
  */
 static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list };
+static DECLARE_MUTEX(misc_sem);
 
 /*
  * Assigned numbers, used for dynamic minors
@@ -96,32 +97,42 @@
 static int misc_open(struct inode * inode, struct file * file)
 {
 	int minor = MINOR(inode->i_rdev);
-	struct miscdevice *c = misc_list.next;
+	struct miscdevice *c;
+	int err = -ENODEV;
+	
 	file->f_op = NULL;
+	
+	down(&misc_sem);
+	
+	c = misc_list.next;
 
 	while ((c != &misc_list) && (c->minor != minor))
 		c = c->next;
 	if (c == &misc_list) {
 		char modname[20];
+		up(&misc_sem);
 		sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
 		request_module(modname);
+		down(&misc_sem);
 		c = misc_list.next;
 		while ((c != &misc_list) && (c->minor != minor))
 			c = c->next;
 		if (c == &misc_list)
-			return -ENODEV;
+			goto fail;
 	}
 
 	if ((file->f_op = c->fops) && file->f_op->open)
-		return file->f_op->open(inode,file);
-	else
-		return -ENODEV;
+		err=file->f_op->open(inode,file);
+fail:
+	up(&misc_sem);
+	return err;
 }
 
 static struct file_operations misc_fops = {
 	open:		misc_open,
 };
 
+
 /**
  *	misc_register	-	register a miscellaneous device
  *	@misc: device structure
@@ -144,12 +155,17 @@
 
 	if (misc->next || misc->prev)
 		return -EBUSY;
+	down(&misc_sem);
 	if (misc->minor == MISC_DYNAMIC_MINOR) {
 		int i = DYNAMIC_MINORS;
 		while (--i >= 0)
 			if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
 				break;
-		if (i<0) return -EBUSY;
+		if (i<0)
+		{
+			up(&misc_sem);
+			return -EBUSY;
+		}
 		misc->minor = i;
 	}
 	if (misc->minor < DYNAMIC_MINORS)
@@ -170,6 +186,7 @@
 	misc->next = misc_list.next;
 	misc->prev->next = misc;
 	misc->next->prev = misc;
+	up(&misc_sem);
 	return 0;
 }
 
@@ -188,6 +205,7 @@
 	int i = misc->minor;
 	if (!misc->next || !misc->prev)
 		return -EINVAL;
+	down(&misc_sem);
 	misc->prev->next = misc->next;
 	misc->next->prev = misc->prev;
 	misc->next = NULL;
@@ -196,6 +214,7 @@
 	if (i < DYNAMIC_MINORS && i>0) {
 		misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
 	}
+	up(&misc_sem);
 	return 0;
 }
 
@@ -205,9 +224,6 @@
 int __init misc_init(void)
 {
 	create_proc_read_entry("misc", 0, 0, misc_read_proc, NULL);
-#ifdef CONFIG_BUSMOUSE
-	bus_mouse_init();
-#endif
 #if defined CONFIG_82C710_MOUSE
 	qpmouse_init();
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v2.3.99-pre2/linux/drivers/char/rtc.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/char/rtc.c	Mon Mar 20 08:27:36 2000
@@ -670,10 +670,10 @@
 	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 		BCD_TO_BIN(year);       /* This should never happen... */
 	
-	if (year > 10 && year < 44) {
+	if (year >= 20 && year < 48) {
 		epoch = 1980;
 		guess = "ARC console";
-	} else if (year < 96) {
+	} else if (year >= 48 && year < 100) {
 		epoch = 1952;
 		guess = "Digital UNIX";
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.3.99-pre2/linux/drivers/char/serial.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/char/serial.c	Wed Mar 22 22:23:55 2000
@@ -39,13 +39,17 @@
  *
  *  8/99: Generalized PCI support added.  Theodore Ts'o
  * 
+ *  3/00: Rid circular buffer of redundant xmit_cnt.  Fix a
+ *	  few races on freeing buffers too.
+ *	  Alan Modra <alan@linuxcare.com>
+ *
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
  */
 
-static char *serial_version = "4.92";
-static char *serial_revdate = "2000-1-27";
+static char *serial_version = "4.93";
+static char *serial_revdate = "2000-03-20";
 
 /*
  * Serial driver configuration section.  Here are the various options:
@@ -98,7 +102,7 @@
 #endif
 #endif
 
-#ifdef CONFIG_ISAPNP
+#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE))
 #ifndef ENABLE_SERIAL_PNP
 #define ENABLE_SERIAL_PNP
 #endif
@@ -133,7 +137,7 @@
 #define RS_STROBE_TIME (10*HZ)
 #define RS_ISR_PASS_LIMIT 256
 
-#if (defined(__i386__) && (CPU==386 || CPU==486))
+#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
 #define SERIAL_INLINE
 #endif
   
@@ -220,7 +224,6 @@
 				 unsigned int minor);
 extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
 
-
 static char *serial_name = "Serial driver";
 
 static DECLARE_TASK_QUEUE(tq_serial);
@@ -253,7 +256,7 @@
 #endif
 
 static unsigned detect_uart_irq (struct serial_state * state);
-static void autoconfig(struct serial_state * info);
+static void autoconfig(struct serial_state * state);
 static void change_speed(struct async_struct *info, struct termios *old);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 
@@ -321,9 +324,6 @@
 static struct termios *serial_termios[NR_PORTS];
 static struct termios *serial_termios_locked[NR_PORTS];
 
-#ifndef MIN
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
-#endif
 
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
@@ -379,7 +379,7 @@
 		return inb(info->port+1);
 #endif
 	case SERIAL_IO_MEM:
-		return readb(info->iomem_base +
+		return readb((unsigned long) info->iomem_base +
 			     (offset<<info->iomem_reg_shift));
 #ifdef CONFIG_SERIAL_GSC
 	case SERIAL_IO_GSC:
@@ -401,7 +401,7 @@
 		break;
 #endif
 	case SERIAL_IO_MEM:
-		writeb(value, info->iomem_base +
+		writeb(value, (unsigned long) info->iomem_base +
 			      (offset<<info->iomem_reg_shift));
 		break;
 #ifdef CONFIG_SERIAL_GSC
@@ -481,7 +481,9 @@
 		return;
 	
 	save_flags(flags); cli();
-	if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
+	if (info->xmit.head != info->xmit.tail
+	    && info->xmit.buf
+	    && !(info->IER & UART_IER_THRI)) {
 		info->IER |= UART_IER_THRI;
 		serial_out(info, UART_IER, info->IER);
 	}
@@ -633,7 +635,7 @@
 static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
 {
 	int count;
-	
+
 	if (info->x_char) {
 		serial_outp(info, UART_TX, info->x_char);
 		info->state->icount.tx++;
@@ -642,8 +644,9 @@
 			*intr_done = 0;
 		return;
 	}
-	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
-	    info->tty->hw_stopped) {
+	if (info->xmit.head == info->xmit.tail
+	    || info->tty->stopped
+	    || info->tty->hw_stopped) {
 		info->IER &= ~UART_IER_THRI;
 		serial_out(info, UART_IER, info->IER);
 		return;
@@ -651,14 +654,16 @@
 	
 	count = info->xmit_fifo_size;
 	do {
-		serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
-		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]);
+		info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
 		info->state->icount.tx++;
-		if (--info->xmit_cnt <= 0)
+		if (info->xmit.head == info->xmit.tail)
 			break;
 	} while (--count > 0);
 	
-	if (info->xmit_cnt < WAKEUP_CHARS)
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
 		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
 #ifdef SERIAL_DEBUG_INTR
@@ -667,7 +672,7 @@
 	if (intr_done)
 		*intr_done = 0;
 
-	if (info->xmit_cnt <= 0) {
+	if (info->xmit.head == info->xmit.tail) {
 		info->IER &= ~UART_IER_THRI;
 		serial_out(info, UART_IER, info->IER);
 	}
@@ -759,11 +764,11 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rs_interrupt(%d)...", irq);
 #endif
-	
+
 	info = IRQ_ports[irq];
 	if (!info)
 		return;
-	
+
 #ifdef CONFIG_SERIAL_MULTIPORT	
 	multi = &rs_multiport[irq];
 	if (multi->port_monitor)
@@ -833,7 +838,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rs_interrupt_single(%d)...", irq);
 #endif
-	
+
 	info = IRQ_ports[irq];
 	if (!info || !info->tty)
 		return;
@@ -888,7 +893,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rs_interrupt_multi(%d)...", irq);
 #endif
-	
+
 	info = IRQ_ports[irq];
 	if (!info)
 		return;
@@ -1026,7 +1031,7 @@
 					serial_out(info, UART_IER, info->IER);
 					info = info->next_port;
 				} while (info);
-#ifdef CONFIG_SERIAL_MULTIPORT					
+#ifdef CONFIG_SERIAL_MULTIPORT
 				if (rs_multiport[i].port1)
 					rs_interrupt_multi(i, NULL, NULL);
 				else
@@ -1117,10 +1122,10 @@
 		free_page(page);
 		goto errout;
 	}
-	if (info->xmit_buf)
+	if (info->xmit.buf)
 		free_page(page);
 	else
-		info->xmit_buf = (unsigned char *) page;
+		info->xmit.buf = (unsigned char *) page;
 
 #ifdef SERIAL_DEBUG_OPEN
 	printk("starting up ttys%d (irq %d)...", info->line, state->irq);
@@ -1298,7 +1303,7 @@
 
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
-	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	info->xmit.head = info->xmit.tail = 0;
 
 	/*
 	 * Set up serial timers...
@@ -1393,9 +1398,10 @@
 			free_irq(state->irq, &IRQ_ports[state->irq]);
 	}
 
-	if (info->xmit_buf) {
-		free_page((unsigned long) info->xmit_buf);
-		info->xmit_buf = 0;
+	if (info->xmit.buf) {
+		unsigned long pg = (unsigned long) info->xmit.buf;
+		info->xmit.buf = 0;
+		free_page(pg);
 	}
 
 	info->IER = 0;
@@ -1570,7 +1576,7 @@
 	 * when DLL is 0.
 	 */
 	if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) &&
-	    (info->state->revision == 0x5202))
+	    (info->state->revision == 0x5201))
 		quot++;
 	
 	info->quot = quot;
@@ -1654,7 +1660,7 @@
  			serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
  		}
 		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
- 	}
+	}
 	restore_flags(flags);
 }
 
@@ -1666,18 +1672,19 @@
 	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
 		return;
 
-	if (!tty || !info->xmit_buf)
+	if (!tty || !info->xmit.buf)
 		return;
 
 	save_flags(flags); cli();
-	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+	if (CIRC_SPACE(info->xmit.head,
+		       info->xmit.tail,
+		       SERIAL_XMIT_SIZE) == 0) {
 		restore_flags(flags);
 		return;
 	}
 
-	info->xmit_buf[info->xmit_head++] = ch;
-	info->xmit_head &= SERIAL_XMIT_SIZE-1;
-	info->xmit_cnt++;
+	info->xmit.buf[info->xmit.head] = ch;
+	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
 	restore_flags(flags);
 }
 
@@ -1689,8 +1696,10 @@
 	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
 		return;
 
-	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-	    !info->xmit_buf)
+	if (info->xmit.head == info->xmit.tail
+	    || tty->stopped
+	    || tty->hw_stopped
+	    || !info->xmit.buf)
 		return;
 
 	save_flags(flags); cli();
@@ -1709,16 +1718,19 @@
 	if (serial_paranoia_check(info, tty->device, "rs_write"))
 		return 0;
 
-	if (!tty || !info->xmit_buf || !tmp_buf)
+	if (!tty || !info->xmit.buf || !tmp_buf)
 		return 0;
 
 	save_flags(flags);
 	if (from_user) {
 		down(&tmp_buf_sem);
 		while (1) {
-			c = MIN(count,
-				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				    SERIAL_XMIT_SIZE - info->xmit_head));
+			int c1;
+			c = CIRC_SPACE_TO_END(info->xmit.head,
+					      info->xmit.tail,
+					      SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
 			if (c <= 0)
 				break;
 
@@ -1729,12 +1741,14 @@
 				break;
 			}
 			cli();
-			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				       SERIAL_XMIT_SIZE - info->xmit_head));
-			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-			info->xmit_head = ((info->xmit_head + c) &
+			c1 = CIRC_SPACE_TO_END(info->xmit.head,
+					       info->xmit.tail,
+					       SERIAL_XMIT_SIZE);
+			if (c1 < c)
+				c = c1;
+			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+			info->xmit.head = ((info->xmit.head + c) &
 					   (SERIAL_XMIT_SIZE-1));
-			info->xmit_cnt += c;
 			restore_flags(flags);
 			buf += c;
 			count -= c;
@@ -1742,27 +1756,29 @@
 		}
 		up(&tmp_buf_sem);
 	} else {
+		cli();
 		while (1) {
-			cli();		
-			c = MIN(count,
-				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				    SERIAL_XMIT_SIZE - info->xmit_head));
+			c = CIRC_SPACE_TO_END(info->xmit.head,
+					      info->xmit.tail,
+					      SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
 			if (c <= 0) {
-				restore_flags(flags);
 				break;
 			}
-			memcpy(info->xmit_buf + info->xmit_head, buf, c);
-			info->xmit_head = ((info->xmit_head + c) &
+			memcpy(info->xmit.buf + info->xmit.head, buf, c);
+			info->xmit.head = ((info->xmit.head + c) &
 					   (SERIAL_XMIT_SIZE-1));
-			info->xmit_cnt += c;
-			restore_flags(flags);
 			buf += c;
 			count -= c;
 			ret += c;
 		}
+		restore_flags(flags);
 	}
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
-	    !(info->IER & UART_IER_THRI)) {
+	if (info->xmit.head != info->xmit.tail
+	    && !tty->stopped
+	    && !tty->hw_stopped
+	    && !(info->IER & UART_IER_THRI)) {
 		info->IER |= UART_IER_THRI;
 		serial_out(info, UART_IER, info->IER);
 	}
@@ -1772,14 +1788,10 @@
 static int rs_write_room(struct tty_struct *tty)
 {
 	struct async_struct *info = (struct async_struct *)tty->driver_data;
-	int	ret;
-				
+
 	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
 		return 0;
-	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
-	if (ret < 0)
-		ret = 0;
-	return ret;
+	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 }
 
 static int rs_chars_in_buffer(struct tty_struct *tty)
@@ -1788,7 +1800,7 @@
 				
 	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
 		return 0;
-	return info->xmit_cnt;
+	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 }
 
 static void rs_flush_buffer(struct tty_struct *tty)
@@ -1799,7 +1811,7 @@
 	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
 		return;
 	save_flags(flags); cli();
-	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	info->xmit.head = info->xmit.tail = 0;
 	restore_flags(flags);
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
@@ -2071,8 +2083,9 @@
 	 * interrupt happens).
 	 */
 	if (info->x_char || 
-	    ((info->xmit_cnt > 0) && !info->tty->stopped &&
-	     !info->tty->hw_stopped))
+	    ((CIRC_CNT(info->xmit.head, info->xmit.tail,
+		       SERIAL_XMIT_SIZE) > 0) &&
+	     !info->tty->stopped && !info->tty->hw_stopped))
 		result &= TIOCSER_TEMT;
 
 	if (copy_to_user(value, &result, sizeof(int)))
@@ -2708,8 +2721,8 @@
 	char_time = char_time / 5;
 	if (char_time == 0)
 		char_time = 1;
-	if (timeout)
-	  char_time = MIN(char_time, timeout);
+	if (timeout && timeout < char_time)
+		char_time = timeout;
 	/*
 	 * If the transmitter hasn't cleared in twice the approximate
 	 * amount of time to send the entire FIFO, it probably won't
@@ -3060,7 +3073,7 @@
 	int	ret;
 	unsigned long flags;
 
-	ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
+	ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d",
 		      state->line, uart_config[state->type].name, 
 		      state->port, state->irq);
 
@@ -3083,9 +3096,9 @@
 	}
 	save_flags(flags); cli();
 	status = serial_in(info, UART_MSR);
-	control = info ? info->MCR : serial_in(info, UART_MCR);
+	control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR);
 	restore_flags(flags); 
-	
+
 	stat_buf[0] = 0;
 	stat_buf[1] = 0;
 	if (control & UART_MCR_RTS)
@@ -3403,7 +3416,7 @@
 	state->type = PORT_UNKNOWN;
 
 #ifdef SERIAL_DEBUG_AUTOCONF
-	printk("Testing ttyS%d (0x%04x, 0x%04x)...\n", state->line,
+	printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line,
 	       state->port, (unsigned) state->iomem_base);
 #endif
 	
@@ -3743,15 +3756,10 @@
 	
 	/* enable/disable interrupts */
 	p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80);
-	if (dev->vendor == PCI_VENDOR_ID_PANACOM) {
-		scratch = readl(p + 0x4c);
-		if (enable)
-			scratch |= 0x40;
-		else
-			scratch &= ~0x40;
-		writel(scratch, p + 0x4c);
-	} else
-		writel(enable ? 0x41 : 0x00, p + 0x4c);
+	scratch = 0x41;
+	if (dev->vendor == PCI_VENDOR_ID_PANACOM)
+		scratch = 0x43;
+	writel(enable ? scratch : 0x00, (unsigned long)p + 0x4c);
 	iounmap(p);
 
 	if (!enable)
@@ -3806,7 +3814,7 @@
                        break;
        }
 
-       writew(readw(p + 0x28) & data, p + 0x28);
+       writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28);
        iounmap(p);
        return 0;
 }
@@ -4181,6 +4189,19 @@
 	{	PCI_VENDOR_ID_USR, 0x1008,
 		PCI_ANY_ID, PCI_ANY_ID,
 		SPCI_FL_BASE0, 1, 115200 },
+	/* Titan Electronic cards */
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 2, 921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 4, 921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 4, 921600 },
 	/*
 	 * Untested PCI modems, sent in from various folks...
 	 */
@@ -4188,10 +4209,9 @@
 	{	PCI_VENDOR_ID_ROCKWELL, 0x1004,
 		0x1048, 0x1500, 
 		SPCI_FL_BASE1, 1, 115200 },
-#ifdef CONFIG_DDB5074
+#if 0			/* No definition for PCI_DEVICE_ID_NEC_NILE4 */
 	/*
 	 * NEC Vrc-5074 (Nile 4) builtin UART.
-	 * Conditionally compiled in since this is a motherboard device.
 	 */
 	{	PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4,
 		PCI_ANY_ID, PCI_ANY_ID,
@@ -4306,18 +4326,41 @@
 #ifdef ENABLE_SERIAL_PNP
 
 static struct pci_board pnp_devices[] __initdata = {
+	/* Motorola VoiceSURFR 56K Modem */
+	{	ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
 	/* Rockwell 56K ACF II Fax+Data+Voice Modem */
 	{	ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_NO_SHIRQ | SPCI_FL_PNPDEFAULT,
+		1, 115200 },
+	/* AZT3005 PnP SOUND DEVICE */
+	{	ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+	/* Best Data Products Inc. Smart One 336F PnP Modem */
+	{	ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336), 0, 0,
 		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
 	/* Boca Research 33,600 ACF Modem */
 	{	ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400), 0, 0,
 		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
-	/* Best Data Products Inc. Smart One 336F PnP Modem */
-	{	ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336), 0, 0,
+	/* Creative Modem Blaster Flash56 DI5601-1 */
+	{	ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+	/* Creative Modem Blaster V.90 DI5660 */
+	{	ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+	/* Pace 56 Voice Internal Plug & Play Modem */
+	{	ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430), 0, 0,
 		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
 	/* SupraExpress 28.8 Data/Fax PnP modem */
 	{	ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310), 0, 0,
 		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+	/* US Robotics Sporster 33600 Modem */
+	{	ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+	/* U.S. Robotics 56K FAX INT */
+	{	ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031), 0, 0,
+		SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+
 	/* These ID's are taken from M$ documentation */
 	/* Compaq 14400 Modem */
 	{	ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000), 0, 0,
@@ -4334,6 +4377,24 @@
 	{	0, }
 };
 
+static void inline avoid_irq_share(struct pci_dev *dev)
+{
+	int i, map = 0x1FF8;
+	struct serial_state *state = rs_table;
+	struct isapnp_irq *irq;
+	struct isapnp_resources *res = dev->sysdata;
+
+	for (i = 0; i < NR_PORTS; i++) {
+		if (state->type != PORT_UNKNOWN)
+			clear_bit(state->irq, &map);
+		state++;
+	}
+
+	for ( ; res; res = res->alt)
+		for(irq = res->irq; irq; irq = irq->next)
+			irq->map = map;
+}
+
 static void __init probe_serial_pnp(void)
 {
        struct pci_dev *dev = NULL;
@@ -4352,9 +4413,9 @@
        for (board = pnp_devices; board->vendor; board++) {
                while ((dev = isapnp_find_dev(NULL, board->vendor,
 					     board->device, dev))) {
-
-		       start_pci_pnp_board(dev, board);
-		       
+			if (board->flags & SPCI_FL_NO_SHIRQ)
+				avoid_irq_share(dev);
+			start_pci_pnp_board(dev, board);
                }
        }
 
@@ -4417,7 +4478,11 @@
 #if (LINUX_VERSION_CODE > 0x20100)
 	serial_driver.driver_name = "serial";
 #endif
+#if (LINUX_VERSION_CODE > 0x2032D)
 	serial_driver.name = "tts/%d";
+#else
+	serial_driver.name = "ttyS";
+#endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
 	serial_driver.num = NR_PORTS;
@@ -4461,7 +4526,11 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
+#if (LINUX_VERSION_CODE > 0x2032D)
 	callout_driver.name = "cua/%d";
+#else
+	callout_driver.name = "cua";
+#endif
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 #if (LINUX_VERSION_CODE >= 131343)
@@ -4503,7 +4572,7 @@
 		    && (state->flags & ASYNC_AUTO_IRQ)
 		    && (state->port != 0))
 			state->irq = detect_uart_irq(state);
-		printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n",
+		printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n",
 		       state->line + SERIAL_DEV_OFFSET,
 		       (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
 		       state->port, state->irq,
@@ -4538,7 +4607,7 @@
  *	The port is then probed and if neccessary the IRQ is autodetected
  *	If this fails an error is returned.
  *
- *	On success the port is ready to use and the line number is returned.	
+ *	On success the port is ready to use and the line number is returned.
  */
  
 int register_serial(struct serial_struct *req)
@@ -4548,8 +4617,7 @@
 	struct serial_state *state;
 	struct async_struct *info;
 
-	save_flags(flags);
-	cli();
+	save_flags(flags); cli();
 	for (i = 0; i < NR_PORTS; i++) {
 		if ((rs_table[i].port == req->port) &&
 		    (rs_table[i].iomem_base == req->iomem_base))
@@ -4568,7 +4636,7 @@
 	state = &rs_table[i];
 	if (rs_table[i].count) {
 		restore_flags(flags);
-		printk("Couldn't configure serial #%d (port=%d,irq=%d): "
+		printk("Couldn't configure serial #%d (port=%ld,irq=%d): "
 		       "device already open\n", i, req->port, req->irq);
 		return -1;
 	}
@@ -4598,12 +4666,11 @@
 	if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
 		state->irq = detect_uart_irq(state);
 
-	printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
+       printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
 	      state->line + SERIAL_DEV_OFFSET,
 	      state->iomem_base ? "iomem" : "port",
 	      state->iomem_base ? (unsigned long)state->iomem_base :
-	      (unsigned long)state->port,
-	      state->irq, uart_config[state->type].name);
+	      state->port, state->irq, uart_config[state->type].name);
 	tty_register_devfs(&serial_driver, 0,
 			   serial_driver.minor_start + state->line); 
 	tty_register_devfs(&callout_driver, 0,
@@ -4625,8 +4692,7 @@
 	unsigned long flags;
 	struct serial_state *state = &rs_table[line];
 
-	save_flags(flags);
-	cli();
+	save_flags(flags); cli();
 	if (state->info && state->info->tty)
 		tty_hangup(state->info->tty);
 	state->type = PORT_UNKNOWN;
@@ -4642,12 +4708,7 @@
 }
 
 #ifdef MODULE
-int init_module(void)
-{
-	return rs_init();
-}
-
-void cleanup_module(void) 
+void rs_fini(void) 
 {
 	unsigned long flags;
 	int e1, e2;
@@ -4655,8 +4716,7 @@
 	struct async_struct *info;
 
 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
-	save_flags(flags);
-	cli();
+	save_flags(flags); cli();
 	timer_active &= ~(1 << RS_TIMER);
 	timer_table[RS_TIMER].fn = NULL;
 	timer_table[RS_TIMER].expires = 0;
@@ -4693,12 +4753,16 @@
 	}
 #endif	
 	if (tmp_buf) {
-		free_page((unsigned long) tmp_buf);
+		unsigned long pg = (unsigned long) tmp_buf;
 		tmp_buf = NULL;
+		free_page(pg);
 	}
 }
 #endif /* MODULE */
 
+module_init(rs_init);
+module_exit(rs_fini);
+
 
 /*
  * ------------------------------------------------------------
@@ -4960,6 +5024,6 @@
 
 /*
   Local variables:
-  compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce  -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -DEXPORT_SYMTAB -c serial.c"
+  compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce  -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -DEXPORT_SYMTAB -c serial.c"
   End:
 */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c
--- v2.3.99-pre2/linux/drivers/char/sysrq.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/char/sysrq.c	Thu Mar 23 08:13:35 2000
@@ -28,7 +28,7 @@
 extern void wakeup_bdflush(int);
 extern void reset_vc(unsigned int);
 extern int console_loglevel;
-extern struct vfsmount *vfsmntlist;
+extern struct list_head super_blocks;
 
 /* Machine specific power off function */
 void (*sysrq_power_off)(void) = NULL;
@@ -174,22 +174,16 @@
 	}
 }
 
-static void go_sync(kdev_t dev, int remount_flag)
+static void go_sync(struct super_block *sb, int remount_flag)
 {
 	printk(KERN_INFO "%sing device %s ... ",
 	       remount_flag ? "Remount" : "Sync",
-	       kdevname(dev));
+	       kdevname(sb->s_dev));
 
 	if (remount_flag) {				    /* Remount R/O */
-		struct super_block *sb = get_super(dev);
-		struct vfsmount *vfsmnt;
 		int ret, flags;
 		struct list_head *p;
 
-		if (!sb) {
-			printk("Superblock not found\n");
-			return;
-		}
 		if (sb->s_flags & MS_RDONLY) {
 			printk("R/O\n");
 			return;
@@ -204,7 +198,7 @@
 		}
 		file_list_unlock();
 		DQUOT_OFF(sb);
-		fsync_dev(dev);
+		fsync_dev(sb->s_dev);
 		flags = MS_RDONLY;
 		if (sb->s_op && sb->s_op->remount_fs) {
 			ret = sb->s_op->remount_fs(sb, &flags, NULL);
@@ -217,7 +211,7 @@
 		} else
 			printk("nothing to do\n");
 	} else {
-		fsync_dev(dev);				    /* Sync only */
+		fsync_dev(sb->s_dev);			    /* Sync only */
 		printk("OK\n");
 	}
 }
@@ -233,20 +227,24 @@
 
 void do_emergency_sync(void)
 {
-	struct vfsmount *mnt;
+	struct super_block *sb;
 	int remount_flag;
 
 	lock_kernel();
 	remount_flag = (emergency_sync_scheduled == EMERG_REMOUNT);
 	emergency_sync_scheduled = 0;
 
-	for (mnt = vfsmntlist; mnt; mnt = mnt->mnt_next)
-		if (is_local_disk(mnt->mnt_dev))
-			go_sync(mnt->mnt_dev, remount_flag);
-
-	for (mnt = vfsmntlist; mnt; mnt = mnt->mnt_next)
-		if (!is_local_disk(mnt->mnt_dev) && MAJOR(mnt->mnt_dev))
-			go_sync(mnt->mnt_dev, remount_flag);
+	for (sb = sb_entry(super_blocks.next);
+	     sb != sb_entry(&super_blocks); 
+	     sb = sb_entry(sb->s_list.next))
+		if (is_local_disk(sb->s_dev))
+			go_sync(sb, remount_flag);
+
+	for (sb = sb_entry(super_blocks.next);
+	     sb != sb_entry(&super_blocks); 
+	     sb = sb_entry(sb->s_list.next))
+		if (!is_local_disk(sb->s_dev) && MAJOR(sb->s_dev))
+			go_sync(sb, remount_flag);
 
 	unlock_kernel();
 	printk(KERN_INFO "Done.\n");
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.3.99-pre2/linux/drivers/char/tty_io.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/char/tty_io.c	Tue Mar 21 13:04:53 2000
@@ -2278,9 +2278,6 @@
 #ifdef CONFIG_ESPSERIAL  /* init ESP before rs, so rs doesn't see the port */
 	espserial_init();
 #endif
-#ifdef CONFIG_SERIAL
-	rs_init();
-#endif
 #if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
 	vme_scc_init();
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v2.3.99-pre2/linux/drivers/char/wdt.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/wdt.c	Tue Mar 21 14:43:39 2000
@@ -159,10 +159,6 @@
  *	Handle an interrupt from the board. These are raised when the status
  *	map changes in what the board considers an interesting way. That means
  *	a failure condition occuring.
- *
- *	FIXME:	We need to pass a dev_id as the PCI card can share irqs
- *	although its arguably a _very_ dumb idea to share watchdog
- *	irq lines
  */
  
 void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -494,7 +490,7 @@
 int __init wdt_init(void)
 {
 	printk(KERN_INFO "WDT500/501-P driver 0.07 at %X (Interrupt %d)\n", io,irq);
-	if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL))
+	if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", &wdt_miscdev))
 	{
 		printk(KERN_ERR "IRQ %d is not free.\n", irq);
 		return -EIO;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/i2c/i2c-algo-pcf.c linux/drivers/i2c/i2c-algo-pcf.c
--- v2.3.99-pre2/linux/drivers/i2c/i2c-algo-pcf.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/i2c/i2c-algo-pcf.c	Tue Mar 21 12:36:21 2000
@@ -581,7 +581,7 @@
 	return 0;
 }
 
-static int __init i2c_algo_pcf_init (void)
+int __init i2c_algo_pcf_init (void)
 {
 	printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n");
 	return 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/Config.in linux/drivers/ide/Config.in
--- v2.3.99-pre2/linux/drivers/ide/Config.in	Sun Mar 19 18:35:30 2000
+++ linux/drivers/ide/Config.in	Wed Mar 22 17:18:43 2000
@@ -38,11 +38,12 @@
 	    dep_bool '    AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_mbool '      AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING $CONFIG_BLK_DEV_AEC6210 $CONFIG_IDEDMA_PCI_WIP
 	    dep_bool '    ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_mbool '      ALI M15x3 WDC support (DANGEROUS)' CONFIG_WDC_ALI15X3 $CONFIG_BLK_DEV_ALI15X3
 	    dep_bool '    AMD Viper support' CONFIG_BLK_DEV_AMD7409 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_mbool '      AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE $CONFIG_BLK_DEV_AMD7409 $CONFIG_IDEDMA_PCI_WIP
 	    dep_bool '    CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_mbool '      CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID $CONFIG_BLK_DEV_CMD64X $CONFIG_IDEDMA_PCI_WIP
-	    dep_bool '    CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+	    dep_bool '    CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_bool '    Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
   	    dep_bool '    HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_mbool '      HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP
@@ -53,14 +54,14 @@
 	       dep_mbool '    Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI
 	       dep_mbool '      PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO
 	    fi
-	    dep_bool '    NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+	    dep_bool '    NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_bool '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
 	    dep_bool '    PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_bool '      Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX
 	    dep_mbool '      Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER $CONFIG_BLK_DEV_PDC202XX $CONFIG_IDEDMA_PCI_WIP
 	    dep_bool '    SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
-	    dep_bool '    Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
-	    dep_bool '    VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+	    dep_bool '    Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_bool '    VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_BLK_DEV_IDEDMA_PCI
          fi
 	 if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
 	    bool '    Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/aec6210.c linux/drivers/ide/aec6210.c
--- v2.3.99-pre2/linux/drivers/ide/aec6210.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/aec6210.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/aec6210.c		Version 0.05	Feb. 10, 2000
+ * linux/drivers/ide/aec6210.c		Version 0.06	Mar. 18, 2000
  *
  * Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
  * May be copied or modified under the terms of the GNU General Public License
@@ -191,6 +191,7 @@
 	return(err);
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
 {
 	struct hd_driveid *id = drive->id;
@@ -230,6 +231,7 @@
 			((id->dma_1word >> 8) & 7) ? ide_dma_on :
 						     ide_dma_off_quietly);
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 static void aec6210_tune_drive (ide_drive_t *drive, byte pio)
 {
@@ -252,6 +254,7 @@
 	(void) aec6210_tune_chipset(drive, speed);
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -314,6 +317,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 #endif /* CONFIG_AEC6210_TUNING */
 
 unsigned int __init pci_init_aec6210 (struct pci_dev *dev, const char *name)
@@ -336,13 +340,13 @@
 {
 #ifdef CONFIG_AEC6210_TUNING
 	hwif->tuneproc = &aec6210_tune_drive;
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
 
-	if (hwif->dma_base) {
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (hwif->dma_base)
 		hwif->dmaproc = &aec6210_dmaproc;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 #endif /* CONFIG_AEC6210_TUNING */
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ali14xx.c linux/drivers/ide/ali14xx.c
--- v2.3.99-pre2/linux/drivers/ide/ali14xx.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ali14xx.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ali14xx.c		Version 0.03	Feb 09, 1996
+ *  linux/drivers/ide/ali14xx.c		Version 0.03	Feb 09, 1996
  *
  *  Copyright (C) 1996  Linus Torvalds & author (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/alim15x3.c linux/drivers/ide/alim15x3.c
--- v2.3.99-pre2/linux/drivers/ide/alim15x3.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/alim15x3.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/alim15x3.c		Version 0.08	Jan. 14, 2000
+ * linux/drivers/ide/alim15x3.c		Version 0.09	Mar. 18, 2000
  *
  *  Copyright (C) 1998-2000 Michel Aubry, Maintainer
  *  Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer
@@ -37,7 +37,7 @@
 
 static int ali_get_info(char *buffer, char **addr, off_t offset, int count);
 extern int (*ali_display_info)(char *, char **, off_t, int);  /* ide-proc.c */
-struct pci_dev *bmide_dev;
+static struct pci_dev *bmide_dev;
 
 char *fifo[4] = {
 	"FIFO Off",
@@ -67,7 +67,7 @@
 	"error DRQ busy"
 };
 
-static int ali_get_info(char *buffer, char **addr, off_t offset, int count)
+static int ali_get_info (char *buffer, char **addr, off_t offset, int count)
 {
 	byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1;
 	unsigned int bibma;
@@ -356,6 +356,12 @@
 	return (err);
 }
 
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	ali15x3_tune_drive(drive, 5);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int config_chipset_for_dma (ide_drive_t *drive, byte ultra33)
 {
 	struct hd_driveid *id	= drive->id;
@@ -401,26 +407,21 @@
 	return rval;
 }
 
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	ali15x3_tune_drive(drive, 5);
-}
-
-
 static byte ali15x3_can_ultra (ide_drive_t *drive)
 {
+#ifdef CONFIG_WDC_ALI15X3
 	struct hd_driveid *id	= drive->id;
+#endif /* CONFIG_WDC_ALI15X3 */
 
-#if 0
-	if (m5229_revision < 0x20) {
-#else
 	if (m5229_revision <= 0x20) {
-#endif
 		return 0;
 	} else if ((m5229_revision < 0xC2) &&
-		   ((drive->media!=ide_disk) ||
-		    (chip_is_1543c_e &&
-		     strstr(id->model, "WDC ")))) {
+#ifdef CONFIG_WDC_ALI15X3
+		   ((chip_is_1543c_e && strstr(id->model, "WDC ")) ||
+		    (drive->media!=ide_disk))) {
+#else /* CONFIG_WDC_ALI15X3 */
+		   (drive->media!=ide_disk)) {
+#endif /* CONFIG_WDC_ALI15X3 */
 		return 0;
 	} else {
 		return 1;
@@ -495,6 +496,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name)
 {
@@ -519,9 +521,11 @@
 	}
 
 #if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
-	ali_proc = 1;
-	bmide_dev = dev;
-	ali_display_info = &ali_get_info;
+	if (!ali_proc) {
+		ali_proc = 1;
+		bmide_dev = dev;
+		ali_display_info = &ali_get_info;
+	}
 #endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
 
 	return 0;
@@ -666,18 +670,20 @@
 	}
 
 	hwif->tuneproc = &ali15x3_tune_drive;
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+#ifndef CONFIG_BLK_DEV_IDEDMA
+	hwif->autodma = 0;
+	return;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
 	if ((hwif->dma_base) && (m5229_revision >= 0x20)) {
 		/*
 		 * M1543C or newer for DMAing
 		 */
 		hwif->dmaproc = &ali15x3_dmaproc;
 		hwif->autodma = 1;
-	} else {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
 	}
-	return;
 }
 
 void ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/amd7409.c linux/drivers/ide/amd7409.c
--- v2.3.99-pre2/linux/drivers/ide/amd7409.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/amd7409.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/amd7409.c		Version 0.03	Feb. 10, 2000
+ * linux/drivers/ide/amd7409.c		Version 0.04	Mar. 18, 2000
  *
  * Copyright (C) 2000			Andre Hedrick <andre@suse.com>
  * May be copied or modified under the terms of the GNU General Public License
@@ -194,49 +194,6 @@
 	return (err);
 }
 
-/*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS.
- */
-static int config_chipset_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-	byte udma_66		= ((id->hw_config & 0x2000) &&
-				   (HWIF(drive)->udma_four)) ? 1 : 0;
-	byte speed		= 0x00;
-	int  rval;
-
-	if ((id->dma_ultra & 0x0010) && (udma_66)) {
-		speed = XFER_UDMA_4;
-	} else if ((id->dma_ultra & 0x0008) && (udma_66)) {
-		speed = XFER_UDMA_3;
-	} else if (id->dma_ultra & 0x0004) {
-		speed = XFER_UDMA_2;
-	} else if (id->dma_ultra & 0x0002) {
-		speed = XFER_UDMA_1;
-	} else if (id->dma_ultra & 0x0001) {
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-        } else {
-		return ((int) ide_dma_off_quietly);
-	}
-
-	(void) amd7409_tune_chipset(drive, speed);
-
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-
-	return rval;
-}
-
 static void config_chipset_for_pio (ide_drive_t *drive)
 {
 	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
@@ -288,6 +245,52 @@
 	(void) amd7409_tune_chipset(drive, speed);
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+	byte udma_66		= ((id->hw_config & 0x2000) &&
+				   (HWIF(drive)->udma_four)) ? 1 : 0;
+	byte speed		= 0x00;
+	int  rval;
+
+	if ((id->dma_ultra & 0x0010) && (udma_66)) {
+		speed = XFER_UDMA_4;
+	} else if ((id->dma_ultra & 0x0008) && (udma_66)) {
+		speed = XFER_UDMA_3;
+	} else if (id->dma_ultra & 0x0004) {
+		speed = XFER_UDMA_2;
+	} else if (id->dma_ultra & 0x0002) {
+		speed = XFER_UDMA_1;
+	} else if (id->dma_ultra & 0x0001) {
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else {
+		return ((int) ide_dma_off_quietly);
+	}
+
+	(void) amd7409_tune_chipset(drive, speed);
+
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+
+	return rval;
+}
+
+
+
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -351,6 +354,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 unsigned int __init pci_init_amd7409 (struct pci_dev *dev, const char *name)
 {
@@ -370,9 +374,11 @@
 			printk("%s: simplex device: DMA will fail!!\n", name);
 	}
 #if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
-	amd7409_proc = 1;
-	bmide_dev = dev;
-	amd7409_display_info = &amd7409_get_info;
+	if (!amd7409_proc) {
+		amd7409_proc = 1;
+		bmide_dev = dev;
+		amd7409_display_info = &amd7409_get_info;
+	}
 #endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */
 
 	return 0;
@@ -396,8 +402,17 @@
 void __init ide_init_amd7409 (ide_hwif_t *hwif)
 {
 	hwif->tuneproc = &amd7409_tune_drive;
+
+#ifndef CONFIG_BLK_DEV_IDEDMA
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+	hwif->autodma = 0;
+	return;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
 	if (hwif->dma_base) {
 		hwif->dmaproc = &amd7409_dmaproc;
+		hwif->autodma = 1;
 	} else {
 		hwif->autodma = 0;
 		hwif->drives[0].autotune = 1;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/buddha.c linux/drivers/ide/buddha.c
--- v2.3.99-pre2/linux/drivers/ide/buddha.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/buddha.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver
+ *  linux/drivers/ide/buddha.c -- Amiga Buddha and Catweasel IDE Driver
  *
  *	Copyright (C) 1997 by Geert Uytterhoeven
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/cmd640.c linux/drivers/ide/cmd640.c
--- v2.3.99-pre2/linux/drivers/ide/cmd640.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/cmd640.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/cmd640.c		Version 1.02  Sep 01, 1996
+ *  linux/drivers/ide/cmd640.c		Version 1.02  Sep 01, 1996
  *
  *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/cmd64x.c linux/drivers/ide/cmd64x.c
--- v2.3.99-pre2/linux/drivers/ide/cmd64x.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/ide/cmd64x.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,7 @@
 /* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16
  *
+ * linux/drivers/ide/cmd64x.c		Version 1.21	Mar. 18, 2000
+ *
  * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
  *           Note, this driver is not used at all on other systems because
  *           there the "BIOS" has done all of the following already.
@@ -116,19 +118,27 @@
 
 	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
 	p += sprintf(p, "                %sabled                         %sabled\n",
-		(reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
+		(reg72&0x80)?"dis":" en",(reg7a&0x80)?"dis":" en");
 	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
 	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-		(reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
+		(reg72&0x20)?"yes":"no ",(reg72&0x40)?"yes":"no ",(reg7a&0x20)?"yes":"no ",(reg7a&0x40)?"yes":"no ");
 	p += sprintf(p, "DMA Mode:       %s(%s)          %s(%s)         %s(%s)           %s(%s)\n",
 		(reg72&0x20)?((reg73&0x01)?"UDMA":" DMA"):" PIO",
-		(reg72&0x20)?(((reg73&0x15)==0x15)?"4":((reg73&0x25)==0x25)?"3":((reg73&0x10)==0x10)?"2":((reg73&0x20)==0x20)?"1":((reg73&0x30)==0x30)?"0":"X"):"?",
+		(reg72&0x20)?(  ((reg73&0x30)==0x30)?(((reg73&0x35)==0x35)?"3":"0"):
+				((reg73&0x20)==0x20)?(((reg73&0x25)==0x25)?"3":"1"):
+				((reg73&0x10)==0x10)?(((reg73&0x15)==0x15)?"4":"2"):"X"):"?",
 		(reg72&0x40)?((reg73&0x02)?"UDMA":" DMA"):" PIO",
-		(reg72&0x40)?(((reg73&0x4A)==0x4A)?"4":((reg73&0x8A)==0x8A)?"3":((reg73&0x40)==0x40)?"2":((reg73&0x80)==0x80)?"1":((reg73&0xC0)==0xC0)?"0":"X"):"?",
+		(reg72&0x40)?(	((reg73&0xC0)==0xC0)?(((reg73&0xC5)==0xC5)?"3":"0"):
+				((reg73&0x80)==0x80)?(((reg73&0x85)==0x85)?"3":"1"):
+				((reg73&0x40)==0x40)?(((reg73&0x4A)==0x4A)?"4":"2"):"X"):"?",
 		(reg7a&0x20)?((reg7b&0x01)?"UDMA":" DMA"):" PIO",
-		(reg7a&0x20)?(((reg7b&0x15)==0x15)?"4":((reg7b&0x25)==0x25)?"3":((reg7b&0x10)==0x10)?"2":((reg7b&0x20)==0x20)?"1":((reg7b&0x30)==0x30)?"0":"X"):"?",
+		(reg7a&0x20)?(	((reg7b&0x30)==0x30)?(((reg7b&0x35)==0x35)?"3":"0"):
+				((reg7b&0x20)==0x20)?(((reg7b&0x25)==0x25)?"3":"1"):
+				((reg7b&0x10)==0x10)?(((reg7b&0x15)==0x15)?"4":"2"):"X"):"?",
 		(reg7a&0x40)?((reg7b&0x02)?"UDMA":" DMA"):" PIO",
-		(reg7a&0x40)?(((reg7b&0x4A)==0x4A)?"4":((reg7b&0x8A)==0x8A)?"3":((reg7b&0x40)==0x40)?"2":((reg7b&0x80)==0x80)?"1":((reg7b&0xC0)==0xC0)?"0":"X"):"?" );
+		(reg7a&0x40)?(	((reg7b&0xC0)==0xC0)?(((reg7b&0xC5)==0xC5)?"3":"0"):
+				((reg7b&0x80)==0x80)?(((reg7b&0x85)==0x85)?"3":"1"):
+				((reg7b&0x40)==0x40)?(((reg7b&0x4A)==0x4A)?"4":"2"):"X"):"?" );
 	p += sprintf(p, "PIO Mode:       %s                %s               %s                 %s\n",
 		"?", "?", "?", "?");
 
@@ -155,6 +165,22 @@
 
 	return p-buffer;	/* => must be less than 4k! */
 }
+
+#if 0
+static char * cmd64x_chipset_data (char *buf, struct pci_dev *dev)
+{
+	char *p = buf;
+	p += sprintf(p, "thingy stuff\n");
+	return (char *)p;
+}
+static int __init cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+	p = cmd64x_chipset_data(buffer, bmide_dev);
+	return p-buffer;	/* hoping it is less than 4K... */
+}
+#endif
+
 #endif	/* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
 
 byte cmd64x_proc = 0;
@@ -305,76 +331,60 @@
 		setup_count, active_count, recovery_count);
 }
 
+static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+{
+	byte speed= 0x00;
+	byte set_pio= ide_get_best_pio_mode(drive, 4, 5, NULL);
+
+	cmd64x_tuneproc(drive, set_pio);
+	speed = XFER_PIO_0 + set_pio;
+	if (set_speed)
+		(void) ide_config_drive_speed(drive, speed);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int tune_chipset_for_dma (ide_drive_t *drive, byte speed)
 {
-#if 0
-	struct hd_driveid *id	= drive->id;
 	ide_hwif_t *hwif	= HWIF(drive);
 	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long dma_base	= hwif->dma_base;
 	byte unit		= (drive->select.b.unit & 0x01);
+	int err			= 0;
 
-	u8 reg72 = 0, reg73 = 0;	/* primary */
-	u8 reg7a = 0, reg7b = 0;	/* secondary */
-	u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
-	u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0;
-	u8 regU = (hwif->channel) ? 2 : 0;
-	u8 regD = (hwif->channel) ? 2 : 0;
-
-	(void) pci_read_config_byte(dev, BMIDESR0,  &reg72);
-	(void) pci_read_config_byte(dev, UDIDETCR0, &reg73);
-	(void) pci_read_config_byte(dev, BMIDESR1,  &reg7a);
-	(void) pci_read_config_byte(dev, UDIDETCR1, &reg7b);
+	u8 pciU			= (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
+	u8 pciD			= (hwif->channel) ? BMIDESR1 : BMIDESR0;
+	u8 regU			= 0;
+	u8 regD			= 0;
+
+	(void) pci_read_config_byte(dev, pciD, &regD);
+	(void) pci_read_config_byte(dev, pciU, &regU);
+	regD &= ~(unit ? 0x40 : 0x20);
+	regU &= ~(unit ? 0xCA : 0x35);
+	(void) pci_write_config_byte(dev, pciD, regD);
+	(void) pci_write_config_byte(dev, pciU, regU);
 
+	(void) pci_read_config_byte(dev, pciD, &regD);
+	(void) pci_read_config_byte(dev, pciU, &regU);
 	switch(speed) {
-		case XFER_UDMA_4:
-			pciU = unit ? 0x4A : 0x15;
-		case XFER_UDMA_3:
-			pciU = unit ? 0x8A : 0x25;
-		case XFER_UDMA_2:
-			pciU = unit ? 0x42 : 0x11;
-		case XFER_UDMA_1:
-			pciU = unit ? 0x82 : 0x21;
-		case XFER_UDMA_0:
-			pciU = unit ? 0xC2 : 0x31
-(reg73&0x15)?"4":(reg73&0x25)?"3":(reg73&0x11)?"2":(reg73&0x21)?"1":(reg73&0x31)?"0":"X",
-(reg73&0x4A)?"4":(reg73&0x8A)?"3":(reg73&0x42)?"2":(reg73&0x82)?"1":(reg73&0xC2)?"0":"X",
-(reg7b&0x15)?"4":(reg7b&0x25)?"3":(reg7b&0x11)?"2":(reg7b&0x21)?"1":(reg7b&0x31)?"0":"X",
-(reg7b&0x4A)?"4":(reg7b&0x8A)?"3":(reg7b&0x42)?"2":(reg7b&0x82)?"1":(reg7b&0xC2)?"0":"X",
-
-		case XFER_MW_DMA_2:
-			pciD = unit ? 0x40 : 0x10;
-		case XFER_MW_DMA_1:
-			pciD = unit ? 0x80 : 0x20;
-		case XFER_MW_DMA_0:
-			pciD = unit ? 0xC0 : 0x30;
-		case XFER_SW_DMA_2:
-		case XFER_SW_DMA_1:
-		case XFER_SW_DMA_0:
-(reg73&0x10)?"2":(reg73&0x20)?"1":(reg73&0x30)?"0":"X",
-(reg73&0x40)?"2":(reg73&0x80)?"1":(reg73&0xC0)?"0":"X",
-(reg7b&0x10)?"2":(reg7b&0x20)?"1":(reg7b&0x30)?"0":"X",
-(reg7b&0x40)?"2":(reg7b&0x80)?"1":(reg7b&0xC0)?"0":"X" );
-
+		case XFER_UDMA_4:	regU |= (unit ? 0x4A : 0x15); break;
+		case XFER_UDMA_3:	regU |= (unit ? 0x8A : 0x25); break;
+		case XFER_UDMA_2:	regU |= (unit ? 0x42 : 0x11); break;
+		case XFER_UDMA_1:	regU |= (unit ? 0x82 : 0x21); break;
+		case XFER_UDMA_0:	regU |= (unit ? 0xC2 : 0x31); break;
+		case XFER_MW_DMA_2:	regD |= (unit ? 0x40 : 0x10); break;
+		case XFER_MW_DMA_1:	regD |= (unit ? 0x80 : 0x20); break;
+		case XFER_MW_DMA_0:	regD |= (unit ? 0xC0 : 0x30); break;
+		case XFER_SW_DMA_2:	regD |= (unit ? 0x40 : 0x10); break;
+		case XFER_SW_DMA_1:	regD |= (unit ? 0x80 : 0x20); break;
+		case XFER_SW_DMA_0:	regD |= (unit ? 0xC0 : 0x30); break;
 		default:
 			return 1;
 	}
+	(void) pci_write_config_byte(dev, pciU, regU);
+	err = ide_config_drive_speed(drive, speed);
+	regD |= (unit ? 0x40 : 0x20);
+	(void) pci_write_config_byte(dev, pciD, regD);
 
-	(void) ide_config_drive_speed(drive, speed);
-	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-#endif
-	return 0;
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
-{
-	byte speed	= 0x00;
-	byte set_pio	= ide_get_best_pio_mode(drive, 4, 5, NULL);	
-
-	cmd64x_tuneproc(drive, set_pio);
-	speed = XFER_PIO_0 + set_pio;
-	if (set_speed)
-		(void) ide_config_drive_speed(drive, speed);
+	return err;
 }
 
 static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
@@ -382,21 +392,17 @@
 	struct hd_driveid *id	= drive->id;
 	ide_hwif_t *hwif	= HWIF(drive);
 	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long dma_base	= hwif->dma_base;
 
- 	byte unit		= (drive->select.b.unit & 0x01);
 	byte speed		= 0x00;
 	byte set_pio		= 0x00;
-	byte udma_timing_bits	= 0x00;
 	byte udma_33		= ((rev >= 0x05) || (ultra_66)) ? 1 : 0;
 	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
-	/* int drive_number	= ((hwif->channel ? 2 : 0) + unit); */
 	int rval;
 
 	switch(dev->device) {
-		case PCI_DEVICE_ID_CMD_643:
-		case PCI_DEVICE_ID_CMD_646:
 		case PCI_DEVICE_ID_CMD_648:
+		case PCI_DEVICE_ID_CMD_646:
+		case PCI_DEVICE_ID_CMD_643:
 		default:
 			break;
 	}
@@ -416,22 +422,16 @@
 	 * in the 646U2.
 	 * So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
 	 */
-
 	if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
 		speed = XFER_UDMA_4;
-		udma_timing_bits = 0x10;	/* 2 clock */
 	} else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
 		speed = XFER_UDMA_3;
-		udma_timing_bits = 0x20;	/* 3 clock */
 	} else if ((id->dma_ultra & 0x0004) && (udma_33)) {
 		speed = XFER_UDMA_2;
-		udma_timing_bits = 0x10;	/* 2 clock */
 	} else if ((id->dma_ultra & 0x0002) && (udma_33)) {
 		speed = XFER_UDMA_1;
-		udma_timing_bits = 0x20;	/* 3 clock */
 	} else if ((id->dma_ultra & 0x0001) && (udma_33)) {
 		speed = XFER_UDMA_0;
-		udma_timing_bits = 0x30;	/* 4 clock */
 	} else if (id->dma_mword & 0x0004) {
 		speed = XFER_MW_DMA_2;
 	} else if (id->dma_mword & 0x0002) {
@@ -453,27 +453,6 @@
 	if (set_pio)
 		return ((int) ide_dma_off_quietly);
 
-#if 1
-	/*
-	 * This the alternate access method. :-(
-	 * The correct method is to directly setup the pci-config space.
-	 */ 
-	(void) ide_config_drive_speed(drive, speed);
-	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-
-	if (speed >= XFER_UDMA_0) {
-		byte udma_ctrl = inb(dma_base + 3);
-		/* Put this channel into UDMA mode. */
-		udma_ctrl |= (1 << unit);
-		udma_ctrl &= ~(0x04 << unit);
-		if (udma_66)
-			udma_ctrl |= (0x04 << unit);
-		udma_ctrl &= ~(0x30 << (unit * 2));
-		udma_ctrl |=  (udma_timing_bits << (unit * 2));
-		outb(udma_ctrl, dma_base+3);
-	}
-#endif
-
 	if (tune_chipset_for_dma(drive, speed))
 		return ((int) ide_dma_off);
 
@@ -500,17 +479,14 @@
 	class_rev &= 0xff;	
 
 	switch(dev->device) {
+		case PCI_DEVICE_ID_CMD_648:
+			can_ultra_66  = 1;
 		case PCI_DEVICE_ID_CMD_643:
-			can_ultra_33 = 1;
-			can_ultra_66 = 0;
+			can_ultra_33  = 1;
 			break;
 		case PCI_DEVICE_ID_CMD_646:
-			can_ultra_33 = (class_rev >= 0x05) ? 1 : 0;
-			can_ultra_66 = 0;
-			break;
-		case PCI_DEVICE_ID_CMD_648:
-			can_ultra_33 = 1;
-			can_ultra_66 = 1;
+			can_ultra_33  = (class_rev >= 0x05) ? 1 : 0;
+			can_ultra_66  = 0;
 			break;
 		default:
 			return hwif->dmaproc(ide_dma_off, drive);
@@ -595,6 +571,7 @@
 	/* Other cases are done by generic IDE-DMA code. */
 	return cmd64x_dmaproc(func, drive);
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
 {
@@ -604,9 +581,11 @@
 	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
 	class_rev &= 0xff;
 
-#if 0
-	if (dev->resource[PCI_ROM_RESOURCE].start)
+#ifdef __i386__
+	if (dev->resource[PCI_ROM_RESOURCE].start) {
 		pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+		printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
+	}
 #endif
 
 	switch(dev->device) {
@@ -702,7 +681,9 @@
 	if (!hwif->dma_base)
 		return;
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	switch(dev->device) {
+		case PCI_DEVICE_ID_CMD_648:
 		case PCI_DEVICE_ID_CMD_643:
 			hwif->dmaproc = &cmd64x_dmaproc;
 			break;
@@ -714,10 +695,8 @@
 				hwif->dmaproc = &cmd64x_dmaproc;
 			}
 			break;
-		case PCI_DEVICE_ID_CMD_648:
-			hwif->dmaproc = &cmd64x_dmaproc;
-			break;
 		default:
 			break;
 	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/cs5530.c linux/drivers/ide/cs5530.c
--- v2.3.99-pre2/linux/drivers/ide/cs5530.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/cs5530.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,8 @@
 /*
- * linux/drivers/block/cs5530.c			Version 0.5	Feb 13, 2000
+ * linux/drivers/ide/cs5530.c		Version 0.6	Mar. 18, 2000
+ *
+ * Copyright (C) 2000			Andre Hedrick <andre@suse.com>
+ * Ditto of GNU General Public License.
  *
  * Copyright (C) 2000			Mark Lord <mlord@pobox.com>
  * May be copied or modified under the terms of the GNU General Public License
@@ -23,6 +26,7 @@
 #include <linux/ide.h>
 #include <asm/io.h>
 #include <asm/irq.h>
+
 #include "ide_modes.h"
 
 #define DISPLAY_CS5530_TIMINGS
@@ -120,6 +124,7 @@
 	}
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 /*
  * cs5530_config_dma() handles selection/setting of DMA/UDMA modes
  * for both the chipset and drive.
@@ -241,6 +246,7 @@
 	/* Other cases are done by generic IDE-DMA code. */
 	return ide_dmaproc(func, drive);
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 /*
  * Initialize the cs5530 bridge for reliable IDE DMA operation.
@@ -322,9 +328,11 @@
 	restore_flags(flags);
 
 #if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
-	cs5530_proc = 1;
-	bmide_dev = dev;
-	cs5530_display_info = &cs5530_get_info;
+	if (!cs5530_proc) {
+		cs5530_proc = 1;
+		bmide_dev = dev;
+		cs5530_display_info = &cs5530_get_info;
+	}
 #endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
 
 	return 0;
@@ -343,7 +351,12 @@
 	} else {
 		unsigned int basereg, d0_timings;
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 		hwif->dmaproc  = &cs5530_dmaproc;
+#else
+		hwif->autodma = 0;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
 		hwif->tuneproc = &cs5530_tuneproc;
 		basereg = CS5530_BASEREG(hwif);
 		d0_timings = inl(basereg+0);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/cy82c693.c linux/drivers/ide/cy82c693.c
--- v2.3.99-pre2/linux/drivers/ide/cy82c693.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/cy82c693.c	Wed Mar 22 22:22:05 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/cy82c693.c		Version 0.34	Dec. 13, 1999
+ * linux/drivers/ide/cy82c693.c		Version 0.34	Dec. 13, 1999
  *
  *  Copyright (C) 1998-99 Andreas S. Krebs (akrebs@altavista.net), Maintainer
  *  Copyright (C) 1998-99 Andre Hedrick, Integrater
@@ -44,6 +44,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
@@ -176,6 +177,7 @@
 	p_pclk->time_8 = (byte)clk1;
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 /*
  * set DMA mode a specific channel for CY82C693
  */
@@ -262,6 +264,7 @@
 	}
         return ide_dmaproc(func, drive);
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 /*
  * tune ide drive - set PIO mode
@@ -432,10 +435,14 @@
 {
 	hwif->chipset = ide_cy82c693;
 	hwif->tuneproc = &cy82c693_tune_drive;
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+	hwif->autodma = 0;
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->dmaproc = &cy82c693_dmaproc;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
+		hwif->autodma = 1;
 	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/dtc2278.c linux/drivers/ide/dtc2278.c
--- v2.3.99-pre2/linux/drivers/ide/dtc2278.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/dtc2278.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/dtc2278.c		Version 0.02	Feb 10, 1996
+ *  linux/drivers/ide/dtc2278.c		Version 0.02	Feb 10, 1996
  *
  *  Copyright (C) 1996  Linus Torvalds & author (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/falconide.c linux/drivers/ide/falconide.c
--- v2.3.99-pre2/linux/drivers/ide/falconide.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/falconide.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/falconide.c -- Atari Falcon IDE Driver
+ *  linux/drivers/ide/falconide.c -- Atari Falcon IDE Driver
  *
  *     Created 12 Jul 1997 by Geert Uytterhoeven
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/gayle.c linux/drivers/ide/gayle.c
--- v2.3.99-pre2/linux/drivers/ide/gayle.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/gayle.c	Wed Mar 22 17:18:43 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver
+ *  linux/drivers/ide/gayle.c -- Amiga Gayle IDE Driver
  *
  *     Created 9 Jul 1997 by Geert Uytterhoeven
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/hd.c linux/drivers/ide/hd.c
--- v2.3.99-pre2/linux/drivers/ide/hd.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/hd.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/hd.c
+ *  linux/drivers/ide/hd.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/hpt34x.c linux/drivers/ide/hpt34x.c
--- v2.3.99-pre2/linux/drivers/ide/hpt34x.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/hpt34x.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/hpt34x.c		Version 0.29	Feb. 10, 2000
+ * linux/drivers/ide/hpt34x.c		Version 0.30	Mar. 18, 2000
  *
  * Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
  * May be copied or modified under the terms of the GNU General Public License
@@ -144,6 +144,59 @@
 	return(err);
 }
 
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio = drive->id->eide_pio_modes;
+
+	byte	timing, speed, pio;
+
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+	switch(timing) {
+		case 4: speed = XFER_PIO_4;break;
+		case 3: speed = XFER_PIO_3;break;
+		case 2: speed = XFER_PIO_2;break;
+		case 1: speed = XFER_PIO_1;break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+			break;
+		}
+	(void) hpt34x_tune_chipset(drive, speed);
+}
+
+static void hpt34x_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4;break;
+		case 3:		speed = XFER_PIO_3;break;
+		case 2:		speed = XFER_PIO_2;break;
+		case 1:		speed = XFER_PIO_1;break;
+		default:	speed = XFER_PIO_0;break;
+	}
+	hpt34x_clear_chipset(drive);
+	(void) hpt34x_tune_chipset(drive, speed);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 /*
  * This allows the configuration of ide_pci chipset registers
  * for cards that learn about the drive's UDMA, DMA, PIO capabilities
@@ -195,58 +248,6 @@
 						     ide_dma_off_quietly);
 }
 
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio	= drive->id->eide_pio_modes;
-
-	byte			timing, speed, pio;
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
-			break;
-	}
-	(void) hpt34x_tune_chipset(drive, speed);
-}
-
-static void hpt34x_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	hpt34x_clear_chipset(drive);
-	(void) hpt34x_tune_chipset(drive, speed);
-}
-
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -347,6 +348,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 /*
  * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
@@ -395,9 +397,11 @@
 	__restore_flags(flags);	/* local CPU only */
 
 #if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
-	hpt34x_proc = 1;
-	bmide_dev = dev;
-	hpt34x_display_info = &hpt34x_get_info;
+	if (!hpt34x_proc) {
+		hpt34x_proc = 1;
+		bmide_dev = dev;
+		hpt34x_display_info = &hpt34x_get_info;
+	}
 #endif /* DISPLAY_HPT34X_TIMINGS && CONFIG_PROC_FS */
 
 	return dev->irq;
@@ -406,6 +410,8 @@
 void __init ide_init_hpt34x (ide_hwif_t *hwif)
 {
 	hwif->tuneproc = &hpt34x_tune_drive;
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		unsigned short pcicmd = 0;
 
@@ -416,4 +422,9 @@
 		hwif->drives[0].autotune = 1;
 		hwif->drives[1].autotune = 1;
 	}
+#else /* !CONFIG_BLK_DEV_IDEDMA */
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+	hwif->autodma = 0;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/hpt366.c linux/drivers/ide/hpt366.c
--- v2.3.99-pre2/linux/drivers/ide/hpt366.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/hpt366.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/hpt366.c		Version 0.16	Feb. 10, 2000
+ * linux/drivers/ide/hpt366.c		Version 0.17	Mar. 18, 2000
  *
  * Copyright (C) 1999-2000		Andre Hedrick <andre@suse.com>
  * May be copied or modified under the terms of the GNU General Public License
@@ -259,6 +259,64 @@
 	return(err);
 }
 
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio = drive->id->eide_pio_modes;
+	byte	timing, speed, pio;
+
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: config_chipset_for_pio\n", drive->name);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 :
+			   (drive->id->tPIO & 2) ? 0x02 :
+			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+	switch(timing) {
+		case 4: speed = XFER_PIO_4;break;
+		case 3: speed = XFER_PIO_3;break;
+		case 2: speed = XFER_PIO_2;break;
+		case 1: speed = XFER_PIO_1;break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+			break;
+	}
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: config_chipset_for_pio:  speed=0x%04x\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	(void) hpt366_tune_chipset(drive, speed);
+}
+
+static void hpt366_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4;break;
+		case 3:		speed = XFER_PIO_3;break;
+		case 2:		speed = XFER_PIO_2;break;
+		case 1:		speed = XFER_PIO_1;break;
+		default:	speed = XFER_PIO_0;break;
+	}
+	(void) hpt366_tune_chipset(drive, speed);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 /*
  * This allows the configuration of ide_pci chipset registers
  * for cards that learn about the drive's UDMA, DMA, PIO capabilities
@@ -347,63 +405,6 @@
 	return rval;
 }
 
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio	= drive->id->eide_pio_modes;
-	byte			timing, speed, pio;
-
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: config_chipset_for_pio\n", drive->name);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 :
-			   (drive->id->tPIO & 2) ? 0x02 :
-			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
-			break;
-	}
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: config_chipset_for_pio:  speed=0x%04x\n", drive->name, speed);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	(void) hpt366_tune_chipset(drive, speed);
-}
-
-static void hpt366_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	(void) hpt366_tune_chipset(drive, speed);
-}
-
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -478,6 +479,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name)
 {
@@ -532,13 +534,20 @@
 void __init ide_init_hpt366 (ide_hwif_t *hwif)
 {
 	hwif->tuneproc = &hpt366_tune_drive;
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->dmaproc = &hpt366_dmaproc;
+		hwif->autodma = 1;
 	} else {
 		hwif->autodma = 0;
 		hwif->drives[0].autotune = 1;
 		hwif->drives[1].autotune = 1;
 	}
+#else /* !CONFIG_BLK_DEV_IDEDMA */
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+	hwif->autodma = 0;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 }
 
 void ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ht6560b.c linux/drivers/ide/ht6560b.c
--- v2.3.99-pre2/linux/drivers/ide/ht6560b.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ht6560b.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ht6560b.c		Version 0.07	Feb  1, 2000
+ *  linux/drivers/ide/ht6560b.c		Version 0.07	Feb  1, 2000
  *
  *  Copyright (C) 1995-2000  Linus Torvalds & author (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/icside.c linux/drivers/ide/icside.c
--- v2.3.99-pre2/linux/drivers/ide/icside.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/icside.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/icside.c
+ * linux/drivers/ide/icside.c
  *
  * Copyright (c) 1996,1997 Russell King.
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c
--- v2.3.99-pre2/linux/drivers/ide/ide-cd.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-cd.c	Wed Mar 22 22:22:05 2000
@@ -1,5 +1,6 @@
 /*
- * linux/drivers/block/ide-cd.c
+ * linux/drivers/ide/ide-cd.c
+ *
  * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
  * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
  * Copyright (C) 1998, 1999 Jens Axboe <axboe@image.dk>
@@ -276,6 +277,7 @@
  
 #define IDECD_VERSION "4.56"
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -2400,21 +2402,10 @@
 
 	printk (", %dkB Cache", be16_to_cpu(buf.cap.buffer_size));
 
-	if (drive->using_dma) {
-		if ((drive->id->field_valid & 4) &&
-		    (drive->id->hw_config & 0x2000) &&
-		    (HWIF(drive)->udma_four) &&
-		    (drive->id->dma_ultra & (drive->id->dma_ultra >> 11) & 3)) {
-			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
-		} else if ((drive->id->field_valid & 4) &&
-			   (drive->id->dma_ultra & (drive->id->dma_ultra >> 8) & 7)) {
-			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
-		} else if (drive->id->field_valid & 4) {
-			printk(", (U)DMA");	/* Can be BIOS-enabled! */
-		} else {
-			printk(", DMA");
-		}
-	}
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (drive->using_dma)
+		(void) HWIF(drive)->dmaproc(ide_dma_verbose, drive);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 	printk("\n");
 
 	return nslots;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-cd.h linux/drivers/ide/ide-cd.h
--- v2.3.99-pre2/linux/drivers/ide/ide-cd.h	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-cd.h	Thu Mar 23 15:38:23 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide_cd.h
+ *  linux/drivers/ide/ide_cd.h
  *
  *  Copyright (C) 1996-98  Erik Andersen
  *  Copyright (C) 1998-2000 Jens Axboe
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-disk.c linux/drivers/ide/ide-disk.c
--- v2.3.99-pre2/linux/drivers/ide/ide-disk.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-disk.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-disk.c	Version 1.09	April 23, 1999
+ *  linux/drivers/ide/ide-disk.c	Version 1.09	April 23, 1999
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -821,21 +821,10 @@
 			drive->name, id->model,
 			capacity/2048L, id->buf_size/2,
 			drive->bios_cyl, drive->bios_head, drive->bios_sect);
-
-	if (drive->using_dma) {
-		if ((id->field_valid & 4) && (id->hw_config & 0x2000) &&
-		    (HWIF(drive)->udma_four) &&
-		    (id->dma_ultra & (id->dma_ultra >> 11) & 3)) {
-			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
-		} else if ((id->field_valid & 4) &&
-			   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
-			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
-		} else if (id->field_valid & 4) {
-			printk(", (U)DMA");	/* Can be BIOS-enabled! */
-		} else {
-			printk(", DMA");
-		}
-	}
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (drive->using_dma)
+		(void) HWIF(drive)->dmaproc(ide_dma_verbose, drive);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 	printk("\n");
 
 	drive->mult_count = 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-dma.c linux/drivers/ide/ide-dma.c
--- v2.3.99-pre2/linux/drivers/ide/ide-dma.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-dma.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-dma.c		Version 4.09	April 23, 1999
+ *  linux/drivers/ide/ide-dma.c		Version 4.09	April 23, 1999
  *
  *  Copyright (c) 1999  Andre Hedrick
  *  May be copied or modified under the terms of the GNU General Public License
@@ -354,6 +354,29 @@
 	return 0;
 }
 
+int report_drive_dmaing (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+
+	if ((id->field_valid & 4) && (id->hw_config & 0x2000) &&
+	    (HWIF(drive)->udma_four) &&
+	    (id->dma_ultra & (id->dma_ultra >> 11) & 3)) {
+		if ((id->dma_ultra >> 12) & 1) {
+			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
+		} else {
+			printk(", UDMA(44)");	/* UDMA BIOS-enabled! */
+		}
+	} else if ((id->field_valid & 4) &&
+		   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+		printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
+	} else if (id->field_valid & 4) {
+		printk(", (U)DMA");	/* Can be BIOS-enabled! */
+	} else {
+		printk(", DMA");
+	}
+	return 1;
+}
+
 static int config_drive_for_dma (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -453,6 +476,9 @@
 		case ide_dma_bad_drive:
 		case ide_dma_good_drive:
 			return check_drive_lists(drive, (func == ide_dma_good_drive));
+		case ide_dma_verbose:
+			return report_drive_dmaing(drive);
+		case ide_dma_retune:
 		case ide_dma_lostirq:
 		case ide_dma_timeout:
 			printk("ide_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func),  func);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-features.c linux/drivers/ide/ide-features.c
--- v2.3.99-pre2/linux/drivers/ide/ide-features.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-features.c	Wed Mar 22 17:18:44 2000
@@ -100,6 +100,8 @@
 		case ide_dma_test_irq:		return("ide_dma_test_irq");
 		case ide_dma_bad_drive:		return("ide_dma_bad_drive");
 		case ide_dma_good_drive:	return("ide_dma_good_drive");
+		case ide_dma_verbose:		return("ide_dma_verbose");
+		case ide_dma_retune:		return("ide_dma_retune");
 		case ide_dma_lostirq:		return("ide_dma_lostirq");
 		case ide_dma_timeout:		return("ide_dma_timeout");
 		default:			return("unknown");
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-floppy.c linux/drivers/ide/ide-floppy.c
--- v2.3.99-pre2/linux/drivers/ide/ide-floppy.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-floppy.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-floppy.c	Version 0.9	Jul   4, 1999
+ * linux/drivers/ide/ide-floppy.c	Version 0.9	Jul   4, 1999
  *
  * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-geometry.c linux/drivers/ide/ide-geometry.c
--- v2.3.99-pre2/linux/drivers/ide/ide-geometry.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/ide/ide-geometry.c	Wed Mar 22 17:18:44 2000
@@ -1,9 +1,9 @@
 /*
- * linux/drivers/block/ide-geometry.c
+ * linux/drivers/ide/ide-geometry.c
  */
 #include <linux/config.h>
 
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+#ifdef CONFIG_IDE
 #include <linux/ide.h>
 
 #include <asm/io.h>
@@ -211,4 +211,4 @@
 		       drive->bios_cyl, drive->bios_head, drive->bios_sect);
 	return ret;
 }
-#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
+#endif /* CONFIG_IDE */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-pci.c linux/drivers/ide/ide-pci.c
--- v2.3.99-pre2/linux/drivers/ide/ide-pci.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-pci.c	Wed Mar 22 17:18:44 2000
@@ -1,7 +1,7 @@
 /*
- *  linux/drivers/block/ide-pci.c		Version 1.04	July 27, 1999
+ *  linux/drivers/ide/ide-pci.c		Version 1.04	July 27, 1999
  *
- *  Copyright (c) 1998-1999  Andre Hedrick
+ *  Copyright (c) 1998-2000  Andre Hedrick <andre@linux-ide.org>
  *
  *  Copyright (c) 1995-1998  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-pmac.c linux/drivers/ide/ide-pmac.c
--- v2.3.99-pre2/linux/drivers/ide/ide-pmac.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-pmac.c	Wed Mar 22 17:18:44 2000
@@ -1,4 +1,6 @@
 /*
+ * linux/drivers/ide/ide-pmac.c		Version ?.??	Mar. 18, 2000
+ *
  * Support for IDE interfaces on PowerMacs.
  * These IDE interfaces are memory-mapped and have a DBDMA channel
  * for doing DMA.
@@ -10,7 +12,7 @@
  *  as published by the Free Software Foundation; either version
  *  2 of the License, or (at your option) any later version.
  *
- * Some code taken from drivers/block/ide-dma.c:
+ * Some code taken from drivers/ide/ide-dma.c:
  *
  *  Copyright (c) 1995-1998  Mark Lord
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-pnp.c linux/drivers/ide/ide-pnp.c
--- v2.3.99-pre2/linux/drivers/ide/ide-pnp.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-pnp.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-pnp.c
+ * linux/drivers/ide/ide-pnp.c
  *
  * This file provides autodetection for ISA PnP IDE interfaces.
  * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-probe.c linux/drivers/ide/ide-probe.c
--- v2.3.99-pre2/linux/drivers/ide/ide-probe.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/ide/ide-probe.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-probe.c	Version 1.05	July 3, 1999
+ *  linux/drivers/ide/ide-probe.c	Version 1.05	July 3, 1999
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-proc.c linux/drivers/ide/ide-proc.c
--- v2.3.99-pre2/linux/drivers/ide/ide-proc.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-proc.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-proc.c	Version 1.03	January  2, 1998
+ *  linux/drivers/ide/ide-proc.c	Version 1.03	January  2, 1998
  *
  *  Copyright (C) 1997-1998	Mark Lord
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide-tape.c linux/drivers/ide/ide-tape.c
--- v2.3.99-pre2/linux/drivers/ide/ide-tape.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide-tape.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-tape.c		Version 1.16f	Dec  15, 1999
+ * linux/drivers/ide/ide-tape.c		Version 1.16f	Dec  15, 1999
  *
  * Copyright (C) 1995 - 1999 Gadi Oxman <gadio@netvision.net.il>
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide.c linux/drivers/ide/ide.c
--- v2.3.99-pre2/linux/drivers/ide/ide.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c		Version 6.30	Dec 28, 1999
+ *  linux/drivers/ide/ide.c		Version 6.30	Dec 28, 1999
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -1809,9 +1809,9 @@
 static void ide_probe_module (void)
 {
 	if (!ide_probe) {
-#ifdef CONFIG_KMOD
+#if defined(CONFIG_KMOD) && defined(CONFIG_BLK_DEV_IDE_MODULE)
 		(void) request_module("ide-probe-mod");
-#endif /* CONFIG_KMOD */
+#endif /* (CONFIG_KMOD) && (CONFIG_BLK_DEV_IDE_MODULE) */
 	} else {
 		(void) ide_probe->init();
 	}
@@ -2491,6 +2491,30 @@
 			return 0;
 		}
 
+		case HDIO_GETGEO_BIG:
+		{
+			struct hd_big_geometry *loc = (struct hd_big_geometry *) arg;
+			if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
+			if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
+			if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
+			if (put_user(drive->bios_cyl, (unsigned int *) &loc->cylinders)) return -EFAULT;
+			if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+				(unsigned long *) &loc->start)) return -EFAULT;
+			return 0;
+		}
+
+		case HDIO_GETGEO_BIG_RAW:
+		{
+			struct hd_big_geometry *loc = (struct hd_big_geometry *) arg;
+			if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
+			if (put_user(drive->head, (byte *) &loc->heads)) return -EFAULT;
+			if (put_user(drive->sect, (byte *) &loc->sectors)) return -EFAULT;
+			if (put_user(drive->cyl, (unsigned int *) &loc->cylinders)) return -EFAULT;
+			if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+				(unsigned long *) &loc->start)) return -EFAULT;
+			return 0;
+		}
+
 	 	case BLKGETSIZE:   /* Return device size */
 			return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
 
@@ -2564,8 +2588,7 @@
 		}
 	        case HDIO_UNREGISTER_HWIF:
 			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-			/* should I check here for arg > MAX_HWIFS, or
-			   just let ide_unregister fail silently? -- shaver */
+			/* (arg > MAX_HWIFS) checked in function */
 			ide_unregister(arg);
 			return 0;
 		case HDIO_SET_NICE:
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ide_modes.h linux/drivers/ide/ide_modes.h
--- v2.3.99-pre2/linux/drivers/ide/ide_modes.h	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ide_modes.h	Thu Mar 23 15:37:58 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide_modes.h
+ *  linux/drivers/ide/ide_modes.h
  *
  *  Copyright (C) 1996  Linus Torvalds, Igor Abramov, and Mark Lord
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/ns87415.c linux/drivers/ide/ns87415.c
--- v2.3.99-pre2/linux/drivers/ide/ns87415.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/ns87415.c	Wed Mar 22 22:22:05 2000
@@ -1,12 +1,14 @@
 /*
- * linux/drivers/block/ns87415.c		Version 1.00  December 7, 1997
+ * linux/drivers/ide/ns87415.c		Version 1.01  Mar. 18, 2000
  *
- * Copyright (C) 1997-1998  Mark Lord
- * Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 1997-1998	Mark Lord <mlord@pobox.com>
+ * Copyright (C) 1998		Eddie C. Dost <ecd@skynet.be>
+ * Copyright (C) 1999-2000	Andre Hedrick <andre@suse.com>
  *
- * Inspired by an earlier effort from David S. Miller (davem@caipfs.rutgers.edu)
+ * Inspired by an earlier effort from David S. Miller <davem@redhat.com>
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
@@ -78,6 +80,7 @@
 	ns87415_prepare_drive (drive, drive->using_dma);
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
 {
 	ide_hwif_t	*hwif = HWIF(drive);
@@ -106,6 +109,7 @@
 			return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 	}
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 void __init ide_init_ns87415 (ide_hwif_t *hwif)
 {
@@ -179,7 +183,10 @@
 	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
 		hwif->irq = hwif->mate->irq;	/* share IRQ with mate */
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base)
 		hwif->dmaproc = &ns87415_dmaproc;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
 	hwif->selectproc = &ns87415_selectproc;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/opti621.c linux/drivers/ide/opti621.c
--- v2.3.99-pre2/linux/drivers/ide/opti621.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/opti621.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/opti621.c		Version 0.6	Jan 02, 1999
+ *  linux/drivers/ide/opti621.c		Version 0.6	Jan 02, 1999
  *
  *  Copyright (C) 1996-1998  Linus Torvalds & authors (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/pdc202xx.c linux/drivers/ide/pdc202xx.c
--- v2.3.99-pre2/linux/drivers/ide/pdc202xx.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/pdc202xx.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/pdc202xx.c	Version 0.29	Feb. 10, 2000
+ *  linux/drivers/ide/pdc202xx.c	Version 0.30	Mar. 18, 2000
  *
  *  Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
  *  May be copied or modified under the terms of the GNU General Public License
@@ -9,6 +9,9 @@
  *  Note that BIOS v1.29 is reported to fix the problem.  Since this is
  *  safe chipset tuning, including this support is harmless
  *
+ *  Promise Ultra66 cards with BIOS v1.11 this
+ *  compiled into the kernel if you have more than one card installed.
+ *
  *  The latest chipset code will support the following ::
  *  Three Ultra33 controllers and 12 drives.
  *  8 are UDMA supported and 4 are limited to DMA mode 2 multi-word.
@@ -16,60 +19,6 @@
  *
  *  UNLESS you enable "CONFIG_PDC202XX_BURST"
  *
- *  There is only one BIOS in the three contollers.
- *
- *  May  8 20:56:17 Orion kernel:
- *  Uniform Multi-Platform E-IDE driver Revision: 6.19
- *  PDC20246: IDE controller on PCI bus 00 dev a0
- *  PDC20246: not 100% native mode: will probe irqs later
- *  PDC20246: ROM enabled at 0xfebd0000
- *  PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
- *      ide0: BM-DMA at 0xef80-0xef87, BIOS settings: hda:DMA, hdb:DMA
- *      ide1: BM-DMA at 0xef88-0xef8f, BIOS settings: hdc:pio, hdd:pio
- *  PDC20246: IDE controller on PCI bus 00 dev 98
- *  PDC20246: not 100% native mode: will probe irqs later
- *  PDC20246: ROM enabled at 0xfebc0000
- *  PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
- *      ide2: BM-DMA at 0xef40-0xef47, BIOS settings: hde:DMA, hdf:DMA
- *      ide3: BM-DMA at 0xef48-0xef4f, BIOS settings: hdg:DMA, hdh:DMA
- *  PDC20246: IDE controller on PCI bus 00 dev 90
- *  PDC20246: not 100% native mode: will probe irqs later
- *  PDC20246: ROM enabled at 0xfebb0000
- *  PDC20246: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
- *  PDC20246: FORCING BURST BIT 0x00 -> 0x01 ACTIVE
- *      ide4: BM-DMA at 0xef00-0xef07, BIOS settings: hdi:DMA, hdj:pio
- *      ide5: BM-DMA at 0xef08-0xef0f, BIOS settings: hdk:pio, hdl:pio
- *  PIIX3: IDE controller on PCI bus 00 dev 39
- *  PIIX3: device not capable of full native PCI mode
- *
- *  ide0 at 0xeff0-0xeff7,0xefe6 on irq 19
- *  ide1 at 0xefa8-0xefaf,0xebe6 on irq 19
- *  ide2 at 0xefa0-0xefa7,0xef7e on irq 18
- *  ide3 at 0xef68-0xef6f,0xef66 on irq 18
- *  ide4 at 0xef38-0xef3f,0xef62 on irq 17
- *  hda: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=13328/15/63, UDMA(33)
- *  hdb: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=6256/16/63, UDMA(33)
- *  hde: Maxtor 72004 AP, 1916MB w/128kB Cache, CHS=3893/16/63, DMA
- *  hdf: Maxtor 71626 A, 1554MB w/64kB Cache, CHS=3158/16/63, DMA
- *  hdi: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
- *  hdj: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
- *
- *  Promise Ultra66 cards with BIOS v1.11 this
- *  compiled into the kernel if you have more than one card installed.
- *
- *  PDC20262: IDE controller on PCI bus 00 dev a0
- *  PDC20262: not 100% native mode: will probe irqs later
- *  PDC20262: ROM enabled at 0xfebb0000
- *  PDC20262: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
- *      ide0: BM-DMA at 0xef00-0xef07, BIOS settings: hda:pio, hdb:pio
- *      ide1: BM-DMA at 0xef08-0xef0f, BIOS settings: hdc:pio, hdd:pio
- *
- *  UDMA 4/2 and UDMA 3/1 only differ by the testing bit 13 in word93.
- *  Chipset timing speeds must be identical
- *
- *  drive_number
- *      = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- *      = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
  */
 
 /*
@@ -100,7 +49,7 @@
 #define PDC202XX_DEBUG_DRIVE_INFO		0
 #define PDC202XX_DECODE_REGISTER_INFO		0
 
-#undef DISPLAY_PDC202XX_TIMINGS
+#define DISPLAY_PDC202XX_TIMINGS
 
 #if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
 #include <linux/stat.h>
@@ -111,13 +60,56 @@
 extern char *ide_media_verbose(ide_drive_t *);
 static struct pci_dev *bmide_dev;
 
+char *pdc202xx_pio_verbose (u32 drive_pci)
+{
+	if ((drive_pci & 0x000ff000) == 0x000ff000) return("NOTSET");
+	if ((drive_pci & 0x00000401) == 0x00000401) return("PIO 4");
+	if ((drive_pci & 0x00000602) == 0x00000602) return("PIO 3");
+	if ((drive_pci & 0x00000803) == 0x00000803) return("PIO 2");
+	if ((drive_pci & 0x00000C05) == 0x00000C05) return("PIO 1");
+	if ((drive_pci & 0x00001309) == 0x00001309) return("PIO 0");
+	return("PIO ?");
+}
+
+char *pdc202xx_dma_verbose (u32 drive_pci)
+{
+	if ((drive_pci & 0x00036000) == 0x00036000) return("MWDMA 2");
+	if ((drive_pci & 0x00046000) == 0x00046000) return("MWDMA 1");
+	if ((drive_pci & 0x00056000) == 0x00056000) return("MWDMA 0");
+	if ((drive_pci & 0x00056000) == 0x00056000) return("SWDMA 2");
+	if ((drive_pci & 0x00068000) == 0x00068000) return("SWDMA 1");
+	if ((drive_pci & 0x000BC000) == 0x000BC000) return("SWDMA 0");
+	return("PIO---");
+}
+
+char *pdc202xx_ultra_verbose (u32 drive_pci, u16 slow_cable)
+{
+	if ((drive_pci & 0x000ff000) == 0x000ff000)
+		return("NOTSET");
+	if ((drive_pci & 0x00012000) == 0x00012000)
+		return((slow_cable) ? "UDMA 2" : "UDMA 4");
+	if ((drive_pci & 0x00024000) == 0x00024000)
+		return((slow_cable) ? "UDMA 1" : "UDMA 3");
+	if ((drive_pci & 0x00036000) == 0x00036000)
+		return("UDMA 0");
+	return(pdc202xx_dma_verbose(drive_pci));
+}
+
 static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count)
 {
 	char *p = buffer;
 
 	u32 bibma = bmide_dev->resource[4].start;
+	u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0;
+	u16 reg50h = 0, pmask = (1<<10), smask = (1<<11);
 	u8 c0 = 0, c1 = 0;
 
+	pci_read_config_word(bmide_dev, 0x50, &reg50h);
+	pci_read_config_dword(bmide_dev, 0x60, &reg60h);
+	pci_read_config_dword(bmide_dev, 0x64, &reg64h);
+	pci_read_config_dword(bmide_dev, 0x68, &reg68h);
+	pci_read_config_dword(bmide_dev, 0x6c, &reg6ch);
+
         /*
          * at that point bibma+0x2 et bibma+0xa are byte registers
          * to investigate:
@@ -131,6 +123,7 @@
 			break;
 		case PCI_DEVICE_ID_PROMISE_20246:
 			p += sprintf(p, "\n                                PDC20246 Chipset.\n");
+			reg50h |= 0x0c00;
 			break;
 		default:
 			p += sprintf(p, "\n                                PDC202XX Chipset.\n");
@@ -139,17 +132,18 @@
 
 	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
 	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
+		(c0&0x80)?"dis":" en",(c1&0x80)?"dis":" en");
 	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
 	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
+		(c0&0x20)?"yes":"no ",(c0&0x40)?"yes":"no ",(c1&0x20)?"yes":"no ",(c1&0x40)?"yes":"no ");
+	p += sprintf(p, "DMA Mode:       %s           %s          %s            %s\n",
+		pdc202xx_ultra_verbose(reg60h, (reg50h & pmask)),
+		pdc202xx_ultra_verbose(reg64h, (reg50h & pmask)),
+		pdc202xx_ultra_verbose(reg68h, (reg50h & smask)),
+		pdc202xx_ultra_verbose(reg6ch, (reg50h & smask)));
+	p += sprintf(p, " PIO Mode:      %s            %s           %s             %s\n",
+		pdc202xx_pio_verbose(reg60h),pdc202xx_pio_verbose(reg64h),
+		pdc202xx_pio_verbose(reg68h),pdc202xx_pio_verbose(reg6ch));
 	return p-buffer;	/* => must be less than 4k! */
 }
 #endif  /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */
@@ -268,6 +262,90 @@
 
 #endif /* PDC202XX_DECODE_REGISTER_INFO */
 
+/*   0    1    2    3    4    5    6   7   8
+ * 960, 480, 390, 300, 240, 180, 120, 90, 60
+ *           180, 150, 120,  90,  60
+ * DMA_Speed
+ * 180, 120,  90,  90,  90,  60,  30
+ *  11,   5,   4,   3,   2,   1,   0
+ */
+static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	byte			drive_pci, speed;
+	byte			AP, BP, TA, TB;
+
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	int err;
+
+	switch (drive_number) {
+		case 0: drive_pci = 0x60; break;
+		case 1: drive_pci = 0x64; break;
+		case 2: drive_pci = 0x68; break;
+		case 3: drive_pci = 0x6c; break;
+		default: return 1;
+	}
+
+	pci_read_config_byte(dev, (drive_pci), &AP);
+	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+
+	if ((AP & 0x0F) || (BP & 0x07)) {
+		/* clear PIO modes of lower 8421 bits of A Register */
+		pci_write_config_byte(dev, (drive_pci), AP & ~0x0F);
+		pci_read_config_byte(dev, (drive_pci), &AP);
+
+		/* clear PIO modes of lower 421 bits of B Register */
+		pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07);
+		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+		pci_read_config_byte(dev, (drive_pci), &AP);
+		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	}
+
+	pio = (pio == 5) ? 4 : pio;
+	switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) {
+		case 4:speed = XFER_PIO_4; TA=0x01; TB=0x04; break;
+		case 3:speed = XFER_PIO_3; TA=0x02; TB=0x06; break;
+		case 2:speed = XFER_PIO_2; TA=0x03; TB=0x08; break;
+		case 1:speed = XFER_PIO_1; TA=0x05; TB=0x0C; break;
+		case 0:
+		default:speed = XFER_PIO_0; TA=0x09; TB=0x13; break;
+	}
+	pci_write_config_byte(dev, (drive_pci), AP|TA);
+	pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+	pci_read_config_byte(dev, (drive_pci), &AP);
+	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+	pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+	decode_registers(REG_A, AP);
+	decode_registers(REG_B, BP);
+	decode_registers(REG_C, CP);
+	decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+	err = ide_config_drive_speed(drive, speed);
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d 0x%08x ",
+		drive->name, ide_xfer_verbose(speed),
+		drive_number, drive_conf);
+		pci_read_config_dword(dev, drive_pci, &drive_conf);
+	printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+	return err;
+}
+
+static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+	(void) config_chipset_for_pio(drive, pio);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
 {
 	struct hd_driveid *id	= drive->id;
@@ -476,91 +554,6 @@
 						     ide_dma_off_quietly);
 }
 
-/*   0    1    2    3    4    5    6   7   8
- * 960, 480, 390, 300, 240, 180, 120, 90, 60
- *           180, 150, 120,  90,  60
- * DMA_Speed
- * 180, 120,  90,  90,  90,  60,  30
- *  11,   5,   4,   3,   2,   1,   0
- */
-
-static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
-{
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	byte			drive_pci, speed;
-	byte			AP, BP, TA, TB;
-
-	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-	int err;
-
-	switch (drive_number) {
-		case 0: drive_pci = 0x60;	break;
-		case 1: drive_pci = 0x64;	break;
-		case 2: drive_pci = 0x68;	break;
-		case 3: drive_pci = 0x6c;	break;
-		default:			return 1;
-	}
-
-        pci_read_config_byte(dev, (drive_pci), &AP);
-        pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-
-
-        if ((AP & 0x0F) || (BP & 0x07)) {
-		/* clear PIO modes of lower 8421 bits of A Register */
-		pci_write_config_byte(dev, (drive_pci), AP & ~0x0F);
-		pci_read_config_byte(dev, (drive_pci), &AP);
-
-		/* clear PIO modes of lower 421 bits of B Register */
-		pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07);
-		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-
-		pci_read_config_byte(dev, (drive_pci), &AP);
-		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	}
-
-	pio = (pio == 5) ? 4 : pio;
-	switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) {
-		case 4:  speed = XFER_PIO_4; TA=0x01; TB=0x04; break;
-		case 3:  speed = XFER_PIO_3; TA=0x02; TB=0x06; break;
-		case 2:  speed = XFER_PIO_2; TA=0x03; TB=0x08; break;
-		case 1:  speed = XFER_PIO_1; TA=0x05; TB=0x0C; break;
-		case 0:
-		default: speed = XFER_PIO_0; TA=0x09; TB=0x13; break;
-	}
-	pci_write_config_byte(dev, (drive_pci), AP|TA);
-	pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
-
-#if PDC202XX_DECODE_REGISTER_INFO
-	pci_read_config_byte(dev, (drive_pci), &AP);
-	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
-	pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
-
-	decode_registers(REG_A, AP);
-	decode_registers(REG_B, BP);
-	decode_registers(REG_C, CP);
-	decode_registers(REG_D, DP);
-#endif /* PDC202XX_DECODE_REGISTER_INFO */
-
-	err = ide_config_drive_speed(drive, speed);
-
-#if PDC202XX_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d 0x%08x ",
-		drive->name, ide_xfer_verbose(speed),
-		drive_number, drive_conf);
-	pci_read_config_dword(dev, drive_pci, &drive_conf);
-	printk("0x%08x\n", drive_conf);
-#endif /* PDC202XX_DEBUG_DRIVE_INFO */
-
-	return err;
-}
-
-static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio)
-{
-	(void) config_chipset_for_pio(drive, pio);
-}
-
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -625,6 +618,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name)
 {
@@ -701,11 +695,12 @@
 #endif /* CONFIG_PDC202XX_MASTER */
 
 #if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
-        pdc202xx_proc = 1;
-        bmide_dev = dev;
-        pdc202xx_display_info = &pdc202xx_get_info;
+	if (!pdc202xx_proc) {
+		pdc202xx_proc = 1;
+		bmide_dev = dev;
+		pdc202xx_display_info = &pdc202xx_get_info;
+	}
 #endif /* DISPLAY_PDC202XX_TIMINGS && CONFIG_PROC_FS */
-
 	return dev->irq;
 }
 
@@ -722,10 +717,18 @@
 {
 	hwif->tuneproc = &pdc202xx_tune_drive;
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->dmaproc = &pdc202xx_dmaproc;
+		hwif->autodma = 1;
 	} else {
 		hwif->drives[0].autotune = 1;
 		hwif->drives[1].autotune = 1;
+		hwif->autodma = 0;
 	}
+#else /* !CONFIG_BLK_DEV_IDEDMA */
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+	hwif->autodma = 0;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/piix.c linux/drivers/ide/piix.c
--- v2.3.99-pre2/linux/drivers/ide/piix.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/ide/piix.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/piix.c		Version 0.30	Feb. 26, 2000
+ *  linux/drivers/ide/piix.c		Version 0.31	Mar. 18, 2000
  *
  *  Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
  *  Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
@@ -51,31 +51,6 @@
  * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, &reg4a);
  * pci_read_config_word(HWIF(drive)->pci_dev, 0x54, &reg54);
  *
- * 00:1f.1 IDE interface: Intel Corporation:
- *                          Unknown device 2411 (rev 01) (prog-if 80 [Master])
- *         Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop-
- *                          ParErr- Stepping- SERR- FastB2B-
- *         Status: Cap- 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
- *                          <TAbort- <MAbort- >SERR- <PERR-
- *         Latency: 0 set
- *         Region 4: I/O ports at ffa0
- * 00: 86 80 11 24 05 00 80 02 01 80 01 01 00 00 00 00
- * 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 20: a1 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 40: 07 a3 03 a3 00 00 00 00 05 00 02 02 00 00 00 00
- * 50: 00 00 00 00 11 04 00 00 00 00 00 00 00 00 00 00
- * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * f0: 00 00 00 00 00 00 00 00 3a 0f 00 00 00 00 00 00
- *
  */
 
 #include <linux/config.h>
@@ -281,6 +256,7 @@
 	restore_flags(flags);
 }
 
+#if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_PIIX_TUNING)
 static int piix_config_drive_for_dma (ide_drive_t *drive)
 {
 	struct hd_driveid *id	= drive->id;
@@ -387,13 +363,16 @@
 	/* Other cases are done by generic IDE-DMA code. */
 	return ide_dmaproc(func, drive);
 }
+#endif /* defined(CONFIG_BLK_DEV_IDEDMA) && (CONFIG_PIIX_TUNING) */
 
 unsigned int __init pci_init_piix (struct pci_dev *dev, const char *name)
 {
 #if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
-	piix_proc = 1;
-	bmide_dev = dev;
-	piix_display_info = &piix_get_info;
+	if (!piix_proc) {
+		piix_proc = 1;
+		bmide_dev = dev;
+		piix_display_info = &piix_get_info;
+	}
 #endif /* DISPLAY_PIIX_TIMINGS && CONFIG_PROC_FS */
 	return 0;
 }
@@ -427,12 +406,12 @@
 	if (!hwif->dma_base)
 		return;
 
+#ifndef CONFIG_BLK_DEV_IDEDMA
+	hwif->autodma = 0;
+#else /* CONFIG_BLK_DEV_IDEDMA */
 #ifdef CONFIG_PIIX_TUNING
 	hwif->autodma = 1;
 	hwif->dmaproc = &piix_dmaproc;
-#else
-	if (hwif->autodma)
-		hwif->autodma = 0;
-
 #endif /* CONFIG_PIIX_TUNING */
+#endif /* !CONFIG_BLK_DEV_IDEDMA */
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/q40ide.c linux/drivers/ide/q40ide.c
--- v2.3.99-pre2/linux/drivers/ide/q40ide.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/q40ide.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/q40ide.c -- Q40 I/O port IDE Driver
+ *  linux/drivers/ide/q40ide.c -- Q40 I/O port IDE Driver
  *
  *     original file created 12 Jul 1997 by Geert Uytterhoeven
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/qd6580.c linux/drivers/ide/qd6580.c
--- v2.3.99-pre2/linux/drivers/ide/qd6580.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/qd6580.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/qd6580.c		Version 0.02	Feb 09, 1996
+ *  linux/drivers/ide/qd6580.c		Version 0.02	Feb 09, 1996
  *
  *  Copyright (C) 1996  Linus Torvalds & author (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/rapide.c linux/drivers/ide/rapide.c
--- v2.3.99-pre2/linux/drivers/ide/rapide.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/rapide.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/rapide.c
+ * linux/drivers/ide/rapide.c
  *
  * Copyright (c) 1996-1998 Russell King.
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/rz1000.c linux/drivers/ide/rz1000.c
--- v2.3.99-pre2/linux/drivers/ide/rz1000.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/rz1000.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/rz1000.c		Version 0.05	December 8, 1997
+ *  linux/drivers/ide/rz1000.c		Version 0.05	December 8, 1997
  *
  *  Copyright (C) 1995-1998  Linus Torvalds & author (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/sis5513.c linux/drivers/ide/sis5513.c
--- v2.3.99-pre2/linux/drivers/ide/sis5513.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/sis5513.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/sis5513.c		Version 0.09	Feb. 10, 2000
+ * linux/drivers/ide/sis5513.c		Version 0.10	Mar. 18, 2000
  *
  * Copyright (C) 1999-2000	Andre Hedrick (andre@suse.com)
  * May be copied or modified under the terms of the GNU General Public License
@@ -109,14 +109,14 @@
 
 static int sis_get_info(char *, char **, off_t, int);
 extern int (*sis_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-struct pci_dev *bmide_dev;
+static struct pci_dev *bmide_dev;
 
-static char *cable_type[] = {
+static char *cable_type[] __initdata = {
 	"80 pins",
 	"40 pins"
 };
 
-static char *recovery_time [] ={
+static char *recovery_time [] __initdata ={
 	"12 PCICLK", "1 PCICLK",
 	"2 PCICLK", "3 PCICLK",
 	"4 PCICLK", "5 PCICLCK",
@@ -127,14 +127,14 @@
 	"15 PCICLK", "15 PCICLK"
 };
 
-static char *cycle_time [] = {
+static char * cycle_time [] __initdata = {
 	"Undefined", "2 CLCK",
 	"3 CLK", "4 CLK",
 	"5 CLK", "6 CLK",
 	"7 CLK", "8 CLK"
 };
 
-static char *active_time [] = {
+static char * active_time [] __initdata = {
 	"8 PCICLK", "1 PCICLCK",
 	"2 PCICLK", "2 PCICLK",
 	"4 PCICLK", "5 PCICLK",
@@ -222,6 +222,123 @@
 byte sis_proc = 0;
 extern char *ide_xfer_verbose (byte xfer_rate);
 
+static void config_drive_art_rwp (ide_drive_t *drive)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	byte reg4bh		= 0;
+	byte rw_prefetch	= (0x11 << drive_number);
+
+	pci_read_config_byte(dev, 0x4b, &reg4bh);
+	if (drive->media != ide_disk)
+		return;
+	
+	if ((reg4bh & rw_prefetch) != rw_prefetch)
+		pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch);
+}
+
+static void config_art_rwp_pio (ide_drive_t *drive, byte pio)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+
+	byte			timing, drive_pci, test1, test2;
+
+	unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90};
+	unsigned short xfer_pio = drive->id->eide_pio_modes;
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+
+#if 0
+	config_drive_art_rwp(drive);
+#endif
+
+	pio = ide_get_best_pio_mode(drive, 255, pio, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+/*
+ *               Mode 0       Mode 1     Mode 2     Mode 3     Mode 4
+ * Active time    8T (240ns)  6T (180ns) 4T (120ns) 3T  (90ns) 3T  (90ns)
+ * 0x41 2:0 bits  000          110        100        011        011
+ * Recovery time 12T (360ns)  7T (210ns) 4T (120ns) 3T  (90ns) 1T  (30ns)
+ * 0x40 3:0 bits 0000         0111       0100       0011       0001
+ * Cycle time    20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns)
+ */
+
+	switch(drive_number) {
+		case 0:		drive_pci = 0x40; break;
+		case 1:		drive_pci = 0x42; break;
+		case 2:		drive_pci = 0x44; break;
+		case 3:		drive_pci = 0x46; break;
+		default:	return;
+	}
+
+	pci_read_config_byte(dev, drive_pci, &test1);
+	pci_read_config_byte(dev, drive_pci|0x01, &test2);
+
+	/*
+	 * Do a blanket clear of active and recovery timings.
+	 */
+
+	test1 &= ~0x07;
+	test2 &= ~0x0F;
+
+	switch(timing) {
+		case 4:		test1 |= 0x01; test2 |= 0x03; break;
+		case 3:		test1 |= 0x03; test2 |= 0x03; break;
+		case 2:		test1 |= 0x04; test2 |= 0x04; break;
+		case 1:		test1 |= 0x07; test2 |= 0x06; break;
+		default:	break;
+	}
+
+	pci_write_config_byte(dev, drive_pci, test1);
+	pci_write_config_byte(dev, drive_pci|0x01, test2);
+}
+
+static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
+{
+	int err;
+	byte speed;
+
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4; break;
+		case 3:		speed = XFER_PIO_3; break;
+		case 2:		speed = XFER_PIO_2; break;
+		case 1:		speed = XFER_PIO_1; break;
+		default:	speed = XFER_PIO_0; break;
+	}
+
+	config_art_rwp_pio(drive, pio);
+	err = ide_config_drive_speed(drive, speed);
+	return err;
+}
+
+#undef SIS5513_TUNEPROC
+
+#ifdef SIS5513_TUNEPROC
+static void sis5513_tune_drive (ide_drive_t *drive, byte pio)
+{
+	(void) config_chipset_for_pio(drive, pio);
+}
+#endif /* SIS5513_TUNEPROC */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 /*
  * ((id->hw_config & 0x2000) && (HWIF(drive)->udma_four))
  */
@@ -331,84 +448,6 @@
 						     ide_dma_off_quietly);
 }
 
-static void config_drive_art_rwp (ide_drive_t *drive)
-{
-	ide_hwif_t *hwif		= HWIF(drive);
-	struct pci_dev *dev		= hwif->pci_dev;
-
-	byte				timing, pio, drive_pci, test1, test2;
-
-	unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90};
-	unsigned short xfer_pio		= drive->id->eide_pio_modes;
-	int drive_number		= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-
-	if (drive->media == ide_disk) {
-		struct pci_dev *dev	= hwif->pci_dev;
-		byte reg4bh		= 0;
-                byte rw_prefetch	= (0x11 << drive_number);
-
-		pci_read_config_byte(dev, 0x4b, &reg4bh);
-		if ((reg4bh & rw_prefetch) != rw_prefetch)
-			pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch);
-	}
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-/*
- *               Mode 0       Mode 1     Mode 2     Mode 3     Mode 4
- * Active time    8T (240ns)  6T (180ns) 4T (120ns) 3T  (90ns) 3T  (90ns)
- * 0x41 2:0 bits  000          110        100        011        011
- * Recovery time 12T (360ns)  7T (210ns) 4T (120ns) 3T  (90ns) 1T  (30ns)
- * 0x40 3:0 bits 0000         0111       0100       0011       0001
- * Cycle time    20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns)
- */
-
-	switch(drive_number) {
-		case 0:		drive_pci = 0x40;break;
-		case 1:		drive_pci = 0x42;break;
-		case 2:		drive_pci = 0x44;break;
-		case 3:		drive_pci = 0x46;break;
-		default:	return;
-	}
-
-	pci_read_config_byte(dev, drive_pci, &test1);
-	pci_read_config_byte(dev, drive_pci|0x01, &test2);
-
-	/*
-	 * Do a blanket clear of active and recovery timings.
-	 */
-
-	test1 &= ~0x07;
-	test2 &= ~0x0F;
-
-	switch(timing) {
-		case 4:		test1 |= 0x01;test2 |= 0x03;break;
-		case 3:		test1 |= 0x03;test2 |= 0x03;break;
-		case 2:		test1 |= 0x04;test2 |= 0x04;break;
-		case 1:		test1 |= 0x07;test2 |= 0x06;break;
-		default:	break;
-	}
-
-	pci_write_config_byte(dev, drive_pci, test1);
-	pci_write_config_byte(dev, drive_pci|0x01, test2);
-}
-
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id		= drive->id;
@@ -417,9 +456,10 @@
 	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
 		/* Consult the list of known "bad" drives */
 		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			return HWIF(drive)->dmaproc(ide_dma_off, drive);
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
 		}
-
+		dma_func = ide_dma_off_quietly;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x001F) {
 				/* Force if Capable UltraDMA */
@@ -434,13 +474,25 @@
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
 				dma_func = config_chipset_for_dma(drive, 0);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
 			}
 		} else if ((ide_dmaproc(ide_dma_good_drive, drive)) &&
 			   (id->eide_dma_time > 150)) {
 			/* Consult the list of known "good" drives */
 			dma_func = config_chipset_for_dma(drive, 0);
-		}
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		(void) config_chipset_for_pio(drive, 5);
 	}
+
 	return HWIF(drive)->dmaproc(dma_func, drive);
 }
 
@@ -452,12 +504,14 @@
 	switch (func) {
 		case ide_dma_check:
 			config_drive_art_rwp(drive);
+			config_art_rwp_pio(drive, 5);
 			return config_drive_xfer_rate(drive);
 		default:
 			break;
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name)
 {
@@ -492,9 +546,11 @@
 			pci_write_config_byte(dev, 0x52, reg52h|0x04);
 		}
 #if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
-		sis_proc = 1;
-		bmide_dev = dev;
-		sis_display_info = &sis_get_info;
+		if (!sis_proc) {
+			sis_proc = 1;
+			bmide_dev = dev;
+			sis_display_info = &sis_get_info;
+		}
 #endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
 	}
 	return 0;
@@ -525,11 +581,16 @@
 
 	hwif->irq = hwif->channel ? 15 : 14;
 
+#ifdef SIS5513_TUNEPROC
+	hwif->tuneproc = &sis5513_tune_drive;
+#endif /* SIS5513_TUNEPROC */
+
 	if (!(hwif->dma_base))
 		return;
 
 	if (host_dev) {
 		switch(host_dev->device) {
+#ifdef CONFIG_BLK_DEV_IDEDMA
 			case PCI_DEVICE_ID_SI_530:
 			case PCI_DEVICE_ID_SI_540:
 			case PCI_DEVICE_ID_SI_620:
@@ -540,6 +601,7 @@
 				hwif->autodma = 1;
 				hwif->dmaproc = &sis5513_dmaproc;
 				break;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 			default:
 				hwif->autodma = 0;
 				break;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/sl82c105.c linux/drivers/ide/sl82c105.c
--- v2.3.99-pre2/linux/drivers/ide/sl82c105.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/sl82c105.c	Wed Mar 22 17:18:44 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/sl82c105.c
+ * linux/drivers/ide/sl82c105.c
  *
  * SL82C105/Winbond 553 IDE driver
  *
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/trm290.c linux/drivers/ide/trm290.c
--- v2.3.99-pre2/linux/drivers/ide/trm290.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/trm290.c	Wed Mar 22 22:22:05 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/trm290.c		Version 1.01	December 5, 1997
+ *  linux/drivers/ide/trm290.c		Version 1.02	Mar. 18, 2000
  *
  *  Copyright (c) 1997-1998  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -124,6 +124,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
@@ -171,6 +172,7 @@
 	trm290_prepare_drive(drive, drive->using_dma);
 }
 
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
 {
 	ide_hwif_t *hwif = HWIF(drive);
@@ -209,6 +211,7 @@
 	trm290_prepare_drive(drive, 0);	/* select PIO xfer */
 	return 1;
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 
 /*
  * Invoked from ide-dma.c at boot time.
@@ -247,7 +250,11 @@
 	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
 		hwif->irq = hwif->mate->irq;		/* sharing IRQ with mate */
 	ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	hwif->dmaproc = &trm290_dmaproc;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
 	hwif->selectproc = &trm290_selectproc;
 	hwif->autodma = 0;				/* play it safe for now */
 #if 1
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/umc8672.c linux/drivers/ide/umc8672.c
--- v2.3.99-pre2/linux/drivers/ide/umc8672.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/ide/umc8672.c	Wed Mar 22 17:18:45 2000
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/umc8672.c		Version 0.05	Jul 31, 1996
+ *  linux/drivers/ide/umc8672.c		Version 0.05	Jul 31, 1996
  *
  *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ide/via82cxxx.c linux/drivers/ide/via82cxxx.c
--- v2.3.99-pre2/linux/drivers/ide/via82cxxx.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/ide/via82cxxx.c	Wed Mar 22 17:18:45 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/via82cxxx.c	Version 0.07	Feb. 10, 2000
+ * linux/drivers/ide/via82cxxx.c	Version 0.08	Mar. 18, 2000
  *
  *  Copyright (C) 1998-99	Michel Aubry, Maintainer
  *  Copyright (C) 1999		Jeff Garzik, MVP4 Support
@@ -143,14 +143,14 @@
 #include <linux/stat.h>
 #include <linux/proc_fs.h>
 
-static char *FIFO_str[] = {
+static char *FIFO_str[] __initdata = {
 	" 1 ",
 	"3/4",
 	"1/2",
 	"1/4"
 };
 
-static char *control3_str[] = {
+static char *control3_str[] __initdata = {
 	"No limitation",
 	"64",
 	"128",
@@ -516,6 +516,62 @@
 
 #ifdef CONFIG_VIA82CXXX_TUNING
 
+struct chipset_bus_clock_list_entry {
+	unsigned short	bus_speed;
+	byte		xfer_speed;
+	byte		chipset_settings;
+};
+
+PCI_DEVICE_ID_VIA_82C586_1
+PCI_DEVICE_ID_VIA_82C596
+PCI_DEVICE_ID_VIA_82C686
+PCI_DEVICE_ID_VIA_8231
+
+PCI_DEVICE_ID_VIA_82C586_1	TYPE_1
+PCI_DEVICE_ID_VIA_82C596	TYPE_2
+PCI_DEVICE_ID_VIA_82C686	TYPE_2
+PCI_DEVICE_ID_VIA_82C596	TYPE_3
+PCI_DEVICE_ID_VIA_82C686	TYPE_3
+PCI_DEVICE_ID_VIA_8231		TYPE_4
+
+struct chipset_bus_clock_list_entry ultra_33_base [] = {
+{ TYPE_1,25,0x00,0x00,0x60,0x61,0x62,0x03,0x20,0x31,0x65,0x65,0xA8 },
+{ TYPE_1,33,0x00,0x00,0x60,0x61,0x62,0x03,0x20,0x31,0x65,0x65,0xA8 },
+{ TYPE_1,37,0x00,0x00,0x60,0x61,0x62,0x03,0x21,0x32,0x76,0x76,0xA9 },
+{ TYPE_2,25,0x00,0x00,0xE0,0xE1,0xE2,0x03,0x20,0x31,0x65,0x65,0xA8 },
+{ TYPE_2,33,0x00,0x00,0xE0,0xE1,0xE2,0x03,0x20,0x31,0x65,0x65,0xA8 },
+{ TYPE_2,37,0x00,0x00,0xE1,0xE2,0xE2,0x03,0x31,0x42,0x87,0x87,0xDB },
+{ TYPE_2,41,0x00,0x00,0xE1,0xE2,0xE2,0x03,0x32,0x53,0xA8,0xA8,0xFE },
+{ TYPE_3,25,0x00,0x00,0xE0,0xE1,0xE2,0x03,0x20,0x31,0x65,0x65,0xA8 },
+{ TYPE_3,33,0x00,0x00,0xE0,0xE1,0xE2,0x03,0x20,0x31,0x65,0x65,0xA8 },
+{ TYPE_3,37,0x00,0x00,0xE1,0xE2,0xE2,0x03,0x31,0x42,0x87,0x87,0xDB },
+{ TYPE_3,41,0x00,0x00,0xE1,0xE2,0xE2,0x03,0x32,0x53,0xA8,0xA8,0xFE },
+{ TYPE_4,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 }
+};
+
+struct chipset_bus_clock_list_entry timing_66_base [] = {
+	{ 37,	XFER_PIO_4,	0x21 },
+	{ 37,	XFER_PIO_3,	0x32 },
+	{ 37,	XFER_PIO_2,	0x76 },
+	{ 37,	XFER_PIO_1,	0x76 },
+	{ 37,	XFER_PIO_0,	0xA9 },
+	{ ANY,	XFER_PIO_4,	0x20 },
+	{ ANY,	XFER_PIO_3,	0x31 },
+	{ ANY,	XFER_PIO_2,	0x65 },
+	{ ANY,	XFER_PIO_1,	0x65 },
+	{ ANY,	XFER_PIO_0,	0xA8 },
+};
+
+static byte pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+	for ( ; chipset_table->xfer_speed ; chipset_table++)
+		if (chipset_table->xfer_speed == speed) {
+			return chipset_table->chipset_settings;
+		}
+	return 0x01208585;
+}
+
 static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed)
 {
 	struct hd_driveid *id	= drive->id;
@@ -539,7 +595,7 @@
 		case 2: ata2_pci = 0x4a; ata3_pci = 0x52; break;
 		case 3: ata2_pci = 0x4b; ata3_pci = 0x53; break;
 		default:
-			return err;
+			return -1;
 	}
 
 	pci_read_config_byte(dev, ata2_pci, &timing);
@@ -575,6 +631,57 @@
 	return(err);
 }
 
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio		= drive->id->eide_pio_modes;
+	byte				timing, speed, pio;
+
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 :
+			   (drive->id->tPIO & 2) ? 0x02 :
+			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+	switch(timing) {
+		case 4:	speed = XFER_PIO_4; break;
+		case 3:	speed = XFER_PIO_3; break;
+		case 2:	speed = XFER_PIO_2; break;
+		case 1:	speed = XFER_PIO_1; break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+			break;
+	}
+	(void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static void via82cxxx_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4; break;
+		case 3:		speed = XFER_PIO_3; break;
+		case 2:		speed = XFER_PIO_2; break;
+		case 1:		speed = XFER_PIO_1; break;
+		default:	speed = XFER_PIO_0; break;
+	}
+	(void) via82cxxx_tune_chipset(drive, speed);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
 static int config_chipset_for_dma (ide_drive_t *drive)
 {
 	struct hd_driveid *id	= drive->id;
@@ -617,56 +724,6 @@
 	return rval;
 }
 
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio = drive->id->eide_pio_modes;
-	byte			timing, speed, pio;
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 :
-			   (drive->id->tPIO & 2) ? 0x02 :
-			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;                        break;
-	}
-	(void) via82cxxx_tune_chipset(drive, speed);
-}
-
-static void via82cxxx_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	(void) via82cxxx_tune_chipset(drive, speed);
-}
-
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
@@ -726,6 +783,7 @@
 	}
 	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 #endif /* CONFIG_VIA82CXXX_TUNING */
 
 unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name)
@@ -745,7 +803,7 @@
 
 		host_dev = host;
 		printk(ApolloHostChipInfo[i].name);
-
+		printk("\n");
 		for (j = 0; j < arraysize (ApolloISAChipInfo) && !isa_dev; j++) {
 			if (ApolloISAChipInfo[j].host_id !=
 			    ApolloHostChipInfo[i].host_id)
@@ -777,9 +835,11 @@
 	}
 
 #if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
-	via_proc = 1;
-	bmide_dev = dev;
-	via_display_info = &via_get_info;
+	if (!via_proc) {
+		via_proc = 1;
+		bmide_dev = dev;
+		via_display_info = &via_get_info;
+	}
 #endif /* DISPLAY_VIA_TIMINGS &&  CONFIG_PROC_FS*/
 
 	return 0;
@@ -797,13 +857,15 @@
 
 #ifdef CONFIG_VIA82CXXX_TUNING
 	hwif->tuneproc = &via82cxxx_tune_drive;
+	hwif->drives[0].autotune = 1;
+	hwif->drives[1].autotune = 1;
+	hwif->autodma = 0;
+#ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->dmaproc = &via82cxxx_dmaproc;
-	} else {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
+		hwif->autodma = 1;
 	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
 #endif /* CONFIG_VIA82CXXX_TUNING */
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/Config.in linux/drivers/ieee1394/Config.in
--- v2.3.99-pre2/linux/drivers/ieee1394/Config.in	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/Config.in	Wed Mar 22 00:02:48 2000
@@ -14,11 +14,12 @@
 	fi
 
 	dep_tristate 'Adaptec AIC-5800 (AHA-89xx) support' CONFIG_IEEE1394_AIC5800 $CONFIG_IEEE1394
-	
+
 	dep_tristate 'OHCI (Open Host Controller Interface) support' CONFIG_IEEE1394_OHCI1394 $CONFIG_IEEE1394
 
 	dep_tristate 'Raw IEEE1394 I/O support' CONFIG_IEEE1394_RAWIO $CONFIG_IEEE1394
 
+	bool 'Excessive debugging output' CONFIG_IEEE1394_VERBOSEDEBUG
     fi
     endmenu
 fi
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/csr.c linux/drivers/ieee1394/csr.c
--- v2.3.99-pre2/linux/drivers/ieee1394/csr.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/csr.c	Wed Mar 22 00:02:48 2000
@@ -14,6 +14,7 @@
 #include "highlevel.h"
 
 
+/* FIXME: this one won't work on little endian with big endian data */
 static u16 csr_crc16(unsigned *data, int length)
 {
         int check=0, i;
@@ -47,14 +48,18 @@
                 host->csr.state &= ~0x100;
         }
 
-        host->csr.topology_map[1]++;
-        host->csr.topology_map[2] = host->node_count << 16 | host->selfid_count;
-        host->csr.topology_map[0] = (host->selfid_count + 2) << 16
-                | csr_crc16(host->csr.topology_map + 1, host->selfid_count + 2);
-
-        /* FIXME - generate speed map */
-        host->csr.speed_map[0] = 0x3f1 << 16 | csr_crc16(host->csr.speed_map+1,
-                                                         0x3f1);
+        host->csr.topology_map[1] = 
+                cpu_to_be32(be32_to_cpu(host->csr.topology_map[1]) + 1);
+        host->csr.topology_map[2] = cpu_to_be32(host->node_count << 16 
+                                                | host->selfid_count);
+        host->csr.topology_map[0] = 
+                cpu_to_be32((host->selfid_count + 2) << 16
+                            | csr_crc16(host->csr.topology_map + 1,
+                                        host->selfid_count + 2));
+
+        host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16 
+                                             | csr_crc16(host->csr.speed_map+1,
+                                                         0x3f1));
 }
 
 
@@ -78,8 +83,8 @@
 
 
 /* Read topology / speed maps and configuration ROM */
-static int read_maps(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
-                     unsigned int length)
+static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+                     u64 addr, unsigned int length)
 {
         int csraddr = addr - CSR_REGISTER_BASE;
         const char *src;
@@ -100,43 +105,11 @@
         return RCODE_COMPLETE;
 }
 
-/* Read FCP register space */
-static int read_fcp(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
-                    unsigned int length)
-{
-        int csraddr = addr - CSR_REGISTER_BASE;
-        const char *src;
-
-        if (csraddr + length > CSR_FCP_END) {
-                return RCODE_ADDRESS_ERROR;
-        }
-        src = ((char *)host->csr.fcp_data) + csraddr - CSR_FCP_COMMAND;
-        
-        memcpy(buffer, src, length);
-        return RCODE_COMPLETE;
-}
-
-/* Write FCP register space */
-static int write_fcp(struct hpsb_host *host, quadlet_t *data, u64 addr,
-                     unsigned int length)
-{
-        int csraddr = addr - CSR_REGISTER_BASE;
-        char *dest;
-
-        if (csraddr + length > CSR_FCP_END) {
-                return RCODE_ADDRESS_ERROR;
-        }
-        dest = ((char *)host->csr.fcp_data) + csraddr - CSR_FCP_COMMAND;
-        
-        memcpy(dest, data, length);
-        return RCODE_COMPLETE;
-}
-
 
 #define out if (--length == 0) break
 
-static int read_regs(struct hpsb_host *host, quadlet_t *buf, u64 addr,
-                     unsigned int length)
+static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
+                     u64 addr, unsigned int length)
 {
         int csraddr = addr - CSR_REGISTER_BASE;
         int oldcycle;
@@ -149,13 +122,13 @@
 
         switch (csraddr) {
         case CSR_STATE_CLEAR:
-                *(buf++) = host->csr.state;
+                *(buf++) = cpu_to_be32(host->csr.state);
                 out;
         case CSR_STATE_SET:
-                *(buf++) = host->csr.state;
+                *(buf++) = cpu_to_be32(host->csr.state);
                 out;
         case CSR_NODE_IDS:
-                *(buf++) = host->csr.node_ids;
+                *(buf++) = cpu_to_be32(host->csr.node_ids);
                 out;
 
         case CSR_RESET_START:
@@ -164,10 +137,10 @@
                 /* address gap - handled by default below */
 
         case CSR_SPLIT_TIMEOUT_HI:
-                *(buf++) = host->csr.split_timeout_hi;
+                *(buf++) = cpu_to_be32(host->csr.split_timeout_hi);
                 out;
         case CSR_SPLIT_TIMEOUT_LO:
-                *(buf++) = host->csr.split_timeout_lo;
+                *(buf++) = cpu_to_be32(host->csr.split_timeout_lo);
                 out;
 
                 /* address gap */
@@ -182,7 +155,7 @@
                         /* cycle time wrapped around */
                         host->csr.bus_time += 1 << 7;
                 }
-                *(buf++) = host->csr.cycle_time;
+                *(buf++) = cpu_to_be32(host->csr.cycle_time);
                 out;
         case CSR_BUS_TIME:
                 oldcycle = host->csr.cycle_time;
@@ -193,7 +166,8 @@
                         /* cycle time wrapped around */
                         host->csr.bus_time += (1 << 7);
                 }
-                *(buf++) = host->csr.bus_time | (host->csr.cycle_time >> 25);
+                *(buf++) = cpu_to_be32(host->csr.bus_time 
+                                       | (host->csr.cycle_time >> 25));
                 out;
 
                 /* address gap */
@@ -204,16 +178,16 @@
                 return RCODE_ADDRESS_ERROR;
 
         case CSR_BUS_MANAGER_ID:
-                *(buf++) = host->csr.bus_manager_id;
+                *(buf++) = cpu_to_be32(host->csr.bus_manager_id);
                 out;
         case CSR_BANDWIDTH_AVAILABLE:
-                *(buf++) = host->csr.bandwidth_available;
+                *(buf++) = cpu_to_be32(host->csr.bandwidth_available);
                 out;
         case CSR_CHANNELS_AVAILABLE_HI:
-                *(buf++) = host->csr.channels_available_hi;
+                *(buf++) = cpu_to_be32(host->csr.channels_available_hi);
                 out;
         case CSR_CHANNELS_AVAILABLE_LO:
-                *(buf++) = host->csr.channels_available_lo;
+                *(buf++) = cpu_to_be32(host->csr.channels_available_lo);
                 out;
 
                 /* address gap to end - fall through to default */
@@ -224,8 +198,8 @@
         return RCODE_COMPLETE;
 }
 
-static int write_regs(struct hpsb_host *host, quadlet_t *data, u64 addr,
-                      unsigned int length)
+static int write_regs(struct hpsb_host *host, int nodeid, quadlet_t *data,
+                      u64 addr, unsigned int length)
 {
         int csraddr = addr - CSR_REGISTER_BASE;
         
@@ -246,7 +220,7 @@
 
         case CSR_NODE_IDS:
                 host->csr.node_ids &= NODE_MASK << 16;
-                host->csr.node_ids |= *(data++) & (BUS_MASK << 16);
+                host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16);
                 host->node_id = host->csr.node_ids >> 16;
                 host->template->devctl(host, SET_BUS_ID, host->node_id >> 6);
                 out;
@@ -259,10 +233,12 @@
                 return RCODE_ADDRESS_ERROR;
 
         case CSR_SPLIT_TIMEOUT_HI:
-                host->csr.split_timeout_hi = *(data++) & 0x00000007;
+                host->csr.split_timeout_hi = 
+                        be32_to_cpu(*(data++)) & 0x00000007;
                 out;
         case CSR_SPLIT_TIMEOUT_LO:
-                host->csr.split_timeout_lo = *(data++) & 0xfff80000;
+                host->csr.split_timeout_lo = 
+                        be32_to_cpu(*(data++)) & 0xfff80000;
                 out;
 
                 /* address gap */
@@ -270,11 +246,12 @@
 
         case CSR_CYCLE_TIME:
                 /* should only be set by cycle start packet, automatically */
-                host->csr.cycle_time = *data;
-                host->template->devctl(host, SET_CYCLE_COUNTER, *(data++));
+                host->csr.cycle_time = be32_to_cpu(*data);
+                host->template->devctl(host, SET_CYCLE_COUNTER,
+                                       be32_to_cpu(*(data++)));
                 out;
         case CSR_BUS_TIME:
-                host->csr.bus_time = *(data++) & 0xffffff80;
+                host->csr.bus_time = be32_to_cpu(*(data++)) & 0xffffff80;
                 out;
 
                 /* address gap */
@@ -305,13 +282,13 @@
 /* helper function for lock_regs */
 inline static void compare_swap(quadlet_t *old, quadlet_t data, quadlet_t arg)
 {
-        if (*old == arg) {
-                *old = data;
+        if (*old == be32_to_cpu(arg)) {
+                *old = be32_to_cpu(data);
         }
 }
 
-static int lock_regs(struct hpsb_host *host, quadlet_t *store, u64 addr, 
-                     quadlet_t data, quadlet_t arg, int extcode)
+static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
+                     u64 addr, quadlet_t data, quadlet_t arg, int extcode)
 {
         int csraddr = addr - CSR_REGISTER_BASE;
         unsigned long flags;
@@ -327,25 +304,28 @@
 
                         switch (csraddr) {
                         case CSR_BUS_MANAGER_ID:
-                                *store = host->csr.bus_manager_id;
+                                *store = cpu_to_be32(host->csr.bus_manager_id);
                                 compare_swap(&host->csr.bus_manager_id,
                                              data, arg);
                                 break;
 
                         case CSR_BANDWIDTH_AVAILABLE:
-                                *store = host->csr.bandwidth_available;
+                                *store = cpu_to_be32(host->
+                                                     csr.bandwidth_available);
                                 compare_swap(&host->csr.bandwidth_available,
                                              data, arg);
                                 break;
 
                         case CSR_CHANNELS_AVAILABLE_HI:
-                                *store = host->csr.channels_available_hi;
+                                *store = cpu_to_be32(host->
+                                                     csr.channels_available_hi);
                                 compare_swap(&host->csr.channels_available_hi,
                                              data, arg);
                                 break;
 
                         case CSR_CHANNELS_AVAILABLE_LO:
-                                *store = host->csr.channels_available_lo;
+                                *store = cpu_to_be32(host->
+                                                     csr.channels_available_lo);
                                 compare_swap(&host->csr.channels_available_lo,
                                              data, arg);
                                 break;
@@ -377,34 +357,48 @@
         }
 }
 
+static int write_fcp(struct hpsb_host *host, int nodeid, quadlet_t *data,
+                     u64 addr, unsigned int length)
+{
+        int csraddr = addr - CSR_REGISTER_BASE;
+
+        if (length > 512) {
+                return RCODE_TYPE_ERROR;
+        }
+
+        switch (csraddr) {
+        case CSR_FCP_COMMAND:
+                highlevel_fcp_request(host, nodeid, 0, (u8 *)data, length);
+                break;
+        case CSR_FCP_RESPONSE:
+                highlevel_fcp_request(host, nodeid, 1, (u8 *)data, length);
+                break;
+        default:
+                return RCODE_TYPE_ERROR;
+        }
+
+        return RCODE_COMPLETE;
+}
+
 
 struct hpsb_highlevel_ops csr_ops = {
-        add_host,
-        NULL,
-        host_reset,
-        NULL
+        add_host: add_host,
+        host_reset: host_reset,
 };
 
 
 struct hpsb_address_ops map_ops = {
-        read_maps,
-        NULL,
-        NULL,
-        NULL
+        read: read_maps,
 };
 
 struct hpsb_address_ops fcp_ops = {
-        read_fcp,
-        write_fcp,
-        NULL,
-        NULL
+        write: write_fcp,
 };
 
 struct hpsb_address_ops reg_ops = {
-        read_regs,
-        write_regs,
-        lock_regs,
-        NULL
+        read: read_regs,
+        write: write_regs,
+        lock: lock_regs,
 };
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/csr.h linux/drivers/ieee1394/csr.h
--- v2.3.99-pre2/linux/drivers/ieee1394/csr.h	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/csr.h	Wed Mar 22 00:02:48 2000
@@ -46,7 +46,6 @@
 
         quadlet_t topology_map[256];
         quadlet_t speed_map[1024];
-        quadlet_t fcp_data[1024];
 };
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/highlevel.c linux/drivers/ieee1394/highlevel.c
--- v2.3.99-pre2/linux/drivers/ieee1394/highlevel.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/highlevel.c	Wed Mar 22 00:02:48 2000
@@ -192,23 +192,35 @@
         read_unlock(&hl_drivers_lock);
 }
 
+void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
+                           u8 *data, unsigned int length)
+{
+        struct list_head *entry;
+        struct hpsb_highlevel *hl;
+        int cts = data[0];
+
+        read_lock(&hl_drivers_lock);
+        entry = hl_drivers.next;
+
+        while (entry != &hl_drivers) {
+                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+                if (hl->op->fcp_request) {
+                        hl->op->fcp_request(host, nodeid, direction, cts, data,
+                                            length);
+                }
+                entry = entry->next;
+        }
+        read_unlock(&hl_drivers_lock);
+}
 
-int highlevel_read(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
-                   unsigned int length)
+int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+                   u64 addr, unsigned int length)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
         unsigned int partlength;
         int rcode = RCODE_ADDRESS_ERROR;
 
-        if ((addr | length) & 0x3) {
-                /* Addresses or lengths not a multiple of a quadlet pose a big
-                 * problem on little endian machines because we always do this
-                 * in arch endian and swapping would mess it all up.  So we 
-                 * simply don't allow this at all. */
-                return RCODE_TYPE_ERROR;
-        }
-
         read_lock(&addr_space_lock);
 
         entry = addr_space.next;
@@ -220,7 +232,7 @@
                                          length);
 
                         if (as->op->read != NULL) {
-                                rcode = as->op->read(host, buffer, addr,
+                                rcode = as->op->read(host, nodeid, buffer, addr,
                                                      partlength);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
@@ -247,18 +259,14 @@
         return rcode;
 }
 
-int highlevel_write(struct hpsb_host *host, quadlet_t *data, u64 addr,
-                    unsigned int length)
+int highlevel_write(struct hpsb_host *host, int nodeid, quadlet_t *data,
+                    u64 addr, unsigned int length)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
         unsigned int partlength;
         int rcode = RCODE_ADDRESS_ERROR;
 
-        if ((addr | length) & 0x3) {
-                return RCODE_TYPE_ERROR;
-        }
-
         read_lock(&addr_space_lock);
 
         entry = addr_space.next;
@@ -270,7 +278,7 @@
                                          length);
 
                         if (as->op->write != NULL) {
-                                rcode = as->op->write(host, data, addr,
+                                rcode = as->op->write(host, nodeid, data, addr,
                                                       partlength);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
@@ -298,8 +306,8 @@
 }
 
 
-int highlevel_lock(struct hpsb_host *host, quadlet_t *store, u64 addr,
-                   quadlet_t data, quadlet_t arg, int ext_tcode)
+int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
+                   u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -313,8 +321,8 @@
         while (as->start <= addr) {
                 if (as->end > addr) {
                         if (as->op->lock != NULL) {
-                                rcode = as->op->lock(host, store, addr, data, 
-                                                     arg, ext_tcode);
+                                rcode = as->op->lock(host, nodeid, store, addr,
+                                                     data, arg, ext_tcode);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
@@ -331,8 +339,8 @@
         return rcode;
 }
 
-int highlevel_lock64(struct hpsb_host *host, octlet_t *store, u64 addr,
-                     octlet_t data, octlet_t arg, int ext_tcode)
+int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
+                     u64 addr, octlet_t data, octlet_t arg, int ext_tcode)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -346,8 +354,9 @@
         while (as->start <= addr) {
                 if (as->end > addr) {
                         if (as->op->lock64 != NULL) {
-                                rcode = as->op->lock64(host, store, addr, data, 
-                                                       arg, ext_tcode);
+                                rcode = as->op->lock64(host, nodeid, store,
+                                                       addr, data, arg,
+                                                       ext_tcode);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/highlevel.h linux/drivers/ieee1394/highlevel.h
--- v2.3.99-pre2/linux/drivers/ieee1394/highlevel.h	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/highlevel.h	Wed Mar 22 00:02:48 2000
@@ -55,6 +55,13 @@
          * for channel/host combinations you did not request. */
         void (*iso_receive) (struct hpsb_host *host, int channel,
                              quadlet_t *data, unsigned int length);
+
+        /* A write request was received on either the FCP_COMMAND (direction =
+         * 0) or the FCP_RESPONSE (direction = 1) register.  The cts arg
+         * contains the cts field (first byte of data).
+         */
+        void (*fcp_request) (struct hpsb_host *host, int nodeid, int direction,
+                             int cts, u8 *data, unsigned int length);
 };
 
 struct hpsb_address_ops {
@@ -67,17 +74,17 @@
          */
 
         /* These functions have to implement block reads for themselves. */
-        int (*read) (struct hpsb_host *host, quadlet_t *buffer, u64 addr,
-                     unsigned int length);
-        int (*write) (struct hpsb_host *host, quadlet_t *data, u64 addr,
-                      unsigned int length);
+        int (*read) (struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+                     u64 addr, unsigned int length);
+        int (*write) (struct hpsb_host *host, int nodeid, quadlet_t *data,
+                      u64 addr, unsigned int length);
 
         /* Lock transactions: write results of ext_tcode operation into
          * *store. */
-        int (*lock) (struct hpsb_host *host, quadlet_t *store, u64 addr,
-                     quadlet_t data, quadlet_t arg, int ext_tcode);
-        int (*lock64) (struct hpsb_host *host, octlet_t *store, u64 addr,
-                       octlet_t data, octlet_t arg, int ext_tcode);
+        int (*lock) (struct hpsb_host *host, int nodeid, quadlet_t *store,
+                     u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode);
+        int (*lock64) (struct hpsb_host *host, int nodeid, octlet_t *store,
+                       u64 addr, octlet_t data, octlet_t arg, int ext_tcode);
 };
 
 
@@ -87,17 +94,19 @@
 void highlevel_remove_host(struct hpsb_host *host);
 void highlevel_host_reset(struct hpsb_host *host);
 
-int highlevel_read(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
-                   unsigned int length);
-int highlevel_write(struct hpsb_host *host, quadlet_t *data, u64 addr,
-                    unsigned int length);
-int highlevel_lock(struct hpsb_host *host, quadlet_t *store, u64 addr,
-                   quadlet_t data, quadlet_t arg, int ext_tcode);
-int highlevel_lock64(struct hpsb_host *host, octlet_t *store, u64 addr,
-                     octlet_t data, octlet_t arg, int ext_tcode);
+int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+                   u64 addr, unsigned int length);
+int highlevel_write(struct hpsb_host *host, int nodeid, quadlet_t *data,
+                    u64 addr, unsigned int length);
+int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
+                   u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode);
+int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
+                     u64 addr, octlet_t data, octlet_t arg, int ext_tcode);
 
 void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
                            unsigned int length);
+void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
+                           u8 *data, unsigned int length);
 
 
 /*
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ieee1394.h linux/drivers/ieee1394/ieee1394.h
--- v2.3.99-pre2/linux/drivers/ieee1394/ieee1394.h	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/ieee1394.h	Wed Mar 22 00:02:48 2000
@@ -63,4 +63,103 @@
 #define SELFID_PORT_NCONN        0x1
 #define SELFID_PORT_NONE         0x0   
 
+
+#include <asm/byteorder.h>
+
+#ifdef __BIG_ENDIAN_BITFIELD
+
+struct selfid {
+        u32 packet_identifier:2; /* always binary 10 */
+        u32 phy_id:6;
+        /* byte */
+        u32 extended:1; /* if true is struct ext_selfid */
+        u32 link_active:1;
+        u32 gap_count:6;
+        /* byte */
+        u32 speed:2;
+        u32 phy_delay:2;
+        u32 contender:1;
+        u32 power_class:3;
+        /* byte */
+        u32 port0:2;
+        u32 port1:2;
+        u32 port2:2;
+        u32 initiated_reset:1;
+        u32 more_packets:1;
+} __attribute__((packed));
+
+struct ext_selfid {
+        u32 packet_identifier:2; /* always binary 10 */
+        u32 phy_id:6;
+        /* byte */
+        u32 extended:1; /* if false is struct selfid */
+        u32 seq_nr:3;
+        u32 reserved:2;
+        u32 porta:2;
+        /* byte */
+        u32 portb:2;
+        u32 portc:2;
+        u32 portd:2;
+        u32 porte:2;
+        /* byte */
+        u32 portf:2;
+        u32 portg:2;
+        u32 porth:2;
+        u32 reserved2:1;
+        u32 more_packets:1;
+} __attribute__((packed));
+
+#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */
+
+/*
+ * Note: these mean to be bit fields of a big endian SelfID as seen on a little
+ * endian machine.
+ */
+
+struct selfid {
+        u32 phy_id:6;
+        u32 packet_identifier:2; /* always binary 10 */
+        /* byte */
+        u32 gap_count:6;
+        u32 link_active:1;
+        u32 extended:1; /* if true is struct ext_selfid */
+        /* byte */
+        u32 power_class:3;
+        u32 contender:1;
+        u32 phy_delay:2;
+        u32 speed:2;
+        /* byte */
+        u32 more_packets:1;
+        u32 initiated_reset:1;
+        u32 port2:2;
+        u32 port1:2;
+        u32 port0:2;
+} __attribute__((packed));
+
+struct ext_selfid {
+        u32 phy_id:6;
+        u32 packet_identifier:2; /* always binary 10 */
+        /* byte */
+        u32 porta:2;
+        u32 reserved:2;
+        u32 seq_nr:3;
+        u32 extended:1; /* if false is struct selfid */
+        /* byte */
+        u32 porte:2;
+        u32 portd:2;
+        u32 portc:2;
+        u32 portb:2;
+        /* byte */
+        u32 more_packets:1;
+        u32 reserved2:1;
+        u32 porth:2;
+        u32 portg:2;
+        u32 portf:2;
+} __attribute__((packed));
+
+#else
+#error What? PDP endian?
+#endif /* __BIG_ENDIAN_BITFIELD */
+
+
 #endif /* _IEEE1394_IEEE1394_H */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_core.c linux/drivers/ieee1394/ieee1394_core.c
--- v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_core.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/ieee1394/ieee1394_core.c	Wed Mar 22 00:02:48 2000
@@ -2,11 +2,12 @@
  * IEEE 1394 for Linux
  *
  * Core support: hpsb_packet management, packet handling and forwarding to
- *               csr or lowlevel code
+ *               highlevel or lowlevel code
  *
  * Copyright (C) 1999, 2000 Andreas E. Bombe
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/string.h>
@@ -62,7 +63,7 @@
         packet->header = header;
 
         if (data_size) {
-                data = kmalloc(data_size + 4, kmflags);
+                data = kmalloc(data_size + 8, kmflags);
                 if (data == NULL) {
                         kfree(header);
                         kfree(packet);
@@ -70,18 +71,14 @@
                 }
 
                 packet->data = data;
-                packet->data_size = data_size - 4;
+                packet->data_size = data_size;
         }
 
         INIT_LIST_HEAD(&packet->list);
         sema_init(&packet->state_change, 0);
         packet->state = unused;
         packet->generation = get_hpsb_generation();
-
-#ifdef __BIG_ENDIAN
-        /* set default */
         packet->data_be = 1;
-#endif
 
         return packet;
 }
@@ -133,59 +130,55 @@
 {
         int nodeid = -1;
         int rest_of_selfids = num_of_selfids;
-        quadlet_t *sidp = host->topology_map;
-        quadlet_t sid = *sidp;
+        struct selfid *sid = (struct selfid *)host->topology_map;
+        struct ext_selfid *esid;
         int esid_seq = 23;
-        int i;
 
         while (rest_of_selfids--) {
-                sid = *(sidp++);
-
-                if (!(sid & 0x00800000) /* !extended */) {
+                if (!sid->extended) {
                         nodeid++;
                         esid_seq = 0;
-
-                        if (((sid >> 24) & NODE_MASK) != nodeid) {
+                        
+                        if (sid->phy_id != nodeid) {
                                 HPSB_INFO("SelfIDs failed monotony check with "
-                                          "%d", (sid >> 24) & NODE_MASK);
+                                          "%d", sid->phy_id);
                                 return 0;
                         }
-
-                        /* "if is contender and link active" */
-                        if ((sid & (1<<11)) && (sid & (1<<22))) {
-                                host->irm_id = LOCAL_BUS | ((sid >> 24) 
-                                                            & NODE_MASK);
+                        
+                        if (sid->contender && sid->link_active) {
+                                host->irm_id = LOCAL_BUS | sid->phy_id;
                         }
                 } else {
-                        if ((((sid >> 24) & NODE_MASK) != nodeid)
-                            || (((sid >> 20) & 0x7) != esid_seq)) {
+                        esid = (struct ext_selfid *)sid;
+
+                        if ((esid->phy_id != nodeid) 
+                            || (esid->seq_nr != esid_seq)) {
                                 HPSB_INFO("SelfIDs failed monotony check with "
-                                          "%d/%d", (sid >> 24) & NODE_MASK,
-                                          (sid >> 20) & 0x7);
+                                          "%d/%d", esid->phy_id, esid->seq_nr);
                                 return 0;
                         }
                         esid_seq++;
                 }
+                sid++;
         }
-
-        sidp--;
-        while (sid & 0x00800000 /* extended */) {
-                /* check that no ports go to a parent */
-                for (i = 2; i < 18; i += 2) {
-                        if ((sid & (0x3 << i)) == (0x2 << i)) {
+        
+        esid = (struct ext_selfid *)(sid - 1);
+        while (esid->extended) {
+                if ((esid->porta == 0x2) || (esid->portb == 0x2)
+                    || (esid->portc == 0x2) || (esid->portd == 0x2)
+                    || (esid->porte == 0x2) || (esid->portf == 0x2)
+                    || (esid->portg == 0x2) || (esid->porth == 0x2)) {
                                 HPSB_INFO("SelfIDs failed root check on "
                                           "extended SelfID");
                                 return 0;
-                        }
                 }
-                sid = *(sidp--);
+                esid--;
         }
 
-        for (i = 2; i < 8; i += 2) {
-                if ((sid & (0x3 << i)) == (0x2 << i)) {
+        sid = (struct selfid *)esid;
+        if ((sid->port0 == 0x2) || (sid->port1 == 0x2) || (sid->port2 == 0x2)) {
                         HPSB_INFO("SelfIDs failed root check");
                         return 0;
-                }
         }
 
         return nodeid + 1;
@@ -196,7 +189,8 @@
         char speedcap[nodecount];
         char cldcnt[nodecount];
         u8 *map = host->speed_map;
-        quadlet_t *sidp;
+        struct selfid *sid;
+        struct ext_selfid *esid;
         int i, j, n;
 
         for (i = 0; i < (nodecount * 64); i += 64) {
@@ -210,22 +204,26 @@
         }
 
         /* find direct children count and speed */
-        for (sidp = &host->topology_map[host->selfid_count-1],
+        for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1],
                      n = nodecount - 1;
-             sidp >= host->topology_map; sidp--) {
-                if (*sidp & 0x00800000 /* extended */) {
-                        for (i = 2; i < 18; i += 2) {
-                                if ((*sidp & (0x3 << i)) == (0x3 << i)) {
-                                        cldcnt[n]++;
-                                }
-                        }
+             (void *)sid >= (void *)host->topology_map; sid--) {
+                if (sid->extended) {
+                        esid = (struct ext_selfid *)sid;
+
+                        if (esid->porta == 0x3) cldcnt[n]++;
+                        if (esid->portb == 0x3) cldcnt[n]++;
+                        if (esid->portc == 0x3) cldcnt[n]++;
+                        if (esid->portd == 0x3) cldcnt[n]++;
+                        if (esid->porte == 0x3) cldcnt[n]++;
+                        if (esid->portf == 0x3) cldcnt[n]++;
+                        if (esid->portg == 0x3) cldcnt[n]++;
+                        if (esid->porth == 0x3) cldcnt[n]++;
                 } else {
-                        for (i = 2; i < 8; i += 2) {
-                                if ((*sidp & (0x3 << i)) == (0x3 << i)) {
-                                        cldcnt[n]++;
-                                }
-                        }
-                        speedcap[n] = (*sidp >> 14) & 0x3;
+                        if (sid->port0 == 0x3) cldcnt[n]++;
+                        if (sid->port1 == 0x3) cldcnt[n]++;
+                        if (sid->port2 == 0x3) cldcnt[n]++;
+
+                        speedcap[n] = sid->speed;
                         n--;
                 }
         }
@@ -262,7 +260,7 @@
 void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid)
 {
         if (host->in_bus_reset) {
-                printk("including selfid 0x%x\n", sid);
+                HPSB_DEBUG("including selfid 0x%x", sid);
                 host->topology_map[host->selfid_count++] = sid;
         } else {
                 /* FIXME - info on which host */
@@ -293,7 +291,12 @@
         }
 
         /* irm_id is kept up to date by check_selfids() */
-        host->is_irm = (host->irm_id == host->node_id);
+        if (host->irm_id == host->node_id) {
+                host->is_irm = 1;
+                host->is_busmgr = 1;
+                host->busmgr_id = host->node_id;
+                host->csr.bus_manager_id = host->node_id;
+        }
 
         host->reset_retries = 0;
         inc_hpsb_generation();
@@ -346,6 +349,7 @@
         packet->speed_code = host->speed_map[(host->node_id & NODE_MASK) * 64
                                             + (packet->node_id & NODE_MASK)];
 
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
         switch (packet->speed_code) {
         case 2:
                 dump_packet("send packet 400:", packet->header,
@@ -359,6 +363,7 @@
                 dump_packet("send packet 100:", packet->header,
                             packet->header_size);
         }
+#endif
 
         return host->template->transmit_packet(host, packet);
 }
@@ -459,12 +464,14 @@
 {
         struct hpsb_packet *p;
 
+        dsize += (dsize % 4 ? 4 - (dsize % 4) : 0);
+
         p = alloc_hpsb_packet(dsize);
         if (p == NULL) {
                 /* FIXME - send data_error response */
                 return NULL;
         }
-                
+
         p->type = async;
         p->state = unused;
         p->host = host;
@@ -472,6 +479,10 @@
         p->tlabel = (data[0] >> 10) & 0x3f;
         p->no_waiter = 1;
 
+        if (dsize % 4) {
+                p->data[dsize / 4] = 0;
+        }
+
         return p;
 }
 
@@ -479,21 +490,12 @@
                 packet = create_reply_packet(host, data, length); \
                 if (packet == NULL) break
 
-inline void swap_quadlets_on_le(quadlet_t *q)
-{
-#ifdef __LITTLE_ENDIAN
-        quadlet_t saved = q[0];
-        q[0] = q[1];
-        q[1] = saved;
-#endif
-}
-
-
 void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data,
                             size_t size)
 {
         struct hpsb_packet *packet;
         int length, rcode, extcode;
+        int source = data[1] >> 16;
         u64 addr;
 
         /* big FIXME - no error checking is done for an out of bounds length */
@@ -501,7 +503,7 @@
         switch (tcode) {
         case TCODE_WRITEQ:
                 addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-                rcode = highlevel_write(host, data+3, addr, 4);
+                rcode = highlevel_write(host, source, data+3, addr, 4);
 
                 if (((data[0] >> 16) & NODE_MASK) != NODE_MASK) {
                         /* not a broadcast write, reply */
@@ -513,7 +515,8 @@
 
         case TCODE_WRITEB:
                 addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-                rcode = highlevel_write(host, data+4, addr, data[3]>>16);
+                rcode = highlevel_write(host, source, data+4, addr,
+                                        data[3]>>16);
 
                 if (((data[0] >> 16) & NODE_MASK) != NODE_MASK) {
                         /* not a broadcast write, reply */
@@ -527,7 +530,7 @@
                 PREP_REPLY_PACKET(0);
 
                 addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-                rcode = highlevel_read(host, data, addr, 4);
+                rcode = highlevel_read(host, source, data, addr, 4);
                 fill_async_readquad_resp(packet, rcode, *data);
                 send_packet_nocare(packet);
                 break;
@@ -537,7 +540,8 @@
                 PREP_REPLY_PACKET(length);
 
                 addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-                rcode = highlevel_read(host, packet->data, addr, length);
+                rcode = highlevel_read(host, source, packet->data, addr,
+                                       length);
                 fill_async_readblock_resp(packet, rcode, length);
                 send_packet_nocare(packet);
                 break;
@@ -556,35 +560,32 @@
 
                 switch (length) {
                 case 4:
-                        rcode = highlevel_lock(host, packet->data, addr, 
+                        rcode = highlevel_lock(host, source, packet->data, addr,
                                                data[4], 0, extcode);
                         fill_async_lock_resp(packet, rcode, extcode, 4);
                         break;
                 case 8:
                         if ((extcode != EXTCODE_FETCH_ADD) 
                             && (extcode != EXTCODE_LITTLE_ADD)) {
-                                rcode = highlevel_lock(host, packet->data, addr,
+                                rcode = highlevel_lock(host, source,
+                                                       packet->data, addr,
                                                        data[5], data[4], 
                                                        extcode);
                                 fill_async_lock_resp(packet, rcode, extcode, 4);
                         } else {
-                                swap_quadlets_on_le(data + 4);
-                                rcode = highlevel_lock64(host,
+                                rcode = highlevel_lock64(host, source,
                                              (octlet_t *)packet->data, addr,
                                              *(octlet_t *)(data + 4), 0ULL,
                                              extcode);
-                                swap_quadlets_on_le(packet->data);
                                 fill_async_lock_resp(packet, rcode, extcode, 8);
                         }
                         break;
                 case 16:
-                        swap_quadlets_on_le(data + 4);
-                        swap_quadlets_on_le(data + 6);
-                        rcode = highlevel_lock64(host, (octlet_t *)packet->data,
-                                                 addr, *(octlet_t *)(data + 6),
+                        rcode = highlevel_lock64(host, source,
+                                                 (octlet_t *)packet->data, addr,
+                                                 *(octlet_t *)(data + 6),
                                                  *(octlet_t *)(data + 4), 
                                                  extcode);
-                        swap_quadlets_on_le(packet->data);
                         fill_async_lock_resp(packet, rcode, extcode, 8);
                         break;
                 default:
@@ -609,7 +610,9 @@
                 return;
         }
 
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
         dump_packet("received packet:", data, size);
+#endif
 
         tcode = (data[0] >> 4) & 0xf;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_core.h linux/drivers/ieee1394/ieee1394_core.h
--- v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_core.h	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/ieee1394_core.h	Wed Mar 22 00:02:48 2000
@@ -40,29 +40,26 @@
         unsigned no_waiter:1;
 
         /* Data big endianness flag - may vary from request to request.  The
-         * header is always in machine byte order.  */
+         * header is always in machine byte order.
+         * Not really used currently.  */
         unsigned data_be:1;
 
         /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */
         unsigned speed_code:2;
 
-        /* --- 16 bytes (one cacheline) --- */
-
-        /* *header and *data are guaranteed to be 32-bit DMAable and may be
+        /*
+         * *header and *data are guaranteed to be 32-bit DMAable and may be
          * overwritten to allow in-place byte swapping.  Neither of these is
          * CRCed (the sizes also don't include CRC), but contain space for at
          * least one additional quadlet to allow in-place CRCing.  The memory is
          * also guaranteed to have physical mapping (virt_to_bus() is meaningful
          * on these pointers).
-         * NOTE: The 32-bit DMA guarantee is currently not enforced.
-         *       That's a Linux 2.3 issue.  
          */
         quadlet_t *header;
         quadlet_t *data;
         size_t header_size;
         size_t data_size;
 
-        /* --- 32 bytes --- */
 
         struct hpsb_host *host;
         unsigned int generation;
@@ -139,12 +136,14 @@
  * for other cases (internal errors that don't justify a panic).  Safe to call
  * from within a transmit packet routine.
  */
-void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, int ackcode);
+void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
+                      int ackcode);
 
 /*
  * Hand over received packet to the core.  The contents of data are expected to
  * be the full packet but with the CRCs left out (data block follows header
- * immediately) and in machine byte order.  *data can be safely overwritten
+ * immediately), with the header (i.e. the first four quadlets) in machine byte
+ * order and the data block in big endian.  *data can be safely overwritten
  * after this call.
  */
 void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_transactions.c linux/drivers/ieee1394/ieee1394_transactions.c
--- v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_transactions.c	Tue Feb  1 01:35:43 2000
+++ linux/drivers/ieee1394/ieee1394_transactions.c	Wed Mar 22 00:02:49 2000
@@ -68,7 +68,7 @@
         PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE);
         packet->header[3] = length << 16;
         packet->header_size = 16;
-        packet->data_size = length;
+        packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0);
 }
 
 void fill_async_writequad(struct hpsb_packet *packet, u64 addr, quadlet_t data)
@@ -85,8 +85,8 @@
         PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB);
         packet->header[3] = length << 16;
         packet->header_size = 16;
-        packet->data_size = length;
         packet->expect_response = 1;
+        packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0);
 }
 
 void fill_async_write_resp(struct hpsb_packet *packet, int rcode)
@@ -260,7 +260,7 @@
                        quadlet_t *buffer, size_t length)
 {
         if (host->node_id != node) return -1;
-        return highlevel_read(host, buffer, addr, length);
+        return highlevel_read(host, node, buffer, addr, length);
 }
 
 struct hpsb_packet *hpsb_make_readqpacket(struct hpsb_host *host, nodeid_t node,
@@ -284,7 +284,7 @@
 {
         struct hpsb_packet *p;
 
-        p = alloc_hpsb_packet(length);
+        p = alloc_hpsb_packet(length + (length % 4 ? 4 - (length % 4) : 0));
         if (!p) return NULL;
 
         p->host = host;
@@ -318,9 +318,13 @@
 {
         struct hpsb_packet *p;
 
-        p = alloc_hpsb_packet(length);
+        p = alloc_hpsb_packet(length + (length % 4 ? 4 - (length % 4) : 0));
         if (!p) return NULL;
 
+        if (length % 4) {
+                p->data[length / 4] = 0;
+        }
+
         p->host = host;
         p->tlabel = get_tlabel(host, node, 1);
         p->node_id = node;
@@ -370,7 +374,7 @@
         }
 
         if (host->node_id == node) {
-                switch(highlevel_read(host, buffer, addr, length)) {
+                switch(highlevel_read(host, node, buffer, addr, length)) {
                 case RCODE_COMPLETE:
                         return 0;
                 case RCODE_TYPE_ERROR:
@@ -381,16 +385,6 @@
                 }
         }
 
-        if (length & 0x3) {
-                /* FIXME: Lengths not multiple of 4 are not implemented.  Mainly
-                 * there is the problem with little endian machines because we
-                 * always swap to little endian on receive.  If we read 5 bytes
-                 * 12345 we receive them as 12345000 and swap them to 43210005.
-                 * How should we copy that to the caller?  Require *buffer to be
-                 * a full quadlet multiple in length? */
-                return -EACCES;
-        }
-
         if (length == 4) {
                 packet = hpsb_make_readqpacket(host, node, addr);
         } else {
@@ -432,7 +426,7 @@
         }
 
         if (host->node_id == node) {
-                switch(highlevel_write(host, buffer, addr, length)) {
+                switch(highlevel_write(host, node, buffer, addr, length)) {
                 case RCODE_COMPLETE:
                         return 0;
                 case RCODE_TYPE_ERROR:
@@ -443,12 +437,6 @@
                 }
         }
 
-        if (length & 0x3) {
-                /* FIXME: Lengths not multiple of 4 are not implemented.  See function
-                 * hpsb_read for explanation, same reason, different direction. */
-                return -EACCES;
-        }
-
         if (length == 4) {
                 packet = hpsb_make_writeqpacket(host, node, addr, *buffer);
         } else {
@@ -483,7 +471,8 @@
         int retval = 0, length;
         
         if (host->node_id == node) {
-                switch(highlevel_lock(host, data, addr, *data, arg, extcode)) {
+                switch(highlevel_lock(host, node, data, addr, *data, arg,
+                                      extcode)) {
                 case RCODE_COMPLETE:
                         return 0;
                 case RCODE_TYPE_ERROR:
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_types.h linux/drivers/ieee1394/ieee1394_types.h
--- v2.3.99-pre2/linux/drivers/ieee1394/ieee1394_types.h	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/ieee1394_types.h	Wed Mar 22 00:02:49 2000
@@ -6,6 +6,7 @@
 #include <linux/types.h>
 #include <linux/version.h>
 #include <linux/list.h>
+#include <asm/byteorder.h>
 
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
@@ -23,6 +24,8 @@
 {
         __list_add(new, head->prev, head);
 }
+
+#define __constant_cpu_to_be32(x) __constant_htonl((x))
 
 #endif
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ohci1394.c linux/drivers/ieee1394/ohci1394.c
--- v2.3.99-pre2/linux/drivers/ieee1394/ohci1394.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/ieee1394/ohci1394.c	Wed Mar 22 00:02:49 2000
@@ -35,9 +35,18 @@
  * . Config ROM
  *
  * Known bugs:
- * . Self-id are not received properly if card 
- *   is initialized with no other nodes on the
- *   bus.
+ * . Self-id are sometimes not received properly 
+ *   if card is initialized with no other nodes 
+ *   on the bus
+ */
+
+/* 
+ * Acknowledgments:
+ *
+ * Emilie Chung	<emilie.chung@axis.com>
+ *  .Tip on Async Request Filter
+ * Pascal Drolet <pascal.drolet@informission.ca>
+ *  .Various tips for optimization and functionnalities
  */
 
 #include <linux/config.h>
@@ -71,11 +80,15 @@
 #include "ieee1394_core.h"
 #include "ohci1394.h"
 
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define OHCI1394_DEBUG
+#endif
+
 #ifdef DBGMSG
 #undef DBGMSG
 #endif
 
-#if OHCI1394_DEBUG
+#ifdef OHCI1394_DEBUG
 #define DBGMSG(card, fmt, args...) \
 printk(KERN_INFO "ohci1394_%d: " fmt "\n" , card , ## args)
 #else
@@ -97,10 +110,14 @@
 	      return 1;
 
 int supported_chips[][2] = {
-	{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394 },
-	{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_2 },
+	{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_LV22 },
+	{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_LV23 },
+	{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_LV26 },
 	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_OHCI1394 },
 	{ PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SONY_CXD3222 },
+	{ PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72862 },
+	{ PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72870 },
+	{ PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72871 },
 	{ -1, -1 }
 };
 
@@ -213,7 +230,7 @@
 		if (q[0] == ~q[1]) {
 			PRINT(KERN_INFO, ohci->id, "selfid packet 0x%x rcvd", 
 			      q[0]);
-			hpsb_selfid_received(host, q[0]);
+			hpsb_selfid_received(host, cpu_to_be32(q[0]));
 			if (((q[0]&0x3f000000)>>24)==phyid) {
 				lsid=q[0];
 				PRINT(KERN_INFO, ohci->id, 
@@ -369,6 +386,23 @@
 	PRINT(KERN_INFO, ohci->id, "AT dma ctx=%d initialized", d->ctx);
 }
 
+/* Count the number of available iso contexts */
+static int get_nb_iso_ctx(struct ti_ohci *ohci)
+{
+	int i,ctx=0;
+	u32 tmp;
+
+	reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0xffffffff);
+	tmp = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet);
+
+	/* Count the number of contexts */
+	for(i=0; i<32; i++) {
+	    	if(tmp & 1) ctx++;
+		tmp >>= 1;
+	}
+	return ctx;
+}
+
 /* Global initialization */
 static int ohci_initialize(struct hpsb_host *host)
 {
@@ -411,15 +445,22 @@
 		  virt_to_bus(ohci->csr_config_rom));
 
 	/* Write the config ROM header */
-	reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->csr_config_rom[0]);
+	reg_write(ohci, OHCI1394_ConfigROMhdr, 
+		  cpu_to_be32(ohci->csr_config_rom[0]));
 
 	/* Set bus options */
-	reg_write(ohci, OHCI1394_BusOptions, ohci->csr_config_rom[2]);
-
-	/* Write the GUID into the csr config rom */
-	ohci->csr_config_rom[3] = reg_read(ohci, OHCI1394_GUIDHi);
-	ohci->csr_config_rom[4] = reg_read(ohci, OHCI1394_GUIDLo);
+	reg_write(ohci, OHCI1394_BusOptions, 
+		  cpu_to_be32(ohci->csr_config_rom[2]));
 	
+	/* Write the GUID into the csr config rom */
+	ohci->csr_config_rom[3] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDHi));
+	ohci->csr_config_rom[4] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDLo));
+
+	ohci->max_packet_size = 
+		1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
+	PRINT(KERN_INFO, ohci->id, "max packet size = %d bytes",
+	      ohci->max_packet_size);
+
 	/* Don't accept phy packets into AR request context */ 
 	reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
 
@@ -427,7 +468,10 @@
 	reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
 
 	/* Initialize IR dma */
-	for (i=0;i<4;i++) { /* FIXME : how many contexts are available ? */
+	ohci->nb_iso_ctx = get_nb_iso_ctx(ohci);
+	PRINT(KERN_INFO, ohci->id, "%d iso contexts available",
+	      ohci->nb_iso_ctx);
+	for (i=0;i<ohci->nb_iso_ctx;i++) {
 		reg_write(ohci, OHCI1394_IrRcvContextControlClear+32*i,
 			  0xffffffff);
 		reg_write(ohci, OHCI1394_IrRcvContextMatch+32*i, 0);
@@ -465,7 +509,6 @@
 	 * Accept AT requests from all nodes. This probably 
 	 * will have to be controlled from the subsystem
 	 * on a per node basis.
-	 * (Tip by Emilie Chung <emilie.chung@axis.com>)
 	 */
 	reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000);
 
@@ -578,8 +621,9 @@
 	unsigned char tcode;
 	int i=50;
 
-	if (packet->data_size >= 4096) {
-		PRINT(KERN_ERR, ohci->id, "transmit packet data too big (%d)",
+	if (packet->data_size >= ohci->max_packet_size) {
+		PRINT(KERN_ERR, ohci->id, 
+		      "transmit packet size = %d too big",
 		      packet->data_size);
 		return 0;
 	}
@@ -587,24 +631,8 @@
 
 	/* Decide wether we have a request or a response packet */
 	tcode = (packet->header[0]>>4)&0xf;
-	if ((tcode==TCODE_READQ)||
-	    (tcode==TCODE_WRITEQ)||
-	    (tcode==TCODE_READB)||
-	    (tcode==TCODE_WRITEB)||
-	    (tcode==TCODE_LOCK_REQUEST))
-		d = ohci->at_req_context;
-
-	else if ((tcode==TCODE_WRITE_RESPONSE)||
-		 (tcode==TCODE_READQ_RESPONSE)||
-		 (tcode==TCODE_READB_RESPONSE)||
-		 (tcode==TCODE_LOCK_RESPONSE)) 
-		d = ohci->at_resp_context;
-
-	else {
-		PRINT(KERN_ERR, ohci->id, 
-		      "Unexpected packet tcode=%d in AT DMA", tcode);
-		return 0;
-	}
+	if (tcode & 0x02) d = ohci->at_resp_context;
+	else d = ohci->at_req_context;
 
 	spin_lock(&d->lock);
 
@@ -659,6 +687,7 @@
 
 	switch (cmd) {
 	case RESET_BUS:
+		host->attempt_root=1;
 		PRINT(KERN_INFO, ohci->id, "resetting bus on request%s",
 		      (host->attempt_root ? " and attempting to become root"
 		       : ""));
@@ -679,15 +708,21 @@
 		break;
 
 	case ACT_CYCLE_MASTER:
-#if 0
 		if (arg) {
-			/* enable cycleTimer, cycleMaster, cycleSource */
-			reg_write(ohci, OHCI1394_LinkControlSet, 0x00700000);
+			/* check if we are root and other nodes are present */
+			u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
+			if ((nodeId & (1<<30)) && (nodeId & 0x3f)) {
+				/*
+				 * enable cycleTimer cycleMaster cycleSource
+				 */
+				DBGMSG(ohci->id, "Cycle master enabled");
+				reg_write(ohci, OHCI1394_LinkControlSet, 
+					  0x00700000);
+			}
 		} else {
 			/* disable cycleTimer, cycleMaster, cycleSource */
 			reg_write(ohci, OHCI1394_LinkControlClear, 0x00700000);
-		};
-#endif
+		}
 		break;
 
 	case CANCEL_REQUESTS:
@@ -811,8 +846,20 @@
 	struct hpsb_host *host = ohci->host;
 	int phyid = -1, isroot = 0;
 
+	/* read the interrupt event register */
 	event=reg_read(ohci, OHCI1394_IntEventSet);
 
+#if 0
+	/* 
+	 * clear the interrupt event register, except for the
+	 * bus reset event interrupt (if any). This is an
+	 * attempt to comply with ohci spec 7.2.3.2
+	 */
+	reg_write(ohci, OHCI1394_IntEventClear, event & (~OHCI1394_busReset));
+#else
+	/* The above attempt doesn't work */
+	reg_write(ohci, OHCI1394_IntEventClear, event);
+#endif
 	if (event & OHCI1394_busReset) {
 		if (!host->in_bus_reset) {
 			PRINT(KERN_INFO, ohci->id, "Bus reset");
@@ -821,6 +868,11 @@
 			dma_trm_reset(ohci->at_req_context);
 			dma_trm_reset(ohci->at_resp_context);
 
+#if 0
+			/* clear the bus reset event */
+			reg_write(ohci, OHCI1394_IntEventClear,
+				  OHCI1394_busReset);
+#endif
 			/* Subsystem call */
 			hpsb_bus_reset(ohci->host);
 
@@ -891,7 +943,7 @@
 	if (event & OHCI1394_selfIDComplete) {
 		if (host->in_bus_reset) {
 			node_id = reg_read(ohci, OHCI1394_NodeID);
-			if (node_id & 0x8000000) { /* NodeID valid */
+			if (node_id & 0x80000000) { /* NodeID valid */
 				phyid =  node_id & 0x0000003f;
 				isroot = (node_id & 0x40000000) != 0;
 
@@ -924,8 +976,6 @@
 #endif
 	}
 
-	/* clear the interrupt event register */
-	reg_write(ohci, OHCI1394_IntEventClear, event);
 }
 
 /* Put the buffer back into the dma context */
@@ -963,44 +1013,34 @@
 	return length;
 }
 
-static int packet_length(struct dma_rcv_ctx *d, int idx, 
-			 quadlet_t *buf_ptr, int offset)
+const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0, 
+			    -1, 0, -1, 0, -1, -1, 16, -1};
+
+/* 
+ * Determine the length of a packet in the buffer
+ * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca>
+ */
+static int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr,
+int offset)
 {
-	unsigned char tcode;
-	int length;
+	unsigned char 	tcode;
+	int 		length 	= -1;
 
 	/* Let's see what kind of packet is in there */
-	tcode = (buf_ptr[0]>>4)&0xf;
+	tcode = (buf_ptr[0] >> 4) & 0xf;
 
-	if (d->ctx==0) { /* Async Receive Request */
-		if (tcode==TCODE_READQ) return 16;
-		else if (tcode==TCODE_WRITEQ ||
-			 tcode==TCODE_READB) return 20;
-		else if (tcode==TCODE_WRITEB ||
-			 tcode==TCODE_LOCK_REQUEST) {
-			return block_length(d, idx, buf_ptr, offset) + 20;
-		}
-		else if (tcode==0xE) { /* Phy packet */
-			return 16;
-		}
-		else return -1;
-	}
-	else if (d->ctx==1) { /* Async Receive Response */
-		if (tcode==TCODE_WRITE_RESPONSE) return 16;
-		else if (tcode==TCODE_READQ_RESPONSE) return 20;
-		else if (tcode==TCODE_READB_RESPONSE ||
-			 tcode==TCODE_LOCK_RESPONSE) {
-			return block_length(d, idx, buf_ptr, offset) + 20;
-		}
-		else return -1;
+	if (d->ctx < 2) { /* Async Receive Response/Request */
+		length = TCODE_SIZE[tcode];
+		if (length == 0) 
+			length = block_length(d, idx, buf_ptr, offset) + 20;
 	}
 	else if (d->ctx==2) { /* Iso receive */
 		/* Assumption: buffer fill mode with header/trailer */
 		length = (buf_ptr[0]>>16);
 		if (length % 4) length += 4 - (length % 4);
-		return length+8;
+		length+=8;
 	}
-	return -1;
+	return length;
 }
 
 /* Bottom half that processes dma receive buffers */
@@ -1336,6 +1376,7 @@
 static int add_card(struct pci_dev *dev)
 {
 	struct ti_ohci *ohci;	/* shortcut to currently handled device */
+	int i;
 
 	if (num_of_cards == MAX_OHCI1394_CARDS) {
 		PRINT_G(KERN_WARNING, "cannot handle more than %d cards.  "
@@ -1344,6 +1385,10 @@
 		return 1;
 	}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	pci_enable_device(dev);
+#endif
+
 	ohci = &cards[num_of_cards++];
 
 	ohci->id = num_of_cards-1;
@@ -1363,7 +1408,11 @@
 	if (ohci->csr_config_rom == NULL) {
 		FAIL("failed to allocate buffer config rom");
 	}
-	memcpy(ohci->csr_config_rom, ohci_csr_rom, sizeof(ohci_csr_rom));
+	for (i=0;i<sizeof(ohci_csr_rom)/4;i++)
+		ohci->csr_config_rom[i] = cpu_to_be32(ohci_csr_rom[i]);
+
+	DBGMSG(ohci->id, "The 1st byte at offset 0x404 is: 0x%02x",
+	       *((char *)ohci->csr_config_rom+4));
 
 	/* self-id dma buffer allocation */
 	ohci->self_id_buffer = kmalloc(2048, GFP_KERNEL);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/ohci1394.h linux/drivers/ieee1394/ohci1394.h
--- v2.3.99-pre2/linux/drivers/ieee1394/ohci1394.h	Sun Feb 20 21:12:39 2000
+++ linux/drivers/ieee1394/ohci1394.h	Wed Mar 22 00:02:49 2000
@@ -4,16 +4,18 @@
 
 #include "ieee1394_types.h"
 
-#define OHCI1394_DEBUG 1
-
 #define OHCI1394_DRIVER_NAME      "ohci1394"
 
-#ifndef PCI_DEVICE_ID_TI_OHCI1394
-#define PCI_DEVICE_ID_TI_OHCI1394 0x8009
-#endif 
+#ifndef PCI_DEVICE_ID_TI_OHCI1394_LV22
+#define PCI_DEVICE_ID_TI_OHCI1394_LV22 0x8009
+#endif
+
+#ifndef PCI_DEVICE_ID_TI_OHCI1394_LV23
+#define PCI_DEVICE_ID_TI_OHCI1394_LV23 0x8019
+#endif
 
-#ifndef PCI_DEVICE_ID_TI_OHCI1394_2
-#define PCI_DEVICE_ID_TI_OHCI1394_2 0x8019
+#ifndef PCI_DEVICE_ID_TI_OHCI1394_LV26
+#define PCI_DEVICE_ID_TI_OHCI1394_LV26 0x8020
 #endif
 
 #ifndef PCI_DEVICE_ID_VIA_OHCI1394
@@ -28,6 +30,22 @@
 #define PCI_DEVICE_ID_SONY_CXD3222 0x8039
 #endif
 
+#ifndef PCI_DEVICE_ID_NEC_1394
+#define PCI_DEVICE_ID_NEC_1394 0x00cd
+#endif
+
+#ifndef PCI_DEVICE_ID_NEC_UPD72862
+#define PCI_DEVICE_ID_NEC_UPD72862      0x0063
+#endif
+
+#ifndef PCI_DEVICE_ID_NEC_UPD72870
+#define PCI_DEVICE_ID_NEC_UPD72870      0x00cd
+#endif
+
+#ifndef PCI_DEVICE_ID_NEC_UPD72871
+#define PCI_DEVICE_ID_NEC_UPD72871      0x00ce
+#endif
+
 #define MAX_OHCI1394_CARDS        4
 
 #define OHCI1394_MAX_AT_REQ_RETRIES       0x2
@@ -113,6 +131,8 @@
         quadlet_t *self_id_buffer; /* dma buffer for self-id packets */
         quadlet_t *csr_config_rom; /* buffer for csr config rom */
 
+	unsigned int max_packet_size;
+
         /* async receive */
 	struct dma_rcv_ctx *ar_resp_context;
 	struct dma_rcv_ctx *ar_req_context;
@@ -125,6 +145,7 @@
 	struct dma_rcv_ctx *ir_context;
         u64 IR_channel_usage;
         spinlock_t IR_channel_lock;
+	int nb_iso_ctx;
 
         /* IEEE-1394 part follows */
         struct hpsb_host *host;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/pcilynx.c linux/drivers/ieee1394/pcilynx.c
--- v2.3.99-pre2/linux/drivers/ieee1394/pcilynx.c	Thu Feb 10 17:11:09 2000
+++ linux/drivers/ieee1394/pcilynx.c	Wed Mar 22 00:02:49 2000
@@ -59,6 +59,13 @@
 /* print card specific information */
 #define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)
 
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
+#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)
+#else
+#define PRINT_GD(level, fmt, args...)
+#define PRINTD(level, card, fmt, args...)
+#endif
 
 static struct ti_lynx cards[MAX_PCILYNX_CARDS];
 static int num_of_cards = 0;
@@ -313,7 +320,8 @@
                 }
         }
 
-        printk("-%d- generated own selfid 0x%x\n", lynx->id, lsid);
+        cpu_to_be32s(&lsid);
+        PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid);
         return lsid;
 }
 
@@ -322,7 +330,14 @@
         quadlet_t *q = lynx->rcv_page;
         int phyid, isroot;
         quadlet_t lsid = 0;
+        int i;
 
+        i = (size > 16 ? 16 : size) / 4 - 1;
+        while (i >= 0) {
+                cpu_to_be32s(&q[i]);
+                i--;
+        }
+        
         if (!lynx->phyic.reg_1394a) {
                 lsid = generate_own_selfid(lynx, host);
         }
@@ -339,17 +354,20 @@
         }
 
         while (size > 0) {
-                if (!lynx->phyic.reg_1394a 
-                    && (q[0] & 0x3f800000) == ((phyid + 1) << 24)) {
+                struct selfid *sid = (struct selfid *)q;
+
+                if (!lynx->phyic.reg_1394a && !sid->extended 
+                    && (sid->phy_id == (phyid + 1))) {
                         hpsb_selfid_received(host, lsid);
                 }
 
                 if (q[0] == ~q[1]) {
-                        printk("-%d- selfid packet 0x%x rcvd\n", lynx->id, q[0]);
+                        PRINT(KERN_DEBUG, lynx->id, "selfid packet 0x%x rcvd",
+                              q[0]);
                         hpsb_selfid_received(host, q[0]);
                 } else {
-                        printk("-%d- inconsistent selfid 0x%x/0x%x\n", lynx->id,
-                               q[0], q[1]);
+                        PRINT(KERN_INFO, lynx->id,
+                              "inconsistent selfid 0x%x/0x%x", q[0], q[1]);
                 }
                 q += 2;
                 size -= 8;
@@ -429,14 +447,14 @@
         pcl.next = PCL_NEXT_INVALID;
         pcl.async_error_next = PCL_NEXT_INVALID;
 #ifdef __BIG_ENDIAN
-        pcl.buffer[0].control = PCL_CMD_RCV | 2048;
-        pcl.buffer[1].control = PCL_LAST_BUFF | 2048;
+        pcl.buffer[0].control = PCL_CMD_RCV | 16;
+        pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
 #else
-        pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 2048;
-        pcl.buffer[1].control = PCL_LAST_BUFF | PCL_BIGENDIAN | 2048;
+        pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16;
+        pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
 #endif
         pcl.buffer[0].pointer = virt_to_bus(lynx->rcv_page);
-        pcl.buffer[1].pointer = virt_to_bus(lynx->rcv_page) + 2048;
+        pcl.buffer[1].pointer = virt_to_bus(lynx->rcv_page) + 16;
         put_pcl(lynx, lynx->rcv_pcl, &pcl);
         
         pcl.next = pcl_bus(lynx, lynx->async_pcl);
@@ -445,10 +463,11 @@
 
         pcl.next = PCL_NEXT_INVALID;
         pcl.async_error_next = PCL_NEXT_INVALID;
-        pcl.buffer[0].control = PCL_CMD_RCV | PCL_LAST_BUFF | 2048;
+        pcl.buffer[0].control = PCL_CMD_RCV | 4;
 #ifndef __BIG_ENDIAN
         pcl.buffer[0].control |= PCL_BIGENDIAN;
 #endif
+        pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
 
         for (i = 0; i < NUM_ISORCV_PCL; i++) {
                 int page = i / ISORCV_PER_PAGE;
@@ -456,6 +475,7 @@
 
                 pcl.buffer[0].pointer = virt_to_bus(lynx->iso_rcv.page[page])
                         + sec * MAX_ISORCV_SIZE;
+                pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
                 put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
         }
 
@@ -539,6 +559,10 @@
         }
 
         packet->xnext = NULL;
+        if (packet->tcode == TCODE_WRITEQ
+            || packet->tcode == TCODE_READQ_RESPONSE) {
+                cpu_to_be32s(&packet->header[3]);
+        }
 
         spin_lock_irqsave(&lynx->async_queue_lock, flags);
 
@@ -1054,8 +1078,8 @@
                 }
                 if (linkint & LINK_INT_PHY_REG_RCVD) {
                         if (!host->in_bus_reset) {
-                                printk("-%d- phy reg received without reset\n",
-                                       lynx->id);
+                                PRINT(KERN_INFO, lynx->id,
+                                      "phy reg received without reset");
                         }
                 }
                 if (linkint & LINK_INT_ISO_STUCK) {
@@ -1082,7 +1106,7 @@
         }
 
         if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) {
-                PRINT(KERN_INFO, lynx->id, "iso receive");
+                PRINTD(KERN_DEBUG, lynx->id, "iso receive");
 
                 spin_lock(&lynx->iso_rcv.lock);
 
@@ -1094,7 +1118,7 @@
 
                 if ((lynx->iso_rcv.next == lynx->iso_rcv.last)
                     || !lynx->iso_rcv.chan_count) {
-                        printk("stopped\n");
+                        PRINTD(KERN_DEBUG, lynx->id, "stopped");
                         reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
                 }
 
@@ -1125,9 +1149,9 @@
                 spin_unlock(&lynx->async_queue_lock);
 
                 if (ack & DMA_CHAN_STAT_SPECIALACK) {
-                        printk("-%d- special ack %d\n", lynx->id,
-                               (ack >> 15) & 0xf);
-                        ack = ACKX_SEND_ERROR;
+                        ack = (ack >> 15) & 0xf;
+                        PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+                        ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
                 } else {
                         ack = (ack >> 15) & 0xf;
                 }
@@ -1139,7 +1163,7 @@
                 /* general receive DMA completed */
                 int stat = reg_read(lynx, DMA1_CHAN_STAT);
 
-                printk("-%d- received packet size %d\n", lynx->id, 
+                PRINTD(KERN_DEBUG, lynx->id, "received packet size %d",
                        stat & 0x1fff); 
 
                 if (stat & DMA_CHAN_STAT_SELFID) {
@@ -1149,8 +1173,12 @@
                                      | LINK_CONTROL_TX_ASYNC_EN
                                      | LINK_CONTROL_RX_ASYNC_EN);
                 } else {
-                        hpsb_packet_received(host, lynx->rcv_page,
-                                             stat & 0x1fff);
+                        quadlet_t *q_data = lynx->rcv_page;
+                        if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE
+                            || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) {
+                                cpu_to_be32s(q_data + 3);
+                        }
+                        hpsb_packet_received(host, q_data, stat & 0x1fff);
                 }
 
                 run_pcl(lynx, lynx->rcv_pcl_start, 1);
@@ -1411,7 +1439,7 @@
         }
 
         if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) {
-                PRINT_G(KERN_ERR, "allocation of char major number %d failed\n",
+                PRINT_G(KERN_ERR, "allocation of char major number %d failed",
                         PCILYNX_MAJOR);
                 return -EBUSY;
         }
@@ -1453,13 +1481,13 @@
 void cleanup_module(void)
 {
         hpsb_unregister_lowlevel(get_lynx_template());
-        PRINT_G(KERN_INFO, "removed " PCILYNX_DRIVER_NAME " module\n");
+        PRINT_G(KERN_INFO, "removed " PCILYNX_DRIVER_NAME " module");
 }
 
 int init_module(void)
 {
         if (hpsb_register_lowlevel(get_lynx_template())) {
-                PRINT_G(KERN_ERR, "registering failed\n");
+                PRINT_G(KERN_ERR, "registering failed");
                 return -ENXIO;
         } else {
                 return 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/pcilynx.h linux/drivers/ieee1394/pcilynx.h
--- v2.3.99-pre2/linux/drivers/ieee1394/pcilynx.h	Sun Feb 20 21:12:39 2000
+++ linux/drivers/ieee1394/pcilynx.h	Wed Mar 22 22:22:05 2000
@@ -462,71 +462,75 @@
 #define PCL_BIGENDIAN          (1<<16)
 
 
+#define _(x) (__constant_cpu_to_be32(x))
+
 quadlet_t lynx_csr_rom[] = {
         /* bus info block */
-        0x04040000, /* info/CRC length, CRC */
-        0x31333934, /* 1394 magic number */
-        0xf064a000, /* misc. settings */
-        0x08002850, /* vendor ID, chip ID high */
-        0x0000ffff, /* chip ID low */
+        _(0x04040000), /* info/CRC length, CRC */
+        _(0x31333934), /* 1394 magic number */
+        _(0xf064a000), /* misc. settings */
+        _(0x08002850), /* vendor ID, chip ID high */
+        _(0x0000ffff), /* chip ID low */
         /* root directory */
-        0x00090000, /* CRC length, CRC */
-        0x03080028, /* vendor ID (Texas Instr.) */
-        0x81000009, /* offset to textual ID */
-        0x0c000200, /* node capabilities */
-        0x8d00000e, /* offset to unique ID */
-        0xc7000010, /* offset to module independent info */
-        0x04000000, /* module hardware version */
-        0x81000026, /* offset to textual ID */
-        0x09000000, /* node hardware version */
-        0x81000026, /* offset to textual ID */
+        _(0x00090000), /* CRC length, CRC */
+        _(0x03080028), /* vendor ID (Texas Instr.) */
+        _(0x81000009), /* offset to textual ID */
+        _(0x0c000200), /* node capabilities */
+        _(0x8d00000e), /* offset to unique ID */
+        _(0xc7000010), /* offset to module independent info */
+        _(0x04000000), /* module hardware version */
+        _(0x81000026), /* offset to textual ID */
+        _(0x09000000), /* node hardware version */
+        _(0x81000026), /* offset to textual ID */
         /* module vendor ID textual */
-        0x00080000, /* CRC length, CRC */
-        0x00000000,
-        0x00000000,
-        0x54455841, /* "Texas Instruments" */
-        0x5320494e,
-        0x53545255,
-        0x4d454e54,
-        0x53000000,
+        _(0x00080000), /* CRC length, CRC */
+        _(0x00000000),
+        _(0x00000000),
+        _(0x54455841), /* "Texas Instruments" */
+        _(0x5320494e),
+        _(0x53545255),
+        _(0x4d454e54),
+        _(0x53000000),
         /* node unique ID leaf */
-        0x00020000, /* CRC length, CRC */
-        0x08002850, /* vendor ID, chip ID high */
-        0x0000ffff, /* chip ID low */
+        _(0x00020000), /* CRC length, CRC */
+        _(0x08002850), /* vendor ID, chip ID high */
+        _(0x0000ffff), /* chip ID low */
         /* module dependent info */
-        0x00060000, /* CRC length, CRC */
-        0xb8000006, /* offset to module textual ID */
-        0x81000004, /* ??? textual descriptor */
-        0x39010000, /* SRAM size */
-        0x3a010000, /* AUXRAM size */
-        0x3b000000, /* AUX device */
+        _(0x00060000), /* CRC length, CRC */
+        _(0xb8000006), /* offset to module textual ID */
+        _(0x81000004), /* ??? textual descriptor */
+        _(0x39010000), /* SRAM size */
+        _(0x3a010000), /* AUXRAM size */
+        _(0x3b000000), /* AUX device */
         /* module textual ID */
-        0x00050000, /* CRC length, CRC */
-        0x00000000,
-        0x00000000,
-        0x54534231, /* "TSB12LV21" */
-        0x324c5632,
-        0x31000000,
+        _(0x00050000), /* CRC length, CRC */
+        _(0x00000000),
+        _(0x00000000),
+        _(0x54534231), /* "TSB12LV21" */
+        _(0x324c5632),
+        _(0x31000000),
         /* part number */
-        0x00060000, /* CRC length, CRC */
-        0x00000000,
-        0x00000000,
-        0x39383036, /* "9806000-0001" */
-        0x3030342d,
-        0x30303431,
-        0x20000001,
+        _(0x00060000), /* CRC length, CRC */
+        _(0x00000000),
+        _(0x00000000),
+        _(0x39383036), /* "9806000-0001" */
+        _(0x3030342d),
+        _(0x30303431),
+        _(0x20000001),
         /* module hardware version textual */
-        0x00050000, /* CRC length, CRC */
-        0x00000000,
-        0x00000000,
-        0x5453424b, /* "TSBKPCITST" */
-        0x50434954,
-        0x53540000,
+        _(0x00050000), /* CRC length, CRC */
+        _(0x00000000),
+        _(0x00000000),
+        _(0x5453424b), /* "TSBKPCITST" */
+        _(0x50434954),
+        _(0x53540000),
         /* node hardware version textual */
-        0x00050000, /* CRC length, CRC */
-        0x00000000,
-        0x00000000,
-        0x54534232, /* "TSB21LV03" */
-        0x313c5630,
-        0x33000000
+        _(0x00050000), /* CRC length, CRC */
+        _(0x00000000),
+        _(0x00000000),
+        _(0x54534232), /* "TSB21LV03" */
+        _(0x313c5630),
+        _(0x33000000)
 };
+
+#undef _
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/raw1394.c linux/drivers/ieee1394/raw1394.c
--- v2.3.99-pre2/linux/drivers/ieee1394/raw1394.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/ieee1394/raw1394.c	Wed Mar 22 00:02:49 2000
@@ -59,7 +59,7 @@
 {
         if (req->ibs) {
                 if (atomic_dec_and_test(&req->ibs->refcount)) {
-                        atomic_sub((req->data[0] >> 16) + 4, &iso_buffer_size);
+                        atomic_sub(req->ibs->data_size, &iso_buffer_size);
                         kfree(req->ibs);
                 }
         } else if (req->free_data) {
@@ -109,6 +109,7 @@
         }
 
         free_tlabel(packet->host, packet->node_id, packet->tlabel);
+
         queue_complete_req(req);
 }
 
@@ -215,6 +216,7 @@
         LIST_HEAD(reqs);
 
         if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
+                HPSB_INFO("dropped iso packet");
                 return;
         }
 
@@ -230,22 +232,23 @@
                                 continue;
                         }
 
+                        req = __alloc_pending_request(SLAB_ATOMIC);
+                        if (!req) break;
+
                         if (!ibs) {
                                 ibs = kmalloc(sizeof(struct iso_block_store)
                                               + length, SLAB_ATOMIC);
-                                if (!ibs) break;
+                                if (!ibs) {
+                                        kfree(req);
+                                        break;
+                                }
 
                                 atomic_add(length, &iso_buffer_size);
                                 atomic_set(&ibs->refcount, 0);
+                                ibs->data_size = length;
                                 memcpy(ibs->data, data, length);
                         }
 
-                        req = __alloc_pending_request(SLAB_ATOMIC);
-                        if (!req) {
-                                kfree(ibs);
-                                break;
-                        }
-
                         atomic_inc(&ibs->refcount);
 
                         req->file_info = fi;
@@ -270,6 +273,75 @@
         }
 }
 
+static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
+                        int cts, u8 *data, unsigned int length)
+{
+        unsigned long flags;
+        struct list_head *lh;
+        struct host_info *hi;
+        struct file_info *fi;
+        struct pending_request *req;
+        struct iso_block_store *ibs = NULL;
+        LIST_HEAD(reqs);
+
+        if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
+                HPSB_INFO("dropped fcp request");
+                return;
+        }
+
+        spin_lock_irqsave(&host_info_lock, flags);
+        hi = find_host_info(host);
+
+        if (hi != NULL) {
+                for (lh = hi->file_info_list.next; lh != &hi->file_info_list;
+                     lh = lh->next) {
+                        fi = list_entry(lh, struct file_info, list);
+
+                        if (!fi->fcp_buffer) {
+                                continue;
+                        }
+
+                        req = __alloc_pending_request(SLAB_ATOMIC);
+                        if (!req) break;
+
+                        if (!ibs) {
+                                ibs = kmalloc(sizeof(struct iso_block_store)
+                                              + length, SLAB_ATOMIC);
+                                if (!ibs) {
+                                        kfree(req);
+                                        break;
+                                }
+
+                                atomic_add(length, &iso_buffer_size);
+                                atomic_set(&ibs->refcount, 0);
+                                ibs->data_size = length;
+                                memcpy(ibs->data, data, length);
+                        }
+
+                        atomic_inc(&ibs->refcount);
+
+                        req->file_info = fi;
+                        req->ibs = ibs;
+                        req->data = ibs->data;
+                        req->req.type = RAW1394_REQ_FCP_REQUEST;
+                        req->req.generation = get_hpsb_generation();
+                        req->req.misc = nodeid | (direction << 16);
+                        req->req.recvb = (quadlet_t *)fi->fcp_buffer;
+                        req->req.length = length;
+                        
+                        list_add_tail(&req->list, &reqs);
+                }
+        }
+        spin_unlock_irqrestore(&host_info_lock, flags);
+
+        lh = reqs.next;
+        while (lh != &reqs) {
+                req = list_entry(lh, struct pending_request, list);
+                lh = lh->next;
+                queue_complete_req(req);
+        }
+}
+
 
 static int dev_read(struct file *file, char *buffer, size_t count,
                     loff_t *offset_is_ignored)
@@ -455,8 +527,28 @@
         spin_unlock(&host_info_lock);
 }
 
+static void handle_fcp_listen(struct file_info *fi, struct pending_request *req)
+{
+        if (req->req.misc) {
+                if (fi->fcp_buffer) {
+                        req->req.error = RAW1394_ERROR_ALREADY;
+                } else {
+                        fi->fcp_buffer = (u8 *)req->req.recvb;
+                }
+        } else {
+                if (!fi->fcp_buffer) {
+                        req->req.error = RAW1394_ERROR_ALREADY;
+                } else {
+                        fi->fcp_buffer = NULL;
+                }
+        }
+
+        req->req.length = 0;
+        queue_complete_req(req);
+}
+
 static int handle_local_request(struct file_info *fi,
-                                struct pending_request *req)
+                                struct pending_request *req, int node)
 {
         u64 addr = req->req.address & 0xffffffffffffULL;
 
@@ -466,7 +558,7 @@
 
         switch (req->req.type) {
         case RAW1394_REQ_ASYNC_READ:
-                req->req.error = highlevel_read(fi->host, req->data, addr,
+                req->req.error = highlevel_read(fi->host, node, req->data, addr,
                                                 req->req.length);
                 break;
 
@@ -477,8 +569,8 @@
                         break;
                 }
 
-                req->req.error = highlevel_write(fi->host, req->data, addr,
-                                                 req->req.length);
+                req->req.error = highlevel_write(fi->host, node, req->data,
+                                                 addr, req->req.length);
                 req->req.length = 0;
                 break;
 
@@ -503,14 +595,16 @@
                 }
 
                 if (req->req.length == 8) {
-                        req->req.error = highlevel_lock(fi->host, req->data,
-                                                        addr, req->data[1],
+                        req->req.error = highlevel_lock(fi->host, node,
+                                                        req->data, addr,
+                                                        req->data[1],
                                                         req->data[0],
                                                         req->req.misc);
                         req->req.length = 4;
                 } else {
-                        req->req.error = highlevel_lock(fi->host, req->data,
-                                                        addr, req->data[0], 0,
+                        req->req.error = highlevel_lock(fi->host, node,
+                                                        req->data, addr,
+                                                        req->data[0], 0,
                                                         req->req.misc);
                 }
                 break;
@@ -539,28 +633,27 @@
                         if (!packet) return -ENOMEM;
 
                         req->data = &packet->header[3];
-                } else if ((req->req.length % 4) == 0) {
+                } else {
                         packet = hpsb_make_readbpacket(fi->host, node, addr,
                                                        req->req.length);
                         if (!packet) return -ENOMEM;
 
                         req->data = packet->data;
-                } else {
-                        req->req.error = RAW1394_ERROR_UNTIDY_LEN;
                 }
                 break;
 
         case RAW1394_REQ_ASYNC_WRITE:
                 if (req->req.length == 4) {
-                        packet = hpsb_make_writeqpacket(fi->host, node, addr,
-                                                        0);
-                        if (!packet) return -ENOMEM;
+                        quadlet_t x;
 
-                        if (copy_from_user(&packet->header[3], req->req.sendb,
-                                           4)) {
+                        if (copy_from_user(&x, req->req.sendb, 4)) {
                                 req->req.error = RAW1394_ERROR_MEMFAULT;
                         }
-                } else if ((req->req.length % 4) == 0) {
+
+                        packet = hpsb_make_writeqpacket(fi->host, node, addr,
+                                                        x);
+                        if (!packet) return -ENOMEM;
+                } else {
                         packet = hpsb_make_writebpacket(fi->host, node, addr,
                                                         req->req.length);
                         if (!packet) return -ENOMEM;
@@ -569,8 +662,6 @@
                                            req->req.length)) {
                                 req->req.error = RAW1394_ERROR_MEMFAULT;
                         }
-                } else {
-                        req->req.error = RAW1394_ERROR_UNTIDY_LEN;
                 }
                 req->req.length = 0;
                 break;
@@ -649,6 +740,11 @@
                 return sizeof(struct raw1394_request);
         }
 
+        if (req->req.type == RAW1394_REQ_FCP_LISTEN) {
+                handle_fcp_listen(fi, req);
+                return sizeof(struct raw1394_request);
+        }
+
         if (req->req.length == 0) {
                 req->req.error = RAW1394_ERROR_INVALID_ARG;
                 queue_complete_req(req);
@@ -656,7 +752,7 @@
         }
 
         if (fi->host->node_id == node) {
-                return handle_local_request(fi, req);
+                return handle_local_request(fi, req, node);
         }
 
         return handle_remote_request(fi, req, node);
@@ -806,10 +902,11 @@
 }
 
 static struct hpsb_highlevel_ops hl_ops = {
-        add_host,
-        remove_host,
-        host_reset,
-        iso_receive
+        add_host:     add_host,
+        remove_host:  remove_host,
+        host_reset:   host_reset,
+        iso_receive:  iso_receive,
+        fcp_request:  fcp_request,
 };
 
 static struct file_operations file_ops = {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/ieee1394/raw1394.h linux/drivers/ieee1394/raw1394.h
--- v2.3.99-pre2/linux/drivers/ieee1394/raw1394.h	Fri Jan 21 18:19:16 2000
+++ linux/drivers/ieee1394/raw1394.h	Wed Mar 22 00:02:49 2000
@@ -5,7 +5,7 @@
 #define RAW1394_DEVICE_MAJOR      171
 #define RAW1394_DEVICE_NAME       "raw1394"
 
-#define RAW1394_KERNELAPI_VERSION 1
+#define RAW1394_KERNELAPI_VERSION 2
 
 /* state: opened */
 #define RAW1394_REQ_INITIALIZE    1
@@ -21,10 +21,12 @@
 #define RAW1394_REQ_LOCK64        103
 
 #define RAW1394_REQ_ISO_LISTEN    200
+#define RAW1394_REQ_FCP_LISTEN    201
 
 /* kernel to user */
 #define RAW1394_REQ_BUS_RESET     10000
 #define RAW1394_REQ_ISO_RECEIVE   10001
+#define RAW1394_REQ_FCP_REQUEST   10002
 
 /* error codes */
 #define RAW1394_ERROR_NONE        0
@@ -67,6 +69,7 @@
 
 struct iso_block_store {
         atomic_t refcount;
+        size_t data_size;
         quadlet_t data[0];
 };
 
@@ -82,6 +85,8 @@
         struct semaphore complete_sem;
         spinlock_t reqlists_lock;
         wait_queue_head_t poll_wait_complete;
+
+        u8 *fcp_buffer;
 
         u64 listen_channels;
         quadlet_t *iso_buffer;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.3.99-pre2/linux/drivers/isdn/Config.in	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/Config.in	Thu Mar 23 08:38:57 2000
@@ -85,8 +85,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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v2.3.99-pre2/linux/drivers/isdn/Makefile	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/Makefile	Thu Mar 23 08:38:57 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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/Makefile linux/drivers/isdn/avmb1/Makefile
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/Makefile	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/avmb1/Makefile	Thu Mar 23 08:38:57 2000
@@ -1,5 +1,5 @@
 #
-# $Id: Makefile,v 1.8 2000/01/25 14:33:38 calle Exp $
+# $Id: Makefile,v 1.16 2000/03/17 12:15:44 calle Exp $
 #
 # Makefile for the CAPI and AVM-B1 device drivers.
 #
@@ -11,6 +11,49 @@
 # parent makes..
 #
 # $Log: Makefile,v $
+# 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 +107,9 @@
 #
 #
 
+SUB_DIRS     :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS := # fcpci fcclassic
 #
 # Objects that don't export a symtab
 #
@@ -82,53 +128,78 @@
 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
+  	O_OBJS   += t1isa.o
   endif
   ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
-  OX_OBJS   += b1pcmcia.o
+  	OX_OBJS   += b1pcmcia.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
+  ifdef CONFIG_ISDN_DRV_AVMB1_FCPCI
+	SUB_DIRS += fcpci
+	MOD_SUB_DIRS += fcpci
+  endif
+  ifdef CONFIG_ISDN_DRV_AVMB1_FCCLASSIC
+	SUB_DIRS += fcclassic
+	MOD_SUB_DIRS += fcclassic
   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_B1PCMCIA
+  		MX_OBJS  += b1pcmcia.o
+		ifeq ($(CONFIG_HOTPLUG),y)
+			ifneq ($(CONFIG_PCMCIA),n)
+  				M_OBJS   += avm_cs.o
+			endif
+		endif
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI
+  		M_OBJS   += t1pci.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_C4
+  		M_OBJS   += c4.o
+  	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_FCPCI
+    		MOD_SUB_DIRS += fcpci
+	endif
+  	ifdef CONFIG_ISDN_DRV_AVMB1_FCCLASSIC
+    		MOD_SUB_DIRS += fcclassic
+	endif
+  	MX_OBJS  += capiutil.o capidrv.o b1.o b1dma.o
   endif
 endif
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/avm_cs.c linux/drivers/isdn/avmb1/avm_cs.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/avm_cs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/avm_cs.c	Thu Mar 23 08:38:57 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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/b1dma.c linux/drivers/isdn/avmb1/b1dma.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/b1dma.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/isdn/avmb1/b1dma.c	Thu Mar 23 08:38:57 2000
@@ -40,7 +40,7 @@
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 
-static int suppress_pollack = 0;
+int suppress_pollack = 0;
 MODULE_PARM(suppress_pollack, "0-1i");
 
 /* ------------------------------------------------------------- */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/b1pcmcia.c linux/drivers/isdn/avmb1/b1pcmcia.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/b1pcmcia.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/avmb1/b1pcmcia.c	Thu Mar 23 23:18:20 2000
@@ -1,11 +1,16 @@
 /*
- * $Id: b1pcmcia.c,v 1.7 2000/02/02 18:36:03 calle Exp $
+ * $Id: b1pcmcia.c,v 1.8 2000/03/06 18:00:23 calle 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.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 +75,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.7 $";
+static char *revision = "$Revision: 1.8 $";
 
 /* ------------------------------------------------------------- */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/c4.c linux/drivers/isdn/avmb1/c4.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/c4.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/isdn/avmb1/c4.c	Thu Mar 23 08:38:57 2000
@@ -1,11 +1,17 @@
 /*
- * $Id: c4.c,v 1.4 2000/02/02 18:36:03 calle Exp $
+ * $Id: c4.c,v 1.6 2000/03/17 12:21:08 calle Exp $
  * 
  * Module for AVM C4 card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: c4.c,v $
+ * 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 :-(
+ *
  * Revision 1.4  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
@@ -40,7 +46,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.4 $";
+static char *revision = "$Revision: 1.6 $";
 
 #undef CONFIG_C4_DEBUG
 #undef CONFIG_C4_POLLDEBUG
@@ -65,7 +71,7 @@
 
 /* ------------------------------------------------------------- */
 
-static int suppress_pollack = 0;
+int suppress_pollack = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 
@@ -770,45 +776,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);
 	}
@@ -816,25 +855,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;
 }
 
@@ -871,8 +900,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);
 
@@ -904,7 +940,7 @@
 
  	c4_reset(card);
 
-        for (i=0; i <= 4; i++) {
+        for (i=0; i < 4; i++) {
 		cinfo = &card->ctrlinfo[i];
 		if (cinfo->capi_ctrl)
 			di->detach_ctr(cinfo->capi_ctrl);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capi.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/isdn/avmb1/capi.c	Thu Mar 23 23:18:20 2000
@@ -1,11 +1,51 @@
 /*
- * $Id: capi.c,v 1.23 2000/02/26 01:00:53 keil Exp $
+ * $Id: capi.c,v 1.30 2000/03/19 12:31:36 calle Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capi.c,v $
+ * 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
  *
@@ -114,6 +154,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -126,52 +167,686 @@
 #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 <linux/devfs_fs_kernel.h>
-
 #include "capiutil.h"
 #include "capicmd.h"
-#include "capidev.h"
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#include "capifs.h"
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+#include <linux/slab.h>
+
+static char *revision = "$Revision: 1.30 $";
 
 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;
+	wait_queue_head_t recvwait;
+	wait_queue_head_t 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;
+	wait_queue_head_t 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 */
+
+static kmem_cache_t *capidev_cachep = 0; 
+static kmem_cache_t *capincci_cachep = 0; 
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+static kmem_cache_t *capiminor_cachep = 0; 
+static kmem_cache_t *capidh_cachep = 0; 
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
-/* -------- function called by lower level -------------------------- */
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- datahandles --------------------------------------------- */
+
+int capincci_add_ack(struct capiminor *mp, __u16 datahandle)
+{
+	struct datahandle_queue *n, **pp;
+
+	n = (struct datahandle_queue *)
+	kmem_cache_alloc(capidh_cachep, 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;
+			kmem_cache_free(capidh_cachep, 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;
+		kmem_cache_free(capidh_cachep, p);
+		mp->nack--;
+	}
+}
+
+
+/* -------- struct capiminor ---------------------------------------- */
+
+struct capiminor *capiminor_alloc(__u16 applid, __u32 ncci)
+{
+	struct capiminor *mp, **pp;
+        unsigned int minor = 0;
+
+	mp = (struct capiminor *)kmem_cache_alloc(capiminor_cachep, GFP_ATOMIC);
+	if (!mp) {
+		printk(KERN_ERR "capi: can't alloc capiminor\n");
+		return 0;
+	}
+	MOD_INC_USE_COUNT;
+#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);
+	init_waitqueue_head(&mp->recvwait);
+	init_waitqueue_head(&mp->sendwait);
+
+	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);
+			kmem_cache_free(capiminor_cachep, 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 *)kmem_cache_alloc(capincci_cachep, 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 */
+			kmem_cache_free(capincci_cachep, np);
+			if (*pp == 0) return;
+		} else {
+			pp = &(*pp)->next;
+		}
+	}
+}
 
-static void capi_signal(__u16 applid, __u32 minor)
+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 *)kmem_cache_alloc(capidev_cachep, 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);
+	init_waitqueue_head(&cdev->recvwait);
+	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;
+
+	kmem_cache_free(capidev_cachep, 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);
+		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)
@@ -182,29 +857,25 @@
 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;
@@ -213,20 +884,22 @@
 			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;
@@ -235,41 +908,34 @@
 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);
 
@@ -279,26 +945,26 @@
 		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;
 }
@@ -306,36 +972,28 @@
 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:
 		{
@@ -441,8 +1099,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,
@@ -452,112 +1108,770 @@
 			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)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-
-	if (minor >= CAPI_MAXMINOR)
-		return -ENXIO;
-
-	if (minor) {
-		if (capidevs[minor].is_open)
-			return -EEXIST;
-
-		capidevs[minor].is_open = 1;
-		skb_queue_head_init(&capidevs[minor].recv_queue);
-		MOD_INC_USE_COUNT;
-		capidevs[minor].nopen++;
+	if (file->private_data)
+		return -EEXIST;
 
-	} else {
-		capidevs[minor].is_open++;
-		MOD_INC_USE_COUNT;
-	}
+	if ((file->private_data = capidev_alloc(file)) == 0)
+		return -ENOMEM;
 
+	MOD_INC_USE_COUNT;
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capi_open %d\n", GET_USE_COUNT(THIS_MODULE));
+#endif
 	return 0;
 }
 
-static int
-capi_release(struct inode *inode, struct file *file)
+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];
+	struct capidev *cdev = (struct capidev *)file->private_data;
 
-	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--;
-	}
+	capincci_free(cdev, 0xffffffff);
+	capidev_free(cdev);
 
 	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 =
 {
-	llseek:		capi_llseek,
-	read:		capi_read,
-	write:		capi_write,
-	poll:		capi_poll,
-	ioctl:		capi_ioctl,
-	open:		capi_open,
-	release:	capi_release,
+	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)
+int capinc_raw_open(struct inode *inode, struct file *file)
 {
-        struct capidev *cp;
-	int i;
-	int len = 0;
-	off_t begin = 0;
+	struct capiminor *mp;
 
-	for (i=0; i < CAPI_MAXMINOR; i++) {
-		cp = &capidevs[i+1];
-		if (cp->nopen == 0) continue;
-		len += sprintf(page+len, "%d %lu %lu %lu %lu %lu\n",
-			i+1,
-			cp->nopen,
-			cp->nrecvctlpkt,
-			cp->nrecvdatapkt,
-			cp->nsentctlpkt,
-			cp->nsentdatapkt);
-		if (len+begin > off+count)
-			goto endloop;
-		if (len+begin < off) {
-			begin += len;
-			len = 0;
-		}
-	}
-endloop:
-	if (i >= CAPI_MAXMINOR)
-		*eof = 1;
+	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;
+}
+
+long long capinc_raw_llseek(struct file *file,
+			     long long offset, int origin)
+{
+	return -ESPIPE;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+	}
+
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capinc_raw_release %d\n", GET_USE_COUNT(THIS_MODULE));
+#endif
+	return 0;
+}
+
+struct file_operations capinc_raw_fops =
+{
+	capinc_raw_llseek,
+	capinc_raw_read,
+	capinc_raw_write,
+	NULL,			/* capi_readdir */
+	capinc_raw_poll,
+	capinc_raw_ioctl,
+	NULL,			/* capi_mmap */
+	capinc_raw_open,
+        NULL,                   /* capi_flush */
+	capinc_raw_release,
+	NULL,			/* capi_fsync */
+	NULL,			/* capi_fasync */
+};
+
+/* -------- 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);
+	init_waitqueue_head(&mp->recvwait);
+	init_waitqueue_head(&mp->sendwait);
+	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 (cdev == 0)
+		*eof = 1;
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	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);
@@ -573,6 +1887,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)
@@ -600,78 +1915,226 @@
 	}
     }
 }
+
 /* -------- init function and module interface ---------------------- */
 
+
+static void alloc_exit(void)
+{
+	if (capidev_cachep) {
+		(void)kmem_cache_destroy(capidev_cachep);
+		capidev_cachep = 0;
+	}
+	if (capincci_cachep) {
+		(void)kmem_cache_destroy(capincci_cachep);
+		capincci_cachep = 0;
+	}
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	if (capidh_cachep) {
+		(void)kmem_cache_destroy(capidh_cachep);
+		capidh_cachep = 0;
+	}
+	if (capiminor_cachep) {
+		(void)kmem_cache_destroy(capiminor_cachep);
+		capiminor_cachep = 0;
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+}
+
+static int alloc_init(void)
+{
+	capidev_cachep = kmem_cache_create("capi20_dev", 
+					 sizeof(struct capidev),
+					 0, 
+					 SLAB_HWCACHE_ALIGN,
+					 NULL, NULL);
+	if (!capidev_cachep) {
+		alloc_exit();
+		return -ENOMEM;
+	}
+
+	capincci_cachep = kmem_cache_create("capi20_ncci", 
+					 sizeof(struct capincci),
+					 0, 
+					 SLAB_HWCACHE_ALIGN,
+					 NULL, NULL);
+	if (!capincci_cachep) {
+		alloc_exit();
+		return -ENOMEM;
+	}
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	capidh_cachep = kmem_cache_create("capi20_dh", 
+					 sizeof(struct datahandle_queue),
+					 0, 
+					 SLAB_HWCACHE_ALIGN,
+					 NULL, NULL);
+	if (!capidh_cachep) {
+		alloc_exit();
+		return -ENOMEM;
+	}
+	capiminor_cachep = kmem_cache_create("capi20_minor", 
+					 sizeof(struct capiminor),
+					 0, 
+					 SLAB_HWCACHE_ALIGN,
+					 NULL, NULL);
+	if (!capiminor_cachep) {
+		alloc_exit();
+		return -ENOMEM;
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	return 0;
+}
+
+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)
 {
-	int j;
-	
-	memset(capidevs, 0, sizeof(capidevs));
-	for ( j = 0; j < CAPI_MAXMINOR+1; j++ ) {
-		init_waitqueue_head(&capidevs[j].recv_wait);
-	}
+	char *p;
+
+	MOD_INC_USE_COUNT;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 2);
+		p = strchr(rev, '$');
+		*(p-1) = 0;
+	} else
+		strcpy(rev, "???");
 
 	if (devfs_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 (devfs_register_chrdev(capi_rawmajor, "capi/r%d", &capinc_raw_fops)) {
+		devfs_unregister_chrdev(capi_major, "capi20");
+		printk(KERN_ERR "capi20: unable to get major %d\n", capi_rawmajor);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+        devfs_register_series (NULL, "capi/r%u", CAPINC_NR_PORTS,
+			      DEVFS_FL_DEFAULT,
+                              capi_rawmajor, 0,
+                              S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+                              &capinc_raw_fops, NULL);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 	devfs_register (NULL, "isdn/capi20", 0, DEVFS_FL_DEFAULT,
 			capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
 			&capi_fops, NULL);
-	devfs_register_series (NULL, "isdn/capi20.0%u", 10, DEVFS_FL_DEFAULT,
-			       capi_major, 1,
-			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
-			       &capi_fops, NULL);
-	devfs_register_series (NULL, "isdn/capi20.1%u", 10, DEVFS_FL_DEFAULT,
-			       capi_major, 11,
-			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
-			       &capi_fops, NULL);
 	printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major);
 
 	if ((capifuncs = attach_capi_interface(&cuser)) == 0) {
+
+		MOD_DEC_USE_COUNT;
 		devfs_unregister_chrdev(capi_major, "capi20");
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+		devfs_unregister_chrdev(capi_rawmajor, "capi/r%d");
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 		devfs_unregister(devfs_find_handle(NULL, "capi20", 0,
 						   capi_major, 0,
 						   DEVFS_SPECIAL_CHR, 0));
-		for (j = 0; j < 10; j++) {
-			char devname[32];
+		return -EIO;
+	}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	if (capinc_tty_init() < 0) {
+		(void) detach_capi_interface(&cuser);
+		devfs_unregister_chrdev(capi_major, "capi20");
+		devfs_unregister_chrdev(capi_rawmajor, "capi/r%d");
+		MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
-			sprintf(devname, "isdn/capi20.0%i", j);
-			devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, j + 1, DEVFS_SPECIAL_CHR, 0));
-			sprintf (devname, "isdn/capi20.1%i", j);
-			devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, j + 11, DEVFS_SPECIAL_CHR, 0));
+	if (alloc_init() < 0) {
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+		unsigned int j;
+		devfs_unregister_chrdev(capi_rawmajor, "capi/r%d");
+		for (j = 0; j < CAPINC_NR_PORTS; j++) {
+			char devname[32];
+			sprintf(devname, "capi/r%u", j);
+			devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_rawmajor, j, DEVFS_SPECIAL_CHR, 0));
 		}
-		return -EIO;
+		capinc_tty_exit();
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+		(void) detach_capi_interface(&cuser);
+		devfs_unregister_chrdev(capi_major, "capi20");
+		devfs_unregister(devfs_find_handle(NULL, "capi20", 0,
+						   capi_major, 0,
+						   DEVFS_SPECIAL_CHR, 0));
+		MOD_DEC_USE_COUNT;
+		return -ENOMEM;
 	}
+
 	(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)
 {
-	int i;
-	char devname[32];
-
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	unsigned int j;
+#endif
+	alloc_exit();
 	(void)proc_exit();
+
 	devfs_unregister_chrdev(capi_major, "capi20");
 	devfs_unregister(devfs_find_handle(NULL, "isdn/capi20", 0, capi_major, 0, DEVFS_SPECIAL_CHR, 0));
-	for (i = 0; i < 10; i++) {
-		sprintf (devname, "isdn/capi20.0%i", i);
-		devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 1, DEVFS_SPECIAL_CHR, 0));
-		sprintf (devname, "isdn/capi20.1%i", i);
-		devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 11, DEVFS_SPECIAL_CHR, 0));
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	capinc_tty_exit();
+	devfs_unregister_chrdev(capi_rawmajor, "capi/r%d");
+	for (j = 0; j < CAPINC_NR_PORTS; j++) {
+		char devname[32];
+		sprintf(devname, "capi/r%u", j);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_rawmajor, j, DEVFS_SPECIAL_CHR, 0));
 	}
+#endif
 	(void) detach_capi_interface(&cuser);
+	printk(KERN_NOTICE "capi: Rev%s: unloaded\n", rev);
 }
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capicmd.h linux/drivers/isdn/avmb1/capicmd.h
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capicmd.h	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/avmb1/capicmd.h	Thu Mar 23 08:38:57 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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capidev.h linux/drivers/isdn/avmb1/capidev.h
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capidev.h	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/capidev.h	Thu Mar 23 08:38:57 2000
@@ -1,11 +1,22 @@
 /*
- * $Id: capidev.h,v 1.4 1999/07/01 15:26:32 calle Exp $
+ * $Id: capidev.h,v 1.5 2000/03/03 15:50:42 calle Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidev.h,v $
+ * Revision 1.5  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.4  1999/07/01 15:26:32  calle
  * complete new version (I love it):
  * + new hardware independed "capi_driver" interface that will make it easy to:
@@ -40,18 +51,18 @@
  */
 
 struct capidev {
-	int is_open;
-	int is_registered;
-	__u16 applid;
+	struct capidev *next;
+	struct file    *file;
+	__u16		applid;
+	__u16		errcode;
+	unsigned int    minor;
+
 	struct sk_buff_head recv_queue;
 	wait_queue_head_t 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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capidrv.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/avmb1/capidrv.c	Thu Mar 23 08:38:57 2000
@@ -1,11 +1,22 @@
 /*
- * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $
+ * $Id: capidrv.c,v 1.30 2000/03/03 15:50:42 calle Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * 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,7 +182,7 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.29 $";
+static char *revision = "$Revision: 1.30 $";
 int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
@@ -440,25 +451,33 @@
 static inline capidrv_contr *findcontrbydriverid(int driverid)
 {
 	capidrv_contr *p = global.contr_list;
+    	long flags;
 
+        save_flags(flags);
+        cli();
 	while (p) {
 		if (p->myid == driverid)
-			return p;
+			break;
 		p = p->next;
 	}
-	return (capidrv_contr *) 0;
+	restore_flags(flags);
+	return p;
 }
 
 static capidrv_contr *findcontrbynumber(__u32 contr)
 {
 	capidrv_contr *p = global.contr_list;
+	long flags;
 
+	save_flags(flags);
+	cli();
 	while (p) {
 		if (p->contrnr == contr)
-			return p;
+			break;
 		p = p->next;
 	}
-	return (capidrv_contr *) 0;
+	restore_flags(flags);
+	return p;
 }
 
 
@@ -1501,7 +1520,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;
 
@@ -2218,14 +2237,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 +2261,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;
@@ -2258,29 +2282,35 @@
 	    ISDN_FEATURE_P_UNKNOWN;
 	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;
 
+	save_flags(flags);
+	cli();
+	card->next = global.contr_list;
+	global.contr_list = card;
+	global.ncontr++;
+	restore_flags(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 */
@@ -2304,30 +2334,41 @@
 {
 	capidrv_contr **pp, *card;
 	isdn_ctrl cmd;
+	long flags;
 	int i;
 
+	save_flags(flags);
+	cli();
 	for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
 		if ((*pp)->contrnr == contr)
 			break;
 	}
 	if (!*pp) {
+		restore_flags(flags);
 		printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr);
 		return -1;
 	}
 	card = *pp;
+	*pp = (*pp)->next;
+	global.ncontr--;
+	restore_flags(flags);
 
 	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--;
-
 	for (i = 0; i < card->nbchan; i++) {
+
+		cmd.command = ISDN_STAT_DISCH;
+		cmd.driver = card->myid;
+		cmd.arg = i;
+	        cmd.parm.num[0] = 0;
+		card->interface.statcallb(&cmd);
+
 		if (card->bchans[i].nccip)
 			free_ncci(card, card->bchans[i].nccip);
 		if (card->bchans[i].plcip)
@@ -2338,10 +2379,16 @@
 	kfree(card->bchans);
 	del_timer(&card->listentimer);
 
-	printk(KERN_INFO "%s: now down.\n", card->name);
+	cmd.command = ISDN_STAT_UNLOAD;
+	cmd.driver = card->myid;
+	card->interface.statcallb(&cmd);
 
 	kfree(card);
 
+	printk(KERN_INFO "%s: now down.\n", card->name);
+
+	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
@@ -2438,10 +2485,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 +2507,7 @@
 	errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
 	if (errcode) {
 		detach_capi_interface(&cuser);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -2463,6 +2515,7 @@
 	if (errcode != CAPI_NOERROR) {
 		(void) (*capifuncs->capi_release) (global.appid);
 		detach_capi_interface(&cuser);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
@@ -2477,6 +2530,9 @@
 	}
 	proc_init();
 
+	printk(KERN_NOTICE "capidrv: Rev%s: loaded\n", rev);
+	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
@@ -2484,6 +2540,7 @@
 void cleanup_module(void)
 {
 	capidrv_contr *card, *next;
+	long flags;
 	char rev[10];
 	char *p;
 
@@ -2498,8 +2555,15 @@
 	for (card = global.contr_list; card; card = next) {
 		next = card->next;
 		disable_dchannel_trace(card);
+	}
+
+    	save_flags(flags);
+    	cli();
+	for (card = global.contr_list; card; card = next) {
+		next = card->next;
 		capidrv_delcontr(card->contrnr);
 	}
+	restore_flags(flags);
 
 	(void) (*capifuncs->capi_release) (global.appid);
 	detach_capi_interface(&cuser);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capifs.c linux/drivers/isdn/avmb1/capifs.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capifs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capifs.c	Thu Mar 23 23:18:20 2000
@@ -0,0 +1,595 @@
+/*
+ * $Id: capifs.c,v 1.5 2000/03/13 17:49:52 calle 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.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/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/ctype.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Carsten Paeth <calle@calle.de>");
+
+static char *revision = "$Revision: 1.5 $";
+
+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 = {
+	read:		generic_read_dir,
+	readdir:	capifs_root_readdir,
+};
+
+struct inode_operations capifs_root_inode_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, 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);
+
+	/*
+	 * 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_fop = &capifs_root_operations;
+		inode->i_nlink = 2;
+		return;
+	} 
+
+	/* need dummy inode operations .... */
+	inode->i_op = &capifs_inode_operations;
+
+	ino -= 2;
+	if ( ino >= sbi->max_ncci )
+		return;		/* Bogus */
+	
+	init_special_inode(inode, S_IFCHR, 0);
+
+	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->used = 0;
+				inode->i_nlink--;
+				iput(inode);
+				break;
+			}
+		}
+	}
+}
+
+int __init capifs_init(void)
+{
+	char rev[10];
+	char *p;
+	int err;
+
+	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)
+		return err;
+#ifdef MODULE
+        printk(KERN_NOTICE "capifs: Rev%s: loaded\n", rev);
+#else
+	printk(KERN_NOTICE "capifs: Rev%s: started\n", rev);
+#endif
+	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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capifs.h linux/drivers/isdn/avmb1/capifs.h
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capifs.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capifs.h	Thu Mar 23 08:38:57 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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capiutil.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/capiutil.c	Thu Mar 23 08:38:57 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: capiutil.c,v 1.10 1999/08/31 11:19:54 paul Exp $
+ * $Id: capiutil.c,v 1.11 2000/03/03 15:50:42 calle Exp $
  *
  * CAPI 2.0 convert capi message to capi message struct
  *
@@ -7,6 +7,17 @@
  * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capiutil.c,v $
+ * Revision 1.11  2000/03/03 15:50:42  calle
+ * - kernel CAPI:
+ *   - Changed parameter "param" in capi_signal from __u32 to void *.
+ *   - rewrote notifier handling in kcapi.c
+ *   - new notifier NCCI_UP and NCCI_DOWN
+ * - User CAPI:
+ *   - /dev/capi20 is now a cloning device.
+ *   - middleware extentions prepared.
+ * - capidrv.c
+ *   - locking of list operations and module count updates.
+ *
  * Revision 1.10  1999/08/31 11:19:54  paul
  * various spelling corrections (new checksums may be needed, Karsten!)
  *
@@ -794,7 +805,7 @@
     /*15 */ "Class",
     /*16 */ "ConnectedNumber",
     /*17 */ "ConnectedSubaddress",
-    /*18 */ "Data",
+    /*18 */ "Data32",
     /*19 */ "DataHandle",
     /*1a */ "DataLength",
     /*1b */ "FacilityConfirmationParameter",
@@ -892,13 +903,7 @@
 			cmsg->l += 2;
 			break;
 		case _CDWORD:
-			if (strcmp(NAME, "Data") == 0) {
-				bufprint("%-*s = ", slen, NAME);
-				printstructlen((__u8 *) * (__u32 *) (cmsg->m + cmsg->l),
-					       *(__u16 *) (cmsg->m + cmsg->l + sizeof(__u32)));
-				bufprint("\n");
-			} else
-				bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l));
+			bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l));
 			cmsg->l += 4;
 			break;
 		case _CSTRUCT:
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/capiutil.h linux/drivers/isdn/avmb1/capiutil.h
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/capiutil.h	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/capiutil.h	Thu Mar 23 08:38:57 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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/avmb1/kcapi.c linux/drivers/isdn/avmb1/kcapi.c
--- v2.3.99-pre2/linux/drivers/isdn/avmb1/kcapi.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/avmb1/kcapi.c	Thu Mar 23 08:38:57 2000
@@ -1,11 +1,22 @@
 /*
- * $Id: kcapi.c,v 1.12 2000/01/28 16:45:39 calle Exp $
+ * $Id: kcapi.c,v 1.13 2000/03/03 15:50:42 calle Exp $
  * 
  * Kernel CAPI 2.0 Module
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: kcapi.c,v $
+ * 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 +89,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 +98,7 @@
 #include <linux/b1lli.h>
 #endif
 
-static char *revision = "$Revision: 1.12 $";
+static char *revision = "$Revision: 1.13 $";
 
 /* ------------------------------------------------------------- */
 
@@ -125,8 +137,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 +149,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 +180,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 +324,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 +338,7 @@
 		}
 	}
 endloop:
+	spin_unlock(&drivers_lock);
 	if (!driver)
 		*eof = 1;
 	if (off >= len+begin)
@@ -336,6 +358,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 +369,7 @@
 		}
 	}
 endloop:
+	spin_unlock(&capi_users_lock);
 	if (cp == 0)
 		*eof = 1;
 	if (off >= len+begin)
@@ -510,6 +534,161 @@
     }
 }
 
+/* -------- Notifier handling --------------------------------- */
+
+static struct capi_notifier_list{
+	struct capi_notifier *head;
+	struct capi_notifier *tail;
+} notifier_list;
+
+static inline void notify_enqueue(struct capi_notifier *np)
+{
+	struct capi_notifier_list *q = &notifier_list;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (q->tail) {
+		q->tail->next = np;
+		q->tail = np;
+	} else {
+		q->head = q->tail = np;
+	}
+	restore_flags(flags);
+}
+
+static inline struct capi_notifier *notify_dequeue(void)
+{
+	struct capi_notifier_list *q = &notifier_list;
+	struct capi_notifier *np = 0;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (q->head) {
+		np = q->head;
+		if ((q->head = np->next) == 0)
+ 			q->tail = 0;
+		np->next = 0;
+	}
+	restore_flags(flags);
+	return np;
+}
+
+static int notify_push(unsigned int cmd, __u32 controller,
+				__u16 applid, __u32 ncci)
+{
+	struct capi_notifier *np;
+
+	np = (struct capi_notifier *)kmalloc(sizeof(struct capi_notifier), GFP_ATOMIC);
+	if (!np)
+		return -1;
+	memset(np, 0, sizeof(struct capi_notifier));
+	np->cmd = cmd;
+	np->controller = controller;
+	np->applid = applid;
+	np->ncci = ncci;
+	notify_enqueue(np);
+	MOD_INC_USE_COUNT;
+	queue_task(&tq_state_notify, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	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 +796,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 +814,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 +915,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 +927,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 +956,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 +964,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)
@@ -952,9 +1098,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 +1123,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 +1131,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 +1193,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 +1202,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 +1282,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 +1348,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 +1428,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 +1650,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 +1673,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;
 }
@@ -1623,7 +1790,6 @@
 		strcpy(rev, "1.0");
 	}
 
-	schedule(); /* execute queued tasks .... */
         proc_capi_exit();
 	printk(KERN_NOTICE "CAPI-driver Rev%s: unloaded\n", rev);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/divert/divert_procfs.c linux/drivers/isdn/divert/divert_procfs.c
--- v2.3.99-pre2/linux/drivers/isdn/divert/divert_procfs.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/isdn/divert/divert_procfs.c	Thu Mar 23 08:38:57 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: divert_procfs.c,v 1.6 2000/02/14 19:23:03 werner Exp $
+ * $Id: divert_procfs.c,v 1.8 2000/03/03 16:37:11 kai Exp $
  *
  * Filesystem handling for the diversion supplementary services.
  *
@@ -20,6 +20,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: divert_procfs.c,v $
+ * Revision 1.8  2000/03/03 16:37:11  kai
+ * incorporated some cosmetic changes from the official kernel tree back
+ * into CVS
+ *
+ * Revision 1.7  2000/03/02 00:11:06  werner
+ *
+ * Changes related to procfs for 2.3.48
+ *
  * Revision 1.6  2000/02/14 19:23:03  werner
  *
  * Changed handling of proc filesystem tables to a more portable version
@@ -302,13 +310,13 @@
 
 static struct file_operations isdn_fops =
 {
-	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,
+	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,                                      
 };
 
 /****************************/
@@ -336,7 +344,7 @@
 		remove_proc_entry("isdn", proc_net);
 		return (-1);
 	}
-	isdn_divert_entry->proc_fops = &isdn_fops;
+	isdn_divert_entry->proc_fops = &isdn_fops; 
 #endif	/* CONFIG_PROC_FS */
 
 	return (0);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/eicon/eicon_idi.c linux/drivers/isdn/eicon/eicon_idi.c
--- v2.3.99-pre2/linux/drivers/isdn/eicon/eicon_idi.c	Sat Feb 26 22:31:45 2000
+++ linux/drivers/isdn/eicon/eicon_idi.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.c,v 1.31 2000/02/22 16:26:40 armin Exp $
+/* $Id: eicon_idi.c,v 1.33 2000/03/06 15:45:17 armin Exp $
  *
  * ISDN lowlevel-module for Eicon active cards.
  *        IDI interface 
@@ -26,6 +26,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_idi.c,v $
+ * Revision 1.33  2000/03/06 15:45:17  armin
+ * Fixed incomplete number handling with BRI PtP connection.
+ *
+ * Revision 1.32  2000/03/04 17:04:21  armin
+ * Fix of statemachine, B-connect before D-connect,
+ * thanks to Helmut Adams <adams@ipcon.de>
+ * Minor change in send-data packet handling.
+ *
  * Revision 1.31  2000/02/22 16:26:40  armin
  * Fixed membase error message.
  * Fixed missing log buffer struct.
@@ -156,7 +164,7 @@
 
 #undef EICON_FULL_SERVICE_OKTETT
 
-char *eicon_idi_revision = "$Revision: 1.31 $";
+char *eicon_idi_revision = "$Revision: 1.33 $";
 
 eicon_manifbuf *manbuf;
 
@@ -2515,15 +2523,7 @@
 						break;
 					case 3: /* incomplete number */
 						eicon_log(ccard, 8, "idi_req: Ch%d: Incomplete Number\n", chan->No);
-					        switch(ccard->type) {
-					                case EICON_CTYPE_MAESTRAP:
-					                case EICON_CTYPE_S2M:
-								/* TODO (other protocols) */
-								chan->fsm_state = EICON_STATE_ICALLW;
-								break;
-							default:
-								idi_do_req(ccard, chan, HANGUP, 0);
-						}
+						chan->fsm_state = EICON_STATE_ICALLW;
 						break;
 				}
 				break;
@@ -2560,8 +2560,10 @@
 							/* On most incoming calls we use automatic connect */
 							/* idi_do_req(ccard, chan, IDI_N_CONNECT, 1); */
 					}
-				} else
-					idi_hangup(ccard, chan);
+				} else {
+					if (chan->fsm_state != EICON_STATE_ACTIVE)
+						idi_hangup(ccard, chan);
+				}
 				break;
 			case CALL_CON:
 				eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Con\n", chan->No);
@@ -3012,11 +3014,13 @@
 
 	        reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
 		if (((len - offset) > 270) &&
+			(chan->l2prot != ISDN_PROTO_L2_MODEM) &&
+			(chan->l2prot != ISDN_PROTO_L2_FAX) &&
 			(chan->l2prot != ISDN_PROTO_L2_TRANS)) {
 		        reqbuf->Req = IDI_N_MDATA;
 		} else {
 		        reqbuf->Req = IDI_N_DATA;
-			if (ack) reqbuf->Req |= N_D_BIT;
+			/* if (ack) reqbuf->Req |= N_D_BIT; */
 		}	
         	reqbuf->ReqCh = chan->e.IndCh;
 	        reqbuf->ReqId = 1;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.3.99-pre2/linux/drivers/isdn/hisax/callc.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/hisax/callc.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $
+/* $Id: callc.c,v 2.41 2000/03/17 07:07:42 kai Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,9 @@
  *              Fritz Elfert
  *
  * $Log: callc.c,v $
+ * Revision 2.41  2000/03/17 07:07:42  kai
+ * fixed oops when dialing out without l3 protocol selected
+ *
  * Revision 2.40  1999/12/19 12:59:56  keil
  * fix leased line handling
  * and cosmetics
@@ -167,7 +170,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.41 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -349,6 +352,8 @@
 {
 	isdn_ctrl ic;
 
+	if (!chanp->proc)
+		return;
 	if (chanp->proc->para.cause == NO_CAUSE)
 		return;
 	ic.driver = chanp->cs->myid;
@@ -641,7 +646,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 +662,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 +695,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
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.3.99-pre2/linux/drivers/isdn/hisax/l3dss1.c	Sat Feb 26 22:31:46 2000
+++ linux/drivers/isdn/hisax/l3dss1.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: l3dss1.c,v 2.23 2000/02/26 01:38:14 keil Exp $
+/* $Id: l3dss1.c,v 2.24 2000/03/19 15:26:35 kai Exp $
 
  * EURO/DSS1 D-channel protocol
  *
@@ -13,6 +13,9 @@
  *              Fritz Elfert
  *
  * $Log: l3dss1.c,v $
+ * Revision 2.24  2000/03/19 15:26:35  kai
+ * changed keypad to use specified bearer, instead of always a-law
+ *
  * Revision 2.23  2000/02/26 01:38:14  keil
  * Fixes for V.110 encoding LLC from Jens Jakobsen
  *
@@ -107,7 +110,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.24 $";
 
 #define EXT_BEARER_CAPS 1
 
@@ -1313,39 +1316,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 +1389,7 @@
 			sp++;
 	}
 	if (*msn) {
-		*p++ = 0x6c;
+		*p++ = IE_CALLING_PN;
 		*p++ = strlen(msn) + (screen ? 2 : 1);
 		/* Classify as AnyPref. */
 		if (screen) {
@@ -1407,7 +1402,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 +1420,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 +1448,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 +1457,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 +1466,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;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc
--- v2.3.99-pre2/linux/drivers/isdn/hisax/md5sums.asc	Sat Feb 26 22:31:46 2000
+++ linux/drivers/isdn/hisax/md5sums.asc	Thu Mar 23 08:38:57 2000
@@ -11,9 +11,9 @@
 bb51bd223040b511c18f091da5ab6456  isdnl2.c
 b7aa7f97b2374967a4aca7c52991142c  isdnl3.c
 a23fbf8879c1432b04640b8b04bdf419  tei.c
-ce248e56c2e1326012d0b25f92bbf99b  callc.c
+838791b14269ec94c74ba4ae89c022e6  callc.c
 bf9605b36429898f7be6630034e83230  cert.c
-6ce0a184127be1a44747e2017ed24ad9  l3dss1.c
+a30e6253837739f6f54d9dadcd42d9f2  l3dss1.c
 a3a570781f828b6d59e6b231653133de  l3_1tr6.c
 4aeba32c4c3480d2a6b9af34600b974f  elsa.c
 a296edc459b508bf0346c3132815a4db  diva.c
@@ -23,9 +23,9 @@
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBOLcvXDpxHvX/mS9tAQGPWAP9Fg14RXcAwjCy4VeFoDBMOFpxllvG7xZR
-HQKENCYIzXKPb6I/IBUv3+BhL8Lnhjw8a2DXz6c6u+0nmUIFnzyt1BfzT70P9rKd
-BBN7f1KdIiQEmv0fZwd79Rz5PYvRDbY520bNTJZhorwqGI/qc3gGgHVtSR8OHhuS
-ZMQ1pb9W6jE=
-=CA5N
+iQCVAwUBONlcejpxHvX/mS9tAQFWYAP/WmxgwQ7W6gVnsMkUlwzE1TKGWVJdGUTC
+7WrKEtdweuzW3/7WEzjBoZgUEcpugJYK3JENby1xjh3yIHfws4HqLme1yXAmkYUK
+6LmW96HC2g4wj7PH0hvLnzTOtXDGTppU7KJibbB+ziyhBbs6SGXOD4zHrCWmT9ld
+CURhfNgftIs=
+=AA+Q
 -----END PGP SIGNATURE-----
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c
--- v2.3.99-pre2/linux/drivers/isdn/hisax/q931.c	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/hisax/q931.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: q931.c,v 1.7 1998/11/15 23:55:17 keil Exp $
+/* $Id: q931.c,v 1.8 2000/03/21 23:53:22 kai Exp $
 
  * q931.c       code to decode ITU Q.931 call control messages
  *
@@ -14,6 +14,9 @@
  *
  *
  * $Log: q931.c,v $
+ * Revision 1.8  2000/03/21 23:53:22  kai
+ * fix backwards compatibility
+ *
  * Revision 1.7  1998/11/15 23:55:17  keil
  * changes from 2.0
  *
@@ -201,29 +204,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)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hisax/w6692.c linux/drivers/isdn/hisax/w6692.c
--- v2.3.99-pre2/linux/drivers/isdn/hisax/w6692.c	Sat Feb 26 22:31:46 2000
+++ linux/drivers/isdn/hisax/w6692.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: w6692.c,v 1.2 2000/02/26 00:35:13 keil Exp $
+/* $Id: w6692.c,v 1.4 2000/03/16 23:24:11 werner Exp $
 
  * w6692.c   Winbond W6692 specific routines
  *
@@ -8,6 +8,14 @@
  *              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
  *
@@ -50,7 +58,7 @@
 
 extern const char *CardType[];
 
-const char *w6692_revision = "$Revision: 1.2 $";
+const char *w6692_revision = "$Revision: 1.4 $";
 
 #define DBUSY_TIMER_VALUE 80
 
@@ -317,10 +325,13 @@
 {
 	u_char val;
 	u_char r;
-	struct BCState *bcs = cs->bcs + bchan;
+	struct BCState *bcs = cs->bcs;
 	struct sk_buff *skb;
 	int count;
 
+	if (bcs->channel != bchan)
+	  bcs++; /* hardware bchan must match ! */
+
 	val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR);
 	debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val);
 
@@ -724,7 +735,9 @@
 W6692Bmode(struct BCState *bcs, int mode, int bc)
 {
 	struct IsdnCardState *cs = bcs->cs;
-	int bchan = bcs->hw.w6692.bchan;
+	int bchan = bc;
+
+	bcs->hw.w6692.bchan = bc;
 
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "w6692 %c mode %d ichan %d",
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hysdn/hysdn_procconf.c linux/drivers/isdn/hysdn/hysdn_procconf.c
--- v2.3.99-pre2/linux/drivers/isdn/hysdn/hysdn_procconf.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/isdn/hysdn/hysdn_procconf.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: hysdn_procconf.c,v 1.2 2000/02/14 19:23:03 werner Exp $
+/* $Id: hysdn_procconf.c,v 1.4 2000/03/03 16:37:12 kai Exp $
 
  * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
  * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
@@ -20,6 +20,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hysdn_procconf.c,v $
+ * Revision 1.4  2000/03/03 16:37:12  kai
+ * incorporated some cosmetic changes from the official kernel tree back
+ * into CVS
+ *
+ * Revision 1.3  2000/03/02 00:11:07  werner
+ *
+ * Changes related to procfs for 2.3.48
+ *
  * Revision 1.2  2000/02/14 19:23:03  werner
  *
  * Changed handling of proc filesystem tables to a more portable version
@@ -40,7 +48,7 @@
 
 #include "hysdn_defs.h"
 
-static char *hysdn_procconf_revision = "$Revision: 1.2 $";
+static char *hysdn_procconf_revision = "$Revision: 1.4 $";
 
 #define INFO_OUT_LEN 80		/* length of info line including lf */
 
@@ -403,11 +411,11 @@
 /******************************************************/
 static struct file_operations conf_fops =
 {
-	llseek:		hysdn_dummy_lseek,
-	read:		hysdn_conf_read,
-	write:		hysdn_conf_write,
-	open:		hysdn_conf_open,
-	release:	hysdn_conf_close,
+	llseek:         hysdn_dummy_lseek,
+	read:           hysdn_conf_read,
+	write:          hysdn_conf_write,
+	open:           hysdn_conf_open,
+	release:        hysdn_conf_close,                                       
 };
 
 /*****************************/
@@ -438,7 +446,6 @@
 		if ((card->procconf = (void *) create_proc_entry(conf_name,
 					     S_IFREG | S_IRUGO | S_IWUSR,
 					    hysdn_proc_entry)) != NULL) {
-
 			((struct proc_dir_entry *) card->procconf)->proc_fops = &conf_fops;
 			hysdn_proclog_init(card);	/* init the log file entry */
 		}
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/hysdn/hysdn_proclog.c linux/drivers/isdn/hysdn/hysdn_proclog.c
--- v2.3.99-pre2/linux/drivers/isdn/hysdn/hysdn_proclog.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/isdn/hysdn/hysdn_proclog.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: hysdn_proclog.c,v 1.2 2000/02/14 19:23:03 werner Exp $
+/* $Id: hysdn_proclog.c,v 1.4 2000/03/03 16:37:12 kai Exp $
 
  * Linux driver for HYSDN cards, /proc/net filesystem log functions.
  * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
@@ -20,6 +20,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hysdn_proclog.c,v $
+ * Revision 1.4  2000/03/03 16:37:12  kai
+ * incorporated some cosmetic changes from the official kernel tree back
+ * into CVS
+ *
+ * Revision 1.3  2000/03/02 00:11:07  werner
+ *
+ * Changes related to procfs for 2.3.48
+ *
  * Revision 1.2  2000/02/14 19:23:03  werner
  *
  * Changed handling of proc filesystem tables to a more portable version
@@ -40,7 +48,7 @@
 
 #include "hysdn_defs.h"
 
-static char *hysdn_proclog_revision = "$Revision: 1.2 $";
+static char *hysdn_proclog_revision = "$Revision: 1.4 $";
 
 /* the proc subdir for the interface is defined in the procconf module */
 extern struct proc_dir_entry *hysdn_proc_entry;
@@ -420,14 +428,15 @@
 /**************************************************/
 static struct file_operations log_fops =
 {
-	llseek:		hysdn_dummy_lseek,
-	read:		hysdn_log_read,
-	write:		hysdn_log_write,
-	poll:		hysdn_log_poll,
-	open:		hysdn_log_open,
-	release:	hysdn_log_close,
+	llseek:         hysdn_dummy_lseek,
+	read:           hysdn_log_read,
+	write:          hysdn_log_write,
+	poll:           hysdn_log_poll,
+	open:           hysdn_log_open,
+	release:        hysdn_log_close,                                        
 };
 
+
 /***********************************************************************************/
 /* hysdn_proclog_init is called when the module is loaded after creating the cards */
 /* conf files.                                                                     */
@@ -441,10 +450,9 @@
 
 	if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
 		memset(pd, 0, sizeof(struct procdata));
-
 		sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
 		if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL)
-			pd->log->proc_fops = &log_fops;	/* set new operations table */
+		        pd->log->proc_fops = &log_fops; 
 
 		init_waitqueue_head(&(pd->rd_queue));
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- v2.3.99-pre2/linux/drivers/isdn/isdn_audio.c	Thu Aug 26 13:05:37 1999
+++ linux/drivers/isdn/isdn_audio.c	Wed Mar 22 22:23:55 2000
@@ -292,38 +292,25 @@
 	{'*', '0', '#', 'D'}
 };
 
-
-/*
- * egcs 2.95 complain about invalid asm statement:
- * "fixed or forbidden register 2 (cx) was spilled for class CREG."
- */
-#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) && defined(__GNUC__)
-#if __GNUC__ == 2 && __GNUC_MINOR__ < 95
-#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
-#endif
-#endif
-
-#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
 static inline void
 isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
 {
-	__asm__("cld\n"
+#ifdef __i386__
+	unsigned long d0, d1, d2, d3;
+	__asm__ __volatile__(
+		"cld\n"
 		"1:\tlodsb\n\t"
 		"xlatb\n\t"
 		"stosb\n\t"
 		"loop 1b\n\t"
-      : :  "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff)
-      :        "bx", "cx", "di", "si", "ax");
-}
-
+	:	"=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3)
+	:	"0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
+	:	"memory", "ax");
 #else
-static inline void
-isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
-{
 	while (n--)
 		*buff++ = table[*(unsigned char *)buff];
-}
 #endif
+}
 
 void
 isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.3.99-pre2/linux/drivers/isdn/isdn_common.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/isdn/isdn_common.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.99 2000/02/26 01:00:52 keil Exp $
+/* $Id: isdn_common.c,v 1.100 2000/03/03 16:37:11 kai Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.100  2000/03/03 16:37:11  kai
+ * incorporated some cosmetic changes from the official kernel tree back
+ * into CVS
+ *
  * Revision 1.99  2000/02/26 01:00:52  keil
  * changes from 2.3.47
  *
@@ -449,7 +453,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.99 $";
+static char *isdn_revision = "$Revision: 1.100 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_concap.c linux/drivers/isdn/isdn_concap.c
--- v2.3.99-pre2/linux/drivers/isdn/isdn_concap.c	Thu Aug 26 13:05:37 1999
+++ linux/drivers/isdn/isdn_concap.c	Thu Mar 23 08:38:57 2000
@@ -1,10 +1,13 @@
-/* $Id: isdn_concap.c,v 1.6 1999/08/22 20:26:01 calle Exp $
+/* $Id: isdn_concap.c,v 1.7 2000/03/21 23:53:22 kai Exp $
  
  * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
  * stuff goes here. Stuff that depends only on the concap protocol goes to
  * another -- protocol specific -- source file.
  *
  * $Log: isdn_concap.c,v $
+ * Revision 1.7  2000/03/21 23:53:22  kai
+ * fix backwards compatibility
+ *
  * Revision 1.6  1999/08/22 20:26:01  calle
  * backported changes from kernel 2.3.14:
  * - several #include "config.h" gone, others come.
@@ -58,15 +61,20 @@
 
 int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
 {
-	int tmp;
 	struct net_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);
+	spin_unlock_bh(&lp->xmit_lock);
+	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0);
+	return 0;
 }
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.3.99-pre2/linux/drivers/isdn/isdn_net.c	Sat Feb 26 22:31:46 2000
+++ linux/drivers/isdn/isdn_net.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.110 2000/02/26 01:00:53 keil Exp $
+/* $Id: isdn_net.c,v 1.122 2000/03/20 22:37:46 detabc Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -21,6 +21,50 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.122  2000/03/20 22:37:46  detabc
+ * modify abc-extension to work together with the new LL.
+ * remove abc frame-counter (is obsolete now).
+ * use the new lp->super_tx_queue for internal queueing (bsd-rawip-compress).
+ * modify isdn_net_xmit() and isdn_net_write_super().
+ * -- Kai, please have a look to this two function's. Thank's.
+ *
+ * Revision 1.121  2000/03/19 15:27:53  kai
+ * no known bugs left...
+ *
+ * Revision 1.120  2000/03/18 16:20:25  kai
+ * cosmetics / renaming
+ *
+ * Revision 1.118  2000/03/17 18:20:46  kai
+ * moved to frame_cnt based flow control
+ * some races still need to be fixed
+ *
+ * Revision 1.117  2000/03/17 17:01:00  kai
+ * cleanup
+ *
+ * Revision 1.116  2000/03/17 16:22:55  kai
+ * we keep track of outstanding packets (given to HL, but not confirmed yet)
+ * now, but we don't use it for flow control yet.
+ *
+ * Revision 1.115  2000/03/17 12:49:42  kai
+ * calling statcallb with ISDN_STAT_BSENT in hard-IRQ context is now
+ * officially allowed. writebuf_skb() will never be called in hard-IRQ context
+ * anymore.
+ *
+ * Revision 1.114  2000/03/16 16:37:41  kai
+ * Allow phone numbers starting with "*" as outgoing numbers for
+ * networking interface. Some PBX's need this to allow dialing internal
+ * numbers (mine, for example ;-)
+ *
+ * Revision 1.113  2000/03/16 15:46:37  kai
+ * a little bugfix and cosmetic changes
+ *
+ * Revision 1.112  2000/03/04 16:20:42  detabc
+ * copy frames before rewriting frame's saddr
+ *
+ * Revision 1.111  2000/02/28 22:28:24  he
+ * moved tx_timeout warning messages in old (2.2.x) branch where it really only
+ * indicates problems.
+ *
  * Revision 1.110  2000/02/26 01:00:53  keil
  * changes from 2.3.47
  *
@@ -487,19 +531,6 @@
  * the network layer, and therefore, it only makes sense to call netif_* 
  * functions on them.
  *
- * The old code abused the slaves dev->start to remember the corresponding 
- * master's interface state (ifup'ed or not). This does not work with SOFTNET 
- * any more, because there's now dev->start anymore.
- * Instead I chose to add isdn_net_started() which gives the state of the 
- * master in case of slaves.
- * I'm still not sure if this is how it's supposed to be done this way
- * because it uses netif_running(dev) which might be 
- * considered private to the network layer. However, it works for now.
- * Alternative: set a flag in _open() and clear it in _close() 
- *
- * I left some dead code around in #if 0 which I'm not absolutely sure about.
- * If no problems turn up, it should be removed later
- *
  * --KG
  */
 
@@ -507,7 +538,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 net_device *dev;
@@ -523,7 +554,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);
@@ -531,6 +562,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
@@ -546,9 +646,8 @@
 
 int isdn_net_force_dial_lp(isdn_net_local *);
 static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
-static int isdn_net_xmit(struct net_device *, isdn_net_local *, struct sk_buff *);
 
-char *isdn_net_revision = "$Revision: 1.110 $";
+char *isdn_net_revision = "$Revision: 1.122 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -660,16 +759,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():
@@ -766,6 +861,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
@@ -789,28 +889,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 net_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:
@@ -842,6 +923,7 @@
 #ifdef CONFIG_ISDN_PPP
 					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,
@@ -864,6 +946,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:
@@ -881,13 +964,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;
@@ -903,13 +990,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;
@@ -942,20 +1025,6 @@
 }
 
 /*
- * Check, if a number contains wildcard-characters, in which case it
- * is for incoming purposes only.
- */
-static int
-isdn_net_checkwild(char *num)
-{
-	return ((strchr(num, '?')) ||
-		(strchr(num, '*')) ||
-		(strchr(num, '[')) ||
-		(strchr(num, ']')) ||
-		(strchr(num, '^')));
-}
-
-/*
  * Perform dialout for net-interfaces and timeout-handling for
  * D-Channel-up and B-Channel-up Messages.
  * This function is initially called from within isdn_net_start_xmit() or
@@ -1033,7 +1102,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;
 				}
@@ -1066,7 +1135,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;
 						}
@@ -1084,7 +1153,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;
@@ -1248,6 +1317,7 @@
 #ifdef CONFIG_ISDN_PPP
 		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
@@ -1360,31 +1430,83 @@
 }
 
 /*
- * 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()) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		return;
+	}
+
+	spin_lock_bh(&lp->xmit_lock);
+	if (!isdn_net_lp_busy(lp)) {
+		isdn_net_writebuf_skb(lp, skb);
+	} else {
+		skb_queue_tail(&lp->super_tx_queue, skb);
+	}
+	spin_unlock_bh(&lp->xmit_lock);
+}
+
+/*
+ * called from tq_immediate
  */
-int isdn_net_send_skb
-		(struct net_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;
+
+	spin_lock_bh(&lp->xmit_lock);
+	while (!isdn_net_lp_busy(lp)) {
+		skb = skb_dequeue(&lp->super_tx_queue);
+		if (!skb)
+			break;
+		isdn_net_writebuf_skb(lp, skb);                                
+	}
+	spin_unlock_bh(&lp->xmit_lock);
+}
+
+/* 
+ * 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.
@@ -1397,9 +1519,18 @@
  */
 
 static int
-isdn_net_xmit(struct net_device *ndev, isdn_net_local * lp, struct sk_buff *skb)
+isdn_net_xmit(struct net_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
@@ -1407,31 +1538,27 @@
 		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", lp->name);
+		return 1;
+	}
+	/* we have our lp locked from now on */
+
 	/* Reset hangup-timeout */
-	lp->huptimer = 0;
+	lp->huptimer = 0; // FIXME?
+	isdn_net_writebuf_skb(lp, skb);
+	spin_unlock_bh(&lp->xmit_lock);
+
+	/* 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) {
-		/* Device overloaded */
-
-		/*
-		 * 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 */
 		if (lp->slave) {
 			if (!lp->sqfull) {
 				/* First time overload: set timestamp only */
@@ -1439,17 +1566,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
@@ -1489,7 +1623,7 @@
 		 * actually, this may not matter at all, because ISDN hardware
 		 * should not see transmitter hangs at all IMO
 		 * changed KERN_DEBUG to KERN_WARNING to find out if this is 
-		 * ever called
+		 * ever called   --KG
 		 */
 	}
 	ndev->trans_start = jiffies;
@@ -1608,19 +1742,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");
@@ -1633,14 +1759,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
@@ -1754,7 +1873,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;
 
@@ -1782,9 +1900,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
@@ -2417,7 +2533,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);
@@ -2508,6 +2624,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);
 					}
@@ -2707,10 +2824,17 @@
 	netdev->ib.last = NULL;
 #endif
 	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;
@@ -2718,13 +2842,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 */
@@ -2762,7 +2884,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)));
 	}
@@ -2805,7 +2927,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;
@@ -3066,8 +3188,6 @@
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	isdn_net_phone *n;
 
-	if (isdn_net_checkwild(phone->phone) && (phone->outgoing & 1))
-		return -EINVAL;
 	if (p) {
 		if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
 			return -ENOMEM;
@@ -3240,7 +3360,7 @@
 
 	save_flags(flags);
 	cli();
-	if (isdn_net_started(p)) {
+	if (isdn_net_device_started(p)) {
 		restore_flags(flags);
 		return -EBUSY;
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v2.3.99-pre2/linux/drivers/isdn/isdn_net.h	Thu Aug 26 13:05:37 1999
+++ linux/drivers/isdn/isdn_net.h	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.h,v 1.10 1999/08/22 20:26:06 calle Exp $
+/* $Id: isdn_net.h,v 1.16 2000/03/21 23:53:22 kai Exp $
 
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
@@ -21,6 +21,26 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.h,v $
+ * Revision 1.16  2000/03/21 23:53:22  kai
+ * fix backwards compatibility
+ *
+ * Revision 1.15  2000/03/19 15:27:53  kai
+ * no known bugs left...
+ *
+ * Revision 1.14  2000/03/18 16:20:25  kai
+ * cosmetics / renaming
+ *
+ * Revision 1.13  2000/03/17 18:20:46  kai
+ * moved to frame_cnt based flow control
+ * some races still need to be fixed
+ *
+ * Revision 1.12  2000/03/17 17:01:00  kai
+ * cleanup
+ *
+ * Revision 1.11  2000/03/17 16:22:55  kai
+ * we keep track of outstanding packets (given to HL, but not confirmed yet)
+ * now, but we don't use it for flow control yet.
+ *
  * Revision 1.10  1999/08/22 20:26:06  calle
  * backported changes from kernel 2.3.14:
  * - several #include "config.h" gone, others come.
@@ -124,8 +144,88 @@
 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 net_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 */
+	spin_lock_bh(&nd->queue->xmit_lock);
+	while (isdn_net_lp_busy(nd->queue)) {
+		spin_unlock_bh(&nd->queue->xmit_lock);
+		nd->queue = nd->queue->next;
+		if (nd->queue == lp) /* not found -- should never happen */
+			return 0;
+		spin_lock_bh(&nd->queue->xmit_lock);
+	}
+	lp = nd->queue;
+
+	nd->queue = nd->queue->next;
+	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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.3.99-pre2/linux/drivers/isdn/isdn_ppp.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/isdn/isdn_ppp.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.62 2000/02/12 19:26:55 kai Exp $
+/* $Id: isdn_ppp.c,v 1.69 2000/03/19 15:27:53 kai Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,30 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.69  2000/03/19 15:27:53  kai
+ * no known bugs left...
+ *
+ * Revision 1.67  2000/03/17 18:20:46  kai
+ * moved to frame_cnt based flow control
+ * some races still need to be fixed
+ *
+ * Revision 1.66  2000/03/17 17:01:00  kai
+ * cleanup
+ *
+ * Revision 1.65  2000/03/17 16:22:55  kai
+ * we keep track of outstanding packets (given to HL, but not confirmed yet)
+ * now, but we don't use it for flow control yet.
+ *
+ * Revision 1.64  2000/03/17 10:43:56  kai
+ * 2.3.99 contains MPPP constants which cause a warning because we
+ * redefine them in include/linux/isdn_ppp.h
+ *
+ * So from now on we use the generic PPP constants, change is backwards
+ * compatible, though
+ *
+ * Revision 1.63  2000/03/16 15:46:37  kai
+ * a little bugfix and cosmetic changes
+ *
  * Revision 1.62  2000/02/12 19:26:55  kai
  * adopted to latest 2.3 softnet changes.
  *
@@ -263,10 +287,6 @@
  *
  */
 
-/* TODO: right tbusy handling when using MP */
-
-#define CONFIG_ISDN_CCP 1
-
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
@@ -331,7 +351,7 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.62 $";
+char *isdn_ppp_revision = "$Revision: 1.69 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
@@ -366,36 +386,28 @@
 int
 isdn_ppp_free(isdn_net_local * lp)
 {
-#ifdef CONFIG_ISDN_MPP
 	isdn_net_local *master_lp = lp;
-#endif
 	unsigned long flags;
 	struct ippp_struct *is;
 
 	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
-	if (lp->master)
-		master_lp = (isdn_net_local *) lp->master->priv;
 
-	lp->last->next = lp->next;
-	lp->next->last = lp->last;
-	if (master_lp->netdev->queue == lp) {
-		master_lp->netdev->queue = lp->next;
-		if (lp->next == lp) {	/* last link in queue? */
-			master_lp->netdev->ib.bundled = 0;
-			isdn_ppp_free_mpqueue(master_lp->netdev);
-			isdn_ppp_free_sqqueue(master_lp->netdev);
-		}
+#ifdef CONFIG_ISDN_MPP
+	if (lp->next == lp) {	/* last link in queue? */
+		master_lp->netdev->ib.bundled = 0;
+		isdn_ppp_free_mpqueue(master_lp->netdev);
+		isdn_ppp_free_sqqueue(master_lp->netdev);
 	}
-	lp->next = lp->last = lp;	/* (re)set own pointers */
 #endif
 
+	isdn_net_rm_from_bundle(lp);
+
+	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)
@@ -406,6 +418,7 @@
 
 	is->lp = NULL;          /* link is down .. set lp to NULL */
 	lp->ppp_slot = -1;      /* is this OK ?? */
+
 	restore_flags(flags);
 
 	return 0;
@@ -422,9 +435,6 @@
 	long flags;
 	struct ippp_struct *is;
 
-	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
-		return -1;
-
 	save_flags(flags);
 	cli();
 
@@ -1030,8 +1040,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
@@ -1054,17 +1062,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;
@@ -1162,7 +1160,7 @@
 		int sqno_end;
 
 		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);
@@ -1425,7 +1423,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 */
@@ -1484,19 +1482,13 @@
 int
 isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
-	struct net_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;
 
-	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 */
 	ipts = ippp_table[mlp->ppp_slot];
 
@@ -1515,21 +1507,18 @@
 			break;
 		default:
 			dev_kfree_skb(skb);
-			printk(KERN_ERR "isdn_ppp: skipped frame with unsupported protocoll: %#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 (lp->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", lp->name);
+		return 1;
 	}
+	/* we have our lp locked from now on */
+
 	ipt = ippp_table[lp->ppp_slot];
 	lp->huptimer = 0;
 
@@ -1538,8 +1527,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)
@@ -1612,11 +1601,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;
@@ -1624,7 +1612,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;
@@ -1644,20 +1632,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 */
 	}
@@ -1668,16 +1656,11 @@
 		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:
+	spin_unlock_bh(&lp->xmit_lock);
 	return 0;
 }
 
@@ -1731,28 +1714,21 @@
 	char ifn[IFNAMSIZ + 1];
 	long flags;
 	isdn_net_dev *p;
-	isdn_net_local *lp,
-	*nlp;
+	isdn_net_local *lp, *nlp;
 
 	sprintf(ifn, "ippp%d", unit);
 	p = isdn_net_findif(ifn);
 	if (!p)
 		return -1;
 
-	isdn_timer_ctrl(ISDN_TIMER_IPPP, 1);	/* enable timer for ippp/MP */
-
 	save_flags(flags);
 	cli();
+	isdn_timer_ctrl(ISDN_TIMER_IPPP, 1);	/* enable timer for ippp/MP */
 
 	nlp = is->lp;
-
 	lp = p->queue;
+	isdn_net_add_to_bundle(p, nlp);
 	p->ib.bundled = 1;
-	nlp->last = lp->last;
-	lp->last->next = nlp;
-	lp->last = nlp;
-	nlp->next = lp;
-	p->queue = nlp;
 
 	ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
 /* maybe also SC_CCP stuff */
@@ -1761,7 +1737,6 @@
 
 	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);
-
 	restore_flags(flags);
 	return 0;
 }
@@ -1949,12 +1924,14 @@
 #ifdef CONFIG_ISDN_PPP_VJ
 	int toss = 0;
 #endif
-/* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
-   eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
-   wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
-   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete
-   groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
-   ja ein Paket mit B und eins mit E dazwischenpassen */
+	/* currently we just discard ancient packets. 
+	   To do: 
+	   Maybe, as long as there's no B-packet in front and sqno <= min_sqno: discard.
+	   If sqno < min_sqno and there are gaps: discard (the gaps won't be filled anyway).
+	   Packets with sqno > min_sqno: Larger than mp_mrru: If sum of all pktlen of pending
+	   packets large than mrru: discard - packets need to be consecutive, though, if not 
+	   there could be an B and an E-packet in between.
+	*/
 
 	struct mpqueue *ql,
 	*q = dev->mp_last;
@@ -2249,8 +2226,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;
 
@@ -2291,26 +2267,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 */
@@ -2560,14 +2517,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;
@@ -2617,7 +2566,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;
@@ -2657,7 +2606,6 @@
 		ipc->incomp(stat,skb,proto);
 		return skb;
 	}
-#endif
 }
 
 /*
@@ -2676,13 +2624,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 */
@@ -2883,7 +2827,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 --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_v110.c linux/drivers/isdn/isdn_v110.c
--- v2.3.99-pre2/linux/drivers/isdn/isdn_v110.c	Thu Nov 11 20:11:39 1999
+++ linux/drivers/isdn/isdn_v110.c	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_v110.c,v 1.3 1999/10/30 09:49:28 keil Exp $
+/* $Id: isdn_v110.c,v 1.4 2000/03/16 16:34:12 kai Exp $
 
  * Linux ISDN subsystem, V.110 related functions (linklevel).
  *
@@ -19,6 +19,11 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_v110.c,v $
+ * Revision 1.4  2000/03/16 16:34:12  kai
+ * some translation work
+ *
+ * there shouldn't be any German comments lurking around anymore ;-)
+ *
  * Revision 1.3  1999/10/30 09:49:28  keil
  * Reinit of v110 structs
  *
@@ -39,14 +44,15 @@
 
 #undef ISDN_V110_DEBUG
 
-char *isdn_v110_revision = "$Revision: 1.3 $";
+char *isdn_v110_revision = "$Revision: 1.4 $";
 
 #define V110_38400 255
 #define V110_19200  15
 #define V110_9600    3
 
-/* Die folgenden Daten sind fertig kodierte Matrizen, jeweils
-   als online und offline matrix für 9600, 19200 und 38400
+/* 
+ * The following data are precoded matrices, online and offline matrix 
+ * for 9600, 19200 und 38400, respectively
  */
 static unsigned char V110_OnMatrix_9600[] =
 {0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
@@ -74,13 +80,12 @@
 static unsigned char V110_OffMatrix_38400[] =
 {0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
 
-
-/* FlipBits dreht die Reihenfolge von jeweils keylen bits in einem byte um.
-   Aus der Bitreihenfolge 76543210 werden bei keylen=4 die bits 45670123,
-   bei keylen=2 die bits 67452301. Dies ist notwendig, weil die reihenfolge
-   auf der isdn-leitung falsch herum ist.
+/* 
+ * FlipBits reorders sequences of keylen bits in one byte.
+ * E.g. source order 7654321 will be converted to 45670123 when keylen = 4,
+ * and to 67452301 when keylen = 2. This is necessary because ordering on
+ * the isdn line is the the other way.
  */
-
 static __inline unsigned char
 FlipBits(unsigned char c, int keylen)
 {
@@ -164,8 +169,9 @@
 }
 
 
-/* ValidHeaderBytes prüft, wieviele bytes in v->decodebuf gültig sind */
-
+/* 
+ * ValidHeaderBytes return the number of valid bytes in v->decodebuf 
+ */
 static int
 ValidHeaderBytes(isdn_v110_stream * v)
 {
@@ -176,8 +182,9 @@
 	return i;
 }
 
-/* SyncHeader schiebt den decodebuf pointer auf den nächsten gültigen header */
-
+/* 
+ * SyncHeader moves the decodebuf ptr to the next valid header 
+ */
 static void
 SyncHeader(isdn_v110_stream * v)
 {
@@ -214,68 +221,60 @@
 	int dbit = v->dbit;
 	unsigned char b = v->b;
 
-	while (line < len) {    /* sind schon alle matrizenzeilen abgearbeitet? */
-		if ((line % 10) == 0) {	/* die 0. zeile der matrix ist immer null ! */
-			if (m[line] != 0x00) {	/* nicht 0 ? dann fehler! */
+	while (line < len) {    /* Are we done with all lines of the matrix? */
+		if ((line % 10) == 0) {	/* the 0. line of the matrix is always 0 ! */
+			if (m[line] != 0x00) {	/* not 0 ? -> error! */
 #ifdef ISDN_V110_DEBUG
 				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
+				/* returning now is not the right thing, though :-( */
 #endif
-
-/*
-  dann einen return zu machen, ist auch irgendwie nicht das richtige! :-(
-  v->introducer = 0; v->dbit = 1; v->b = 0;
-  return buflen;                                                                                                     anzahl schon erzeugter daten zurückgeben!
-  */
-			}
-			line++; /* sonst die nächste matrixzeile nehmen */
+			} 
+			line++; /* next line of matrix */
 			continue;
-		} else if ((line % 10) == 5) {	/* in zeile 5 stehen nur e-bits ! */
-			if ((m[line] & 0x70) != 0x30) {	/* 011 muß am anfang stehen! */
+		} else if ((line % 10) == 5) {	/* in line 5 there's only e-bits ! */
+			if ((m[line] & 0x70) != 0x30) {	/* 011 has to be at the beginning! */
 #ifdef ISDN_V110_DEBUG
 				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
+				/* returning now is not the right thing, though :-( */
 #endif
-/* dann einen return zu machen, ist auch irgendwie nicht das richtige! :-(
-   v->introducer = 0; v->dbit = 1; v->b = 0;
-   return buflen;
- */
 			}
-			line++; /* alles klar, nächste zeile */
+			line++; /* next line */
 			continue;
 		} else if (!introducer) {	/* every byte starts with 10 (stopbit, startbit) */
-			introducer = (m[line] & mbit) ? 0 : 1;	/* aktuelles bit der matrix */
+			introducer = (m[line] & mbit) ? 0 : 1;	/* current bit of the matrix */
 		      next_byte:
-			if (mbit > 2) {	/* war es das letzte bit dieser matrixzeile ? */
-				mbit >>= 1;	/* nein, nimm das nächste in dieser zeile */
+			if (mbit > 2) {	/* was it the last bit in this line ? */
+				mbit >>= 1;	/* no -> take next */
 				continue;
-			}       /* sonst links in der nächsten zeile anfangen */
+			}       /* otherwise start with leftmost bit in the next line */
 			mbit = 64;
 			line++;
 			continue;
-		} else {        /* sonst müssen wir ein datenbit setzen */
-			if (m[line] & mbit)	/* war das bit in der matrix gesetzt ? */
-				b |= dbit;	/* ja, dann setz es auch im datenbyte  */
+		} else {        /* otherwise we need to set a data bit */
+			if (m[line] & mbit)	/* was that bit set in the matrix ? */
+				b |= dbit;	/* yes -> set it in the data byte */
 			else
-				b &= dbit - 1;	/* nein, lösch bit im datenbyte */
-			if (dbit < 128)	/* haben wir schon ein ganzes byte voll ? */
-				dbit <<= 1;	/* nein, auf zum nächsten datenbit */
-			else {  /* ein ganzes datenbyte ist voll */
-				buf[buflen++] = b;	/* byte in den output buffer kopieren */
-				introducer = b = 0;	/* Init der Introsequenz und des datenbytes */
-				dbit = 1;	/* als nächstes suchen wir das nullte bit */
+				b &= dbit - 1;	/* no -> clear it in the data byte */
+			if (dbit < 128)	/* is that data byte done ? */
+				dbit <<= 1;	/* no, got the next bit */
+			else {  /* data byte is done */
+				buf[buflen++] = b;	/* copy byte into the output buffer */
+				introducer = b = 0;	/* init of the intro sequence and of the data byte */
+				dbit = 1;	/* next we look for the 0th bit */
 			}
-			goto next_byte;	/* suche das nächste bit in der matrix */
+			goto next_byte;	/* look for next bit in the matrix */
 		}
 	}
 	v->introducer = introducer;
 	v->dbit = dbit;
 	v->b = b;
-	return buflen;          /* return anzahl der bytes im output buffer */
+	return buflen;          /* return number of bytes in the output buffer */
 }
 
-/* DecodeStream erhält vom input stream V110 kodierte Daten, die zu den
-   V110 frames zusammengepackt werden müssen. Die Daten können an diese
-   Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne
-   darauf achten zu müssen, das frames usw. eingehalten werden.
+/* 
+ * DecodeStream receives V.110 coded data from the input stream. It recovers the 
+ * original frames.
+ * The input stream doesn't need to be framed
  */
 struct sk_buff *
 isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
@@ -314,8 +313,8 @@
 		dev_kfree_skb(skb);
 		return NULL;    /* no, try later      */
 	}
-	if (ValidHeaderBytes(v) != v->nbytes) {	/* ist es ein ungültiger header ? */
-		SyncHeader(v);  /* nein, such einen header */
+	if (ValidHeaderBytes(v) != v->nbytes) {	/* is that a valid header? */
+		SyncHeader(v);  /* no -> look for header */
 		goto ReSync;
 	}
 	len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
@@ -357,15 +356,15 @@
 	int introducer = 3;
 	int ibit[] = {0, 1, 1};
 
-	while ((i < len) && (line < mlen)) {	/* solange noch input da ist */
-		switch (line % 10) {	/* in welcher matrixzeile sind wir ? */
+	while ((i < len) && (line < mlen)) {	/* while we still have input data */
+		switch (line % 10) {	/* in which line of the matrix are we? */
 			case 0:
-				m[line++] = 0x00;	/* zeile 0 ist immer 0 */
-				mbit = 128;	/* und es geht mit dem 7. bit weiter */
+				m[line++] = 0x00;	/* line 0 is always 0 */
+				mbit = 128;	/* go on with the 7th bit */
 				break;
 			case 5:
-				m[line++] = 0xbf;	/* zeile 5 ist immer 10111111 */
-				mbit = 128;	/* und es geht mit dem 7. bit weiter */
+				m[line++] = 0xbf;	/* line 5 is always 10111111 */
+				mbit = 128;	/* go on with the 7th bit */
 				break;
 		}
 		if (line >= mlen) {
@@ -373,41 +372,41 @@
 			return line;
 		}
 	next_bit:
-		switch (mbit) { /* ganz linkes oder rechtes bit ? */
+		switch (mbit) { /* leftmost or rightmost bit ? */
 			case 1:
-				line++;	/* ganz rechts ! dann in die nächste */
+				line++;	/* rightmost -> go to next line */
 				if (line >= mlen) {
 					printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
 					return line;
 				}
 			case 128:
-				m[line] = 128;	/* ganz links byte auf 1000000 setzen */
-				mbit = 64;	/* aktuelles bit in der matrixzeile */
+				m[line] = 128;	/* leftmost -> set byte to 1000000 */
+				mbit = 64;	/* current bit in the matrix line */
 				continue;
 		}
-		if (introducer) {	/* 110 sequenz setzen ? */
-			introducer--;	/* ein digit weniger setzen */
-			m[line] |= ibit[introducer] ? mbit : 0;	/* entsprechendes bit setzen */
-			mbit >>= 1;	/* bit der matrixzeile >> 1 */
-			goto next_bit;	/* und dort weiter machen */
-		}               /* else datenbits in die matrix packen! */
-		m[line] |= (buf[i] & dbit) ? mbit : 0;	/* datenbit in matrix setzen */
-		if (dbit == 128) {	/* war es das letzte datenbit ? */
-			dbit = 1;	/* dann mach beim nächsten weiter */
-			i++;    /* nächste datenbyte des input buffers */
-			if (i < len)	/* war es schon das letzte ? */
-				introducer = 3;	/* nein, schreib den introducer 110 */
-			else {  /* war das letzte datenbyte ! */
-				m[line] |= (mbit - 1) & 0xfe;	/* setz restliche bits der zeile auf 1 */
+		if (introducer) {	/* set 110 sequence ? */
+			introducer--;	/* set on digit less */
+			m[line] |= ibit[introducer] ? mbit : 0;	/* set corresponding bit */
+			mbit >>= 1;	/* bit of matrix line  >> 1 */
+			goto next_bit;	/* and go on there */
+		}               /* else push data bits into the matrix! */
+		m[line] |= (buf[i] & dbit) ? mbit : 0;	/* set data bit in matrix */
+		if (dbit == 128) {	/* was it the last one? */
+			dbit = 1;	/* then go on with first bit of  */
+			i++;            /* next byte in input buffer */
+			if (i < len)	/* input buffer done ? */
+				introducer = 3;	/* no, write introducer 110 */
+			else {  /* input buffer done ! */
+				m[line] |= (mbit - 1) & 0xfe;	/* set remaining bits in line to 1 */
 				break;
 			}
-		} else          /* nicht das letzte datenbit */
-			dbit <<= 1;	/* dann gehe zum nächsten datenbit */
-		mbit >>= 1;     /* und setz bit der matrix weiter */
+		} else          /* not the last data bit */
+			dbit <<= 1;	/* then go to next data bit */
+		mbit >>= 1;     /* go to next bit of matrix */
 		goto next_bit;
 
 	}
-	/* evtl. noch restliche zeilen in der matrix generieren... */
+	/* if necessary, generate remaining lines of the matrix... */
 	if ((line) && ((line + 10) < mlen))
 		switch (++line % 10) {
 			case 1:
@@ -429,7 +428,7 @@
 			case 9:
 				m[line++] = 0xfe;
 		}
-	return line;            /* soviele matrixzeilen sind es */
+	return line;            /* that's how many lines we have */
 }
 
 /*
@@ -517,7 +516,7 @@
 		return nskb;
 	}
 	mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
-	/* jetzt noch jeweils 2 oder 4 bits auf den output stream verteilen! */
+	/* now distribute 2 or 4 bits each to the output stream! */
 	rbuf = skb_put(nskb, size);
 	olen = 0;
 	sval1 = 8 - v->nbits;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/isdn/isdn_v110.h linux/drivers/isdn/isdn_v110.h
--- v2.3.99-pre2/linux/drivers/isdn/isdn_v110.h	Thu Nov 11 20:11:39 1999
+++ linux/drivers/isdn/isdn_v110.h	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_v110.h,v 1.2 1999/10/30 09:49:28 keil Exp $
+/* $Id: isdn_v110.h,v 1.3 2000/03/16 16:34:12 kai Exp $
 
  * Linux ISDN subsystem, V.110 related functions (linklevel).
  *
@@ -19,6 +19,11 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_v110.h,v $
+ * Revision 1.3  2000/03/16 16:34:12  kai
+ * some translation work
+ *
+ * there shouldn't be any German comments lurking around anymore ;-)
+ *
  * Revision 1.2  1999/10/30 09:49:28  keil
  * Reinit of v110 structs
  *
@@ -29,17 +34,14 @@
 #ifndef _isdn_v110_h_
 #define _isdn_v110_h_
 
-/* isdn_v110_encode erhält len Nettodaten in buf, kodiert diese und liefert
-   das Ergebnis wieder in buf. Wieviele Bytes kodiert wurden wird als
-   return zurück gegeben. Diese Daten können dann 1:1 auf die Leitung
-   gegeben werden.
-*/
+/* 
+ * isdn_v110_encode will take raw data and encode it using V.110 
+ */
 extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *);
 
-/* isdn_v110_decode erhält vom input stream V110 kodierte Daten, die zu den
-   V110 frames zusammengepackt werden müssen. Die Daten können an diese
-   Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne
-   darauf achten zu müssen, das frames usw. eingehalten werden.
+/* 
+ * isdn_v110_decode receives V.110 coded data from the stream and rebuilds
+ * frames from them. The source stream doesn't need to be framed.
  */
 extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.3.99-pre2/linux/drivers/net/3c509.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/net/3c509.c	Tue Mar 21 14:43:39 2000
@@ -310,9 +310,11 @@
 			   with "nopnp=1" before, does not harm if not. */
 			idev->deactivate(idev);
 			idev->activate(idev);
-			if (!idev->resource[0].start || check_region(idev->resource[0].start,16))
+			if (!idev->resource[0].start || check_region(idev->resource[0].start, EL3_IO_EXTENT))
 				continue;
 			ioaddr = idev->resource[0].start;
+			if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509 PnP"))
+				return -EBUSY;
 			irq = idev->irq_resource[0].start;
 			if (el3_debug > 3)
 				printk ("ISAPnP reports %s at i/o 0x%x, irq %d\n",
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/3c515.c linux/drivers/net/3c515.c
--- v2.3.99-pre2/linux/drivers/net/3c515.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/net/3c515.c	Tue Mar 21 14:43:39 2000
@@ -490,7 +490,7 @@
 				if (inw(ioaddr + Wn0EepromData) != 0x6d50)
 					continue;
 			}
-			printk(KERN_INFO "3c515 Resource configuraiton register %#4.4x, DCR %4.4x.\n",
+			printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
 		     		inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
 			/* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
 			corkscrew_isapnp_phys_addr[pnp_cards] = ioaddr;
@@ -533,7 +533,7 @@
 			if (inw(ioaddr + Wn0EepromData) != 0x6d50)
 				continue;
 		}
-		printk(KERN_INFO "3c515 Resource configuraiton register %#4.4x, DCR %4.4x.\n",
+		printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
 		     inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
 		irq = inw(ioaddr + 0x2002) & 15;
 		corkscrew_found_device(dev, ioaddr, irq, CORKSCREW_ID, dev
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.3.99-pre2/linux/drivers/net/3c59x.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/3c59x.c	Tue Mar 21 11:18:37 2000
@@ -21,8 +21,6 @@
 		
  */
 
-static char *version =
-"3c59x.c:v0.99H+lk1.0 Feb 9, 2000 The Linux Kernel Team http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
@@ -84,6 +82,9 @@
 
 #define PCI_SUPPORT_VER2
 #define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
+
+static char *version __initdata =
+"3c59x.c:v0.99H+lk1.0 Feb 9, 2000 The Linux Kernel Team http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver");
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/8139too.c linux/drivers/net/8139too.c
--- v2.3.99-pre2/linux/drivers/net/8139too.c	Sat Feb 26 22:31:46 2000
+++ linux/drivers/net/8139too.c	Mon Mar 20 07:53:51 2000
@@ -1838,7 +1838,7 @@
 	void *ioaddr = tp->mmio_addr;
 	u32 mc_filter[2];	/* Multicast hash filter */
 	int i, rx_mode;
-	unsigned long flags;
+	unsigned long flags=0;
 
 	DPRINTK ("ENTER\n");
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v2.3.99-pre2/linux/drivers/net/8390.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/8390.c	Tue Mar 21 11:03:00 2000
@@ -114,8 +114,8 @@
 static void set_multicast_list(struct net_device *dev);
 static void do_set_multicast_list(struct net_device *dev);
 
-/*
- *	SMP and the 8390 setup.
+/**
+ *	DOC: SMP and the 8390 setup.
  *
  *	The 8390 isnt exactly designed to be multithreaded on RX/TX. There is
  *	a page register that controls bank and packet buffer access. We guard
@@ -142,10 +142,14 @@
  
 
 
-/* Open/initialize the board.  This routine goes all-out, setting everything
-   up anew at each open, even though many of these registers should only
-   need to be set once at boot.
-   */
+/**
+ * ei_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * This routine goes all-out, setting everything
+ * up anew at each open, even though many of these registers should only
+ * need to be set once at boot.
+ */
 int ei_open(struct net_device *dev)
 {
 	unsigned long flags;
@@ -173,7 +177,12 @@
 	return 0;
 }
 
-/* Opposite of above. Only used when "ifconfig <devname> down" is done. */
+/**
+ * ei_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ei_open. Only used when "ifconfig <devname> down" is done.
+ */
 int ei_close(struct net_device *dev)
 {
 	struct ei_device *ei_local = (struct ei_device *) dev->priv;
@@ -190,6 +199,14 @@
 	return 0;
 }
 
+/**
+ * ei_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+ 
 static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	long e8390_base = dev->base_addr;
@@ -389,8 +406,15 @@
 	return 0;
 }
 
-/* The typical workload of the driver:
-   Handle the ether interface interrupts. */
+/**
+ * ei_interrupt - 
+ * @irq:
+ * @dev_id:
+ * @regs:
+ *
+ * The typical workload of the driver:
+ * Handle the ether interface interrupts.
+ */
 
 void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
@@ -492,7 +516,10 @@
 	return;
 }
 
-/*
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
  * A transmitter error has happened. Most likely excess collisions (which
  * is a fairly normal condition). If the error is one where the Tx will
  * have been aborted, we try and send another one right away, instead of
@@ -538,8 +565,13 @@
 	}
 }
 
-/* We have finished a transmit: check for errors and then trigger the next
-   packet to be sent. Called with lock held */
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held
+ */
 
 static void ei_tx_intr(struct net_device *dev)
 {
@@ -625,8 +657,13 @@
 	netif_wake_queue(dev);
 }
 
-/* We have a good packet(s), get it/them out of the buffers. 
-   Called with lock held */
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers. 
+ * Called with lock held
+ */
 
 static void ei_receive(struct net_device *dev)
 {
@@ -751,7 +788,10 @@
 	return;
 }
 
-/* 
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
  * We have a receiver overrun: we have to kick the 8390 to get it started
  * again. Problem is that you have to kick it exactly as NS prescribes in
  * the updated datasheets, or "the NIC may act in an unpredictable manner."
@@ -900,7 +940,10 @@
 	}
 }
 
-/*
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
  *	Set or clear the multicast filter for this adaptor. May be called
  *	from a BH in 2.1.x. Must be called with lock held. 
  */
@@ -970,7 +1013,10 @@
 	spin_unlock_irqrestore(&ei_local->page_lock, flags);
 }	
 
-/*
+/**
+ * ethdev_init - init rest of 8390 device struct
+ * @dev: network device structure to init
+ *
  * Initialize the rest of the 8390 device structure.  Do NOT __init
  * this, as it is used by 8390 based modular drivers too.
  */
@@ -1006,7 +1052,11 @@
 /* This page of functions should be 8390 generic */
 /* Follow National Semi's recommendations for initializing the "NIC". */
 
-/*
+/**
+ * NS8390_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean.  non-zero value to initiate chip processing
+ *
  *	Must be called with lock held.
  */
 
@@ -1066,7 +1116,6 @@
 		outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on,  */
 		do_set_multicast_list(dev);	/* (re)load the mcast table */
 	}
-	return;
 }
 
 /* Trigger a transmit start, assuming the length is valid. 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.3.99-pre2/linux/drivers/net/Config.in	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/Config.in	Tue Mar 21 14:43:39 2000
@@ -124,9 +124,7 @@
    bool '  EISA, VLB, PCI and on board controllers' CONFIG_NET_PCI
    if [ "$CONFIG_NET_PCI" = "y" ]; then
       tristate '    AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32
-      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-	 tristate '    Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE
-      fi
+      dep_tristate '    Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI $CONFIG_EXPERIMENTAL
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 	 tristate '    Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
       fi
@@ -134,12 +132,12 @@
       tristate '    Apricot Xen-II on board Ethernet' CONFIG_APRICOT
       tristate '    CS89x0 support' CONFIG_CS89x0
       tristate '    Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
-      tristate '    DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP
+      dep_tristate '    DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP $CONFIG_PCI
       tristate '    Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
          tristate '    DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102
       fi
-      tristate '    EtherExpressPro/100 support' CONFIG_EEPRO100
+      dep_tristate '    EtherExpressPro/100 support' CONFIG_EEPRO100 $CONFIG_PCI
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
          if [ "$CONFIG_EEPRO100" = "y" -o "$CONFIG_EEPRO100" = "m" ]; then
 	    bool '      Enable Power Management (EXPERIMENTAL)' CONFIG_EEPRO100_PM
@@ -147,7 +145,7 @@
 	 tristate '    Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390
 	 tristate '    Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210
       fi
-      tristate '    PCI NE2000 support' CONFIG_NE2K_PCI
+      dep_tristate '    PCI NE2000 support' CONFIG_NE2K_PCI $CONFIG_PCI
       # tristate '    Sundance Alta support' CONFIG_ALTA
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
          tristate '    RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)' CONFIG_RTL8129
@@ -166,7 +164,9 @@
    fi
    bool '  Pocket and portable adapters' CONFIG_NET_POCKET
    if [ "$CONFIG_NET_POCKET" = "y" ]; then
-      tristate '    AT-LAN-TEC/RealTek pocket adapter support' CONFIG_ATP
+      if [ "$CONFIG_X86" = "y" ]; then
+         tristate '    AT-LAN-TEC/RealTek pocket adapter support' CONFIG_ATP
+      fi
       tristate '    D-Link DE600 pocket adapter support' CONFIG_DE600
       tristate '    D-Link DE620 pocket adapter support' CONFIG_DE620
    fi
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/defxx.c linux/drivers/net/defxx.c
--- v2.3.99-pre2/linux/drivers/net/defxx.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/defxx.c	Wed Mar 22 22:23:55 2000
@@ -3495,6 +3495,6 @@
 
 /*
  * Local variables:
- * kernel-compile-command: "gcc -D__KERNEL__ -I/root/linux/include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586 -c defxx.c"
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/root/linux/include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -c defxx.c"
  * End:
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/epic100.c linux/drivers/net/epic100.c
--- v2.3.99-pre2/linux/drivers/net/epic100.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/epic100.c	Mon Mar 20 07:53:51 2000
@@ -1099,6 +1099,12 @@
 	struct net_device *dev;
 	long ioaddr;
 	static int card_idx = -1;
+	static int printed_version = 0;
+	
+	if (!printed_version) {
+		printk (KERN_INFO "%s", version);
+		printed_version = 1;
+	}
 	
 	chip_idx = ent->driver_data;
 	
@@ -1280,13 +1286,7 @@
 
 static int __init epic100_init (void)
 {
-	printk (KERN_INFO "%s", version);
-	
-	if (pci_register_driver (&epic100_driver) > 0)
-		return 0;
-	
-	pci_unregister_driver (&epic100_driver);
-	return -ENODEV;
+	return pci_module_init (&epic100_driver);
 }
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- v2.3.99-pre2/linux/drivers/net/irda/irport.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/irda/irport.c	Tue Mar 21 11:17:28 2000
@@ -941,10 +941,14 @@
 	
 	switch (cmd) {
 	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_task_execute(self, __irport_change_speed, NULL, NULL, 
 				  (void *) irq->ifr_baudrate);
 		break;
 	case SIOCSDONGLE: /* Set dongle */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		/* Initialize dongle */
 		dongle = irda_device_dongle_init(dev, irq->ifr_dongle);
 		if (!dongle)
@@ -965,12 +969,16 @@
 				  NULL);	
 		break;
 	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_device_set_media_busy(self->netdev, TRUE);
 		break;
 	case SIOCGRECEIVING: /* Check if we are receiving right now */
 		irq->ifr_receiving = irport_is_receiving(self);
 		break;
 	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irport_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
 		break;
 	default:
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/irda/irtty.c linux/drivers/net/irda/irtty.c
--- v2.3.99-pre2/linux/drivers/net/irda/irtty.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/irda/irtty.c	Tue Mar 21 11:17:28 2000
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:18:38 1997
- * Modified at:   Fri Jan 14 21:02:27 2000
+ * Modified at:   Sat Mar 11 07:43:30 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
@@ -962,10 +962,14 @@
 	
 	switch (cmd) {
 	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_task_execute(self, irtty_change_speed, NULL, NULL, 
 				  (void *) irq->ifr_baudrate);
 		break;
 	case SIOCSDONGLE: /* Set dongle */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		/* Initialize dongle */
 		dongle = irda_device_dongle_init(dev, irq->ifr_dongle);
 		if (!dongle)
@@ -986,15 +990,21 @@
 				  NULL);	
 		break;
 	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_device_set_media_busy(self->netdev, TRUE);
 		break;
 	case SIOCGRECEIVING: /* Check if we are receiving right now */
 		irq->ifr_receiving = irtty_is_receiving(self);
 		break;
 	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irtty_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
 		break;
 	case SIOCSMODE:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irtty_set_mode(dev, irq->ifr_mode);
 		break;
 	default:
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/irda/nsc-ircc.c linux/drivers/net/irda/nsc-ircc.c
--- v2.3.99-pre2/linux/drivers/net/irda/nsc-ircc.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/net/irda/nsc-ircc.c	Tue Mar 21 11:17:28 2000
@@ -1947,9 +1947,13 @@
 	
 	switch (cmd) {
 	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		nsc_ircc_change_speed(self, irq->ifr_baudrate);
 		break;
 	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_device_set_media_busy(self->netdev, TRUE);
 		break;
 	case SIOCGRECEIVING: /* Check if we are receiving right now */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/irda/toshoboe.c linux/drivers/net/irda/toshoboe.c
--- v2.3.99-pre2/linux/drivers/net/irda/toshoboe.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/irda/toshoboe.c	Tue Mar 21 11:17:28 2000
@@ -603,11 +603,15 @@
 	
 	switch (cmd) {
 	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		/* toshoboe_setbaud(self, irq->ifr_baudrate); */
                 /* Just change speed once - inserted by Paul Bristow */
 	        self->new_speed = irq->ifr_baudrate;
 		break;
 	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_device_set_media_busy(self->netdev, TRUE);
 		break;
 	case SIOCGRECEIVING: /* Check if we are receiving right now */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/irda/w83977af_ir.c linux/drivers/net/irda/w83977af_ir.c
--- v2.3.99-pre2/linux/drivers/net/irda/w83977af_ir.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/irda/w83977af_ir.c	Tue Mar 21 11:17:28 2000
@@ -1332,9 +1332,13 @@
 	
 	switch (cmd) {
 	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		w83977af_change_speed(self, irq->ifr_baudrate);
 		break;
 	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		irda_device_set_media_busy(self->netdev, TRUE);
 		break;
 	case SIOCGRECEIVING: /* Check if we are receiving right now */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
--- v2.3.99-pre2/linux/drivers/net/ne2k-pci.c	Thu Feb 10 17:11:10 2000
+++ linux/drivers/net/ne2k-pci.c	Tue Mar 21 11:02:09 2000
@@ -71,28 +71,55 @@
 	STOP_PG_0x60=0x100,
 };
 
-/* This will eventually be converted to the standard PCI probe table. */
+
+enum ne2k_pci_chipsets {
+	CH_RealTek_RTL_8029 = 0,
+	CH_Winbond_89C940,
+	CH_Compex_RL2000,
+	CH_KTI_ET32P2,
+	CH_NetVin_NV5000SC,
+	CH_Via_86C926,
+	CH_SureCom_NE34,
+	CH_Winbond_W89C940F,
+	CH_Holtek_HT80232,
+	CH_Holtek_HT80229,
+};
+
 
 static struct {
-	unsigned short vendor, dev_id;
 	char *name;
 	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 W89C940F", 0},
-	{0x12c3, 0x0058, "Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
-	{0x12c3, 0x5598, "Holtek HT80229",
-	 ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
+} pci_clone_list[] __devinitdata = {
+	{"RealTek RTL-8029", 0},
+	{"Winbond 89C940", 0},
+	{"Compex RL2000", 0},
+	{"KTI ET32P2", 0},
+	{"NetVin NV5000SC", 0},
+	{"Via 86C926", ONLY_16BIT_IO},
+	{"SureCom NE34", 0},
+	{"Winbond W89C940F", 0},
+	{"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
+	{"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
 	{0,}
 };
 
+
+static struct pci_device_id ne2k_pci_tbl[] __devinitdata = {
+	{ 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 },
+	{ 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 },
+	{ 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 },
+	{ 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 },
+	{ 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC },
+	{ 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 },
+	{ 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 },
+	{ 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F },
+	{ 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 },
+	{ 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 },
+	{ 0, },
+};
+MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl);
+
+
 /* ---- No user-serviceable parts below ---- */
 
 #define NE_BASE	 (dev->base_addr)
@@ -104,8 +131,6 @@
 #define NESM_START_PG	0x40	/* First page of TX buffer */
 #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
 
-static int ne2k_pci_probe(void);
-static struct net_device *ne2k_pci_probe1(long ioaddr, int irq, int chip_idx);
 
 static int ne2k_pci_open(struct net_device *dev);
 static int ne2k_pci_close(struct net_device *dev);
@@ -122,47 +147,11 @@
 
 /* No room in the standard 8390 structure for extra info we need. */
 struct ne2k_pci_card {
-	struct ne2k_pci_card *next;
 	struct net_device *dev;
 	struct pci_dev *pci_dev;
 };
-/* A list of all installed devices, for removing the driver module. */
-static struct ne2k_pci_card *ne2k_card_list = NULL;
 
-static int __init ne2k_pci_init_module(void)
-{
-	/* We must emit version information. */
-	if (debug)
-		printk(KERN_INFO "%s", version);
 
-	if (ne2k_pci_probe()) {
-		printk(KERN_NOTICE "ne2k-pci.c: No useable cards found, driver NOT installed.\n");
-		return -ENODEV;
-	}
-	lock_8390_module();
-	return 0;
-}
-
-static void __exit ne2k_pci_cleanup_module(void)
-{
-	struct net_device *dev;
-	struct ne2k_pci_card *this_card;
-
-	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-	while (ne2k_card_list) {
-		dev = ne2k_card_list->dev;
-		unregister_netdev(dev);
-		release_region(dev->base_addr, NE_IO_EXTENT);
-		kfree(dev);
-		this_card = ne2k_card_list;
-		ne2k_card_list = ne2k_card_list->next;
-		kfree(this_card);
-	}
-	unlock_8390_module();
-}
-
-module_init(ne2k_pci_init_module);
-module_exit(ne2k_pci_cleanup_module);
 
 /*
   NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
@@ -177,96 +166,42 @@
   in the 'dev' and 'ei_status' structures.
 */
 
-#ifdef HAVE_DEVLIST
-struct netdev_entry netcard_drv =
-{"ne2k_pci", ne2k_pci_probe1, NE_IO_EXTENT, 0};
-#endif
 
-static int __init ne2k_pci_probe(void)
+static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
 {
-	struct pci_dev *pdev = NULL;
-	int cards_found = 0;
-	int i;
 	struct net_device *dev;
+	int i, irq, reg0, start_page, stop_page;
+	unsigned char SA_prom[32];
+	int chip_idx = ent->driver_data;
+	static unsigned version_printed = 0;
+	long ioaddr;
+	
+	if (version_printed++ == 0)
+		printk(KERN_INFO "%s", version);
 
-	if ( ! pci_present())
+	ioaddr = pci_resource_start (pdev, 0);
+	irq = pdev->irq;
+	
+	if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) {
+		printk (KERN_ERR "ne2k-pci: no I/O resource at PCI BAR #0\n");
 		return -ENODEV;
-
-	while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev)) != NULL) {
-		int pci_irq_line;
-		u16 pci_command, new_command;
-		unsigned long pci_ioaddr;
-
-		/* Note: some vendor IDs (RealTek) have non-NE2k cards as well. */
-		for (i = 0; pci_clone_list[i].vendor != 0; i++)
-			if (pci_clone_list[i].vendor == pdev->vendor
-				&& pci_clone_list[i].dev_id == pdev->device)
-				break;
-		if (pci_clone_list[i].vendor == 0)
-			continue;
-
-		pci_ioaddr = pdev->resource[0].start;
-		pci_irq_line = pdev->irq;
-		pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
-
-		/* Avoid already found cards from previous calls */
-		if (check_region(pci_ioaddr, NE_IO_EXTENT))
-			continue;
-
-		{
-			static unsigned version_printed = 0;
-			if (version_printed++ == 0)
-				printk(KERN_INFO "%s", version);
-		}
-
-		/* Activate the card: fix for brain-damaged Win98 BIOSes. */
-		new_command = pci_command | PCI_COMMAND_IO;
-		if (pci_command != new_command) {
-			printk(KERN_INFO "  The PCI BIOS has not enabled this"
-				   " NE2k clone!  Updating PCI command %4.4x->%4.4x.\n",
-				   pci_command, new_command);
-			pci_write_config_word(pdev, PCI_COMMAND, new_command);
-		}
-#ifndef __sparc__
-		if (pci_irq_line <= 0 || pci_irq_line >= NR_IRQS)
-			printk(KERN_WARNING "  WARNING: The PCI BIOS assigned this PCI NE2k"
-				   " card to IRQ %d, which is unlikely to work!.\n"
-				   KERN_WARNING " You should use the PCI BIOS setup to assign"
-				   " a valid IRQ line.\n", pci_irq_line);
-#endif
-		printk("ne2k-pci.c: PCI NE2000 clone '%s' at I/O %#lx, IRQ %d.\n",
-			   pci_clone_list[i].name, pci_ioaddr, pci_irq_line);
-		dev = ne2k_pci_probe1(pci_ioaddr, pci_irq_line, i);
-		if (dev == 0) {
-			/* Should not happen. */
-			printk(KERN_ERR "ne2k-pci: Probe of PCI card at %#lx failed.\n",
-				   pci_ioaddr);
-			continue;
-		} else {
-			struct ne2k_pci_card *ne2k_card =
-				kmalloc(sizeof(struct ne2k_pci_card), GFP_KERNEL);
-			ne2k_card->next = ne2k_card_list;
-			ne2k_card_list = ne2k_card;
-			ne2k_card->dev = dev;
-			ne2k_card->pci_dev = pdev;
-		}
-
-		cards_found++;
 	}
-
-	return cards_found ? 0 : -ENODEV;
-}
-
-static struct net_device __init *ne2k_pci_probe1(long ioaddr, int irq, int chip_idx)
-{
-	struct net_device *dev;
-	int i;
-	unsigned char SA_prom[32];
-	int start_page, stop_page;
-	int reg0 = inb(ioaddr);
-
+	
+	if (pci_enable_device (pdev)) {
+		printk (KERN_ERR "ne2k-pci: cannot enable device\n");
+		return -EIO;
+	}
+	
+	if (request_region (ioaddr, NE_IO_EXTENT, "ne2k-pci") == NULL) {
+		printk (KERN_ERR "ne2k-pci: I/O resource 0x%x @ 0x%lx busy\n",
+			NE_IO_EXTENT, ioaddr);
+		return -EBUSY;
+	}
+	
+	reg0 = inb(ioaddr);
 	if (reg0 == 0xFF)
-		return 0;
+		goto err_out_free_res;
 
 	/* Do a preliminary verification that we have a 8390. */
 	{
@@ -279,12 +214,16 @@
 		if (inb(ioaddr + EN0_COUNTER0) != 0) {
 			outb(reg0, ioaddr);
 			outb(regd, ioaddr + 0x0d);	/* Restore the old values. */
-			return 0;
+			goto err_out_free_res;
 		}
 	}
 
 	dev = init_etherdev(NULL, 0);
-
+	if (!dev) {
+		printk (KERN_ERR "ne2k-pci: cannot allocate ethernet device\n");
+		goto err_out_free_res;
+	}
+	
 	/* Reset card. Who knows what dain-bramaged state it was left in. */
 	{
 		unsigned long reset_start_time = jiffies;
@@ -298,14 +237,15 @@
 			/* Limit wait: '2' avoids jiffy roll-over. */
 			if (jiffies - reset_start_time > 2) {
 				printk("ne2k-pci: Card failure (no reset ack).\n");
-				return 0;
+				goto err_out_free_netdev;
 			}
 		
 		outb(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
 	}
 
 	if (load_8390_module("ne2k-pci.c")) {
-		return 0;
+		printk (KERN_ERR "ne2k-pci: cannot load 8390 module\n");
+		goto err_out_free_netdev;
 	}
 
 	/* Read the 16 bytes of station address PROM.
@@ -355,12 +295,10 @@
 
 	/* Allocate dev->priv and fill in 8390 specific dev fields. */
 	if (ethdev_init(dev)) {
-		printk ("%s: unable to get memory for dev->priv.\n", dev->name);
-		return 0;
+		printk (KERN_ERR "%s: unable to get memory for dev->priv.\n", dev->name);
+		goto err_out_free_netdev;
 	}
 
-	request_region(ioaddr, NE_IO_EXTENT, dev->name);
-
 	printk("%s: %s found at %#lx, IRQ %d, ",
 		   dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq);
 	for(i = 0; i < 6; i++) {
@@ -387,16 +325,26 @@
 	dev->open = &ne2k_pci_open;
 	dev->stop = &ne2k_pci_close;
 	NS8390_init(dev, 0);
-	return dev;
+	return 0;
+
+err_out_free_netdev:
+	unregister_netdev (dev);
+	kfree (dev);
+err_out_free_res:
+	release_region (ioaddr, NE_IO_EXTENT);
+	return -ENODEV;
+
 }
 
 static int
 ne2k_pci_open(struct net_device *dev)
 {
-	if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev))
+	MOD_INC_USE_COUNT;
+	if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev)) {
+		MOD_DEC_USE_COUNT;
 		return -EAGAIN;
+	}
 	ei_open(dev);
-	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -588,6 +536,58 @@
 	ei_status.dmaing &= ~0x01;
 	return;
 }
+
+
+static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+	
+	if (!dev) {
+		printk (KERN_ERR "bug! ne2k_pci_remove_one called w/o net_device\n");
+		return;
+	}
+	
+	unregister_netdev (dev);
+	release_region (dev->base_addr, NE_IO_EXTENT);
+	kfree (dev);
+}
+
+
+static struct pci_driver ne2k_driver = {
+	name:		"ne2k-pci",
+	probe:		ne2k_pci_init_one,
+	remove:		ne2k_pci_remove_one,
+	id_table:	ne2k_pci_tbl,
+};
+
+
+static int __init ne2k_pci_init(void)
+{
+	int rc;
+	
+	MOD_INC_USE_COUNT;
+	lock_8390_module();
+	
+	rc = pci_module_init (&ne2k_driver);
+	
+	/* XXX should this test CONFIG_HOTPLUG like pci_module_init? */
+	if (rc <= 0)
+		unlock_8390_module();
+
+	MOD_DEC_USE_COUNT;
+	
+	return rc;
+}
+
+
+static void __exit ne2k_pci_cleanup(void)
+{
+	pci_unregister_driver (&ne2k_driver);
+	unlock_8390_module();
+}
+
+module_init(ne2k_pci_init);
+module_exit(ne2k_pci_cleanup);
 
 
 /*
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v2.3.99-pre2/linux/drivers/net/net_init.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/net_init.c	Mon Mar 20 08:01:14 2000
@@ -1,4 +1,4 @@
-/* netdrv_init.c: Initialization for network devices. */
+/* net_init.c: Initialization for network devices. */
 /*
 	Written 1993,1994,1995 by Donald Becker.
 
@@ -27,6 +27,8 @@
 	08/11/99 - Alan Cox: Got fed up of the mess in this file and cleaned it
 			up. We now share common code and have regularised name
 			allocation setups. Abolished the 16 card limits.
+	03/19/2000 - jgarzik and Urban Widmark: init_etherdev 32-byte align
+
 */
 
 #include <linux/config.h>
@@ -139,14 +141,22 @@
 	return dev;
 }
 
-/* Fill in the fields of the device structure with ethernet-generic values.
-
-   If no device structure is passed, a new one is constructed, complete with
-   a SIZEOF_PRIVATE private data area.
-
-   If an empty string area is passed as dev->name, or a new structure is made,
-   a new name string is constructed.  The passed string area should be 8 bytes
-   long.
+/**
+ * init_etherdev - Register ethernet device
+ * @dev: An ethernet device structure to be filled in, or %NULL if a new
+ *	struct should be allocated.
+ * @sizeof_priv: Size of additional driver-private structure to be allocated
+ *	for this ethernet device
+ *
+ * Fill in the fields of the device structure with ethernet-generic values.
+ *
+ * If no device structure is passed, a new one is constructed, complete with
+ * a private data area of size @sizeof_priv.  A 32-byte (not bit)
+ * alignment is enforced for this private data area.
+ *
+ * If an empty string area is passed as dev->name, or a new structure is made,
+ * a new name string is constructed.  The passed string area should be 8 bytes
+ * long.
  */
 
 struct net_device *init_etherdev(struct net_device *dev, int sizeof_priv)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/pcmcia/3c575_cb.c linux/drivers/net/pcmcia/3c575_cb.c
--- v2.3.99-pre2/linux/drivers/net/pcmcia/3c575_cb.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/pcmcia/3c575_cb.c	Mon Mar 20 07:53:51 2000
@@ -12,10 +12,16 @@
 	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
+
+	Linux Kernel Additions:
+	
+	LK1.1.2 (March 19, 2000)
+	* New PCI interface (jgarzik)
+
 */
 
 static char *version =
-"3c59x.c:v0.99L 5/28/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c575_cb.c:v0.99L+LK1.1.2  3/19/2000  Donald Becker and others  http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
@@ -64,6 +70,7 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
@@ -85,9 +92,6 @@
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(compaq_ioaddr, "i");
-MODULE_PARM(compaq_irq, "i");
-MODULE_PARM(compaq_device_id, "i");
 
 /* Operational parameter that usually are not changed. */
 
@@ -103,6 +107,10 @@
    code size of a per-interface flag is not worthwhile. */
 static char mii_preamble_required = 0;
 
+#define PFX "3c575_cb: "
+
+
+
 /*
 				Theory of Operation
 
@@ -186,76 +194,134 @@
 	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,
 };
-struct pci_id_info {
-	const char *name;
-	u16	vendor_id, device_id, device_id_mask, flags;
-	int drv_flags, io_size;
-	struct net_device *(*probe1)(struct pci_dev *pdev, struct net_device *dev,
-				     long ioaddr, int irq, int chip_idx, int fnd_cnt);
-};
 
 enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4,
 	   HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, };
-static struct net_device *vortex_probe1(struct pci_dev *pdev,
-					struct net_device *dev, long ioaddr,
-					int irq, int dev_id, int card_idx);
-static struct pci_id_info pci_tbl[] = {
-	{"3c590 Vortex 10Mbps",			0x10B7, 0x5900, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
-	{"3c595 Vortex 100baseTx",		0x10B7, 0x5950, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
-	{"3c595 Vortex 100baseT4",		0x10B7, 0x5951, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
-	{"3c595 Vortex 100base-MII",	0x10B7, 0x5952, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
-	{"3Com Vortex",					0x10B7, 0x5900, 0xff00,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
-	{"3c900 Boomerang 10baseT",		0x10B7, 0x9000, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
-	{"3c900 Boomerang 10Mbps Combo", 0x10B7, 0x9001, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
-	{"3c900 Cyclone 10Mbps Combo", 0x10B7, 0x9005, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3c900B-FL Cyclone 10base-FL",	0x10B7, 0x900A, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3c905 Boomerang 100baseTx",	0x10B7, 0x9050, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
-	{"3c905 Boomerang 100baseT4",	0x10B7, 0x9051, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
-	{"3c905B Cyclone 100baseTx",	0x10B7, 0x9055, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, vortex_probe1},
-	{"3c905B Cyclone 10/100/BNC",	0x10B7, 0x9058, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, vortex_probe1},
-	{"3c905B-FX Cyclone 100baseFx",	0x10B7, 0x905A, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3c905C Tornado",	0x10B7, 0x9200, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3c980 Cyclone",	0x10B7, 0x9800, 0xfff0,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3cSOHO100-TX Hurricane",	0x10B7, 0x7646, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3c555 Laptop Hurricane",	0x10B7, 0x5055, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
-	{"3c575 Boomerang CardBus",		0x10B7, 0x5057, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
-	{"3CCFE575 Cyclone CardBus",	0x10B7, 0x5157, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
-	 128, vortex_probe1},
-	{"3CCFE575CT Cyclone CardBus",	0x10B7, 0x5257, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
-	 128, vortex_probe1},
-	{"3CCFE656 Cyclone CardBus",	0x10B7, 0x6560, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
-	 128, vortex_probe1},
-	{"3CCFEM656 Cyclone CardBus",	0x10B7, 0x6562, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
-	 128, vortex_probe1},
-	{"3c575 series CardBus (unknown version)", 0x10B7, 0x5057, 0xf0ff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
-	{"3Com Boomerang (unknown version)",	0x10B7, 0x9000, 0xff00,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
+
+
+enum vortex_chips {
+	CH_3C590 = 0,
+	CH_3C595_1,
+	CH_3C595_2,
+	CH_3C595_3,
+	CH_VORTEX,
+	CH_3C900_1,
+	CH_3C900_2,
+	CH_3C900_3,
+	CH_3C900B_FL,
+	CH_3C905_1,
+	CH_3C905_2,
+	CH_3C905B_1,
+	CH_3C905B_2,
+	CH_3C905B_FX,
+	CH_3C905C,
+	CH_3C980,
+	CH_3CSOHO100_TX,
+	CH_3C555,
+	CH_3C575_1,
+	CH_3CCFE575,
+	CH_3CCFE575CT,
+	CH_3CCFE656,
+	CH_3CCFEM656,
+	CH_3C575_2,
+	CH_BOOMERANG,
+};
+
+
+/* note: this array directly indexed by above enums, and MUST
+ * be kept in sync with both the enums above, and the PCI device
+ * table below
+ */
+static struct vortex_chip_info {
+	const char *name;
+	int flags;
+	int drv_flags;
+	int io_size;
+} vortex_info_tbl[] = {
+	{"3c590 Vortex 10Mbps",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+	{"3c595 Vortex 100baseTx",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+	{"3c595 Vortex 100baseT4",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+	{"3c595 Vortex 100base-MII",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+	{"3Com Vortex",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
+	{"3c900 Boomerang 10baseT",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
+	{"3c900 Boomerang 10Mbps Combo",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
+	{"3c900 Cyclone 10Mbps Combo",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3c900B-FL Cyclone 10base-FL",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3c905 Boomerang 100baseTx",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
+	{"3c905 Boomerang 100baseT4",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
+	{"3c905B Cyclone 100baseTx",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+	{"3c905B Cyclone 10/100/BNC",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+	{"3c905B-FX Cyclone 100baseFx",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3c905C Tornado",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3c980 Cyclone",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3cSOHO100-TX Hurricane",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3c555 Laptop Hurricane",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+	{"3c575 Boomerang CardBus",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
+	{"3CCFE575 Cyclone CardBus",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, },
+	{"3CCFE575CT Cyclone CardBus",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, },
+	{"3CCFE656 Cyclone CardBus",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, },
+	{"3CCFEM656 Cyclone CardBus",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, },
+	{"3c575 series CardBus (unknown version)",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
+	{"3Com Boomerang (unknown version)",
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
+	{0,}, /* 0 terminated list. */
+};
+
+
+static struct pci_device_id vortex_pci_tbl[] __devinit = {
+	{ 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C590 },
+	{ 0x10B7, 0x5950, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_1 },
+	{ 0x10B7, 0x5951, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_2 },
+	{ 0x10B7, 0x5952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_3 },
+	{ 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VORTEX },
+	{ 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_1 },
+	{ 0x10B7, 0x9001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_2 },
+	{ 0x10B7, 0x9005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_3 },
+	{ 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL },
+	{ 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 },
+	{ 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 },
+	{ 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 },
+	{ 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 },
+	{ 0x10B7, 0x905A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_FX },
+	{ 0x10B7, 0x9200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C },
+	{ 0x10B7, 0x9800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C980 },
+	{ 0x10B7, 0x7646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CSOHO100_TX },
+	{ 0x10B7, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C555 },
+	{ 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_1 },
+	{ 0x10B7, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575 },
+	{ 0x10B7, 0x5257, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575CT },
+	{ 0x10B7, 0x6560, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE656 },
+	{ 0x10B7, 0x6562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656 },
+	{ 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_2 },
+	{ 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_BOOMERANG },
 	{0,},						/* 0 terminated list. */
 };
+MODULE_DEVICE_TABLE(pci, vortex_pci_tbl);
+
 
 /* Operational definitions.
    These are not used by other compilation units and thus are not
@@ -400,7 +466,7 @@
 	/* The addresses of transmit- and receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	struct net_device *next_module;
+	struct net_device *next_module;		/* NULL if PCI device */
 	void *priv_addr;
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
@@ -463,9 +529,8 @@
   { "Default",	 0,			0xFF, XCVR_10baseT, 10000},
 };
 
-#ifndef CARDBUS
-static int vortex_scan(struct net_device *dev, struct pci_id_info pci_tbl[]);
-#endif
+static int vortex_probe1(struct pci_dev *pdev, long ioaddr, int irq,
+				   int chip_idx, int card_idx);
 static void vortex_up(struct net_device *dev);
 static void vortex_down(struct net_device *dev);
 static int vortex_open(struct net_device *dev);
@@ -484,7 +549,6 @@
 static void set_rx_mode(struct net_device *dev);
 static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void vortex_tx_timeout(struct net_device *dev);
-static void acpi_wake(struct pci_dev *pdev);
 static void acpi_set_WOL(struct net_device *dev);
 
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
@@ -492,280 +556,181 @@
 #define MAX_UNITS 8
 static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1,};
 static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
-/* A list of all installed Vortex devices, for removing the driver module. */
-static struct net_device *root_vortex_dev = NULL;
 
-#ifdef MODULE
-#ifndef CARDBUS
-/* Variables to work-around the Compaq PCI BIOS32 problem. */
-static int compaq_ioaddr = 0, compaq_irq = 0, compaq_device_id = 0x5900;
-#endif
 
-#ifdef CARDBUS
+/* A list of all installed Vortex EISA devices, for removing the driver module. */
+static struct net_device *root_vortex_eisa_dev = NULL;
 
-#include <pcmcia/driver_ops.h>
+static int vortex_cards_found = 0;
 
-static void vortex_reap(void)
-{
-	struct net_device **devp, **next;
-	printk(KERN_DEBUG "vortex_reap()\n");
-	for (devp = &root_vortex_dev; *devp; devp = next) {
-		struct vortex_private *vp = (*devp)->priv;
-		next = &vp->next_module;
-		if (vp->open || !vp->reap) continue;
-		unregister_netdev(*devp);
-		if (vp->cb_fn_base) iounmap(vp->cb_fn_base);
-		kfree(*devp);
-		kfree(vp->priv_addr);
-		*devp = *next; next = devp;
-	}
-}
 
-static dev_node_t *vortex_attach(dev_locator_t *loc)
-{
-	u16 dev_id, vendor_id;
-	u32 io;
-	u8 irq;
-	struct net_device *dev;
-	int chip_idx;
-	struct pci_dev *pdev;
 
-	vortex_reap();
-	if (loc->bus != LOC_PCI) return NULL;
-	pdev = pci_find_slot (loc->b.pci.bus, loc->b.pci.devfn);
-	if (!pdev) return NULL;
-	io = pci_resource_start (pdev, 0);
-	irq = pdev->irq;
-	vendor_id = pdev->vendor;
-	dev_id = pdev->device;
-	printk(KERN_INFO "vortex_attach(bus %d, function %d, device %4.4x)\n",
-		   pdev->bus->number, pdev->devfn, dev_id);
-	if (io == 0 || irq == 0) {
-		printk(KERN_ERR "The 3Com CardBus Ethernet interface was not "
-			   "assigned an %s.\n" KERN_ERR "  It will not be activated.\n",
-			   io == 0 ? "I/O address" : "IRQ");
-		return NULL;
-	}
-	for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
-		if (vendor_id == pci_tbl[chip_idx].vendor_id
-			&& (dev_id & pci_tbl[chip_idx].device_id_mask) ==
-			pci_tbl[chip_idx].device_id)
-			break;
-	if (pci_tbl[chip_idx].vendor_id == 0) { 		/* Compiled out! */
-		printk(KERN_INFO "Unable to match chip type %4.4x %4.4x in "
-			   "vortex_attach().\n", vendor_id, dev_id);
-		return NULL;
-	}
-	dev = vortex_probe1(pdev, NULL, io, irq, chip_idx, MAX_UNITS+1);
-	if (dev) {
-		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
-		strcpy(node->dev_name, dev->name);
-		node->major = node->minor = 0;
-		node->next = NULL;
-		MOD_INC_USE_COUNT;
-		return node;
-	}
-	return NULL;
-}
-
-static void vortex_detach(dev_node_t *node)
-{
-	struct net_device *dev, *next;
-	printk(KERN_DEBUG "vortex_detach(%s)\n", node->dev_name);
-	for (dev = root_vortex_dev; dev; dev = next) {
-		next = ((struct vortex_private *)dev->priv)->next_module;
-		if (strcmp(dev->name, node->dev_name) == 0) break;
-	}
-	if (dev && dev->priv) {
-		struct vortex_private *vp = dev->priv;
-		if (vp->open) vortex_down(dev);
-		vp->reap = 1;
-		kfree(node);
-		MOD_DEC_USE_COUNT;
-	}
-}
 
-static void vortex_suspend(dev_node_t *node)
+static void vortex_suspend (struct pci_dev *pdev)
 {
-	struct net_device *dev, *next;
-	printk(KERN_DEBUG "vortex_suspend(%s)\n", node->dev_name);
-	for (dev = root_vortex_dev; dev; dev = next) {
-		next = ((struct vortex_private *)dev->priv)->next_module;
-		if (strcmp(dev->name, node->dev_name) == 0) break;
-	}
+	struct net_device *dev = pdev->driver_data;
+
+	printk(KERN_DEBUG "vortex_suspend(%s)\n", dev->name);
+
 	if (dev && dev->priv) {
 		struct vortex_private *vp = (struct vortex_private *)dev->priv;
-		if (vp->open) vortex_down(dev);
+		if (vp->open) {
+			netif_device_detach(dev);
+			vortex_down(dev);
+		}
 	}
 }
 
-static void vortex_resume(dev_node_t *node)
+
+static void vortex_resume (struct pci_dev *pdev)
 {
-	struct net_device *dev, *next;
-	printk(KERN_DEBUG "vortex_resume(%s)\n", node->dev_name);
-	for (dev = root_vortex_dev; dev; dev = next) {
-		next = ((struct vortex_private *)dev->priv)->next_module;
-		if (strcmp(dev->name, node->dev_name) == 0) break;
-	}
+	struct net_device *dev = pdev->driver_data;
+
+	printk(KERN_DEBUG "vortex_resume(%s)\n", dev->name);
+
 	if (dev && dev->priv) {
 		struct vortex_private *vp = (struct vortex_private *)dev->priv;
-		if (vp->open) vortex_up(dev);
+		if (vp->open) {
+			vortex_up(dev);
+			netif_device_attach(dev);
+		}
 	}
 }
 
-struct driver_operations vortex_ops = {
-	"3c575_cb", vortex_attach, vortex_suspend, vortex_resume, vortex_detach
-};
-
-#endif  /* Cardbus support */
 
-int init_module(void)
-{
-	if (vortex_debug)
-		printk(KERN_INFO "%s", version);
-#ifdef CARDBUS
-	register_driver(&vortex_ops);
-	return 0;
-#else
-	return vortex_scan(0, pci_tbl);
-#endif
-}
-
-#else
-int tc59x_probe(struct net_device *dev)
+/* returns count found (>= 0), or negative on error */
+static int __init vortex_eisa_init (void)
 {
-	static int did_version = -1;
-	if (++did_version <= 0)
-		printk(KERN_INFO "%s", version);
-	return vortex_scan(dev, pci_tbl);
-}
-#endif  /* not MODULE */
-
-#ifndef CARDBUS
-static int vortex_scan(struct net_device *dev, struct pci_id_info pci_tbl[])
-{
-	int cards_found = 0;
-
-	/* Allow an EISA-only driver. */
-				struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
-				ioaddr = pdev->base_address[0] & ~3;
-				irq = pdev->irq;
-			}
+	long ioaddr;
+	int rc;
+	int orig_cards_found = vortex_cards_found;
 
-			if (ioaddr == 0) {
-				printk(KERN_WARNING "  A 3Com network adapter has been found, "
-					   "however it has not been assigned an I/O address.\n"
-					   "  You may need to power-cycle the machine for this "
-					   "device to work!\n");
-				continue;
-			}
+	/* Now check all slots of the EISA bus. */
+	if (!EISA_bus)
+		return 0;
 
-			/* Activate the card. */
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_COMMAND, &pci_command);
-			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-			if (pci_command != new_command) {
-				printk(KERN_INFO "  The PCI BIOS has not enabled the device "
-					   "at %d/%d. Updating PCI command %4.4x->%4.4x.\n",
-					   pci_bus, pci_device_fn, pci_command, new_command);
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-										  PCI_COMMAND, new_command);
-			}
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		int device_id;
 
-			dev = vortex_probe1(pci_bus, pci_device_fn, dev, ioaddr, irq,
-								chip_idx, cards_found);
+		if (request_region(ioaddr, VORTEX_TOTAL_SIZE, "vortex") == NULL)
+			continue;
 
-			if (dev) {
-				/* Get and check the latency values.  On the 3c590 series
-				   the latency timer must be set to the maximum value to avoid
-				   data corruption that occurs when the timer expires during
-				   a transfer -- a bug in the Vortex chip only. */
-				u8 pci_latency;
-				u8 new_latency = (device & 0xff00) == 0x5900 ? 248 : 32;
-				
-				pcibios_read_config_byte(pci_bus, pci_device_fn,
-										 PCI_LATENCY_TIMER, &pci_latency);
-				if (pci_latency < new_latency) {
-					printk(KERN_INFO "%s: Overriding PCI latency"
-						   " timer (CFLT) setting of %d, new value is %d.\n",
-						   dev->name, pci_latency, new_latency);
-					pcibios_write_config_byte(pci_bus, pci_device_fn,
-											  PCI_LATENCY_TIMER, new_latency);
-				}
-				dev = 0;
-				cards_found++;
-			}
+		/* Check the standard EISA ID register for an encoded '3Com'. */
+		if (inw(ioaddr + 0xC80) != 0x6d50) {
+			release_region (ioaddr, VORTEX_TOTAL_SIZE);
+			continue;
 		}
-	}
 
-	/* Now check all slots of the EISA bus. */
-	if (EISA_bus) {
-		static long ioaddr = 0x1000;
-		for ( ; ioaddr < 0x9000; ioaddr += 0x1000) {
-			int device_id;
-			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
-				continue;
-			/* Check the standard EISA ID register for an encoded '3Com'. */
-			if (inw(ioaddr + 0xC80) != 0x6d50)
-				continue;
-			/* Check for a product that we support, 3c59{2,7} any rev. */
-			device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83);
-			if ((device_id & 0xFF00) != 0x5900)
-				continue;
-			vortex_probe1(0, 0, dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
-						  4, cards_found);
-			dev = 0;
-			cards_found++;
+		/* Check for a product that we support, 3c59{2,7} any rev. */
+		device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83);
+		if ((device_id & 0xFF00) != 0x5900) {
+			release_region (ioaddr, VORTEX_TOTAL_SIZE);
+			continue;
 		}
-	}
 
-#ifdef MODULE
-	/* Special code to work-around the Compaq PCI BIOS32 problem. */
-	if (compaq_ioaddr) {
-		vortex_probe1(0, 0, dev, compaq_ioaddr, compaq_irq,
-					  compaq_device_id, cards_found++);
-		dev = 0;
+		rc = vortex_probe1(NULL, ioaddr, inw(ioaddr + 0xC88) >> 12,
+				   4, /* XXX is 4 correct eisa idx? */
+				   vortex_cards_found);
+		if (rc == 0)
+			vortex_cards_found++;
+		else
+			release_region (ioaddr, VORTEX_TOTAL_SIZE);
 	}
-#endif
 
-	return cards_found ? 0 : -ENODEV;
+	return vortex_cards_found - orig_cards_found;
 }
-#endif  /* ! Cardbus */
 
-static struct net_device *vortex_probe1(struct pci_dev *pdev,
-					struct net_device *dev, long ioaddr,
-					int irq, int chip_idx, int card_idx)
+
+/* returns count (>= 0), or negative on error */
+static int __devinit vortex_init_one (struct pci_dev *pdev,
+				      const struct pci_device_id *ent)
+{
+	int rc;
+
+	rc = vortex_probe1 (pdev, pci_resource_start (pdev, 0), pdev->irq,
+			    ent->driver_data, vortex_cards_found);
+	if (rc == 0)
+		vortex_cards_found++;
+	
+	return rc;
+}
+
+
+/* NOTE: pdev can be NULL, for the case of an EISA driver */
+static int __devinit vortex_probe1(struct pci_dev *pdev,
+				   long ioaddr, int irq,
+				   int chip_idx, int card_idx)
 {
 	struct vortex_private *vp;
 	int option;
 	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */
 	int i;
+	struct net_device *dev;
+	static int printed_version = 0;
+	
+	if (!printed_version) {
+		printk (KERN_INFO "%s", version);
+		printed_version = 1;
+	}
 
-	dev = init_etherdev(dev, 0);
-
-	printk(KERN_INFO "%s: 3Com %s at 0x%lx, ",
-		   dev->name, pci_tbl[chip_idx].name, ioaddr);
-
+	dev = init_etherdev(NULL, sizeof(*vp));
+	if (!dev) {
+		printk (KERN_ERR PFX "unable to allocate etherdev, aborting\n");
+		return -ENOMEM;
+	}
+	
+	printk(KERN_INFO "%s: 3Com %s %s at 0x%lx, ",
+	       dev->name,
+	       pdev ? "PCI" : "EISA",
+	       vortex_info_tbl[chip_idx].name,
+	       ioaddr);
+
+	/* private struct aligned and zeroed by init_etherdev */
+	vp = dev->priv;
+	vp->priv_addr = vp;
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 	dev->mtu = mtu;
 
-	/* Make certain the descriptor lists are aligned. */
-	{
-		void *mem = kmalloc(sizeof(*vp) + 15, GFP_KERNEL);
-		vp =  (void *)(((long)mem + 15) & ~15);
-		memset(vp, 0, sizeof(*vp));
-		vp->priv_addr = mem;
+	/* module list only for EISA devices */
+	if (pdev == NULL) {
+		vp->next_module = root_vortex_eisa_dev;
+		root_vortex_eisa_dev = dev;
+	}
+	
+	/* PCI-only startup logic */
+	if (pdev) {
+		/* EISA resources already marked, so only PCI needs to do this here */
+		if (!request_region (ioaddr, vortex_info_tbl[chip_idx].io_size,
+				     dev->name)) {
+			printk (KERN_ERR "%s: Cannot reserve I/O resource 0x%x @ 0x%lx, aborting\n",
+				dev->name, vortex_info_tbl[chip_idx].io_size, ioaddr);
+			kfree (dev);
+			return -EBUSY;
+		}
+
+		/* wake up and enable device */		
+		if (pci_enable_device (pdev)) {
+			printk (KERN_ERR "%s: Cannot enable device, aborting\n", dev->name);
+			release_region (ioaddr, vortex_info_tbl[chip_idx].io_size);
+			kfree (dev);
+			return -EIO;
+		}
+
+		/* enable bus-mastering if necessary */		
+		if (vortex_info_tbl[chip_idx].flags & PCI_USES_MASTER)
+			pci_set_master (pdev);
 	}
-	dev->priv = vp;
-
-	vp->next_module = root_vortex_dev;
-	root_vortex_dev = dev;
 
 	vp->lock = SPIN_LOCK_UNLOCKED;
 	vp->chip_id = chip_idx;
 	vp->pdev = pdev;
 
+	/* if we are a PCI driver, we store info in pdev->driver_data
+	 * instead of a module list */	
+	if (pdev)
+		pdev->driver_data = dev;
+
 	/* The lower four bits are the media type. */
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -793,7 +758,7 @@
 	EL3WINDOW(0);
 	for (i = 0; i < 0x40; i++) {
 		int timer;
-#ifdef CARDBUS
+#if 1 /* ifdef CARDBUS */
 		outw(0x230 + i, ioaddr + Wn0EepromCmd);
 #else
 		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
@@ -835,7 +800,7 @@
 			   dev->irq);
 #endif
 
-	if (pci_tbl[vp->chip_id].drv_flags & HAS_CB_FNS) {
+	if (pdev && vortex_info_tbl[vp->chip_id].drv_flags & HAS_CB_FNS) {
 		u32 fn_st_addr;			/* Cardbus function status space */
 		fn_st_addr = pci_resource_start (pdev, 2);
 		if (fn_st_addr)
@@ -922,9 +887,6 @@
 		vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
 	}
 
-	/* We do a request_region() to register /proc/ioports info. */
-	request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
-
 	/* The 3c59x-specific entries in the device structure. */
 	dev->open = &vortex_open;
 	dev->hard_start_xmit = &vortex_start_xmit;
@@ -932,10 +894,10 @@
 	dev->get_stats = &vortex_get_stats;
 	dev->do_ioctl = &vortex_ioctl;
 	dev->set_multicast_list = &set_rx_mode;
-	dev->tx_timeout = vortex_tx_timeout;
+	dev->tx_timeout = &vortex_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 
-	return dev;
+	return 0;
 }
 
 static void wait_for_completion(struct net_device *dev, int cmd)
@@ -956,11 +918,13 @@
 	long ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	union wn3_config config;
-	int i;
-	
-	/* Should be if(HAS_ACPI) */
-	acpi_wake(vp->pdev);
+	int i, device_id;
 
+	if (vp->pdev)
+		device_id = vp->pdev->device;
+	else
+		device_id = 0x5900; /* EISA */
+	
 	/* Before initializing select the active media port. */
 	EL3WINDOW(3);
 	config.i = inl(ioaddr + Wn3_Config);
@@ -972,7 +936,7 @@
 				   media_tbl[vp->media_override].name);
 		dev->if_port = vp->media_override;
 	} else if (vp->autoselect) {
-		if (pci_tbl[vp->chip_id].drv_flags & HAS_NWAY)
+		if (vortex_info_tbl[vp->chip_id].drv_flags & HAS_NWAY)
 			dev->if_port = XCVR_NWAY;
 		else {
 			/* Find first available media type, starting with 100baseTx. */
@@ -995,7 +959,7 @@
 
 	vp->full_duplex = vp->force_fd;
 	config.u.xcvr = dev->if_port;
-	if ( ! (pci_tbl[vp->chip_id].drv_flags & HAS_NWAY))
+	if ( ! (vortex_info_tbl[vp->chip_id].drv_flags & HAS_NWAY))
 		outl(config.i, ioaddr + Wn3_Config);
 
 	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
@@ -1045,12 +1009,11 @@
 	if (vp->cb_fn_base) {
 		u_short n = inw(ioaddr + Wn2_ResetOptions);
 		/* Inverted LED polarity */
-		if (pci_tbl[vp->chip_id].device_id != 0x5257)
+		if (device_id != 0x5257)
 			n |= 0x0010;
 		/* Inverted polarity of MII power bit */
-		if ((pci_tbl[vp->chip_id].device_id == 0x6560) ||
-			(pci_tbl[vp->chip_id].device_id == 0x6562) ||
-			(pci_tbl[vp->chip_id].device_id == 0x5257))
+		if ((device_id == 0x6560) || (device_id == 0x6562) ||
+			(device_id == 0x5257))
 			n |= 0x4000;
 		outw(n, ioaddr + Wn2_ResetOptions);
 	}
@@ -2088,66 +2051,107 @@
 	/* Change the power state to D3; RxEnable doesn't take effect. */
 	pci_write_config_word(vp->pdev, 0xe0, 0x8103);
 }
-/* Change from D3 (sleep) to D0 (active).
-   Problem: The Cyclone forgets all PCI config info during the transition! */
-static void acpi_wake(struct pci_dev *pdev)
-{
-	u32 base0, base1, romaddr;
-	u16 pci_command, pwr_command;
-	u8  pci_latency, pci_cacheline, irq;
 
-	pci_read_config_word(pdev, 0xe0, &pwr_command);
-	if ((pwr_command & 3) == 0)
+
+static void __devexit vortex_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+	struct vortex_private *vp;
+
+	if (!dev)
 		return;
-	pci_read_config_word( pdev, PCI_COMMAND, &pci_command);
-	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base0);
-	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &base1);
-	pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &romaddr);
-	pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &pci_latency);
-	pci_read_config_byte( pdev, PCI_CACHE_LINE_SIZE, &pci_cacheline);
-	pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq);
-
-	pci_write_config_word( pdev, 0xe0, 0x0000);
-	pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, base0);
-	pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, base1);
-	pci_write_config_dword(pdev, PCI_ROM_ADDRESS, romaddr);
-	pci_write_config_byte( pdev, PCI_INTERRUPT_LINE, irq);
-	pci_write_config_byte( pdev, PCI_LATENCY_TIMER, pci_latency);
-	pci_write_config_byte( pdev, PCI_CACHE_LINE_SIZE, pci_cacheline);
-	pci_write_config_word( pdev, PCI_COMMAND, pci_command | 5);
+
+	vp = (void *)(dev->priv);
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	unregister_netdev(dev);
+	outw(TotalReset, dev->base_addr + EL3_CMD);
+	release_region(dev->base_addr, vortex_info_tbl[vp->chip_id].io_size);
+	kfree(dev);
 }
 
-#ifdef MODULE
-void cleanup_module(void)
+
+static struct pci_driver vortex_driver = {
+	name:		"3c575_cb",
+	probe:		vortex_init_one,
+	remove:		vortex_remove_one,
+	suspend:	vortex_suspend,
+	resume:		vortex_resume,
+	id_table:	vortex_pci_tbl,
+};
+
+
+static int vortex_have_pci = 0;
+static int vortex_have_eisa = 0;
+
+
+static int __init vortex_init (void)
 {
-	struct net_device *next_dev;
+	int rc;
+	
+	MOD_INC_USE_COUNT;
+	
+	rc = pci_module_init (&vortex_driver);
+	if (rc < 0)
+		goto out;
+	if (rc > 0)
+		vortex_have_pci = 1;
+
+	rc = vortex_eisa_init ();
+	if (rc < 0)
+		goto out;
+	if (rc > 0)
+		vortex_have_eisa = 1;
 
-#ifdef CARDBUS
-	unregister_driver(&vortex_ops);
-	vortex_reap();
-#endif
+out:
+	MOD_DEC_USE_COUNT;
+	return rc;
+}
 
-	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-	while (root_vortex_dev) {
-		struct vortex_private *vp=(void *)(root_vortex_dev->priv);
-		next_dev = vp->next_module;
-		unregister_netdev(root_vortex_dev);
-		outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD);
-		release_region(root_vortex_dev->base_addr,
-					   pci_tbl[vp->chip_id].io_size);
-		kfree(root_vortex_dev);
-		kfree(vp->priv_addr);
-		root_vortex_dev = next_dev;
+
+static void __exit vortex_eisa_cleanup (void)
+{
+	struct net_device *dev, *tmp;
+	struct vortex_private *vp;
+	long ioaddr;
+
+	dev = root_vortex_eisa_dev;
+
+	while (dev) {
+		vp = dev->priv;
+		ioaddr = dev->base_addr;
+
+		unregister_netdev (dev);
+		outw (TotalReset, ioaddr + EL3_CMD);
+		release_region (ioaddr, VORTEX_TOTAL_SIZE);
+
+		tmp = dev;
+		dev = vp->next_module;
+
+		kfree (tmp);
 	}
 }
 
-#endif  /* MODULE */
+
+static void __exit vortex_cleanup (void)
+{
+	if (vortex_have_pci)
+		pci_unregister_driver (&vortex_driver);
+	if (vortex_have_eisa)
+		vortex_eisa_cleanup ();
+}
+
+
+module_init(vortex_init);
+module_exit(vortex_cleanup);
+
+
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
- *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/linux/pcmcia-cs-3.0.9/include/"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c575_cb.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c575_cb.c"
+ *  cardbus-compile-command: "gcc -DCONFIG_HOTPLUG -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c575_cb.c -o 3c575_cb.o -I/usr/src/linux/pcmcia-cs-3.0.9/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in
--- v2.3.99-pre2/linux/drivers/net/pcmcia/Config.in	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/pcmcia/Config.in	Tue Mar 21 12:39:17 2000
@@ -18,6 +18,7 @@
 
    if [ "$CONFIG_CARDBUS" = "y" ]; then
       dep_tristate '  3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m
+      tristate '  Xircom Tulip-like CardBus support' CONFIG_PCMCIA_XIRTULIP
    fi
 
    bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile
--- v2.3.99-pre2/linux/drivers/net/pcmcia/Makefile	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/pcmcia/Makefile	Mon Mar 20 07:56:33 2000
@@ -19,8 +19,6 @@
 # Things that need to export symbols
 export-objs	:= ray_cs.o
 
-CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE
-
 # 16-bit client drivers
 obj-$(CONFIG_PCMCIA_3C589)	+= 3c589_cs.o
 obj-$(CONFIG_PCMCIA_3C574)	+= 3c574_cs.o
@@ -39,6 +37,7 @@
 
 # Cardbus client drivers
 obj-$(CONFIG_PCMCIA_3C575)	+= 3c575_cb.o
+obj-$(CONFIG_PCMCIA_XIRTULIP)	+= xircom_tulip_cb.o
 
 O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
 OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/pcmcia/xircom_tulip_cb.c linux/drivers/net/pcmcia/xircom_tulip_cb.c
--- v2.3.99-pre2/linux/drivers/net/pcmcia/xircom_tulip_cb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/pcmcia/xircom_tulip_cb.c	Tue Mar 21 12:39:17 2000
@@ -0,0 +1,3153 @@
+/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
+/*
+	Written/copyright 1994-1999 by Donald Becker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	This driver is for the Digital "Tulip" Ethernet adapter interface.
+	It should work with most DEC 21*4*-based chips/ethercards, as well as
+	with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
+
+	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
+
+	Support and updates available at
+	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
+*/
+
+#define SMP_CHECK
+#define CARDBUS 1
+static const char version[] = "xircom_tulip_cb.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n";
+
+/* A few user-configurable values. */
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 25;
+
+#define MAX_UNITS 8
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[MAX_UNITS] = {0, };
+static int options[MAX_UNITS] = {0, };
+static int mtu[MAX_UNITS] = {0, };			/* Jumbo MTU for interfaces. */
+
+/*  The possible media types that can be set in options[] are: */
+static const char * const medianame[] = {
+	"10baseT", "10base2", "AUI", "100baseTx",
+	"10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
+	"100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
+	"10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+};
+
+/* Keep the ring sizes a power of two for efficiency.
+   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	16
+#define RX_RING_SIZE	32
+
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
+#ifdef __alpha__
+static int rx_copybreak = 1518;
+#else
+static int rx_copybreak = 100;
+#endif
+
+/*
+  Set the bus performance register.
+	Typical: Set 16 longword cache alignment, no burst limit.
+	Cache alignment bits 15:14	     Burst length 13:8
+		0000	No alignment  0x00000000 unlimited		0800 8 longwords
+		4000	8  longwords		0100 1 longword		1000 16 longwords
+		8000	16 longwords		0200 2 longwords	2000 32 longwords
+		C000	32  longwords		0400 4 longwords
+	Warning: many older 486 systems are broken and require setting 0x00A04800
+	   8 longword cache alignment, 8 longword burst.
+	ToDo: Non-Intel setting could be better.
+*/
+
+#if defined(__alpha__)
+static int csr0 = 0x01A00000 | 0xE000;
+#elif defined(__powerpc__)
+static int csr0 = 0x01B00000 | 0x8000;
+#elif defined(__sparc__)
+static int csr0 = 0x01B00080 | 0x8000;
+#elif defined(__i386__)
+static int csr0 = 0x01A00000 | 0x8000;
+#else
+#warning Processor architecture undefined!
+static int csr0 = 0x00A00000 | 0x4800;
+#endif
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (4*HZ)
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
+#define FULL_DUPLEX_MAGIC	0x6969
+
+#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
+#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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+
+/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
+   This is only in the support-all-kernels source code. */
+
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(reverse_probe, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(csr0, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+#define RUN_AT(x) (jiffies + (x))
+
+#define tulip_debug debug
+#ifdef TULIP_DEBUG
+static int tulip_debug = TULIP_DEBUG;
+#else
+static int tulip_debug = 1;
+#endif
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the DECchip "Tulip", Digital's
+single-chip ethernet controllers for PCI.  Supported members of the family
+are the 21040, 21041, 21140, 21140A, 21142, and 21143.  Similar work-alike
+chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
+supported. 
+
+These chips are used on at least 140 unique PCI board designs.  The great
+number of chips and board designs supported is the reason for the
+driver size and complexity.  Almost of the increasing complexity is in the
+board configuration and media selection code.  There is very little
+increasing in the operational critical path length.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board.  The system BIOS preferably should assign the
+PCI INTA signal to an otherwise unused system IRQ line.
+
+Some boards have EEPROMs tables with default media entry.  The factory default
+is usually "autoselect".  This should only be overridden when using
+transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
+for forcing full-duplex when used with old link partners that do not do
+autonegotiation. 
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
+This driver uses statically allocated rings of Rx and Tx descriptors, set at
+compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
+for the Rx ring buffers at open() time and passes the skb->data field to the
+Tulip as receive data buffers.  When an incoming frame is less than
+RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
+copied to the new skbuff.  When the incoming frame is larger, the skbuff is
+passed directly up the protocol stack and replaced by a newly allocated
+skbuff.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  For small frames the copying cost is negligible (esp. considering
+that we are pre-loading the cache with immediately useful header
+information).  For large frames the copying cost is non-trivial, and the
+larger copy might flush the cache of useful data.  A subtle aspect of this
+choice is that the Tulip only receives into longword aligned buffers, thus
+the IP header at offset 14 isn't longword aligned for further processing.
+Copied frames are put into the new skbuff at an offset of "+2", thus copying
+has the beneficial effect of aligning the IP header and preloading the
+cache.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'tp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.)	 After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero.	 Iff the 'tp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
+Greg LaPolla at Linksys provided PNIC and other Linksys boards.
+Znyx provided a four-port card for testing.
+
+IVb. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
+http://www.national.com/pf/DP/DP83840A.html
+http://www.asix.com.tw/pmac.htm
+http://www.admtek.com.tw/
+
+IVc. Errata
+
+The old DEC databooks were light on details.
+The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written.  Hmmm, now how is that possible?
+
+The DEC SROM format is very badly designed not precisely defined, leading to
+part of the media selection junkheap below.  Some boards do not have EEPROM
+media tables and need to be patched up.  Worse, other boards use the DEC
+design kit media table when it isn't correct for their board.
+
+We cannot use MII interrupts because there is no defined GPIO pin to attach
+them.  The MII transceiver status is polled using an kernel timer.
+
+*/
+
+/* This table use during operation for capabilities and media timer. */
+
+static void tulip_timer(unsigned long data);
+static void t21142_timer(unsigned long data);
+static void mxic_timer(unsigned long data);
+static void pnic_timer(unsigned long data);
+static void comet_timer(unsigned long data);
+
+enum tbl_flag {
+	HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
+	HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
+	HAS_NWAY143=0x40,			/* Uses 21143-like internal NWay. */
+};
+static struct tulip_chip_table {
+	char *chip_name;
+	int io_size;
+	int valid_intrs;			/* CSR7 interrupt enable settings */
+	int flags;
+	void (*media_timer)(unsigned long data);
+} tulip_tbl[] = {
+  { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
+  { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
+  { "Digital DS21140 Tulip", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
+  { "Digital DS21143 Tulip", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer },
+  { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
+	HAS_MII, pnic_timer },
+  { "Macronix 98713 PMAC", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
+  { "Macronix 98715 PMAC", 256, 0x0001ebef,
+	HAS_MEDIA_TABLE, mxic_timer },
+  { "Macronix 98725 PMAC", 256, 0x0001ebef,
+	HAS_MEDIA_TABLE, mxic_timer },
+  { "ASIX AX88140", 128, 0x0001fbff,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
+  { "Lite-On PNIC-II", 256, 0x0001ebef,
+	HAS_MII | HAS_NWAY143, pnic_timer },
+  { "ADMtek Comet", 256, 0x0001abef,
+	MC_HASH_ONLY, comet_timer },
+  { "Compex 9881 PMAC", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
+  { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff,
+       HAS_MII | HAS_ACPI, tulip_timer }, 
+  {0},
+};
+/* 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,
+	X3201_3,
+};
+
+/* A full-duplex map for media types. */
+enum MediaIs {
+	MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
+	MediaIs100=16};
+static const char media_cap[] =
+{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, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+/* Offsets to the Command and Status Registers, "CSRs".  All accesses
+   must be longword instructions and quadword aligned. */
+enum tulip_offsets {
+	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+
+/* The bits in the CSR5 status registers, mostly interrupt sources. */
+enum status_bits {
+	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+	NormalIntr=0x10000, AbnormalIntr=0x8000,
+	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
+};
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct tulip_rx_desc {
+	s32 status;
+	s32 length;
+	u32 buffer1, buffer2;
+};
+
+struct tulip_tx_desc {
+	s32 status;
+	s32 length;
+	u32 buffer1, buffer2;				/* We use only buffer 1.  */
+};
+
+enum desc_status_bits {
+	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
+};
+
+/* Ring-wrap flag in length field, use for last ring entry.
+	0x01000000 means chain on buffer2 address,
+	0x02000000 means use the ring start address in CSR2/3.
+   Note: Some work-alike chips do not function correctly in chained mode.
+   The ASIX chip works only in chained mode.
+   Thus we indicates ring mode, but always write the 'next' field for
+   chained mode as well.
+*/
+#define DESC_RING_WRAP 0x02000000
+
+#ifdef CARDBUS
+#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6)
+#else
+#define EEPROM_ADDRLEN 6
+#endif
+#define EEPROM_SIZE 128 	/* 2 << EEPROM_ADDRLEN */
+
+struct medialeaf {
+	u8 type;
+	u8 media;
+	unsigned char *leafdata;
+};
+
+struct mediatable {
+	u16 defaultmedia;
+	u8 leafcount, csr12dir;				/* General purpose pin directions. */
+	unsigned has_mii:1, has_nonmii:1, has_reset:6;
+	u32 csr15dir, csr15val;				/* 21143 NWay setting. */
+	struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+	struct mediainfo *next;
+	int info_type;
+	int index;
+	unsigned char *info;
+};
+
+struct tulip_private {
+	char devname[8];			/* Used only for kernel debugging. */
+	const char *product_name;
+	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
+	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+#ifdef CARDBUS
+	/* The X3201-3 requires double word aligned tx bufs */
+	struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
+#endif
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	char *rx_buffs;				/* Address of temporary Rx buffers. */
+	u8 setup_buf[96*sizeof(u16) + 7];
+	u16 *setup_frame;		/* Pseudo-Tx frame to init address table. */
+	int chip_id;
+	int revision;
+	struct net_device_stats stats;
+	struct timer_list timer;	/* Media selection timer. */
+	int interrupt;				/* In-interrupt flag. */
+	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
+	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int full_duplex_lock:1;
+	unsigned int fake_addr:1;			/* Multiport board faked address. */
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+	unsigned int media2:4;				/* Secondary monitored media port. */
+	unsigned int medialock:1;			/* Don't sense media type. */
+	unsigned int mediasense:1;			/* Media sensing in progress. */
+	unsigned int nway:1, nwayset:1;		/* 21143 internal NWay. */
+	unsigned int open:1;
+	unsigned int csr0;					/* CSR0 setting. */
+	unsigned int csr6;					/* Current CSR6 control settings. */
+	unsigned char eeprom[EEPROM_SIZE];	/* Serial EEPROM contents. */
+	u16 to_advertise;					/* NWay capabilities advertised.  */
+	u16 lpar;							/* 21143 Link partner ability. */
+	u16 advertising[4];
+	signed char phys[4], mii_cnt;		/* MII device addresses. */
+	struct mediatable *mtable;
+	int cur_index;						/* Current media index. */
+	int saved_if_port;
+	struct pci_dev *pdev;
+	spinlock_t lock;
+	int pad0, pad1;						/* Used for 8-byte alignment */
+};
+
+static void parse_eeprom(struct net_device *dev);
+static int read_eeprom(long ioaddr, int location, int addr_len);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static void select_media(struct net_device *dev, int startup);
+static void tulip_up(struct net_device *dev);
+static void tulip_down(struct net_device *dev);
+static int tulip_open(struct net_device *dev);
+static void tulip_timer(unsigned long data);
+static void t21142_start_nway(struct net_device *dev);
+static void tulip_tx_timeout(struct net_device *dev);
+static void tulip_init_ring(struct net_device *dev);
+static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static int tulip_rx(struct net_device *dev);
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int tulip_close(struct net_device *dev);
+static struct net_device_stats *tulip_get_stats(struct net_device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#endif
+static void set_rx_mode(struct net_device *dev);
+
+/* The Xircom cards are picky about when certain bits in CSR6 can be
+   manipulated.  Keith Owens <kaos@ocs.com.au>. */
+
+static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx)
+{
+	const int strict_bits = 0x0060e202;
+    int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
+    long flags;
+    save_flags(flags);
+    cli();
+    if (chip_idx != X3201_3) {
+		outl(newcsr6, ioaddr + CSR6);
+		restore_flags(flags);
+		return;
+    }
+    newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */
+			   /* read 0 on the Xircom cards */
+    newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
+    currcsr6 = inl(ioaddr + CSR6);
+    if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
+	((currcsr6 & ~0x2002) == 0)) {
+		outl(newcsr6, ioaddr + CSR6);	/* safe */
+		restore_flags(flags);
+		return;
+    }
+    /* make sure the transmitter and receiver are stopped first */
+    currcsr6 &= ~0x2002;
+    while (1) {
+		csr5 = inl(ioaddr + CSR5);
+		if (csr5 == 0xffffffff)
+			break;  /* cannot read csr5, card removed? */
+		csr5_22_20 = csr5 & 0x700000;
+		csr5_19_17 = csr5 & 0x0e0000;
+		if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
+			(csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
+			break;  /* both are stopped or suspended */
+		if (!--attempts) {
+			printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
+				   "csr5=0x%08x\n", csr5);
+			outl(newcsr6, ioaddr + CSR6);  /* unsafe but do it anyway */
+			restore_flags(flags);
+			return;
+		}
+		outl(currcsr6, ioaddr + CSR6);
+		udelay(1);
+    }
+    /* now it is safe to change csr6 */
+    outl(newcsr6, ioaddr + CSR6);
+    restore_flags(flags);
+}
+
+static struct net_device *tulip_probe1(struct pci_dev *pdev,
+				       struct net_device *dev, long ioaddr, int irq,
+				       int chip_idx, int board_idx)
+{
+	static int did_version = 0;			/* Already printed version info. */
+	struct tulip_private *tp;
+	/* See note below on the multiport cards. */
+	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+	static int last_irq = 0;
+	static int multiport_cnt = 0;		/* For four-port boards w/one EEPROM */
+	u8 chip_rev;
+	int i;
+	unsigned short sum;
+
+	if (tulip_debug > 0  &&  did_version++ == 0)
+		printk(KERN_INFO "%s", version);
+
+	dev = init_etherdev(dev, 0);
+
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
+	/* Bring the 21143 out of sleep mode.
+	   Caution: Snooze mode does not work with some boards! */
+	if (tulip_tbl[chip_idx].flags & HAS_ACPI)
+		pci_write_config_dword(pdev, 0x40, 0x00000000);
+
+	printk(KERN_INFO "%s: %s rev %d at %#3lx,",
+		   dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+
+	/* Stop the chip's Tx and Rx processes. */
+	outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx);
+	/* Clear the missed-packet counter. */
+	(volatile int)inl(ioaddr + CSR8);
+
+	if (chip_idx == DC21041) {
+		if (inl(ioaddr + CSR9) & 0x8000) {
+			printk(" 21040 compatible mode,");
+			chip_idx = DC21040;
+		} else {
+			printk(" 21041 mode,");
+		}
+	}
+
+	/* The station address ROM is read byte serially.  The register must
+	   be polled, waiting for the value to be read bit serially from the
+	   EEPROM.
+	   */
+	sum = 0;
+	if (chip_idx == DC21040) {
+		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
+		for (i = 0; i < 6; i++) {
+			int value, boguscnt = 100000;
+			do
+				value = inl(ioaddr + CSR9);
+			while (value < 0  && --boguscnt > 0);
+			dev->dev_addr[i] = value;
+			sum += value & 0xff;
+		}
+	} else if (chip_idx == LC82C168) {
+		for (i = 0; i < 3; i++) {
+			int value, boguscnt = 100000;
+			outl(0x600 | i, ioaddr + 0x98);
+			do
+				value = inl(ioaddr + CSR9);
+			while (value < 0  && --boguscnt > 0);
+			put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
+			sum += value & 0xffff;
+		}
+	} 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 == X3201_3) {
+		/* Xircom has its address stored in the CIS
+		 * we access it through the boot rom interface for now
+		 * this might not work, as the CIS is not parsed but I
+		 * (danilo) use the offset I found on my card's CIS !!!
+		 * 
+		 * Doug Ledford: I changed this routine around so that it
+		 * walks the CIS memory space, parsing the config items, and
+		 * finds the proper lan_node_id tuple and uses the data
+		 * stored there.
+		 */
+		unsigned char j, tuple, link, data_id, data_count;
+		outl(1<<12, ioaddr + CSR9); /* enable boot rom access */
+		for (i = 0x100; i < 0x1f7; i += link+2) {
+			outl(i, ioaddr + CSR10);
+			tuple = inl(ioaddr + CSR9) & 0xff;
+			outl(i + 1, ioaddr + CSR10);
+			link = inl(ioaddr + CSR9) & 0xff;
+			outl(i + 2, ioaddr + CSR10);
+			data_id = inl(ioaddr + CSR9) & 0xff;
+			outl(i + 3, ioaddr + CSR10);
+			data_count = inl(ioaddr + CSR9) & 0xff;
+			if ( (tuple == 0x22) &&
+				 (data_id == 0x04) && (data_count == 0x06) ) {
+				/*
+				 * This is it.  We have the data we want.
+				 */
+				for (j = 0; j < 6; j++) {
+					outl(i + j + 4, ioaddr + CSR10);
+					dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
+				}
+				break;
+			} else if (link == 0) {
+				break;
+			}
+		}
+		sum = 1; // to make check below fail!
+	} else {	/* Must be a new chip, with a serial EEPROM interface. */
+		/* We read the whole EEPROM, and sort it out later.  DEC has a
+		   specification _Digital Semiconductor 21X4 Serial ROM Format_
+		   but early vendor boards just put the address in the first six
+		   EEPROM locations. */
+		unsigned char ee_data[EEPROM_SIZE];
+		int sa_offset = 0;
+
+		for (i = 0; i < sizeof(ee_data)/2; i++)
+			((u16 *)ee_data)[i] =
+				le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
+
+		/* Detect the simple EEPROM format by the duplicated station addr. */
+		for (i = 0; i < 8; i ++)
+			if (ee_data[i] != ee_data[16+i])
+				sa_offset = 20;
+		if (ee_data[0] == 0xff  &&  ee_data[1] == 0xff &&  ee_data[2] == 0) {
+			sa_offset = 2;		/* Grrr, damn Matrox boards. */
+			multiport_cnt = 4;
+		}
+		for (i = 0; i < 6; i ++) {
+			dev->dev_addr[i] = ee_data[i + sa_offset];
+			sum += ee_data[i + sa_offset];
+		}
+	}
+	/* Lite-On boards have the address byte-swapped. */
+	if ((dev->dev_addr[0] == 0xA0  ||  dev->dev_addr[0] == 0xC0)
+		&&  dev->dev_addr[1] == 0x00)
+		for (i = 0; i < 6; i+=2) {
+			char tmp = dev->dev_addr[i];
+			dev->dev_addr[i] = dev->dev_addr[i+1];
+			dev->dev_addr[i+1] = tmp;
+		}
+	/* On the Zynx 315 Etherarray and other multiport boards only the
+	   first Tulip has an EEPROM.
+	   The addresses of the subsequent ports are derived from the first.
+	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
+	   that here as well. */
+	if (sum == 0  || sum == 6*0xff) {
+		printk(" EEPROM not present,");
+		for (i = 0; i < 5; i++)
+			dev->dev_addr[i] = last_phys_addr[i];
+		dev->dev_addr[i] = last_phys_addr[i] + 1;
+#if defined(__i386__)		/* Patch up x86 BIOS bug. */
+		if (last_irq)
+			irq = last_irq;
+#endif
+	}
+
+	for (i = 0; i < 6; i++)
+		printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
+	printk(", IRQ %d.\n", irq);
+	last_irq = irq;
+
+	/* We do a request_region() only to register /proc/ioports info. */
+	/* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
+	request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	/* Make certain the data structures are quadword aligned. */
+	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
+	memset(tp, 0, sizeof(*tp));
+	dev->priv = tp;
+
+	tp->lock = SPIN_LOCK_UNLOCKED;
+	tp->pdev = pdev;
+	tp->chip_id = chip_idx;
+	tp->revision = chip_rev;
+	tp->csr0 = csr0;
+	tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7);
+
+	/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
+	   And the ASIX must have a burst limit or horrible things happen. */
+	if ( (chip_idx == DC21143  &&  chip_rev == 65) ||
+	     (chip_idx == X3201_3) )
+		tp->csr0 &= ~0x01000000;
+	else if (chip_idx == AX88140)
+		tp->csr0 |= 0x2000;
+
+#ifdef TULIP_FULL_DUPLEX
+	tp->full_duplex = 1;
+	tp->full_duplex_lock = 1;
+#endif
+#ifdef TULIP_DEFAULT_MEDIA
+	tp->default_port = TULIP_DEFAULT_MEDIA;
+#endif
+#ifdef TULIP_NO_MEDIA_SWITCH
+	tp->medialock = 1;
+#endif
+
+	/* The lower four bits are the media type. */
+	if (board_idx >= 0  &&  board_idx < MAX_UNITS) {
+		tp->default_port = options[board_idx] & 15;
+		if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
+			tp->full_duplex = 1;
+		if (mtu[board_idx] > 0)
+			dev->mtu = mtu[board_idx];
+	}
+	if (dev->mem_start)
+		tp->default_port = dev->mem_start;
+	if (tp->default_port) {
+		tp->medialock = 1;
+		if (media_cap[tp->default_port] & MediaAlwaysFD)
+			tp->full_duplex = 1;
+	}
+	if (tp->full_duplex)
+		tp->full_duplex_lock = 1;
+
+	/* This is logically part of probe1(), but too complex to write inline. */
+	if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE)
+		parse_eeprom(dev);
+
+	if (media_cap[tp->default_port] & MediaIsMII) {
+		u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
+		tp->to_advertise = media2advert[tp->default_port - 9];
+	} else
+		tp->to_advertise = 0x03e1;
+
+	if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) ||
+		(tp->mtable  &&  tp->mtable->has_mii) ||
+		( ! tp->mtable  &&  (tulip_tbl[chip_idx].flags & HAS_MII))) {
+		int phy, phy_idx;
+		if (tp->mtable  &&  tp->mtable->has_mii) {
+			for (i = 0; i < tp->mtable->leafcount; i++)
+				if (tp->mtable->mleaf[i].media == 11) {
+					tp->cur_index = i;
+					tp->saved_if_port = dev->if_port;
+					select_media(dev, 1);
+					dev->if_port = tp->saved_if_port;
+					break;
+				}
+		}
+		/* Find the connected MII xcvrs.
+		   Doing this in open() would allow detecting external xcvrs later,
+		   but takes much time. */
+		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+			 phy++) {
+			int mii_status = mdio_read(dev, phy, 1);
+			if ((mii_status & 0x8301) == 0x8001 ||
+				((mii_status & 0x8000) == 0  && (mii_status & 0x7800) != 0)) {
+				int mii_reg0 = mdio_read(dev, phy, 0);
+				int mii_advert = mdio_read(dev, phy, 4);
+				int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
+				tp->phys[phy_idx] = phy;
+				tp->advertising[phy_idx++] = reg4;
+				printk(KERN_INFO "%s:  MII transceiver #%d "
+					   "config %4.4x status %4.4x advertising %4.4x.\n",
+					   dev->name, phy, mii_reg0, mii_status, mii_advert);
+				/* Fixup for DLink with miswired PHY. */
+				if (mii_advert != reg4) {
+					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
+						   " previously advertising %4.4x.\n",
+						   dev->name, reg4, phy, mii_advert);
+					mdio_write(dev, phy, 4, reg4);
+				}
+				/* Enable autonegotiation: some boards default to off. */
+				mdio_write(dev, phy, 0, mii_reg0 |
+						   (tp->full_duplex ? 0x1100 : 0x1000) |
+						   (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
+			}
+		}
+		tp->mii_cnt = phy_idx;
+		if (tp->mtable  &&  tp->mtable->has_mii  &&  phy_idx == 0) {
+			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
+				   dev->name);
+			tp->phys[0] = 1;
+		}
+	}
+
+	/* The Tulip-specific entries in the device structure. */
+	dev->open = &tulip_open;
+	dev->hard_start_xmit = &tulip_start_xmit;
+	dev->stop = &tulip_close;
+	dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_PRIVATE_IOCTL
+	dev->do_ioctl = &private_ioctl;
+#endif
+#ifdef HAVE_MULTICAST
+	dev->set_multicast_list = &set_rx_mode;
+#endif
+	dev->tx_timeout = tulip_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	switch (chip_idx) {
+	case DC21041:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+		outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx);
+		outl(0x0000EF05, ioaddr + CSR13);
+		break;
+	case DC21040:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0x00000004, ioaddr + CSR13);
+		break;
+	case DC21140: default:
+		if (tp->mtable)
+			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
+		break;
+	case DC21142:
+	case PNIC2:
+		if (tp->mii_cnt  ||  media_cap[dev->if_port] & MediaIsMII) {
+			outl_CSR6(0x82020000, ioaddr, chip_idx);
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+			outl_CSR6(0x820E0000, ioaddr, chip_idx);
+		} else {
+			outl_CSR6(0x82420200, ioaddr, chip_idx);
+			outl(0x0001, ioaddr + CSR13);
+			outl(0x0003FFFF, ioaddr + CSR14);
+			outl(0x0008, ioaddr + CSR15);
+			outl(0x0001, ioaddr + CSR13);
+			outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+		}
+		break;
+	case X3201_3:
+		outl(0x0008, ioaddr + CSR15);
+		udelay(5);  /* The delays are Xircom recommended to give the
+					 * chipset time to reset the actual hardware
+					 * on the PCMCIA card
+					 */
+		outl(0xa8050000, ioaddr + CSR15);
+		udelay(5);
+		outl(0xa00f0000, ioaddr + CSR15);
+		udelay(5);
+		outl_CSR6(0x32000200, ioaddr, chip_idx);
+		break;
+	case LC82C168:
+		if ( ! tp->mii_cnt) {
+			outl_CSR6(0x00420000, ioaddr, chip_idx);
+			outl(0x30, ioaddr + CSR12);
+			outl(0x0001F078, ioaddr + 0xB8);
+			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+		}
+		break;
+	case MX98713: case COMPEX9881:
+		outl_CSR6(0x00000000, ioaddr, chip_idx);
+		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+		outl(0x00000001, ioaddr + CSR13);
+		break;
+	case MX98715: case MX98725:
+		outl_CSR6(0x01a80000, ioaddr, chip_idx);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00001000, ioaddr + CSR12);
+		break;
+	case COMET:
+		/* No initialization necessary. */
+		break;
+	}
+
+	return dev;
+}
+
+/* Serial EEPROM section. */
+/* The main routine to parse the very complicated SROM structure.
+   Search www.digital.com for "21X4 SROM" to get details.
+   This code is very complex, and will require changes to support
+   additional cards, so I'll be verbose about what is going on.
+   */
+
+/* Known cards that have old-style EEPROMs. */
+static struct fixups {
+  char *name;
+  unsigned char addr0, addr1, addr2;
+  u16 newtable[32];				/* Max length below. */
+} eeprom_fixups[] = {
+  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0903, 0x006D, /* 100baseTx */ }},
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
+								 0x0107, 0x8021, /* 100baseFx */
+								 0x0108, 0x8021, /* 100baseFx-FD */
+								 0x0103, 0x006D, /* 100baseTx */ }},
+  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
+							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
+  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
+							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
+							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+   }},
+  {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
+
+#if defined(__i386__)			/* AKA get_unaligned() */
+#define get_u16(ptr) (*(u16 *)(ptr))
+#else
+#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
+#endif
+
+static void parse_eeprom(struct net_device *dev)
+{
+	/* The last media info list parsed, for multiport boards.  */
+	static struct mediatable *last_mediatable = NULL;
+	static unsigned char *last_ee_data = NULL;
+	static int controller_index = 0;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	unsigned char *ee_data = tp->eeprom;
+	int i;
+#ifdef CARDBUS
+	int chip_rev = tp->revision;
+#endif
+
+	tp->mtable = 0;
+	for (i = 0; i < EEPROM_SIZE/2; i++)
+		((u16 *)ee_data)[i] =
+			le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
+
+	/* Detect an old-style (SA only) EEPROM layout:
+	   memcmp(eedata, eedata+16, 8). */
+	for (i = 0; i < 8; i ++)
+		if (ee_data[i] != ee_data[16+i])
+			break;
+	if (i >= 8) {
+		if (ee_data[0] == 0xff) {
+			if (last_mediatable) {
+				controller_index++;
+				printk(KERN_INFO "%s:  Controller %d of multiport board.\n",
+					   dev->name, controller_index);
+				tp->mtable = last_mediatable;
+				ee_data = last_ee_data;
+				goto subsequent_board;
+			} else
+				printk(KERN_INFO "%s:  Missing EEPROM, this interface may "
+					   "not work correctly!\n",
+			   dev->name);
+			return;
+		}
+	  /* Do a fix-up based on the vendor half of the station address prefix. */
+	  for (i = 0; eeprom_fixups[i].name; i++) {
+		if (dev->dev_addr[0] == eeprom_fixups[i].addr0
+			&&  dev->dev_addr[1] == eeprom_fixups[i].addr1
+			&&  dev->dev_addr[2] == eeprom_fixups[i].addr2) {
+		  if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55)
+			  i++;			/* An Accton EN1207, not an outlaw Maxtech. */
+		  memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+				 sizeof(eeprom_fixups[i].newtable));
+		  printk(KERN_INFO "%s: Old format EEPROM on '%s' board.  Using"
+				 " substitute media control info.\n",
+				 dev->name, eeprom_fixups[i].name);
+		  break;
+		}
+	  }
+	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+		  printk(KERN_INFO "%s: Old style EEPROM with no media selection "
+				 "information.\n",
+			   dev->name);
+		return;
+	  }
+	}
+
+	controller_index = 0;
+	if (ee_data[19] > 1) {		/* Multiport board. */
+		last_ee_data = ee_data;
+	}
+subsequent_board:
+
+	if (ee_data[27] == 0) {		/* No valid media table. */
+	} else if (tp->chip_id == DC21041) {
+		unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
+		short media;
+		int count;
+
+		media = get_u16(p);
+		p += 2;
+		count = *p++;
+
+		printk(KERN_INFO "%s:21041 Media information at %d, default media "
+			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			unsigned char media_code = *p++;
+			u16 csrvals[3];
+			int idx;
+			for (idx = 0; idx < 3; idx++) {
+				csrvals[idx] = get_u16(p);
+				p += 2;
+			}
+			if (media_code & 0x40) {
+				printk(KERN_INFO "%s:  21041 media %2.2x (%s),"
+					   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
+					   dev->name, media_code & 15, medianame[media_code & 15],
+					   csrvals[0], csrvals[1], csrvals[2]);
+			} else
+				printk(KERN_INFO "%s:  21041 media #%d, %s.\n",
+					   dev->name, media_code & 15, medianame[media_code & 15]);
+		}
+	} else {
+		unsigned char *p = (void *)ee_data + ee_data[27];
+		unsigned char csr12dir = 0;
+		int count;
+		struct mediatable *mtable;
+		u16 media = get_u16(p);
+
+		p += 2;
+		if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
+			csr12dir = *p++;
+		count = *p++;
+		mtable = (struct mediatable *)
+			kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
+					GFP_KERNEL);
+		if (mtable == NULL)
+			return;				/* Horrible, impossible failure. */
+		last_mediatable = tp->mtable = mtable;
+		mtable->defaultmedia = media;
+		mtable->leafcount = count;
+		mtable->csr12dir = csr12dir;
+		mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
+		mtable->csr15dir = mtable->csr15val = 0;
+
+		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			struct medialeaf *leaf = &mtable->mleaf[i];
+
+			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+				leaf->type = 0;
+				leaf->media = p[0] & 0x3f;
+				leaf->leafdata = p;
+				if ((p[2] & 0x61) == 0x01)	/* Bogus, but Znyx boards do it. */
+					mtable->has_mii = 1;
+				p += 4;
+			} else {
+				leaf->type = p[1];
+				if (p[1] == 0x05) {
+					mtable->has_reset = i;
+					leaf->media = p[2] & 0x0f;
+				} else if (p[1] & 1) {
+					mtable->has_mii = 1;
+					leaf->media = 11;
+				} else {
+					mtable->has_nonmii = 1;
+					leaf->media = p[2] & 0x0f;
+					if (p[1] == 2) {
+						if (leaf->media == 0) {
+							mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
+							mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
+						} else if (leaf->media == 0x40) {
+							u32 base15 = get_unaligned((u16*)&p[7]);
+							mtable->csr15dir =
+								(get_unaligned((u16*)&p[9])<<16) + base15;
+							mtable->csr15val =
+								(get_unaligned((u16*)&p[11])<<16) + base15;
+						}
+					}
+				}
+				leaf->leafdata = p + 2;
+				p += (p[0] & 0x3f) + 1;
+			}
+			if (tulip_debug > 1  &&  leaf->media == 11) {
+				unsigned char *bp = leaf->leafdata;
+				printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset "
+					   "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
+					   dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
+					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+			}
+			printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described "
+				   "by a %s (%d) block.\n",
+				   dev->name, i, medianame[leaf->media], leaf->media,
+				   block_name[leaf->type], leaf->type);
+		}
+	}
+}
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
+
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
+#define EE_CS			0x01	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
+   We add a bus turn-around to insure that this remains true. */
+#define eeprom_delay()	inl(ee_addr)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << addr_len)
+#define EE_READ_CMD		(6 << addr_len)
+#define EE_ERASE_CMD	(7 << addr_len)
+
+static int read_eeprom(long ioaddr, int location, int addr_len)
+{
+	int i;
+	unsigned short retval = 0;
+	long ee_addr = ioaddr + CSR9;
+	int read_cmd = location | EE_READ_CMD;
+
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	outl(EE_ENB, ee_addr);
+
+	/* Shift the read command bits out. */
+	for (i = 4 + addr_len; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		outl(EE_ENB | dataval, ee_addr);
+		eeprom_delay();
+		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay();
+	}
+	outl(EE_ENB, ee_addr);
+
+	for (i = 16; i > 0; i--) {
+		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay();
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outl(EE_ENB, ee_addr);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+/* MII transceiver control section.
+   Read and write the MII registers using software-generated serial
+   MDIO protocol.  See the MII specifications or DP83840A data sheet
+   for details. */
+
+/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
+   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+   "overclocking" issues or future 66Mhz PCI. */
+#define mdio_delay() inl(mdio_addr)
+
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  It is just different enough from the EEPROM protocol
+   to not share code.  The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK	0x10000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN		0x40000
+#define MDIO_DATA_READ	0x80000
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
+	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	int retval = 0;
+	long ioaddr = dev->base_addr;
+	long mdio_addr = ioaddr + CSR9;
+
+	if (tp->chip_id == LC82C168) {
+		int i = 1000;
+		outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
+		inl(ioaddr + 0xA0);
+		inl(ioaddr + 0xA0);
+		while (--i > 0)
+			if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
+				return retval & 0xffff;
+		return 0xffff;
+	}
+
+	if (tp->chip_id == COMET) {
+		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--) {
+		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Shift the read command bits out. */
+	for (i = 15; i >= 0; i--) {
+		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+		outl(MDIO_ENB | dataval, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Read the two transition, 16 data, and wire-idle bits. */
+	for (i = 19; i > 0; i--) {
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
+	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+	long ioaddr = dev->base_addr;
+	long mdio_addr = ioaddr + CSR9;
+
+	if (tp->chip_id == LC82C168) {
+		int i = 1000;
+		outl(cmd, ioaddr + 0xA0);
+		do
+			if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
+				break;
+		while (--i > 0);
+		return;
+	}
+
+	if (tp->chip_id == COMET) {
+		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);
+		mdio_delay();
+		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Shift the command bits out. */
+	for (i = 31; i >= 0; i--) {
+		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+		outl(MDIO_ENB | dataval, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Clear out extra bits. */
+	for (i = 2; i > 0; i--) {
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	return;
+}
+
+static void
+tulip_up(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int i;
+
+	/* On some chip revs we must set the MII/SYM port before the reset!? */
+	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
+		outl_CSR6(0x00040000, ioaddr, tp->chip_id);
+
+	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+	outl(0x00000001, ioaddr + CSR0);
+
+	/* Deassert reset. */
+	outl(tp->csr0, ioaddr + CSR0);
+	udelay(2);
+
+	if (tulip_tbl[tp->chip_id].flags & HAS_ACPI)
+		pci_write_config_dword(tp->pdev, 0x40, 0x00000000);
+
+	/* Clear the tx ring */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		tp->tx_skbuff[i] = 0;
+		tp->tx_ring[i].status = 0x00000000;
+	}
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+	if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
+		u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
+		u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
+		if (tp->chip_id == AX88140) {
+			outl(0, ioaddr + CSR13);
+			outl(addr_low,  ioaddr + CSR14);
+			outl(1, ioaddr + CSR13);
+			outl(addr_high, ioaddr + CSR14);
+		} else if (tp->chip_id == COMET) {
+			outl(addr_low,  ioaddr + 0xA4);
+			outl(addr_high, ioaddr + 0xA8);
+			outl(0, ioaddr + 0xAC);
+			outl(0, ioaddr + 0xB0);
+		}
+	} else if (tp->chip_id != X3201_3) {
+		/* This is set_rx_mode(), but without starting the transmitter. */
+		u16 *eaddrs = (u16 *)dev->dev_addr;
+		u16 *setup_frm = &tp->setup_frame[15*6];
+
+		/* 21140 bug: you must add the broadcast address. */
+		memset(tp->setup_frame, 0xff, 96*sizeof(u16));
+		/* Fill the final entry of the table with our physical address. */
+		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+		/* Put the setup frame on the Tx list. */
+		tp->tx_ring[0].length = 0x08000000 | 192;
+		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
+		tp->tx_ring[0].status = DescOwned;
+
+		tp->cur_tx++;
+	} else { /* X3201_3 */
+		u16 *eaddrs = (u16 *)dev->dev_addr;
+		u16 *setup_frm = &tp->setup_frame[0*6];
+		
+		/* fill the table with the broadcast address */
+		memset(tp->setup_frame, 0xff, 96*sizeof(u16));
+		/* re-fill the first 14 table entries with our address */
+		for(i=0; i<14; i++) {
+			*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+			*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+			*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+		}
+
+		/* Put the setup frame on the Tx list. */
+		tp->tx_ring[0].length = 0x08000000 | 192;
+		/* Lie about the address of our setup frame to make the */
+		/* chip happy */
+		tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
+		tp->tx_ring[0].status = DescOwned;
+
+		tp->cur_tx++;
+	}
+	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
+	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
+
+	tp->saved_if_port = dev->if_port;
+	if (dev->if_port == 0)
+		dev->if_port = tp->default_port;
+	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
+		/* Invalid: Select initial TP, autosense, autonegotiate.  */
+		dev->if_port = 4;
+
+	/* Allow selecting a default media. */
+	i = 0;
+	if (tp->mtable == NULL)
+		goto media_picked;
+	if (dev->if_port) {
+		int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
+			(dev->if_port == 12 ? 0 : dev->if_port);
+		for (i = 0; i < tp->mtable->leafcount; i++)
+			if (tp->mtable->mleaf[i].media == looking_for) {
+				printk(KERN_INFO "%s: Using user-specified media %s.\n",
+					   dev->name, medianame[dev->if_port]);
+				goto media_picked;
+			}
+	}
+	if ((tp->mtable->defaultmedia & 0x0800) == 0) {
+		int looking_for = tp->mtable->defaultmedia & 15;
+		for (i = 0; i < tp->mtable->leafcount; i++)
+			if (tp->mtable->mleaf[i].media == looking_for) {
+				printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
+					   dev->name, medianame[looking_for]);
+				goto media_picked;
+			}
+	}
+	/* Start sensing first non-full-duplex media. */
+	for (i = tp->mtable->leafcount - 1;
+		 (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
+		;
+media_picked:
+
+	tp->csr6 = 0;
+	tp->cur_index = i;
+	if (dev->if_port == 0  &&  tp->chip_id == DC21142) {
+		if (tp->mii_cnt) {
+			select_media(dev, 1);
+			if (tulip_debug > 1)
+				printk(KERN_INFO "%s: Using MII transceiver %d, status "
+					   "%4.4x.\n",
+					   dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
+			outl_CSR6(0x82020000, ioaddr, tp->chip_id);
+			tp->csr6 = 0x820E0000;
+			dev->if_port = 11;
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+		} else
+			t21142_start_nway(dev);
+	} else if ((tp->chip_id == LC82C168  || tp->chip_id == PNIC2)
+			   &&  tp->mii_cnt && ! tp->medialock) {
+		dev->if_port = 11;
+		tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
+		outl(0x0001, ioaddr + CSR15);
+	} else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
+			   && ! tp->medialock) {
+		dev->if_port = 0;
+		tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
+		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+	} else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
+		/* Provided by BOLO, Macronix - 12/10/1998. */
+		dev->if_port = 0;
+		tp->csr6 = 0x01880200;
+		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+		outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
+	} else if (tp->chip_id == DC21143  &&
+			   media_cap[dev->if_port] & MediaIsMII) {
+		/* We must reset the media CSRs when we force-select MII mode. */
+		outl(0x0000, ioaddr + CSR13);
+		outl(0x0000, ioaddr + CSR14);
+		outl(0x0008, ioaddr + CSR15);
+	} else if (tp->chip_id == X3201_3) {
+		outl(0x0008, ioaddr + CSR15);
+		udelay(5);
+		outl(0xa8050000, ioaddr + CSR15);
+		udelay(5);
+		outl(0xa00f0000, ioaddr + CSR15); 
+		udelay(5);
+		tp->csr6  = 0x32400000;
+	} else if (tp->chip_id == COMET) {
+		dev->if_port = 0;
+		tp->csr6 = 0x00040000;
+	} else
+		select_media(dev, 1);
+
+	/* Start the chip's Tx to process setup frame. */
+	outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
+	outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id);
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
+	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+	outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+	outl(0, ioaddr + CSR2);		/* Rx poll demand */
+	
+	netif_start_queue (dev);
+
+	if (tulip_debug > 2) {
+		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
+			   inl(ioaddr + CSR6));
+	}
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer(&tp->timer);
+	tp->timer.expires = RUN_AT(5*HZ);
+	tp->timer.data = (unsigned long)dev;
+	tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
+	add_timer(&tp->timer);
+}
+
+static int
+tulip_open(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
+	tulip_init_ring(dev);
+
+	tulip_up(dev);
+	tp->open = 1;
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct net_device *dev, int startup)
+{
+	long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct mediatable *mtable = tp->mtable;
+	u32 new_csr6;
+	int i;
+
+	if (mtable) {
+		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+		unsigned char *p = mleaf->leafdata;
+		switch (mleaf->type) {
+		case 0:					/* 21140 non-MII xcvr. */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
+					   " with control setting %2.2x.\n",
+					   dev->name, p[1]);
+			dev->if_port = p[0];
+			if (startup)
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+			outl(p[1], ioaddr + CSR12);
+			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+			break;
+		case 2: case 4: {
+			u16 setup[5];
+			u32 csr13val, csr14val, csr15dir, csr15val;
+			for (i = 0; i < 5; i++)
+				setup[i] = get_u16(&p[i*2 + 1]);
+
+			dev->if_port = p[0] & 15;
+			if (media_cap[dev->if_port] & MediaAlwaysFD)
+				tp->full_duplex = 1;
+
+			if (startup && mtable->has_reset) {
+				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
+				unsigned char *rst = rleaf->leafdata;
+				if (tulip_debug > 1)
+					printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
+						   dev->name);
+				for (i = 0; i < rst[0]; i++)
+					outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
+			}
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
+					   "%4.4x/%4.4x.\n",
+					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
+			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
+				csr13val = setup[0];
+				csr14val = setup[1];
+				csr15dir = (setup[3]<<16) | setup[2];
+				csr15val = (setup[4]<<16) | setup[2];
+				outl(0, ioaddr + CSR13);
+				outl(csr14val, ioaddr + CSR14);
+				outl(csr15dir, ioaddr + CSR15);	/* Direction */
+				outl(csr15val, ioaddr + CSR15);	/* Data */
+				outl(csr13val, ioaddr + CSR13);
+			} else {
+				csr13val = 1;
+				csr14val = 0x0003FF7F;
+				csr15dir = (setup[0]<<16) | 0x0008;
+				csr15val = (setup[1]<<16) | 0x0008;
+				if (dev->if_port <= 4)
+					csr14val = t21142_csr14[dev->if_port];
+				if (startup) {
+					outl(0, ioaddr + CSR13);
+					outl(csr14val, ioaddr + CSR14);
+				}
+				outl(csr15dir, ioaddr + CSR15);	/* Direction */
+				outl(csr15val, ioaddr + CSR15);	/* Data */
+				if (startup) outl(csr13val, ioaddr + CSR13);
+			}
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s:  Setting CSR15 to %8.8x/%8.8x.\n",
+					   dev->name, csr15dir, csr15val);
+			if (mleaf->type == 4)
+				new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
+			else
+				new_csr6 = 0x82420000;
+			break;
+		}
+		case 1: case 3: {
+			int phy_num = p[0];
+			int init_length = p[1];
+			u16 *misc_info;
+			u16 to_advertise;
+
+			dev->if_port = 11;
+			new_csr6 = 0x020E0000;
+			if (mleaf->type == 3) {	/* 21142 */
+				u16 *init_sequence = (u16*)(p+2);
+				u16 *reset_sequence = &((u16*)(p+3))[init_length];
+				int reset_length = p[2 + init_length*2];
+				misc_info = reset_sequence + reset_length;
+				if (startup)
+					for (i = 0; i < reset_length; i++)
+						outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
+				for (i = 0; i < init_length; i++)
+					outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
+			} else {
+				u8 *init_sequence = p + 2;
+				u8 *reset_sequence = p + 3 + init_length;
+				int reset_length = p[2 + init_length];
+				misc_info = (u16*)(reset_sequence + reset_length);
+				if (startup) {
+					outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+					for (i = 0; i < reset_length; i++)
+						outl(reset_sequence[i], ioaddr + CSR12);
+				}
+				for (i = 0; i < init_length; i++)
+					outl(init_sequence[i], ioaddr + CSR12);
+			}
+			to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
+			tp->advertising[phy_num] = to_advertise;
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d (%d).\n",
+					   dev->name, to_advertise, phy_num, tp->phys[phy_num]);
+			/* Bogus: put in by a committee?  */
+			mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
+			break;
+		}
+		default:
+			printk(KERN_DEBUG "%s:  Invalid media table selection %d.\n",
+					   dev->name, mleaf->type);
+			new_csr6 = 0x020E0000;
+		}
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12) & 0xff);
+	} else if (tp->chip_id == DC21041) {
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
+				   dev->name, medianame[dev->if_port & 15],
+				   inl(ioaddr + CSR12) & 0xffff);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+		outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+		new_csr6 = 0x80020000;
+	} else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) {
+		if (startup && ! tp->medialock)
+			dev->if_port = tp->mii_cnt ? 11 : 0;
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
+				   " media %s.\n",
+				   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
+				   medianame[dev->if_port]);
+		if (tp->mii_cnt) {
+			new_csr6 = 0x810C0000;
+			outl(0x0001, ioaddr + CSR15);
+			outl(0x0201B07A, ioaddr + 0xB8);
+		} else if (startup) {
+			/* Start with 10mbps to do autonegotiation. */
+			outl(0x32, ioaddr + CSR12);
+			new_csr6 = 0x00420000;
+			outl(0x0001B078, ioaddr + 0xB8);
+			outl(0x0201B078, ioaddr + 0xB8);
+		} else if (dev->if_port == 3  ||  dev->if_port == 5) {
+			outl(0x33, ioaddr + CSR12);
+			new_csr6 = 0x01860000;
+			if (startup)
+				outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
+			else
+				outl(0x1F868, ioaddr + 0xB8);
+		} else {
+			outl(0x32, ioaddr + CSR12);
+			new_csr6 = 0x00420000;
+			outl(0x1F078, ioaddr + 0xB8);
+		}
+	} else if (tp->chip_id == DC21040) {					/* 21040 */
+		/* Turn on the xcvr interface. */
+		int csr12 = inl(ioaddr + CSR12);
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
+				   dev->name, medianame[dev->if_port], csr12);
+		if (media_cap[dev->if_port] & MediaAlwaysFD)
+			tp->full_duplex = 1;
+		new_csr6 = 0x20000;
+		/* Set the full duplux match frame. */
+		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		if (t21040_csr13[dev->if_port] & 8) {
+			outl(0x0705, ioaddr + CSR14);
+			outl(0x0006, ioaddr + CSR15);
+		} else {
+			outl(0xffff, ioaddr + CSR14);
+			outl(0x0000, ioaddr + CSR15);
+		}
+		outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
+	} else if (tp->chip_id == X3201_3) {					/* Xircom */
+		if (tp->default_port == 0)
+			dev->if_port = tp->mii_cnt ? 11 : 3;
+/* Someone is on crack, the Xircom only does MII, no Fx */
+/*		if (media_cap[dev->if_port] & MediaIsMII) {
+			new_csr6 = 0x020E0000;
+		} else if (media_cap[dev->if_port] & MediaIsFx) {
+			new_csr6 = 0x028600000;
+		} else
+			new_csr6 = 0x038600000;*/
+		new_csr6 = 0x324c0000;
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: Xircom CardBus Adapter: "
+				   "%s transceiver, CSR12 %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12));
+	} else {					/* Unknown chip type with no media table. */
+		if (tp->default_port == 0)
+			dev->if_port = tp->mii_cnt ? 11 : 3;
+		if (media_cap[dev->if_port] & MediaIsMII) {
+			new_csr6 = 0x020E0000;
+		} else if (media_cap[dev->if_port] & MediaIsFx) {
+			new_csr6 = 0x028600000;
+		} else
+			new_csr6 = 0x038600000;
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: No media description table, assuming "
+				   "%s transceiver, CSR12 %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12));
+	}
+
+	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+	return;
+}
+
+/*
+  Check the MII negotiated duplex, and change the CSR6 setting if
+  required.
+  Return 0 if everything is OK.
+  Return < 0 if the transceiver is missing or has no link beat.
+  */
+static int check_duplex(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int mii_reg1, mii_reg5, negotiated, duplex;
+
+	if (tp->full_duplex_lock)
+		return 0;
+	mii_reg1 = mdio_read(dev, tp->phys[0], 1);
+	mii_reg5 = mdio_read(dev, tp->phys[0], 5);
+	if (tulip_debug > 1)
+		printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
+			   "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
+	if (mii_reg1 == 0xffff)
+		return -2;
+	if ((mii_reg1 & 0x0004) == 0) {
+		int new_reg1 = mdio_read(dev, tp->phys[0], 1);
+		if ((new_reg1 & 0x0004) == 0) {
+			if (tulip_debug  > 1)
+				printk(KERN_INFO "%s: No link beat on the MII interface,"
+					   " status %4.4x.\n", dev->name, new_reg1);
+			return -1;
+		}
+	}
+	negotiated = mii_reg5 & tp->advertising[0];
+	duplex = ((negotiated & 0x0300) == 0x0100
+			  || (negotiated & 0x00C0) == 0x0040);
+	/* 100baseTx-FD  or  10T-FD, but not 100-HD */
+	if (tp->full_duplex != duplex) {
+		tp->full_duplex = duplex;
+		if (tp->full_duplex) tp->csr6 |= 0x0200;
+		else				 tp->csr6 &= ~0x0200;
+		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+		if (tulip_debug > 0)
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+				   "#%d link partner capability of %4.4x.\n",
+				   dev->name, tp->full_duplex ? "full" : "half",
+				   tp->phys[0], mii_reg5);
+	}
+	return 0;
+}
+
+static void tulip_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u32 csr12 = inl(ioaddr + CSR12);
+	int next_tick = 2*HZ;
+
+	if (tulip_debug > 2) {
+		printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
+			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
+			   csr12, inl(ioaddr + CSR13),
+			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+	}
+	switch (tp->chip_id) {
+	case DC21040:
+		if (!tp->medialock  &&  csr12 & 0x0002) { /* Network error */
+			printk(KERN_INFO "%s: No link beat found.\n",
+				   dev->name);
+			dev->if_port = (dev->if_port == 2 ? 0 : 2);
+			select_media(dev, 0);
+			dev->trans_start = jiffies;
+		}
+		break;
+	case DC21041:
+		if (tulip_debug > 2)
+			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
+				   dev->name, csr12);
+		switch (dev->if_port) {
+		case 0: case 3: case 4:
+		  if (csr12 & 0x0004) { /*LnkFail */
+			/* 10baseT is dead.  Check for activity on alternate port. */
+			tp->mediasense = 1;
+			if (csr12 & 0x0200)
+				dev->if_port = 2;
+			else
+				dev->if_port = 1;
+			printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			outl(0, ioaddr + CSR13); /* Reset */
+			outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+			outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+			outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+			next_tick = 10*HZ;			/* 2.4 sec. */
+		  } else
+			next_tick = 30*HZ;
+		  break;
+		case 1:					/* 10base2 */
+		case 2:					/* AUI */
+			if (csr12 & 0x0100) {
+				next_tick = (30*HZ);			/* 30 sec. */
+				tp->mediasense = 0;
+			} else if ((csr12 & 0x0004) == 0) {
+				printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
+					   dev->name);
+				dev->if_port = 0;
+				select_media(dev, 0);
+				next_tick = (24*HZ)/10;				/* 2.4 sec. */
+			} else if (tp->mediasense || (csr12 & 0x0002)) {
+				dev->if_port = 3 - dev->if_port; /* Swap ports. */
+				select_media(dev, 0);
+				next_tick = 20*HZ;
+			} else {
+				next_tick = 20*HZ;
+			}
+			break;
+		}
+		break;
+	case DC21140:  case DC21142: case MX98713: case COMPEX9881: default: {
+		struct medialeaf *mleaf;
+		unsigned char *p;
+		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
+			/* Not much that can be done.
+			   Assume this a generic MII or SYM transceiver. */
+			next_tick = 60*HZ;
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
+					   "CSR12 0x%2.2x.\n",
+					   dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
+			break;
+		}
+		mleaf = &tp->mtable->mleaf[tp->cur_index];
+		p = mleaf->leafdata;
+		switch (mleaf->type) {
+		case 0: case 4: {
+			/* Type 0 serial or 4 SYM transceiver.  Check the link beat bit. */
+			int offset = mleaf->type == 4 ? 5 : 2;
+			s8 bitnum = p[offset];
+			if (p[offset+1] & 0x80) {
+				if (tulip_debug > 1)
+					printk(KERN_DEBUG"%s: Transceiver monitor tick "
+						   "CSR12=%#2.2x, no media sense.\n",
+						   dev->name, csr12);
+				if (mleaf->type == 4) {
+					if (mleaf->media == 3 && (csr12 & 0x02))
+						goto select_next_media;
+				}
+				break;
+			}
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
+					   " bit %d is %d, expecting %d.\n",
+					   dev->name, csr12, (bitnum >> 1) & 7,
+					   (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+					   (bitnum >= 0));
+			/* Check that the specified bit has the proper value. */
+			if ((bitnum < 0) !=
+				((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+				if (tulip_debug > 1)
+					printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
+						   medianame[mleaf->media]);
+				if ((p[2] & 0x61) == 0x01)	/* Bogus Znyx board. */
+					goto actually_mii;
+				break;
+			}
+			if (tp->medialock)
+				break;
+	  select_next_media:
+			if (--tp->cur_index < 0) {
+				/* We start again, but should instead look for default. */
+				tp->cur_index = tp->mtable->leafcount - 1;
+			}
+			dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+			if (media_cap[dev->if_port] & MediaIsFD)
+				goto select_next_media; /* Skip FD entries. */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: No link beat on media %s,"
+					   " trying transceiver type %s.\n",
+					   dev->name, medianame[mleaf->media & 15],
+					   medianame[tp->mtable->mleaf[tp->cur_index].media]);
+			select_media(dev, 0);
+			/* Restart the transmit process. */
+			outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+			outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+			next_tick = (24*HZ)/10;
+			break;
+		}
+		case 1:  case 3:		/* 21140, 21142 MII */
+		actually_mii:
+			check_duplex(dev);
+			next_tick = 60*HZ;
+			break;
+		case 2:					/* 21142 serial block has no link beat. */
+		default:
+			break;
+		}
+	}
+	break;
+	}
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
+}
+
+/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
+   of available transceivers.  */
+static void t21142_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr12 = inl(ioaddr + CSR12);
+	int next_tick = 60*HZ;
+	int new_csr6 = 0;
+
+	if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII))
+		printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
+			   dev->name, csr12, medianame[dev->if_port]);
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		check_duplex(dev);
+		next_tick = 60*HZ;
+	} else if (tp->nwayset) {
+		/* Don't screw up a negotiated session! */
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
+				   dev->name, medianame[dev->if_port], csr12);
+	} else if (tp->medialock) {
+			;
+	} else if (dev->if_port == 3) {
+		if (csr12 & 2) {	/* No 100mbps link beat, revert to 10mbps. */
+			if (tulip_debug > 1)
+				printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
+					   "trying NWay.\n", dev->name, csr12);
+			t21142_start_nway(dev);
+			next_tick = 3*HZ;
+		}
+	} else if (((csr12 & 0x7000) != 0x5000)
+			   && tp->chip_id != X3201_3) {
+		/* Negotiation failed.  Search media types. */
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
+				   dev->name, csr12);
+		if (!(csr12 & 4)) {		/* 10mbps link beat good. */
+			new_csr6 = 0x82420000;
+			dev->if_port = 0;
+			outl(0, ioaddr + CSR13);
+			outl(0x0003FFFF, ioaddr + CSR14);
+			outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
+			outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+		} else {
+			/* Select 100mbps port to check for link beat. */
+			new_csr6 = 0x83860000;
+			dev->if_port = 3;
+			outl(0, ioaddr + CSR13);
+			outl(0x0003FF7F, ioaddr + CSR14);
+			outw(8, ioaddr + CSR15);
+			outl(1, ioaddr + CSR13);
+		}
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
+				   dev->name, medianame[dev->if_port]);
+		if (new_csr6 != (tp->csr6 & ~0x00D5)) {
+			tp->csr6 &= 0x00D5;
+			tp->csr6 |= new_csr6;
+			outl(0x0301, ioaddr + CSR12);
+			outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+			outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+		}
+		next_tick = 3*HZ;
+	}
+	if (tp->cur_tx - tp->dirty_tx > 0  &&
+		jiffies - dev->trans_start > TX_TIMEOUT) {
+		printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n",
+			   dev->name, tp->cur_tx, tp->dirty_tx);
+		tulip_tx_timeout(dev);
+	}
+
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
+}
+
+static void t21142_start_nway(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr14 = ((tp->to_advertise & 0x0180) << 9)  |
+		((tp->to_advertise&0x0020)<<1) | 0xffbf;
+
+	dev->if_port = 0;
+	tp->nway = tp->mediasense = 1;
+	tp->nwayset = tp->lpar = 0;
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
+			   dev->name, csr14);
+	outl(0x0001, ioaddr + CSR13);
+	outl(csr14, ioaddr + CSR14);
+	tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
+	outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
+	if (tp->mtable  &&  tp->mtable->csr15dir) {
+		outl(tp->mtable->csr15dir, ioaddr + CSR15);
+		outl(tp->mtable->csr15val, ioaddr + CSR15);
+	} else
+		outw(0x0008, ioaddr + CSR15);
+	outl(0x1301, ioaddr + CSR12); 		/* Trigger NWAY. */
+}
+
+static void t21142_lnk_change(struct net_device *dev, int csr5)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr12 = inl(ioaddr + CSR12);
+
+	if (tulip_debug > 1)
+		printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
+			   "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
+
+	/* If NWay finished and we have a negotiated partner capability. */
+	if (tp->nway  &&  !tp->nwayset  &&  (csr12 & 0x7000) == 0x5000) {
+		int setup_done = 0;
+		tp->lpar = csr12 >> 16;
+		tp->nwayset = 1;
+		if (csr12 & 0x01000000)			dev->if_port = 5;
+		else if (csr12 & 0x00800000)	dev->if_port = 3;
+		else if (csr12 & 0x00400000)	dev->if_port = 4;
+		else if (csr12 & 0x00200000)	dev->if_port = 0;
+		else {
+			tp->nwayset = 0;
+			if ( ! (csr12 & 2))			dev->if_port = 3;
+			else if ( ! (csr12 & 4))	dev->if_port = 0;
+		}
+		tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0;
+
+		if (tulip_debug > 1) {
+			if (tp->nwayset)
+				printk(KERN_INFO "%s: Switching to %s based on link partner "
+					   "advertisement %4.4x.\n",
+					   dev->name, medianame[dev->if_port], tp->lpar);
+			else
+				printk(KERN_INFO "%s: Switching to %s based on link beat "
+					   "status of %4.4x.\n",
+					   dev->name, medianame[dev->if_port], csr12);
+		}
+
+		if (tp->mtable) {
+			int i;
+			for (i = 0; i < tp->mtable->leafcount; i++)
+				if (tp->mtable->mleaf[i].media == dev->if_port) {
+					tp->cur_index = i;
+					select_media(dev, 0);
+					setup_done = 1;
+					break;
+				}
+		}
+		if ( ! setup_done) {
+			tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000;
+			if (tp->full_duplex)
+				tp->csr6 |= 0x0200;
+			outw(0x0000, ioaddr + CSR13);
+			outw(0x0000, ioaddr + CSR14);
+		}
+		outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id);
+		if (debug > 2)
+			printk(KERN_DEBUG "%s:  Restarting Tx and Rx, CSR5 is %8.8x.\n",
+				   dev->name, inl(ioaddr + CSR5));
+		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+	} else if ((tp->nwayset  &&  (csr5 & 0x08000000)
+				&& (dev->if_port == 3  ||  dev->if_port == 5)
+				&& (csr12 & 2) == 2) ||
+			   (tp->nway && (csr5 & (TPLnkFail)))) {
+		/* Link blew? Maybe restart NWay. */
+		del_timer(&tp->timer);
+		t21142_start_nway(dev);
+		tp->timer.expires = RUN_AT(3*HZ);
+		add_timer(&tp->timer);
+	} else if (dev->if_port == 3  ||  dev->if_port == 5) {
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
+				   dev->name, medianame[dev->if_port],
+				   (csr12 & 2) ? "failed" : "good");
+		if ((csr12 & 2)  &&  ! tp->medialock) {
+			del_timer(&tp->timer);
+			t21142_start_nway(dev);
+			tp->timer.expires = RUN_AT(3*HZ);
+			add_timer(&tp->timer);
+		}
+	} else if (dev->if_port == 0  ||  dev->if_port == 4) {
+		if ((csr12 & 4) == 0)
+			printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
+				   dev->name);
+	} else if (!(csr12 & 4)) {		/* 10mbps link beat good. */
+		if (tulip_debug)
+			printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
+				   dev->name);
+		dev->if_port = 0;
+	} else if (tp->nwayset) {
+		if (tulip_debug)
+			printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
+				   dev->name, medianame[dev->if_port], tp->csr6);
+	} else {		/* 100mbps link beat good. */
+		if (tulip_debug)
+			printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
+				   dev->name);
+		dev->if_port = 3;
+		tp->csr6 = 0x83860000;
+		outl(0x0003FF7F, ioaddr + CSR14);
+		outl(0x0301, ioaddr + CSR12);
+		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+	}
+}
+
+static void mxic_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int next_tick = 60*HZ;
+
+	if (tulip_debug > 3) {
+		printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
+			   inl(ioaddr + CSR12));
+	}
+	if (next_tick) {
+		tp->timer.expires = RUN_AT(next_tick);
+		add_timer(&tp->timer);
+	}
+}
+
+static void pnic_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr12 = inl(ioaddr + CSR12);
+	int next_tick = 60*HZ;
+	int new_csr6 = tp->csr6 & ~0x40C40200;
+
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
+
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, "
+				   "CSR5 %8.8x.\n",
+				   dev->name, negotiated, inl(ioaddr + CSR5));
+
+		if (negotiated & 0x0380) 				/* 10 vs 100mbps */
+			new_csr6 |= 0x810E0000;
+		else
+			new_csr6 |= 0x814E0000;
+		if (((negotiated & 0x0300) == 0x0100)			/* Duplex */
+			|| (negotiated & 0x00C0) == 0x0040
+			|| tp->full_duplex_lock) {
+			tp->full_duplex = 1;
+			new_csr6 |= 0x0200;
+		}
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link "
+				   "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
+			   dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
+				   tp->csr6, inl(ioaddr + CSR6));
+	} else {
+		int phy_reg = inl(ioaddr + 0xB8);
+		int csr5 = inl(ioaddr + CSR5);
+
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n",
+				   dev->name, phy_reg, csr5);
+
+		if (phy_reg & 0x04000000) {	/* Remote link fault */
+			/*outl(0x0201F078, ioaddr + 0xB8);*/
+			next_tick = 3*HZ;
+		}
+		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
+					   "CSR5 %8.8x, PHY %3.3x.\n",
+					   dev->name, medianame[dev->if_port], csr12,
+					   inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
+			if (tp->medialock) {
+			} else if (dev->if_port == 0) {
+				dev->if_port = 3;
+				outl(0x33, ioaddr + CSR12);
+				new_csr6 = 0x01860000;
+				outl(0x1F868, ioaddr + 0xB8);
+			} else {
+				dev->if_port = 0;
+				outl(0x32, ioaddr + CSR12);
+				new_csr6 = 0x00420000;
+				outl(0x1F078, ioaddr + 0xB8);
+			}
+			new_csr6 |= (tp->csr6 & 0xfdff);
+			next_tick = 3*HZ;
+		} else
+			new_csr6 = tp->csr6;
+		if (tp->full_duplex_lock  ||  (phy_reg & 0x30000000) != 0) {
+			tp->full_duplex = 1;
+			new_csr6 |= 0x00000200;
+		}
+	}
+	if (tp->csr6 != new_csr6) {
+		tp->csr6 = new_csr6;
+		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);	/* Restart Tx */
+		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+		dev->trans_start = jiffies;
+		if (tulip_debug > 1)
+			printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
+				   "CSR6 %8.8x.\n",
+				   dev->name, tp->full_duplex ? "full" : "half", new_csr6);
+	}
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
+}
+
+static void comet_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int next_tick = 60*HZ;
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
+			   "%4.4x.\n",
+			   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
+}
+
+static void tulip_tx_timeout(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		/* Do nothing -- the media monitor should handle this. */
+		if (tulip_debug > 1)
+			printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
+				   dev->name);
+	} else if (tp->chip_id == DC21040) {
+		if ( !tp->medialock  &&  inl(ioaddr + CSR12) & 0x0002) {
+			dev->if_port = (dev->if_port == 2 ? 0 : 2);
+			printk(KERN_INFO "%s: transmit timed out, switching to "
+				   "%s.\n",
+				   dev->name, medianame[dev->if_port]);
+			select_media(dev, 0);
+		}
+		dev->trans_start = jiffies;
+		return;
+	} else if (tp->chip_id == DC21041) {
+		int csr12 = inl(ioaddr + CSR12);
+
+		printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
+			   "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), csr12,
+			   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
+		tp->mediasense = 1;
+		if ( ! tp->medialock) {
+			if (dev->if_port == 1 || dev->if_port == 2)
+				if (csr12 & 0x0004) {
+					dev->if_port = 2 - dev->if_port;
+				} else
+					dev->if_port = 0;
+			else
+				dev->if_port = 1;
+			select_media(dev, 0);
+		}
+	} else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
+			   || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
+		printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
+			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+			   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+		if ( ! tp->medialock  &&  tp->mtable) {
+			do
+				--tp->cur_index;
+			while (tp->cur_index >= 0
+				   && (media_cap[tp->mtable->mleaf[tp->cur_index].media]
+					   & MediaIsFD));
+			if (--tp->cur_index < 0) {
+				/* We start again, but should instead look for default. */
+				tp->cur_index = tp->mtable->leafcount - 1;
+			}
+			select_media(dev, 0);
+			printk(KERN_WARNING "%s: transmit timed out, switching to %s "
+				   "media.\n", dev->name, medianame[dev->if_port]);
+		}
+	} else {
+		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
+			   "%8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
+		dev->if_port = 0;
+	}
+
+#if defined(way_too_many_messages)
+	if (tulip_debug > 3) {
+		int i;
+		for (i = 0; i < RX_RING_SIZE; i++) {
+			u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
+			int j;
+			printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x  "
+				   "%2.2x %2.2x %2.2x.\n",
+				   i, (unsigned int)tp->rx_ring[i].status,
+				   (unsigned int)tp->rx_ring[i].length,
+				   (unsigned int)tp->rx_ring[i].buffer1,
+				   (unsigned int)tp->rx_ring[i].buffer2,
+				   buf[0], buf[1], buf[2]);
+			for (j = 0; buf[j] != 0xee && j < 1600; j++)
+				if (j < 100) printk(" %2.2x", buf[j]);
+			printk(" j=%d.\n", j);
+		}
+		printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)tp->rx_ring);
+		for (i = 0; i < RX_RING_SIZE; i++)
+			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+		printk("\n" KERN_DEBUG "  Tx ring %8.8x: ", (int)tp->tx_ring);
+		for (i = 0; i < TX_RING_SIZE; i++)
+			printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+		printk("\n");
+	}
+#endif
+
+	/* Stop and restart the chip's Tx processes . */
+	outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+	outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+	/* Trigger an immediate transmit demand. */
+	outl(0, ioaddr + CSR1);
+
+	dev->trans_start = jiffies;
+	netif_wake_queue (dev);
+	tp->stats.tx_errors++;
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void tulip_init_ring(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
+
+	tp->tx_full = 0;
+	tp->cur_rx = tp->cur_tx = 0;
+	tp->dirty_rx = tp->dirty_tx = 0;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		tp->rx_ring[i].status = 0x00000000;
+		tp->rx_ring[i].length = PKT_BUF_SZ;
+		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
+		tp->rx_skbuff[i] = NULL;
+	}
+	/* Mark the last entry as wrapping the ring. */
+	tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
+	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		/* Note the receive buffer must be longword aligned.
+		   dev_alloc_skb() provides 16 byte alignment.  But do *not*
+		   use skb_reserve() to align the IP header! */
+		struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
+		tp->rx_skbuff[i] = skb;
+		if (skb == NULL)
+			break;
+		skb->dev = dev;			/* Mark as being used by this device. */
+		tp->rx_ring[i].status = DescOwned;	/* Owned by Tulip chip */
+		tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+	}
+	tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+	/* The Tx buffer descriptor is filled in as needed, but we
+	   do need to clear the ownership bit. */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		tp->tx_skbuff[i] = 0;
+		tp->tx_ring[i].status = 0x00000000;
+		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
+#ifdef CARDBUS
+		if (tp->chip_id == X3201_3)
+			tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
+#endif CARDBUS
+	}
+	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
+}
+
+static int
+tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int entry;
+	u32 flag;
+
+	/* Caution: the write order is important here, set the base address
+	   with the "ownership" bits last. */
+
+	/* Calculate the next Tx descriptor entry. */
+	entry = tp->cur_tx % TX_RING_SIZE;
+
+	tp->tx_skbuff[entry] = skb;
+#ifdef CARDBUS
+	if (tp->chip_id == X3201_3) {
+		memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
+		tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
+	} else
+#endif
+		tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+
+	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+		flag = 0x60000000; /* No interrupt */
+	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+		flag = 0xe0000000; /* Tx-done intr. */
+	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+		flag = 0x60000000; /* No Tx-done intr. */
+	} else {
+		/* Leave room for set_rx_mode() to fill entries. */
+		flag = 0xe0000000; /* Tx-done intr. */
+		tp->tx_full = 1;
+	}
+	if (entry == TX_RING_SIZE-1)
+		flag |= 0xe0000000 | DESC_RING_WRAP;
+
+	tp->tx_ring[entry].length = skb->len | flag;
+	tp->tx_ring[entry].status = DescOwned;	/* Pass ownership to the chip. */
+	tp->cur_tx++;
+	if (tp->tx_full)
+		netif_stop_queue (dev);
+	else
+		netif_wake_queue (dev);
+
+	/* Trigger an immediate transmit demand. */
+	outl(0, dev->base_addr + CSR1);
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_instance;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr5, work_budget = max_interrupt_work;
+
+	spin_lock (&tp->lock);
+
+	do {
+		csr5 = inl(ioaddr + CSR5);
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
+
+		if (tulip_debug > 4)
+			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
+				   dev->name, csr5, inl(dev->base_addr + CSR5));
+
+		if (csr5 == 0xffffffff)
+			break;	/* all bits set, assume PCMCIA card removed */
+
+		if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
+			break;
+
+		if (csr5 & (RxIntr | RxNoBuf))
+			work_budget -= tulip_rx(dev);
+
+		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
+			unsigned int dirty_tx;
+
+			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
+				 dirty_tx++) {
+				int entry = dirty_tx % TX_RING_SIZE;
+				int status = tp->tx_ring[entry].status;
+
+				if (status < 0)
+					break;			/* It still hasn't been Txed */
+				/* Check for Rx filter setup frames. */
+				if (tp->tx_skbuff[entry] == NULL)
+				  continue;
+
+				if (status & 0x8000) {
+					/* There was an major error, log it. */
+#ifndef final_version
+					if (tulip_debug > 1)
+						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+							   dev->name, status);
+#endif
+					tp->stats.tx_errors++;
+					if (status & 0x4104) tp->stats.tx_aborted_errors++;
+					if (status & 0x0C00) tp->stats.tx_carrier_errors++;
+					if (status & 0x0200) tp->stats.tx_window_errors++;
+					if (status & 0x0002) tp->stats.tx_fifo_errors++;
+					if ((status & 0x0080) && tp->full_duplex == 0)
+						tp->stats.tx_heartbeat_errors++;
+#ifdef ETHER_STATS
+					if (status & 0x0100) tp->stats.collisions16++;
+#endif
+				} else {
+#ifdef ETHER_STATS
+					if (status & 0x0001) tp->stats.tx_deferred++;
+#endif
+					tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
+					tp->stats.collisions += (status >> 3) & 15;
+					tp->stats.tx_packets++;
+				}
+
+				/* Free the original skb. */
+				dev_kfree_skb_irq(tp->tx_skbuff[entry]);
+				tp->tx_skbuff[entry] = 0;
+			}
+
+#ifndef final_version
+			if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
+				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
+				dirty_tx += TX_RING_SIZE;
+			}
+#endif
+
+			if (tp->tx_full && 
+			    tp->cur_tx - dirty_tx  < TX_RING_SIZE - 2)
+				/* The ring is no longer full */
+				tp->tx_full = 0;
+			
+			if (tp->tx_full)
+				netif_stop_queue (dev);
+			else
+				netif_wake_queue (dev);
+
+			tp->dirty_tx = dirty_tx;
+			if (csr5 & TxDied) {
+				if (tulip_debug > 2)
+					printk(KERN_WARNING "%s: The transmitter stopped."
+						   "  CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
+						   dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
+				outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+			}
+		}
+
+		/* Log errors. */
+		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
+			if (csr5 == 0xffffffff)
+				break;
+			if (csr5 & TxJabber) tp->stats.tx_errors++;
+			if (csr5 & TxFIFOUnderflow) {
+				if ((tp->csr6 & 0xC000) != 0xC000)
+					tp->csr6 += 0x4000;	/* Bump up the Tx threshold */
+				else
+					tp->csr6 |= 0x00200000;  /* Store-n-forward. */
+				/* Restart the transmit process. */
+				outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
+				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+			}
+			if (csr5 & RxDied) {		/* Missed a Rx frame. */
+				tp->stats.rx_errors++;
+				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+			}
+			if (csr5 & TimerInt) {
+				if (tulip_debug > 2)
+					printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
+						   dev->name, csr5);
+				outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+			}
+			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
+				if ( tp->chip_id == DC21142)
+					t21142_lnk_change(dev, csr5);
+			}
+			/* Clear all error sources, included undocumented ones! */
+			outl(0x0800f7ba, ioaddr + CSR5);
+		}
+		if (--work_budget < 0) {
+			if (tulip_debug > 1)
+				printk(KERN_WARNING "%s: Too much work during an interrupt, "
+					   "csr5=0x%8.8x.\n", dev->name, csr5);
+			/* Acknowledge all interrupt sources. */
+			outl(0x8001ffff, ioaddr + CSR5);
+#ifdef notdef
+			/* Clear all but standard interrupt sources. */
+			outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
+#endif
+			break;
+		}
+	} while (1);
+
+	if (tulip_debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
+	spin_unlock (&tp->lock);
+}
+
+static int
+tulip_rx(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int entry = tp->cur_rx % RX_RING_SIZE;
+	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
+	int work_done = 0;
+
+	if (tulip_debug > 4)
+		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
+			   tp->rx_ring[entry].status);
+	/* If we own the next entry, it's a new packet. Send it up. */
+	while (tp->rx_ring[entry].status >= 0) {
+		s32 status = tp->rx_ring[entry].status;
+
+		if (tulip_debug > 5)
+			printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
+				   tp->rx_ring[entry].status);
+		if (--rx_work_limit < 0)
+			break;
+		if ((status & 0x38008300) != 0x0300) {
+			if ((status & 0x38000300) != 0x0300) {
+				/* Ingore earlier buffers. */
+				if ((status & 0xffff) != 0x7fff) {
+					if (tulip_debug > 1)
+						printk(KERN_WARNING "%s: Oversized Ethernet frame "
+							   "spanned multiple buffers, status %8.8x!\n",
+							   dev->name, status);
+					tp->stats.rx_length_errors++;
+				}
+			} else if (status & RxDescFatalErr) {
+				/* There was a fatal error. */
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
+						   dev->name, status);
+				tp->stats.rx_errors++; /* end of a packet.*/
+				if (status & 0x0890) tp->stats.rx_length_errors++;
+				if (status & 0x0004) tp->stats.rx_frame_errors++;
+				if (status & 0x0002) tp->stats.rx_crc_errors++;
+				if (status & 0x0001) tp->stats.rx_fifo_errors++;
+			}
+		} else {
+			/* Omit the four octet CRC from the length. */
+			short pkt_len = ((status >> 16) & 0x7ff) - 4;
+			struct sk_buff *skb;
+
+#ifndef final_version
+			if (pkt_len > 1518) {
+				printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
+					   dev->name, pkt_len, pkt_len);
+				pkt_len = 1518;
+				tp->stats.rx_length_errors++;
+			}
+#endif
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
+			if (pkt_len < rx_copybreak
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* 16 byte align the IP header */
+#if ! defined(__alpha__)
+				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
+								 pkt_len, 0);
+				skb_put(skb, pkt_len);
+#else
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
+#endif
+				work_done++;
+			} else { 	/* Pass up the skb already on the Rx ring. */
+				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
+				tp->rx_skbuff[entry] = NULL;
+#ifndef final_version
+				if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
+					printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+						   "do not match in tulip_rx: %p vs. %p / %p.\n",
+						   dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
+						   skb->head, temp);
+#endif
+			}
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+			tp->stats.rx_packets++;
+			tp->stats.rx_bytes += pkt_len;
+		}
+		entry = (++tp->cur_rx) % RX_RING_SIZE;
+	}
+
+	/* Refill the Rx ring buffers. */
+	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
+		entry = tp->dirty_rx % RX_RING_SIZE;
+		if (tp->rx_skbuff[entry] == NULL) {
+			struct sk_buff *skb;
+			skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
+			if (skb == NULL)
+				break;
+			skb->dev = dev;			/* Mark as being used by this device. */
+			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
+			work_done++;
+		}
+		tp->rx_ring[entry].status = DescOwned;
+	}
+
+	return work_done;
+}
+
+static void
+tulip_down(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	outl(0x00000000, ioaddr + CSR7);
+	/* Stop the chip's Tx and Rx processes. */
+	outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id);
+	/* 21040 -- Leave the card in 10baseT state. */
+	if (tp->chip_id == DC21040)
+		outl(0x00000004, ioaddr + CSR13);
+
+	if (inl(ioaddr + CSR6) != 0xffffffff)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+	dev->if_port = tp->saved_if_port;
+}
+
+static int
+tulip_close(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
+	netif_stop_queue(dev);
+
+	if (netif_device_present(dev))
+		tulip_down(dev);
+
+	del_timer(&tp->timer);
+
+	free_irq(dev->irq, dev);
+
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = tp->rx_skbuff[i];
+		tp->rx_skbuff[i] = 0;
+		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */
+		tp->rx_ring[i].length = 0;
+		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
+		if (skb) {
+			dev_kfree_skb(skb);
+		}
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (tp->tx_skbuff[i])
+			dev_kfree_skb(tp->tx_skbuff[i]);
+		tp->tx_skbuff[i] = 0;
+	}
+
+	MOD_DEC_USE_COUNT;
+	tp->open = 0;
+	return 0;
+}
+
+static struct net_device_stats *tulip_get_stats(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	if (netif_device_present(dev))
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+	return &tp->stats;
+}
+
+#ifdef HAVE_PRIVATE_IOCTL
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u16 *data = (u16 *)&rq->ifr_data;
+	int phy = tp->phys[0] & 0x1f;
+	long flags;
+
+	switch(cmd) {
+	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		if (tp->mii_cnt)
+			data[0] = phy;
+		else if (tp->chip_id == DC21142)	/* 21142 pseudo-MII */
+			data[0] = 32;
+		else if (tp->chip_id == PNIC2)
+			data[0] = 32;
+		else if (tp->chip_id == COMET)
+			data[0] = 1;
+		else
+			return -ENODEV;
+		return 0;
+	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+		if (data[0] == 32  &&
+			(tp->chip_id == DC21142 || tp->chip_id == PNIC2)) {
+			int csr12 = inl(ioaddr + CSR12);
+			int csr14 = inl(ioaddr + CSR14);
+			switch (data[1]) {
+			case 0: {
+				data[3] = (csr14<<5) & 0x1000;
+				break; }
+			case 1:
+				data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
+					+ (csr12&0x06 ? 0x04 : 0);
+				break;
+			case 4: {
+				data[3] = ((csr14>>9)&0x0380) +
+					((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1;
+				break;
+			}
+			case 5: data[3] = csr12 >> 16; break;
+			default: data[3] = 0; break;
+			}
+		} else {
+			save_flags(flags);
+			cli();
+			data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+			restore_flags(flags);
+		}
+		return 0;
+	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+#if defined(CAP_NET_ADMIN)
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+#else
+		if (!suser())
+			return -EPERM;
+#endif
+		if (data[0] == 32  &&  tp->chip_id == DC21142) {
+			if (data[1] == 5)
+				tp->to_advertise = data[2];
+		} else {
+			save_flags(flags);
+			cli();
+			mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+			restore_flags(flags);
+		}
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return -EOPNOTSUPP;
+}
+#endif  /* HAVE_PRIVATE_IOCTL */
+
+/* Set or clear the multicast filter for this adaptor.
+   Note that we only use exclusion around actually queueing the
+   new frame, not around filling tp->setup_frame.  This is non-deterministic
+   when re-entered but still correct. */
+
+/* The little-endian AUTODIN32 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_le = 0xedb88320U;
+static inline u32 ether_crc_le(int length, unsigned char *data)
+{
+	u32 crc = 0xffffffff;	/* Initial value. */
+	while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+	}
+	return crc;
+}
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    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;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+	tp->csr6 &= ~0x00D5;
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		tp->csr6 |= 0x00C0;
+		csr6 |= 0x00C0;
+		/* Unconditionally log net taps. */
+		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to filter well -- accept all multicasts. */
+		tp->csr6 |= 0x0080;
+		csr6 |= 0x0080;
+	} else	if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
+		/* Some work-alikes have only a 64-entry hash filter table. */
+		/* Should verify correctness on big-endian/__powerpc__ */
+		struct dev_mc_list *mclist;
+		int i;
+		u32 mc_filter[2];		 /* Multicast hash filter */
+		if (dev->mc_count > 64) {		/* Arbitrary non-effective limit. */
+			tp->csr6 |= 0x0080;
+			csr6 |= 0x0080;
+		} else {
+			mc_filter[1] = mc_filter[0] = 0;
+			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+				 i++, mclist = mclist->next)
+				set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
+			if (tp->chip_id == AX88140) {
+				outl(2, ioaddr + CSR13);
+				outl(mc_filter[0], ioaddr + CSR14);
+				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 {
+		u16 *eaddrs, *setup_frm = tp->setup_frame;
+		struct dev_mc_list *mclist;
+		u32 tx_flags = 0x08000000 | 192;
+		int i;
+
+		/* Note that only the low-address shortword of setup_frame is valid!
+		   The values are doubled for big-endian architectures. */
+		if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */
+			u16 hash_table[32];
+			tx_flags = 0x08400000 | 192;		/* Use hash filter. */
+			memset(hash_table, 0, sizeof(hash_table));
+			set_bit(255, hash_table); 			/* Broadcast entry */
+			/* This should work on big-endian machines as well. */
+			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+				 i++, mclist = mclist->next)
+				set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+						hash_table);
+			for (i = 0; i < 32; i++) {
+				*setup_frm++ = hash_table[i];
+				*setup_frm++ = hash_table[i];
+			}
+			setup_frm = &tp->setup_frame[13*6];
+		} else if(tp->chip_id != X3201_3) {
+			/* We have <= 14 addresses so we can use the wonderful
+			   16 address perfect filtering of the Tulip. */
+			for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+				 i++, mclist = mclist->next) {
+				eaddrs = (u16 *)mclist->dmi_addr;
+				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+			}
+			/* Fill the unused entries with the broadcast address. */
+			memset(setup_frm, 0xff, (15-i)*12);
+			setup_frm = &tp->setup_frame[15*6];
+		} else {
+			/* fill the first two table entries with our address */
+			eaddrs = (u16 *)dev->dev_addr;
+			for(i=0; i<2; i++) {
+				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+			}
+			/* Double fill each entry to accomodate chips that */
+			/* don't like to parse these correctly */
+			for (i=0, mclist=dev->mc_list; i<dev->mc_count;
+				 i++, mclist=mclist->next) {
+				eaddrs = (u16 *)mclist->dmi_addr;
+				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+			}
+			i=((i+1)*2);
+			/* Fill the unused entries with the broadcast address. */
+			memset(setup_frm, 0xff, (15-i)*12);
+			setup_frm = &tp->setup_frame[15*6];
+		}
+
+		/* Fill the final entry with our physical address. */
+		eaddrs = (u16 *)dev->dev_addr;
+		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+		/* Now add this frame to the Tx list. */
+		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+			/* Same setup recently queued, we need not add it. */
+		} else {
+			unsigned long flags;
+			unsigned int entry, dummy = -1;
+
+			save_flags(flags); cli();
+			entry = tp->cur_tx++ % TX_RING_SIZE;
+
+			if (entry != 0) {
+				/* Avoid a chip errata by prefixing a dummy entry. */
+				tp->tx_skbuff[entry] = 0;
+				tp->tx_ring[entry].length =
+					(entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
+				tp->tx_ring[entry].buffer1 = 0;
+				/* race with chip, set DescOwned later */
+				dummy = entry;
+				entry = tp->cur_tx++ % TX_RING_SIZE;
+			}
+
+			tp->tx_skbuff[entry] = 0;
+			/* Put the setup frame on the Tx list. */
+			if (entry == TX_RING_SIZE-1)
+				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */
+			tp->tx_ring[entry].length = tx_flags;
+			if(tp->chip_id == X3201_3)
+				tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
+			else
+				tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
+			tp->tx_ring[entry].status = DescOwned;
+			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
+				tp->tx_full = 1;
+				netif_stop_queue (dev);
+			}
+			if (dummy >= 0)
+				tp->tx_ring[dummy].status = DescOwned;
+			restore_flags(flags);
+			/* Trigger an immediate transmit demand. */
+			outl(0, ioaddr + CSR1);
+		}
+	}
+	outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id);
+}
+
+static  struct pci_device_id tulip_pci_table[] __devinitdata = {
+#if 0 /* these entries conflict with regular tulip driver */
+  { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 },
+  { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 },
+  { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
+  { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 },
+  { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
+  { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
+  { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
+  { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },
+  { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
+  { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
+  { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+  { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
+#endif
+  { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
+  {0},
+};
+
+MODULE_DEVICE_TABLE(pci, tulip_pci_table);
+
+static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	static int board_idx = 0;
+
+	printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name);
+
+	pci_enable_device (pdev);
+	pci_set_master (pdev);
+	dev = tulip_probe1(pdev, NULL,
+			   pci_resource_start (pdev, 0), pdev->irq,
+			   id->driver_data, board_idx++);
+	if (dev) {
+		pdev->driver_data = dev;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static void tulip_suspend(struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	printk(KERN_INFO "tulip_suspend(%s)\n", dev->name);
+	if (tp->open) tulip_down(dev);
+}
+
+static void tulip_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	printk(KERN_INFO "tulip_resume(%s)\n", dev->name);
+	if (tp->open) tulip_up(dev);
+}
+
+static void __devexit tulip_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+	printk(KERN_INFO "tulip_detach(%s)\n", dev->name);
+	unregister_netdev(dev);
+	kfree(dev);
+	kfree(tp);
+}
+
+static struct pci_driver tulip_ops = {
+	name:		"tulip_cb",
+	id_table:	tulip_pci_table,
+	probe:		tulip_pci_probe,
+	remove:		tulip_remove,
+	suspend:	tulip_suspend,
+	resume:		tulip_resume
+};
+
+static int __init tulip_init(void)
+{
+	pci_register_driver(&tulip_ops);
+	return 0;
+}
+
+static __exit void tulip_exit(void)
+{
+	pci_unregister_driver(&tulip_ops);
+}
+
+module_init(tulip_init)
+module_exit(tulip_exit)
+
+
+/*
+ * Local variables:
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.3.99-pre2/linux/drivers/net/plip.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/plip.c	Wed Mar 22 09:55:33 2000
@@ -1325,7 +1325,7 @@
 
 static int parport_ptr = 0;
 
-static void __init plip_setup(char *str)
+static int __init plip_setup(char *str)
 {
 	int ints[4];
 
@@ -1350,6 +1350,7 @@
 			       ints[1]);
 		}
 	}
+	return 1;
 }
 
 __setup("plip=", plip_setup);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/rrunner.c linux/drivers/net/rrunner.c
--- v2.3.99-pre2/linux/drivers/net/rrunner.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/rrunner.c	Wed Mar 22 22:23:55 2000
@@ -1650,6 +1650,6 @@
 
 /*
  * Local variables:
- * compile-command: "gcc -D__SMP__ -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c rrunner.c"
+ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c rrunner.c"
  * End:
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.3.99-pre2/linux/drivers/net/shaper.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/shaper.c	Tue Mar 21 14:43:39 2000
@@ -485,20 +485,24 @@
 
 static int shaper_neigh_setup(struct neighbour *n)
 {
+#ifdef CONFIG_INET
 	if (n->nud_state == NUD_NONE) {
 		n->ops = &arp_broken_ops;
 		n->output = n->ops->output;
 	}
+#endif	
 	return 0;
 }
 
 static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
 {
+#ifdef CONFIG_INET
 	if (p->tbl->family == AF_INET) {
 		p->neigh_setup = shaper_neigh_setup;
 		p->ucast_probes = 0;
 		p->mcast_probes = 0;
 	}
+#endif	
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/starfire.c linux/drivers/net/starfire.c
--- v2.3.99-pre2/linux/drivers/net/starfire.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/starfire.c	Mon Mar 20 07:53:52 2000
@@ -12,10 +12,16 @@
 
 	Support and updates available at
 	http://cesdis.gsfc.nasa.gov/linux/drivers/starfire.html
+	
+	LK1.1.1 (jgarzik):
+	- Use PCI driver interface
+	- Fix MOD_xxx races
+	- softnet fixups
+
 */
 
 static const char *versionA =
-"starfire.c:v0.12 5/28/99  Written by Donald Becker\n",
+"starfire.c:v0.12+LK1.1.1  3/19/2000  Written by Donald Becker and others\n",
 *versionB =" Undates and info at http://www.beowulf.org/linux/drivers.html\n";
 
 /* A few user-configurable values.   These may be modified when a driver
@@ -26,7 +32,6 @@
 
 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 mtu = 0;
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    The Starfire has a 512 element hash table based on the Ethernet CRC.  */
@@ -62,6 +67,9 @@
 
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
+#define PFX "starfire: "
+
+
 #if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
 #warning  You must compile this file with the correct options!
 #warning  See the last lines of the source file.
@@ -94,7 +102,6 @@
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver");
 MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(min_pci_latency, "i");
 MODULE_PARM(mtu, "i");
 MODULE_PARM(debug, "i");
 MODULE_PARM(rx_copybreak, "i");
@@ -196,38 +203,33 @@
 	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,
 };
-struct pci_id_info {
-	const char *name;
-	u16	vendor_id, device_id, device_id_mask, flags;
-	int io_size;
-	struct net_device *(*probe1)(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_idx, int fnd_cnt);
-};
-
-static struct net_device *starfire_probe1(struct pci_dev *pdev, int pci_bus,
-										  int pci_devfn, long ioaddr,
-										  int irq, int chp_idx, int fnd_cnt);
 
 #if 0
 #define ADDR_64BITS 1			/* This chip uses 64 bit addresses. */
 #endif
 #define MEM_ADDR_SZ 0x80000		/* And maps in 0.5MB(!).  */
 
-static struct pci_id_info pci_tbl[] = {
-	{ "Adaptec Starfire 6915",
-	  0x9004, 0x6915, 0xffff, PCI_USES_MASTER, 128, starfire_probe1},
-	{0,},						/* 0 terminated list. */
+
+enum chipset {
+	CH_6915 = 0,
+};
+
+
+static struct pci_device_id starfire_pci_tbl[] __devinitdata = {
+	{ 0x9004, 0x6915, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_6915 },
+	{ 0, },
 };
+MODULE_DEVICE_TABLE(pci, starfire_pci_tbl);
 
 
-/* A chip capabilities table, matching the entries in pci_tbl[] above. */
+/* A chip capabilities table, matching the CH_xxx entries in xxx_pci_tbl[] above. */
 enum chip_capability_flags {CanHaveMII=1, };
-struct chip_info {
+static struct chip_info {
 	char *chip_name;
 	int io_size;
 	int flags;
-	void (*media_timer)(unsigned long data);
-} static skel_netdrv_tbl[] = {
-	{"Adaptec Starfire 6915", 128, CanHaveMII, 0, },
+} netdrv_tbl[] = {
+	{ "Adaptec Starfire 6915", 128, CanHaveMII },
 };
 
 
@@ -322,8 +324,6 @@
 	struct starfire_tx_desc *tx_ring;
 	dma_addr_t rx_ring_dma;
 	dma_addr_t tx_ring_dma;
-	struct net_device *next_module;			/* Link for devices of this type. */
-	const char *product_name;
 	/* The addresses of rx/tx-in-place skbuffs. */
 	struct ring_info rx_info[RX_RING_SIZE];
 	struct ring_info tx_info[TX_RING_SIZE];
@@ -340,7 +340,6 @@
 	/* Frequently used values: keep some adjacent for cache effect. */
 	int chip_id;
 	struct pci_dev *pdev;
-	unsigned char pci_bus, pci_devfn;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
 	unsigned int cur_tx, dirty_tx;
 	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
@@ -378,107 +377,62 @@
 static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  netdev_close(struct net_device *dev);
 
-
-
-/* A list of our installed devices, for removing the driver module. */
-static struct net_device *root_net_dev = NULL;
 
-/* Ideally we would detect all network cards in slot order.  That would
-   be best done a central PCI probe dispatch, which wouldn't work
-   well when dynamically adding drivers.  So instead we detect just the
-   cards we know about in slot order. */
-
-static int pci_etherdev_probe(struct pci_id_info pci_tbl[])
+static int __devinit starfire_init_one (struct pci_dev *pdev,
+					const struct pci_device_id *ent)
 {
-	int cards_found = 0;
-	int pci_index = 0;
-	unsigned char pci_bus, pci_device_fn;
+	struct netdev_private *np;
+	int i, irq, option, chip_id = ent->driver_data;
 	struct net_device *dev;
-
-	for (;pci_index < 0xff; pci_index++) {
-		struct pci_dev *pdev;
-		u16 vendor, device, pci_command, new_command;
-		int chip_idx, irq;
-		long pciaddr;
-		long ioaddr;
-
-		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
-								&pci_bus, &pci_device_fn)
-			!= PCIBIOS_SUCCESSFUL)
-			break;
-		pdev = pci_find_slot (pci_bus, pci_device_fn);
-		if (!pdev) continue;
-		vendor = pdev->vendor;
-		device = pdev->device;
-
-		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
-			if (vendor == pci_tbl[chip_idx].vendor_id
-				&& (device & pci_tbl[chip_idx].device_id_mask) ==
-				pci_tbl[chip_idx].device_id)
-				break;
-		if (pci_tbl[chip_idx].vendor_id == 0)	/* Compiled out! */
-			continue;
-
-		pciaddr = pdev->resource[0].start;
-#if defined(ADDR_64BITS) && defined(__alpha__)
-		pciaddr |= ((long)pdev->base_address[1]) << 32;
-#endif
-		irq = pdev->irq;
-
-		if (debug > 2)
-			printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n",
-				   pci_tbl[chip_idx].name, pciaddr, irq);
-
-		if ((pci_tbl[chip_idx].flags & PCI_USES_IO)) {
-			if (check_region(pciaddr, pci_tbl[chip_idx].io_size))
-				continue;
-			ioaddr = pciaddr;
-		} else if ((ioaddr = (long)ioremap(pciaddr&~0xf, MEM_ADDR_SZ)) == 0) {
-			printk(KERN_INFO "Failed to map PCI address %#lx.\n",
-				   pciaddr);
-			continue;
-		}
-
-		pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
-		new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
-		if (pci_command != new_command) {
-			printk(KERN_INFO "  The PCI BIOS has not enabled the"
-				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n",
-				   pci_bus, pci_device_fn, pci_command, new_command);
-			pci_write_config_word(pdev, PCI_COMMAND, new_command);
-		}
-
-		dev = pci_tbl[chip_idx].probe1(pdev, pci_bus, pci_device_fn, ioaddr,
-									   irq, chip_idx, cards_found);
-
-		if (dev  && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
-			u8 pci_latency;
-			pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
-			if (pci_latency < min_pci_latency) {
-				printk(KERN_INFO "  PCI latency timer (CFLT) is "
-					   "unreasonably low at %d.  Setting to %d clocks.\n",
-					   pci_latency, min_pci_latency);
-				pci_write_config_byte(pdev, PCI_LATENCY_TIMER, min_pci_latency);
-			}
-		}
-		cards_found++;
+	static int card_idx = 0;
+	static int printed_version = 0;
+	long ioaddr;
+	int io_size = netdrv_tbl[chip_id].io_size;
+
+	ioaddr = pci_resource_start (pdev, 0);
+	if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_MEM) == 0)) {
+		printk (KERN_ERR PFX "no PCI MEM resources, aborting\n");
+		return -ENODEV;
+	}
+	
+	dev = init_etherdev(NULL, sizeof(*np));
+	if (!dev) {
+		printk (KERN_ERR PFX "cannot alloc etherdev, aborting\n");
+		return -ENOMEM;
+	}
+	
+	irq = pdev->irq; 
+	
+	if (request_mem_region (ioaddr, io_size, dev->name) == NULL) {
+		printk (KERN_ERR PFX "resource 0x%x @ 0x%lx busy, aborting\n",
+			io_size, ioaddr);
+		goto err_out_free_netdev;
+	}
+	
+	if (pci_enable_device (pdev)) {
+		printk (KERN_ERR PFX "cannot enable PCI device, aborting\n");
+		goto err_out_free_res;
+	}
+	
+	ioaddr = (long) ioremap (ioaddr, io_size);
+	if (!ioaddr) {
+		printk (KERN_ERR PFX "cannot remap 0x%x @ 0x%lx, aborting\n",
+			io_size, ioaddr);
+		goto err_out_free_res;
+	}
+
+	pci_set_master (pdev);
+	
+	option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+	card_idx++;
+	
+	if (!printed_version) {
+		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+		printed_version = 1;
 	}
 
-	return cards_found ? 0 : -ENODEV;
-}
-
-static struct net_device *
-starfire_probe1(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_id, int card_idx)
-{
-	struct netdev_private *np;
-	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
-	struct net_device *dev = init_etherdev(NULL, 0);
-
-	if (!dev)
-		return NULL;
-
-	printk(KERN_INFO "%s: %s at 0x%lx, ",
-		   dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr);
+	printk (KERN_INFO "%s: %s at 0x%lx, ",
+		dev->name, netdrv_tbl[chip_id].chip_name, ioaddr);
 
 	/* Serial EEPROM reads are hidden by the hardware. */
 	for (i = 0; i < 6; i++)
@@ -500,18 +454,13 @@
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 
-	/* Make certain the descriptor lists are aligned. */
-	np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 15) & ~15);
-	memset(np, 0, sizeof(*np));
-	dev->priv = np;
-
-	np->next_module = root_net_dev;
-	root_net_dev = dev;
+	/* private struct aligned and zeroed by init_etherdev */
+	np = dev->priv;
 
 	np->pdev = pdev;
-	np->pci_bus = pci_bus;
-	np->pci_devfn = pci_devfn;
 	np->chip_id = chip_id;
+	
+	pdev->driver_data = dev;
 
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -533,7 +482,7 @@
 	/* The chip-specific entries in the device structure. */
 	dev->open = &netdev_open;
 	dev->hard_start_xmit = &start_tx;
-	dev->tx_timeout = tx_timeout;
+	dev->tx_timeout = &tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 	dev->stop = &netdev_close;
 	dev->get_stats = &get_stats;
@@ -543,7 +492,7 @@
 	if (mtu)
 		dev->mtu = mtu;
 
-	if (skel_netdrv_tbl[np->chip_id].flags & CanHaveMII) {
+	if (netdrv_tbl[np->chip_id].flags & CanHaveMII) {
 		int phy, phy_idx = 0;
 		for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
 			int mii_status = mdio_read(dev, phy, 1);
@@ -558,7 +507,14 @@
 		np->mii_cnt = phy_idx;
 	}
 
-	return dev;
+	return 0;
+
+err_out_free_res:
+	release_mem_region (ioaddr, io_size);
+err_out_free_netdev:
+	unregister_netdev (dev);
+	kfree (dev);
+	return -ENODEV;
 }
 
 
@@ -590,10 +546,13 @@
 	long ioaddr = dev->base_addr;
 	int i;
 
+	MOD_INC_USE_COUNT;
 	/* Do we need to reset the chip??? */
 
-	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev))
-		return -EAGAIN;
+	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
+		MOD_DEC_USE_COUNT;
+		return -EBUSY;
+	}
 
 	/* Disable the Rx and Tx, and reset the chip. */
 	writel(0, ioaddr + GenCtrl);
@@ -624,11 +583,10 @@
 		if (np->rx_ring)
 			pci_free_consistent(np->pdev, PAGE_SIZE,
 								np->rx_ring, np->rx_ring_dma);
+		MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 
-	MOD_INC_USE_COUNT;
-
 	init_ring(dev);
 	/* Set the size of the Rx buffers. */
 	writel((np->rx_buf_sz<<16) | 0xA000, ioaddr + RxDescQCtrl);
@@ -691,6 +649,8 @@
 	/* Enable the Rx and Tx units. */
 	writel(0x000F, ioaddr + GenCtrl);
 
+	netif_start_queue(dev);
+
 	if (debug > 2)
 		printk(KERN_DEBUG "%s: Done netdev_open().\n",
 			   dev->name);
@@ -762,6 +722,7 @@
 	add_timer(&np->timer);
 }
 
+
 static void tx_timeout(struct net_device *dev)
 {
 	struct netdev_private *np = (struct netdev_private *)dev->priv;
@@ -783,15 +744,16 @@
 	}
 #endif
 
-  /* Perhaps we should reinitialize the hardware here. */
-  dev->if_port = 0;
-  /* Stop and restart the chip's Tx processes . */
-
-  /* Trigger an immediate transmit demand. */
-
-  netif_wake_queue(dev);
-  np->stats.tx_errors++;
-  return;
+	/* Perhaps we should reinitialize the hardware here. */
+	dev->if_port = 0;
+
+	/* Stop and restart the chip's Tx processes . */
+	/* XXX todo */
+
+	/* Trigger an immediate transmit demand. */
+	/* XXX todo */
+
+	np->stats.tx_errors++;
 }
 
 
@@ -849,8 +811,6 @@
 	struct netdev_private *np = (struct netdev_private *)dev->priv;
 	unsigned entry;
 
-	netif_stop_queue(dev);
-
 	/* Caution: the write order is important here, set the field
 	   with the "ownership" bits last. */
 
@@ -883,10 +843,10 @@
 	/* Update the producer index. */
 	writel(++entry, dev->base_addr + TxProducerIdx);
 
-	if (np->cur_tx - np->dirty_tx >= TX_RING_SIZE - 1)
+	if (np->cur_tx - np->dirty_tx >= TX_RING_SIZE - 1) {
 		np->tx_full = 1;
-	if (! np->tx_full)
-		netif_start_queue(dev);
+		netif_stop_queue(dev);
+	}
 	dev->trans_start = jiffies;
 
 	if (debug > 4) {
@@ -1347,56 +1307,69 @@
 	return 0;
 }
 
-static int __init starfire_init_module (void)
+
+static void __devexit starfire_remove_one (struct pci_dev *pdev)
 {
-	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
-	if (pci_etherdev_probe(pci_tbl)) {
-		printk(KERN_INFO " No Starfire adapters detected, driver not loaded.\n");
-		return -ENODEV;
+	struct net_device *dev = pdev->driver_data;
+	struct netdev_private *np;
+	
+	if (!dev) {
+		printk (KERN_WARNING "bug: removing starfire pci dev without driver\n");
+		return;
 	}
-	return 0;
-#endif
+
+	np = dev->priv;
+
+	unregister_netdev(dev);
+	iounmap((char *)dev->base_addr);
+
+	if (np->tx_done_q)
+		pci_free_consistent(np->pdev, PAGE_SIZE,
+				    np->tx_done_q, np->tx_done_q_dma);
+	if (np->rx_done_q)
+		pci_free_consistent(np->pdev, PAGE_SIZE,
+				    np->rx_done_q, np->rx_done_q_dma);
+	if (np->tx_ring)
+		pci_free_consistent(np->pdev, PAGE_SIZE,
+				    np->tx_ring, np->tx_ring_dma);
+	if (np->rx_ring)
+		pci_free_consistent(np->pdev, PAGE_SIZE,
+				    np->rx_ring, np->rx_ring_dma);
+
+	kfree(dev);
 }
 
-static void __exit starfire_cleanup_module (void)
+
+static struct pci_driver starfire_driver = {
+	name:		"starfire",
+	probe:		starfire_init_one,
+	remove:		starfire_remove_one,
+	id_table:	starfire_pci_tbl,
+};
+
+
+static int __init starfire_init (void)
 {
-	struct net_device *next_dev;
+	int rc;
+	
+	MOD_INC_USE_COUNT;
+	
+	rc = pci_module_init (&starfire_driver);
+	
+	MOD_DEC_USE_COUNT;
+	
+	return rc;
+}
 
-#ifdef CARDBUS
-	unregister_driver(&etherdev_ops);
-#endif
 
-	/* 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;
-		next_dev = np->next_module;
-		unregister_netdev(root_net_dev);
-		iounmap((char *)root_net_dev->base_addr);
-		if (np->tx_done_q)
-			pci_free_consistent(np->pdev, PAGE_SIZE,
-								np->tx_done_q, np->tx_done_q_dma);
-		if (np->rx_done_q)
-			pci_free_consistent(np->pdev, PAGE_SIZE,
-								np->rx_done_q, np->rx_done_q_dma);
-		if (np->tx_ring)
-			pci_free_consistent(np->pdev, PAGE_SIZE,
-								np->tx_ring, np->tx_ring_dma);
-		if (np->rx_ring)
-			pci_free_consistent(np->pdev, PAGE_SIZE,
-								np->rx_ring, np->rx_ring_dma);
-		kfree(root_net_dev);
-		root_net_dev = next_dev;
-	}
+static void __exit starfire_cleanup (void)
+{
+	pci_unregister_driver (&starfire_driver);
 }
 
-module_init(starfire_init_module);
-module_exit(starfire_cleanup_module);
+
+module_init(starfire_init);
+module_exit(starfire_cleanup);
 
 
 /*
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tlan.c linux/drivers/net/tlan.c
--- v2.3.99-pre2/linux/drivers/net/tlan.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/tlan.c	Wed Mar 22 09:38:14 2000
@@ -66,6 +66,12 @@
  *	                       network cleanup in 2.3.43pre7 (Tigran & myself)
  *	                     - Minor stuff.
  *
+ *	v1.5 March 22, 2000  - Fixed another timer bug that would hang the driver
+ *			       if no cable/link were present.
+ *			     - Cosmetic changes.
+ *			     - TODO: Port completely to new PCI/DMA API
+ *			     	     Auto-Neg fallback.
+ *
  *******************************************************************************/
 
 
@@ -106,7 +112,7 @@
 static	u8		*TLanPadBuffer;
 static	char		TLanSignature[] = "TLAN";
 static	int		TLanVersionMajor = 1;
-static	int		TLanVersionMinor = 4;
+static	int		TLanVersionMinor = 5;
 
 
 static TLanAdapterEntry TLanAdapterList[] __initdata = {
@@ -430,7 +436,8 @@
 
 	}
 	
-	printk(KERN_INFO "TLAN: %d device(s) installed\n", TLanDevicesInstalled);
+	printk(KERN_INFO "TLAN: %d device%s installed\n", 
+		 TLanDevicesInstalled, TLanDevicesInstalled == 1 ? "" : "s");
 	
 	return ((TLanDevicesInstalled > 0) ? 0 : -ENODEV);
 }
@@ -839,8 +846,10 @@
 
 	TLan_ReadAndClearStats( dev, TLAN_RECORD );
 	outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
-	if ( priv->timer.function != NULL )
+	if ( priv->timer.function != NULL ) {
 		del_timer( &priv->timer );
+		priv->timer.function = NULL;
+	}
 	free_irq( dev->irq, dev );
 	TLan_FreeLists( dev );
 	TLAN_DBG( TLAN_DEBUG_GNRL, "Device %s closed.\n", dev->name );
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tokenring/lanstreamer.c linux/drivers/net/tokenring/lanstreamer.c
--- v2.3.99-pre2/linux/drivers/net/tokenring/lanstreamer.c	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/tokenring/lanstreamer.c	Tue Mar 21 14:43:39 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>
+ *  03/13/00 - Added spinlocks for smp
  *  
  *  To Do:
  *
@@ -105,6 +106,7 @@
 #include <linux/stddef.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/spinlock.h>
 #include <net/checksum.h>
 
 #include <asm/io.h>
@@ -121,7 +123,7 @@
  * Official releases will only have an a.b.c version number format.
  */
 
-static char *version = "LanStreamer.c v0.1.0 12/10/99 - Mike Sullivan";
+static char *version = "LanStreamer.c v0.3.1 03/13/99 - Mike Sullivan";
 
 static char *open_maj_error[] = {
 	"No error", "Lobe Media Test", "Physical Insertion",
@@ -210,7 +212,7 @@
 			/* Check to see if io has been allocated, if so, we've already done this card,
 			   so continue on the card discovery loop  */
 
-			if (check_region(pci_device->resource[0].start, STREAMER_IO_SPACE)) 
+			if (check_region(pci_device->resource[0].start & (~3), STREAMER_IO_SPACE)) 
 			{
 				card_no++;
 				continue;
@@ -223,6 +225,8 @@
 				break;
 			}
 			memset(streamer_priv, 0, sizeof(struct streamer_private));
+			init_waitqueue_head(&streamer_priv->srb_wait);
+			init_waitqueue_head(&streamer_priv->trb_wait);
 #ifndef MODULE
 			dev = init_trdev(dev, 0);
 			if(dev==NULL)
@@ -238,11 +242,11 @@
 			       pci_device, dev, dev->priv);
 #endif
 			dev->irq = pci_device->irq;
-			dev->base_addr = pci_device->resource[0].start;
+			dev->base_addr = pci_device->resource[0].start & (~3);
 			dev->init = &streamer_init;
+			streamer_priv->streamer_card_name = (char *)pci_device->resource[0].name;
 			streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256);
-			init_waitqueue_head(&streamer_priv->srb_wait);
-			init_waitqueue_head(&streamer_priv->trb_wait);
+
 			if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000))
 				streamer_priv->pkt_buf_sz = PKT_BUF_SZ;
 			else
@@ -250,7 +254,6 @@
 
 			streamer_priv->streamer_ring_speed = ringspeed[card_no];
 			streamer_priv->streamer_message_level = message_level[card_no];
-			streamer_priv->streamer_multicast_set = 0;
 
 			if (streamer_init(dev) == -1) {
 				unregister_netdevice(dev);
@@ -274,7 +277,7 @@
 }
 
 
-static int __init streamer_init(struct net_device *dev)
+static int streamer_reset(struct net_device *dev)
 {
 	struct streamer_private *streamer_priv;
 	__u8 *streamer_mmio;
@@ -286,12 +289,6 @@
 	streamer_priv = (struct streamer_private *) dev->priv;
 	streamer_mmio = streamer_priv->streamer_mmio;
 
-	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,
-			streamer_priv->streamer_mmio, dev->irq);
-
-	request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer");
 	writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL);
 	t = jiffies;
 	/* Hold soft reset bit for a while */
@@ -330,11 +327,16 @@
 		printk(KERN_INFO "%s: skb allocation for diagnostics failed...proceeding\n",
 		       dev->name);
 	} else {
-		streamer_priv->streamer_rx_ring[0].forward = 0;
-		streamer_priv->streamer_rx_ring[0].status = 0;
-		streamer_priv->streamer_rx_ring[0].buffer = virt_to_bus(skb->data);
-		streamer_priv->streamer_rx_ring[0].framelen_buflen = 512;	/* streamer_priv->pkt_buf_sz; */
-		writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+	        struct streamer_rx_desc *rx_ring;
+                u8 *data;
+
+		rx_ring=(struct streamer_rx_desc *)skb->data;
+		data=((u8 *)skb->data)+sizeof(struct streamer_rx_desc);
+		rx_ring->forward=0;
+		rx_ring->status=0;
+		rx_ring->buffer=virt_to_bus(data);
+		rx_ring->framelen_buflen=512; 
+		writel(virt_to_bus(rx_ring),streamer_mmio+RXBDA);
 	}
 
 #if STREAMER_DEBUG
@@ -382,7 +384,7 @@
 	writew(readw(streamer_mmio + LAPWWO) + 6, streamer_mmio + LAPA);
 	if (readw(streamer_mmio + LAPD)) {
 		printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",
-		       readw(streamer_mmio + LAPD));
+		       ntohs(readw(streamer_mmio + LAPD)));
 		release_region(dev->base_addr, STREAMER_IO_SPACE);
 		return -1;
 	}
@@ -398,17 +400,14 @@
 #endif
 
 	/* setup uaa area for access with LAPD */
-	writew(uaa_addr, streamer_mmio + LAPA);
-
-	/* setup uaa area for access with LAPD */
 	{
 		int i;
 		__u16 addr;
 		writew(uaa_addr, streamer_mmio + LAPA);
 		for (i = 0; i < 6; i += 2) {
-			addr = readw(streamer_mmio + LAPDINC);
-			dev->dev_addr[i] = addr & 0xff;
-			dev->dev_addr[i + 1] = (addr >> 8) & 0xff;
+		        addr=ntohs(readw(streamer_mmio+LAPDINC));
+			dev->dev_addr[i]= (addr >> 8) & 0xff;
+			dev->dev_addr[i+1]= addr & 0xff;
 		}
 #if STREAMER_DEBUG
 		printk("Adapter address: ");
@@ -421,6 +420,32 @@
 	return 0;
 }
 
+static int __init streamer_init(struct net_device *dev) 
+{
+        struct streamer_private *streamer_priv;
+	__u8 *streamer_mmio;
+	int rc;
+  
+	streamer_priv=(struct streamer_private *)dev->priv;
+	streamer_mmio=streamer_priv->streamer_mmio;
+
+	spin_lock_init(&streamer_priv->streamer_lock);
+  
+	printk("%s \n", version);
+	printk("%s: %s. I/O at %hx, MMIO at %p, using irq %d\n",dev->name,
+	       streamer_priv->streamer_card_name,
+	       (unsigned int) dev->base_addr,
+	       streamer_priv->streamer_mmio, 
+	       dev->irq);
+
+	request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer");
+
+	rc=streamer_reset(dev);
+	return rc;
+}
+  
+
+
 static int streamer_open(struct net_device *dev)
 {
 	struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
@@ -430,7 +455,11 @@
 	int i, open_finished = 1;
 	__u16 srb_word;
 	__u16 srb_open;
+	int rc;
 
+	if (readw(streamer_mmio+BMCTL_SUM) & BMCTL_RX_ENABLED) {
+	        rc=streamer_reset(dev);
+	}
 
 	if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "streamer", dev)) {
 		return -EAGAIN;
@@ -461,23 +490,27 @@
 			writew(0, streamer_mmio + LAPDINC);
 		}
 
-		writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
-		writew(SRB_OPEN_ADAPTER, streamer_mmio + LAPDINC);	/* open */
+		writew(readw(streamer_mmio+LAPWWO),streamer_mmio+LAPA);
+		writew(htons(SRB_OPEN_ADAPTER<<8),streamer_mmio+LAPDINC) ; 	/* open */
+		writew(htons(STREAMER_CLEAR_RET_CODE<<8),streamer_mmio+LAPDINC);
 		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
 
 		writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
 #if STREAMER_NETWORK_MONITOR
 		/* If Network Monitor, instruct card to copy MAC frames through the ARB */
-		writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC);	/* offset 8 word contains open options */
+		writew(htons(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC);	/* offset 8 word contains open options */
 #else
-		writew(ntohs(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC);	/* Offset 8 word contains Open.Options */
+		writew(htons(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC);	/* Offset 8 word contains Open.Options */
 #endif
 
 		if (streamer_priv->streamer_laa[0]) {
 			writew(readw(streamer_mmio + LAPWWO) + 12, streamer_mmio + LAPA);
-			writew(((__u16 *) (streamer_priv->streamer_laa))[0], streamer_mmio + LAPDINC);	/* offset 12 word */
-			writew(((__u16 *) (streamer_priv->streamer_laa))[2], streamer_mmio + LAPDINC);	/* offset 14 word */
-			writew(((__u16 *) (streamer_priv->streamer_laa))[4], streamer_mmio + LAPDINC);	/* offset 16 word */
+			writew(htons((streamer_priv->streamer_laa[0] << 8) | 
+				     streamer_priv->streamer_laa[1]),streamer_mmio+LAPDINC);
+			writew(htons((streamer_priv->streamer_laa[2] << 8) | 
+				     streamer_priv->streamer_laa[3]),streamer_mmio+LAPDINC);
+			writew(htons((streamer_priv->streamer_laa[4] << 8) | 
+				     streamer_priv->streamer_laa[5]),streamer_mmio+LAPDINC);
 			memcpy(dev->dev_addr, streamer_priv->streamer_laa, dev->addr_len);
 		}
 
@@ -526,7 +559,7 @@
 		 * timed out.
 		 */
 		writew(srb_open + 2, streamer_mmio + LAPA);
-		srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+		srb_word = ntohs(readw(streamer_mmio + LAPD)) & 0xFF;
 		if (srb_word == STREAMER_CLEAR_RET_CODE) {
 			printk(KERN_WARNING "%s: Adapter Open time out or error.\n",
 			       dev->name);
@@ -574,7 +607,7 @@
 	} while (!(open_finished));	/* Will only loop if ring speed mismatch re-open attempted && autosense is on */
 
 	writew(srb_open + 18, streamer_mmio + LAPA);
-	srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+	srb_word=ntohs(readw(streamer_mmio+LAPD)) >> 8;
 	if (srb_word & (1 << 3))
 		if (streamer_priv->streamer_message_level)
 			printk(KERN_INFO "%s: Opened in FDX Mode\n", dev->name);
@@ -604,6 +637,14 @@
 	writew(~BMCTL_RX_DIS, streamer_mmio + BMCTL_RUM);
 
 	/* setup rx descriptors */
+	streamer_priv->streamer_rx_ring=
+	    kmalloc( sizeof(struct streamer_rx_desc)*
+		     STREAMER_RX_RING_SIZE,GFP_KERNEL);
+	if (!streamer_priv->streamer_rx_ring) {
+	    printk(KERN_WARNING "%s ALLOC of streamer rx ring FAILED!!\n",dev->name);
+	    return -EIO;
+	}
+
 	for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
 		struct sk_buff *skb;
 
@@ -638,6 +679,13 @@
 
 
 	/* setup tx ring */
+	streamer_priv->streamer_tx_ring=kmalloc(sizeof(struct streamer_tx_desc)*
+						STREAMER_TX_RING_SIZE,GFP_KERNEL);
+	if (!streamer_priv->streamer_tx_ring) {
+	    printk(KERN_WARNING "%s ALLOC of streamer_tx_ring FAILED\n",dev->name);
+	    return -EIO;
+	}
+
 	writew(~BMCTL_TX2_DIS, streamer_mmio + BMCTL_RUM);	/* Enables TX channel 2 */
 	for (i = 0; i < STREAMER_TX_RING_SIZE; i++) {
 		streamer_priv->streamer_tx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_tx_ring[i + 1]);
@@ -776,7 +824,7 @@
 						memcpy(skb_put(skb, length),bus_to_virt(rx_desc->buffer), length);	/* copy this fragment */
 						streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
 						streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
-						streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+						
 						/* give descriptor back to the adapter */
 						writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]), streamer_mmio + RXLBDA);
 
@@ -828,10 +876,14 @@
 	misr = readw(streamer_mmio + MISR_RUM);
 	writew(~misr, streamer_mmio + MISR_RUM);
 
-	if (!sisr) {		/* Interrupt isn't for us */
+	if (!sisr) 
+	{		/* Interrupt isn't for us */
+  	        writew(~misr,streamer_mmio+MISR_RUM);
 		return;
 	}
 
+	spin_lock(&streamer_priv->streamer_lock);
+
 	if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY))
 	    || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) {
 		if (sisr & SISR_SRB_REPLY) {
@@ -868,9 +920,9 @@
 			writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
 			printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n",
 			       dev->name, readw(streamer_mmio + LAPDINC),
-			       readw(streamer_mmio + LAPDINC),
-			       readw(streamer_mmio + LAPDINC),
-			       readw(streamer_mmio + LAPDINC));
+			       ntohs(readw(streamer_mmio + LAPDINC)),
+			       ntohs(readw(streamer_mmio + LAPDINC)),
+			       ntohs(readw(streamer_mmio + LAPDINC)));
 			free_irq(dev->irq, dev);
 		}
 
@@ -907,17 +959,19 @@
 	}			/* One if the interrupts we want */
 
 	writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+	spin_unlock(&streamer_priv->streamer_lock) ; 
 }
 
-
 static int streamer_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct streamer_private *streamer_priv =
 	    (struct streamer_private *) dev->priv;
 	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	unsigned long flags ;
 
+	spin_lock_irqsave(&streamer_priv->streamer_lock, flags);
 	netif_stop_queue(dev);
-	
+
 	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;
@@ -941,9 +995,11 @@
 		writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA);
 
 		streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
-		netif_start_queue(dev);
+		netif_wake_queue(dev);
+		spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags);
 		return 0;
 	} else {
+	        spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags);
 		return 1;
 	}
 }
@@ -957,9 +1013,10 @@
 	unsigned long flags;
 	int i;
 
+	netif_stop_queue(dev);
 	writew(streamer_priv->srb, streamer_mmio + LAPA);
-	writew(SRB_CLOSE_ADAPTER, streamer_mmio + LAPDINC);
-	writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+	writew(htons(SRB_CLOSE_ADAPTER << 8),streamer_mmio+LAPDINC);
+	writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);
 
 	save_flags(flags);
 	cli();
@@ -987,7 +1044,9 @@
 	streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
 
 	for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
-		dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]);
+	        if (streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]) {
+		        dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]);
+		} 
 		streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
 	}
 
@@ -1003,11 +1062,10 @@
 	writew(streamer_priv->srb, streamer_mmio + LAPA);
 	printk("srb): ");
 	for (i = 0; i < 2; i++) {
-		printk("%x ", htons(readw(streamer_mmio + LAPDINC)));
+		printk("%x ", ntohs(readw(streamer_mmio + LAPDINC)));
 	}
 	printk("\n");
 #endif
-	netif_stop_queue(dev);
 	free_irq(dev->irq, dev);
 
 	MOD_DEC_USE_COUNT;
@@ -1019,9 +1077,10 @@
 	struct streamer_private *streamer_priv =
 	    (struct streamer_private *) dev->priv;
 	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
-	__u8 options = 0, set_mc_list = 0;
-	__u16 ata1, ata2;
+	__u8 options = 0;
 	struct dev_mc_list *dmi;
+	unsigned char dev_mc_address[5];
+	int i;
 
 	writel(streamer_priv->srb, streamer_mmio + LAPA);
 	options = streamer_priv->streamer_copy_all_options;
@@ -1031,23 +1090,17 @@
 	else
 		options &= ~(3 << 5);
 
-	if (dev->mc_count) {
-		set_mc_list = 1;
-	}
-
 	/* Only issue the srb if there is a change in options */
 
 	if ((options ^ streamer_priv->streamer_copy_all_options)) 
 	{
 		/* Now to issue the srb command to alter the copy.all.options */
-
-		writew(SRB_MODIFY_RECEIVE_OPTIONS,
-		       streamer_mmio + LAPDINC);
-		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
-		writew(streamer_priv->streamer_receive_options | (options << 8), streamer_mmio + LAPDINC);
-		writew(0x414a, streamer_mmio + LAPDINC);
-		writew(0x454d, streamer_mmio + LAPDINC);
-		writew(0x2053, streamer_mmio + LAPDINC);
+		writew(htons(SRB_MODIFY_RECEIVE_OPTIONS << 8), streamer_mmio+LAPDINC);
+		writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);
+		writew(htons((streamer_priv->streamer_receive_options << 8) | options),streamer_mmio+LAPDINC);
+		writew(htons(0x4a41),streamer_mmio+LAPDINC);
+		writew(htons(0x4d45),streamer_mmio+LAPDINC);
+		writew(htons(0x5320),streamer_mmio+LAPDINC);
 		writew(0x2020, streamer_mmio + LAPDINC);
 
 		streamer_priv->srb_queued = 2;	/* Can't sleep, use srb_bh */
@@ -1058,54 +1111,25 @@
 		return;
 	}
 
-	if (set_mc_list ^ streamer_priv->streamer_multicast_set)
-	{	/* Multicast options have changed */
-		dmi = dev->mc_list;
-
-		writel(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
-		ata1 = readw(streamer_mmio + LAPDINC);
-		ata2 = readw(streamer_mmio + LAPD);
-
-		writel(streamer_priv->srb, streamer_mmio + LAPA);
-
-		if (set_mc_list) 
-		{
-			/* Turn multicast on */
-
-			/* RFC 1469 Says we must support using the functional address C0 00 00 04 00 00
-			 * We do this with a set functional address mask.
-			 */
-
-			if (!(ata1 & 0x0400)) {	/* need to set functional mask */
-				writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
-				writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
-				writew(0, streamer_mmio + LAPDINC);
-				writew(ata1 | 0x0400, streamer_mmio + LAPDINC);
-				writew(ata2, streamer_mmio + LAPD);
-
-				streamer_priv->srb_queued = 2;
-				writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
-
-				streamer_priv->streamer_multicast_set = 1;
-			}
-
-		} else {	/* Turn multicast off */
-
-			if ((ata1 & 0x0400)) {	/* Hmmm, need to reset the functional mask */
-				writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
-				writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
-				writew(0, streamer_mmio + LAPDINC);
-				writew(ata1 & ~0x0400, streamer_mmio + LAPDINC);
-				writew(ata2, streamer_mmio + LAPD);
-
-				streamer_priv->srb_queued = 2;
-				writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
-
-				streamer_priv->streamer_multicast_set = 0;
-			}
-		}
-
-	}
+	/* Set the functional addresses we need for multicast */
+	writel(streamer_priv->srb,streamer_mmio+LAPA);
+	dev_mc_address[0] = dev_mc_address[1] = dev_mc_address[2] = dev_mc_address[3] = 0 ; 
+  
+	for (i=0,dmi=dev->mc_list;i < dev->mc_count; i++,dmi = dmi->next) 
+	{ 
+   	        dev_mc_address[0] |= dmi->dmi_addr[2] ; 
+		dev_mc_address[1] |= dmi->dmi_addr[3] ; 
+		dev_mc_address[2] |= dmi->dmi_addr[4] ; 
+		dev_mc_address[3] |= dmi->dmi_addr[5] ; 
+	}
+  
+	writew(htons(SRB_SET_FUNC_ADDRESS << 8),streamer_mmio+LAPDINC);
+	writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);
+	writew(0,streamer_mmio+LAPDINC);
+	writew(htons( (dev_mc_address[0] << 8) | dev_mc_address[1]),streamer_mmio+LAPDINC);
+	writew(htons( (dev_mc_address[2] << 8) | dev_mc_address[3]),streamer_mmio+LAPDINC);
+	streamer_priv->srb_queued = 2 ; 
+	writel(LISR_SRB_CMD,streamer_mmio+LISR_SUM);
 }
 
 static void streamer_srb_bh(struct net_device *dev)
@@ -1115,7 +1139,7 @@
 	__u16 srb_word;
 
 	writew(streamer_priv->srb, streamer_mmio + LAPA);
-	srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
 
 	switch (srb_word) {
 
@@ -1125,7 +1149,8 @@
 		 */
 
 	case SRB_MODIFY_RECEIVE_OPTIONS:
-		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	        srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
+
 		switch (srb_word) {
 		case 0x01:
 			printk(KERN_WARNING "%s: Unrecognized srb command\n", dev->name);
@@ -1147,11 +1172,10 @@
 		/* SRB_SET_GROUP_ADDRESS - Multicast group setting 
 		 */
 	case SRB_SET_GROUP_ADDRESS:
-		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	        srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
 		switch (srb_word) {
 		case 0x00:
-			streamer_priv->streamer_multicast_set = 1;
-			break;
+		        break;
 		case 0x01:
 			printk(KERN_WARNING "%s: Unrecognized srb command \n",dev->name);
 			break;
@@ -1176,11 +1200,10 @@
 		/* SRB_RESET_GROUP_ADDRESS - Remove a multicast address from group list
 		 */
 	case SRB_RESET_GROUP_ADDRESS:
-		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	        srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
 		switch (srb_word) {
 		case 0x00:
-			streamer_priv->streamer_multicast_set = 0;
-			break;
+		        break;
 		case 0x01:
 			printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
 			break;
@@ -1200,7 +1223,7 @@
 		 */
 
 	case SRB_SET_FUNC_ADDRESS:
-		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	        srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
 		switch (srb_word) {
 		case 0x00:
 			if (streamer_priv->streamer_message_level)
@@ -1221,7 +1244,7 @@
 		 */
 
 	case SRB_READ_LOG:
-		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	        srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
 		switch (srb_word) {
 		case 0x00:
 			{
@@ -1250,7 +1273,7 @@
 		/* SRB_READ_SR_COUNTERS - Read and reset the source routing bridge related counters */
 
 	case SRB_READ_SR_COUNTERS:
-		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+	        srb_word=ntohs(readw(streamer_mmio+LAPDINC)) >> 8;
 		switch (srb_word) {
 		case 0x00:
 			if (streamer_priv->streamer_message_level)
@@ -1285,9 +1308,10 @@
 	struct sockaddr *saddr = addr;
 	struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
 
-	if (netif_running(dev)) {
+	if (netif_running(dev)) 
+	{
 		printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name);
-		return -EBUSY;
+		return -EIO;
 	}
 
 	memcpy(streamer_priv->streamer_laa, saddr->sa_data, dev->addr_len);
@@ -1324,12 +1348,12 @@
 #endif
 
 	writew(streamer_priv->arb, streamer_mmio + LAPA);
-	arb_word = readw(streamer_mmio + LAPD) & 0xFF;
-
+	arb_word=ntohs(readw(streamer_mmio+LAPD)) >> 8;
+	
 	if (arb_word == ARB_RECEIVE_DATA) {	/* Receive.data, MAC frames */
 		writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
 		streamer_priv->mac_rx_buffer = buff_off = ntohs(readw(streamer_mmio + LAPDINC));
-		header_len = readw(streamer_mmio + LAPDINC) & 0xff;	/* 802.5 Token-Ring Header Length */
+		header_len=ntohs(readw(streamer_mmio+LAPDINC)) >> 8; /* 802.5 Token-Ring Header Length */
 		frame_len = ntohs(readw(streamer_mmio + LAPDINC));
 
 #if STREAMER_DEBUG
@@ -1340,7 +1364,7 @@
 			__u16 len;
 
 			writew(ntohs(buff_off), streamer_mmio + LAPA);	/*setup window to frame data */
-			next = ntohs(readw(streamer_mmio + LAPDINC));
+			next = htons(readw(streamer_mmio + LAPDINC));
 			status =
 			    ntohs(readw(streamer_mmio + LAPDINC)) & 0xff;
 			len = ntohs(readw(streamer_mmio + LAPDINC));
@@ -1364,7 +1388,7 @@
 			int i;
 			__u16 rx_word;
 
-			writew(ntohs(buff_off), streamer_mmio + LAPA);	/* setup window to frame data */
+			writew(htons(buff_off), streamer_mmio + LAPA);	/* setup window to frame data */
 			next_ptr = ntohs(readw(streamer_mmio + LAPDINC));
 			readw(streamer_mmio + LAPDINC);	/* read thru status word */
 			buffer_len = ntohs(readw(streamer_mmio + LAPDINC));
@@ -1374,9 +1398,9 @@
 
 			i = 0;
 			while (i < buffer_len) {
-				rx_word = readw(streamer_mmio + LAPDINC);
-				frame_data[i] = rx_word & 0xff;
-				frame_data[i + 1] = (rx_word >> 8) & 0xff;
+				rx_word=ntohs(readw(streamer_mmio+LAPDINC));
+				frame_data[i]=rx_word >> 8;
+				frame_data[i+1]=rx_word & 0xff;
 				i += 2;
 			}
 
@@ -1420,10 +1444,10 @@
 
 
 		writew(streamer_priv->asb, streamer_mmio + LAPA);
-		writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC);	/* Receive data */
-		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);	/* Necessary ?? */
+		writew(htons(ASB_RECEIVE_DATA << 8), streamer_mmio+LAPDINC);
+		writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);
 		writew(0, streamer_mmio + LAPDINC);
-		writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+		writew(htons(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
 
 		writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
 
@@ -1433,12 +1457,13 @@
 	} else if (arb_word == ARB_LAN_CHANGE_STATUS) {	/* Lan.change.status */
 		writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
 		lan_status = ntohs(readw(streamer_mmio + LAPDINC));
-		fdx_prot_error = readw(streamer_mmio + LAPD) & 0xFF;
-
+		fdx_prot_error = ntohs(readw(streamer_mmio+LAPD)) >> 8;
+		
 		/* Issue ARB Free */
 		writew(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
 
-		lan_status_diff = streamer_priv->streamer_lan_status ^ lan_status;
+		lan_status_diff = (streamer_priv->streamer_lan_status ^ lan_status) & 
+		    lan_status; 
 
 		if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR)) 
 		{
@@ -1489,8 +1514,8 @@
 			/* Issue READ.LOG command */
 
 			writew(streamer_priv->srb, streamer_mmio + LAPA);
-			writew(SRB_READ_LOG, streamer_mmio + LAPDINC);
-			writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+			writew(htons(SRB_READ_LOG << 8),streamer_mmio+LAPDINC);
+			writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);
 			writew(0, streamer_mmio + LAPDINC);
 			streamer_priv->srb_queued = 2;	/* Can't sleep, use srb_bh */
 
@@ -1503,10 +1528,10 @@
 
 			/* Issue a READ.SR.COUNTERS */
 			writew(streamer_priv->srb, streamer_mmio + LAPA);
-			writew(SRB_READ_SR_COUNTERS,
-			       streamer_mmio + LAPDINC);
-			writew(STREAMER_CLEAR_RET_CODE,
-			       streamer_mmio + LAPDINC);
+			writew(htons(SRB_READ_SR_COUNTERS << 8),
+			       streamer_mmio+LAPDINC);
+			writew(htons(STREAMER_CLEAR_RET_CODE << 8),
+			       streamer_mmio+LAPDINC);
 			streamer_priv->srb_queued = 2;	/* Can't sleep, use srb_bh */
 			writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
 
@@ -1528,10 +1553,10 @@
 		/* Dropped through the first time */
 
 		writew(streamer_priv->asb, streamer_mmio + LAPA);
-		writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC);	/* Receive data */
-		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);	/* Necessary ?? */
+		writew(htons(ASB_RECEIVE_DATA << 8),streamer_mmio+LAPDINC);
+		writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);
 		writew(0, streamer_mmio + LAPDINC);
-		writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+		writew(htons(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
 
 		writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
 		streamer_priv->asb_queued = 2;
@@ -1542,7 +1567,7 @@
 	if (streamer_priv->asb_queued == 2) {
 		__u8 rc;
 		writew(streamer_priv->asb + 2, streamer_mmio + LAPA);
-		rc = readw(streamer_mmio + LAPD) & 0xff;
+		rc=ntohs(readw(streamer_mmio+LAPD)) >> 8;
 		switch (rc) {
 		case 0x01:
 			printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name);
@@ -1594,7 +1619,7 @@
 	off_t pos = 0;
 	int size;
 
-	struct net_device *dev;
+	struct device *dev;
 
 
 	size = sprintf(buffer, "IBM LanStreamer/MPC Chipset Token Ring Adapters\n");
@@ -1607,8 +1632,8 @@
 
 		for (dev = dev_base; dev != NULL; dev = dev->next) 
 		{
-			if (dev->base_addr == (pci_device->base_address[0] & (~3))) 
-			{	/* Yep, a Streamer device */
+		        if (dev->base_addr == pci_device->resource[0].start) 
+			{       /* Yep, a Streamer device */
 				size = sprintf_info(buffer + len, dev);
 				len += size;
 				pos = begin + len;
@@ -1644,17 +1669,17 @@
 	for (i = 0; i < 14; i += 2) {
 		__u16 io_word;
 		__u8 *datap = (__u8 *) & sat;
-		io_word = readw(streamer_mmio + LAPDINC);
-		datap[size] = io_word & 0xff;
-		datap[size + 1] = (io_word >> 8) & 0xff;
+		io_word=ntohs(readw(streamer_mmio+LAPDINC));
+		datap[size]=io_word >> 8;
+		datap[size+1]=io_word & 0xff;
 	}
 	writew(streamer_priv->streamer_parms_addr, streamer_mmio + LAPA);
 	for (i = 0; i < 68; i += 2) {
 		__u16 io_word;
 		__u8 *datap = (__u8 *) & spt;
-		io_word = readw(streamer_mmio + LAPDINC);
-		datap[size] = io_word & 0xff;
-		datap[size + 1] = (io_word >> 8) & 0xff;
+		io_word=ntohs(readw(streamer_mmio+LAPDINC));
+		datap[size]=io_word >> 8;
+		datap[size+1]=io_word & 0xff;
 	}
 
 
@@ -1723,10 +1748,7 @@
 
 #if STREAMER_NETWORK_MONITOR
 #ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *ent;
-
-	ent = create_proc_entry("net/streamer_tr", 0, 0);
-	ent->read_proc = &streamer_proc_info;
+	create_proc_read_entry("net/streamer_tr",0,0,streamer_proc_info,NULL); 
 #endif
 #endif
 	for (i = 0; (i < STREAMER_MAX_ADAPTERS); i++) 
@@ -1758,11 +1780,17 @@
 void cleanup_module(void)
 {
 	int i;
+	struct streamer_private *streamer_priv;
 
 	for (i = 0; i < STREAMER_MAX_ADAPTERS; i++)
 		if (dev_streamer[i]) {
 			unregister_trdev(dev_streamer[i]);
 			release_region(dev_streamer[i]->base_addr, STREAMER_IO_SPACE);
+			streamer_priv=(struct streamer_private *)dev_streamer[i]->priv;
+			kfree_s(streamer_priv->streamer_rx_ring,
+				sizeof(struct streamer_rx_desc)*STREAMER_RX_RING_SIZE);
+			kfree_s(streamer_priv->streamer_tx_ring,
+				sizeof(struct streamer_tx_desc)*STREAMER_TX_RING_SIZE);
 			kfree_s(dev_streamer[i]->priv, sizeof(struct streamer_private));
 			kfree_s(dev_streamer[i], sizeof(struct net_device));
 			dev_streamer[i] = NULL;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tokenring/lanstreamer.h linux/drivers/net/tokenring/lanstreamer.h
--- v2.3.99-pre2/linux/drivers/net/tokenring/lanstreamer.h	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/tokenring/lanstreamer.h	Tue Mar 21 14:43:39 2000
@@ -132,6 +132,7 @@
 #define BMCTL_TX1_DIS (1<<14)
 #define BMCTL_TX2_DIS (1<<10)
 #define BMCTL_RX_DIS (1<<6)
+#define BMCTL_RX_ENABLED  (1<<5)
 
 #define RXLBDA  0x90
 #define RXBDA   0x94
@@ -257,6 +258,9 @@
 	__u16 asb;
 
 	__u8 *streamer_mmio;
+        char *streamer_card_name;
+ 
+        spinlock_t streamer_lock;
 
 	volatile int srb_queued;	/* True if an SRB is still posted */
 	wait_queue_head_t srb_wait;
@@ -264,10 +268,10 @@
 	volatile int asb_queued;	/* True if an ASB is posted */
 
 	volatile int trb_queued;	/* True if a TRB is posted */
-	wait_queue_head_t  trb_wait;
+	wait_queue_head_t trb_wait;
 
-	struct streamer_rx_desc streamer_rx_ring[STREAMER_RX_RING_SIZE];
-	struct streamer_tx_desc streamer_tx_ring[STREAMER_TX_RING_SIZE];
+	struct streamer_rx_desc *streamer_rx_ring;
+	struct streamer_tx_desc *streamer_tx_ring;
 	struct sk_buff *tx_ring_skb[STREAMER_TX_RING_SIZE],
 	    *rx_ring_skb[STREAMER_RX_RING_SIZE];
 	int tx_ring_free, tx_ring_last_status, rx_ring_last_received,
@@ -279,7 +283,6 @@
 	__u16 pkt_buf_sz;
 	__u8 streamer_receive_options, streamer_copy_all_options,
 	    streamer_message_level;
-	__u8 streamer_multicast_set;
 	__u16 streamer_addr_table_addr, streamer_parms_addr;
 	__u16 mac_rx_buffer;
 	__u8 streamer_laa[6];
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/21142.c linux/drivers/net/tulip/21142.c
--- v2.3.99-pre2/linux/drivers/net/tulip/21142.c	Sat Feb 26 22:31:47 2000
+++ linux/drivers/net/tulip/21142.c	Tue Mar 21 12:39:17 2000
@@ -14,7 +14,6 @@
 */
 
 #include "tulip.h"
-#include <asm/io.h>
 
 
 static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/eeprom.c linux/drivers/net/tulip/eeprom.c
--- v2.3.99-pre2/linux/drivers/net/tulip/eeprom.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/net/tulip/eeprom.c	Tue Mar 21 12:39:17 2000
@@ -15,7 +15,6 @@
 
 #include "tulip.h"
 #include <linux/init.h>
-#include <asm/io.h>
 #include <asm/unaligned.h>
 
 
@@ -30,31 +29,31 @@
 /* Known cards that have old-style EEPROMs. */
 static struct eeprom_fixup eeprom_fixups[] __devinitdata = {
   {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
-						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+			  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
   {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f,
-							   0x0000, 0x009E, /* 10baseT */
-							   0x0004, 0x009E, /* 10baseT-FD */
-							   0x0903, 0x006D, /* 100baseTx */
-							   0x0905, 0x006D, /* 100baseTx-FD */ }},
+			   0x0000, 0x009E, /* 10baseT */
+			   0x0004, 0x009E, /* 10baseT-FD */
+			   0x0903, 0x006D, /* 100baseTx */
+			   0x0905, 0x006D, /* 100baseTx-FD */ }},
   {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f,
-								 0x0107, 0x8021, /* 100baseFx */
-								 0x0108, 0x8021, /* 100baseFx-FD */
-								 0x0100, 0x009E, /* 10baseT */
-								 0x0104, 0x009E, /* 10baseT-FD */
-								 0x0103, 0x006D, /* 100baseTx */
-								 0x0105, 0x006D, /* 100baseTx-FD */ }},
+				 0x0107, 0x8021, /* 100baseFx */
+				 0x0108, 0x8021, /* 100baseFx-FD */
+				 0x0100, 0x009E, /* 10baseT */
+				 0x0104, 0x009E, /* 10baseT-FD */
+				 0x0103, 0x006D, /* 100baseTx */
+				 0x0105, 0x006D, /* 100baseTx-FD */ }},
   {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513,
-							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
-							   0x0000, 0x009E, /* 10baseT */
-							   0x0004, 0x009E, /* 10baseT-FD */
-							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */
-							   0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}},
+				   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+				   0x0000, 0x009E, /* 10baseT */
+				   0x0004, 0x009E, /* 10baseT-FD */
+				   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */
+				   0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}},
   {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F,
-								  0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
-								  0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
-								  0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */
-								  0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
-								  0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */
+				  0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+				  0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+				  0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */
+				  0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
+				  0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */
    }},
   {"NetWinder", 0x00, 0x10, 0x57,
 	/* Default media = MII
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/interrupt.c linux/drivers/net/tulip/interrupt.c
--- v2.3.99-pre2/linux/drivers/net/tulip/interrupt.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/tulip/interrupt.c	Tue Mar 21 12:39:17 2000
@@ -14,7 +14,6 @@
 */
 
 #include "tulip.h"
-#include <asm/io.h>
 #include <linux/etherdevice.h>
 #include <linux/pci.h>
 
@@ -211,9 +210,12 @@
 
 				if (status < 0)
 					break;			/* It still has not been Txed */
+
 				/* Check for Rx filter setup frames. */
 				if (tp->tx_buffers[entry].skb == NULL) {
-					pci_unmap_single(tp->pdev,
+					/* test because dummy frames not mapped */
+					if (tp->tx_buffers[entry].mapping)
+						pci_unmap_single(tp->pdev,
 							 tp->tx_buffers[entry].mapping,
 							 sizeof(tp->setup_frame),
 							 PCI_DMA_TODEVICE);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/media.c linux/drivers/net/tulip/media.c
--- v2.3.99-pre2/linux/drivers/net/tulip/media.c	Sat Feb 26 22:31:47 2000
+++ linux/drivers/net/tulip/media.c	Tue Mar 21 12:39:17 2000
@@ -14,7 +14,6 @@
 */
 
 #include "tulip.h"
-#include <asm/io.h>
 
 
 /* This is a mysterious value that can be written to CSR11 in the 21040 (only)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/pnic.c linux/drivers/net/tulip/pnic.c
--- v2.3.99-pre2/linux/drivers/net/tulip/pnic.c	Sat Feb 26 22:31:47 2000
+++ linux/drivers/net/tulip/pnic.c	Tue Mar 21 12:39:17 2000
@@ -15,7 +15,6 @@
 
 #include <linux/kernel.h>
 #include "tulip.h"
-#include <asm/io.h>
 
 
 void pnic_do_nway(struct net_device *dev)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/timer.c linux/drivers/net/tulip/timer.c
--- v2.3.99-pre2/linux/drivers/net/tulip/timer.c	Sat Feb 26 22:31:47 2000
+++ linux/drivers/net/tulip/timer.c	Tue Mar 21 12:39:17 2000
@@ -14,7 +14,6 @@
 */
 
 #include "tulip.h"
-#include <asm/io.h>
 
 
 void tulip_timer(unsigned long data)
@@ -87,7 +86,11 @@
 			break;
 		}
 		break;
-	case DC21140:  case DC21142: case MX98713: case COMPEX9881: default: {
+	case DC21140:
+	case DC21142:
+	case MX98713:
+	case COMPEX9881:
+	default: {
 		struct medialeaf *mleaf;
 		unsigned char *p;
 		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/tulip.h linux/drivers/net/tulip/tulip.h
--- v2.3.99-pre2/linux/drivers/net/tulip/tulip.h	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/tulip/tulip.h	Tue Mar 21 12:39:17 2000
@@ -17,7 +17,7 @@
 #include <linux/spinlock.h>
 #include <linux/netdevice.h>
 #include <linux/timer.h>
-
+#include <asm/io.h>
 
 struct tulip_chip_table {
 	char *chip_name;
@@ -36,8 +36,10 @@
 	HAS_ACPI = 0x10,
 	MC_HASH_ONLY = 0x20,	/* Hash-only multicast filter. */
 	HAS_PNICNWAY = 0x80,
-	HAS_NWAY143 = 0x40,	/* Uses internal NWay xcvr. */
-	HAS_8023X = 0x100,
+	HAS_NWAY = 0x40,	/* Uses internal NWay xcvr. */
+	HAS_INTR_MITIGATION = 0x100,
+	IS_ASIX = 0x200,
+	HAS_8023X = 0x400,
 };
 
 
@@ -58,7 +60,6 @@
 	COMET,
 	COMPEX9881,
 	I21145,
-	X3201_3,
 };
 
 
@@ -137,6 +138,17 @@
 };
 
 
+enum t21041_csr13_bits {
+	csr13_eng = (0xEF0<<4), /* for eng. purposes only, hardcode at EF0h */
+	csr13_aui = (1<<3), /* clear to force 10bT, set to force AUI/BNC */
+	csr13_cac = (1<<2), /* CSR13/14/15 autoconfiguration */
+	csr13_srl = (1<<0), /* When reset, resets all SIA functions, machines */
+
+	csr13_mask_auibnc = (csr13_eng | csr13_aui | csr13_cac | csr13_srl),
+	csr13_mask_10bt = (csr13_eng | csr13_cac | csr13_srl),
+};
+
+
 /* Keep the ring sizes a power of two for efficiency.
    Making the Tx ring too large decreases the effectiveness of channel
    bonding and packet priority.
@@ -334,7 +346,14 @@
 extern u16 t21041_csr13[];
 extern u16 t21041_csr14[];
 extern u16 t21041_csr15[];
-void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6);
+
+
+extern inline void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6)
+{
+	long ioaddr = tp->base_addr;
+
+	outl (newcsr6, ioaddr + CSR6);
+}
 
 
 #endif /* __NET_TULIP_H__ */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/tulip/tulip_core.c linux/drivers/net/tulip/tulip_core.c
--- v2.3.99-pre2/linux/drivers/net/tulip/tulip_core.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/tulip/tulip_core.c	Tue Mar 21 12:39:17 2000
@@ -19,7 +19,7 @@
 
 */
 
-static const char version[] = "Linux Tulip driver version 0.9.4 (Feb 28, 2000)\n";
+static const char version[] = "Linux Tulip driver version 0.9.4.2 (Mar 21, 2000)\n";
 
 #include <linux/module.h>
 #include "tulip.h"
@@ -27,7 +27,6 @@
 #include <linux/init.h>
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
-#include <asm/io.h>
 #include <asm/unaligned.h>
 
 
@@ -92,9 +91,6 @@
 #define TX_TIMEOUT  (4*HZ)
 
 
-/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
-   This is only in the support-all-kernels source code. */
-
 MODULE_AUTHOR("The Linux Kernel Team");
 MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
 MODULE_PARM(tulip_debug, "i");
@@ -123,12 +119,13 @@
 
 struct tulip_chip_table tulip_tbl[] = {
   { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
-  { "Digital DC21041 Tulip", 128, 0x0001ebff, HAS_MEDIA_TABLE, tulip_timer },
+  { "Digital DC21041 Tulip", 128, 0x0001ebef,
+	HAS_MEDIA_TABLE | HAS_NWAY, tulip_timer },
   { "Digital DS21140 Tulip", 128, 0x0001ebef,
 	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
   { "Digital DS21143 Tulip", 128, 0x0801fbff,
-	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143,
-	t21142_timer },
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY
+	| HAS_INTR_MITIGATION, t21142_timer },
   { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
 	HAS_MII | HAS_PNICNWAY, pnic_timer },
   { "Macronix 98713 PMAC", 128, 0x0001ebef,
@@ -138,18 +135,15 @@
   { "Macronix 98725 PMAC", 256, 0x0001ebef,
 	HAS_MEDIA_TABLE, mxic_timer },
   { "ASIX AX88140", 128, 0x0001fbff,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY | IS_ASIX, tulip_timer },
   { "Lite-On PNIC-II", 256, 0x0801fbff,
-	HAS_MII | HAS_NWAY143 | HAS_8023X, t21142_timer },
+	HAS_MII | HAS_NWAY | HAS_8023X, t21142_timer },
   { "ADMtek Comet", 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,
-	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_NWAY143,
-	t21142_timer },
-  { "Xircom tulip work-alike", 128, 0x0801fbff,
-	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY,
 	t21142_timer },
   {0},
 };
@@ -163,16 +157,20 @@
 	{ 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
 	{ 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
 	{ 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
-	{ 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },
+/*	{ 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },*/
 	{ 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
 	{ 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
 	{ 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+	{ 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+	{ 0x1317, 0x1985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
 	{ 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 },
-	{ 0x115d, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
+	{ 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
+	{ 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
+	{ 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
 	{0},
 };
-MODULE_DEVICE_TABLE(pci,tulip_pci_tbl);
+MODULE_DEVICE_TABLE(pci, tulip_pci_tbl);
 
 
 /* A full-duplex map for media types. */
@@ -181,7 +179,13 @@
 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*/
-u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+u16 t21041_csr13[] = {
+	csr13_mask_10bt,		/* 10-T */
+	csr13_mask_auibnc,		/* 10-2 */
+	csr13_mask_auibnc,		/* AUI */
+	csr13_mask_10bt,		/* 10-T */
+	csr13_mask_10bt,		/* 10T-FD */
+};
 u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
 u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
@@ -198,54 +202,6 @@
 static void set_rx_mode(struct net_device *dev);
 
 
-/* The Xircom cards are picky about when certain bits in CSR6 can be
-   manipulated.  Keith Owens <kaos@ocs.com.au>. */
-
-void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6)
-{
-	long ioaddr = tp->base_addr;
-	const int strict_bits = 0x0060e202;
-	int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
-
-	/* common path */
-	if (tp->chip_id != X3201_3) {
-		outl (newcsr6, ioaddr + CSR6);
-		return;
-	}
-
-	newcsr6 &= 0x726cfeca;	/* mask out the reserved CSR6 bits that always */
-	/* read 0 on the Xircom cards */
-	newcsr6 |= 0x320c0000;	/* or in the reserved bits that always read 1 */
-	currcsr6 = inl (ioaddr + CSR6);
-	if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
-	    ((currcsr6 & ~0x2002) == 0))
-		goto out_write;
-
-	/* make sure the transmitter and receiver are stopped first */
-	currcsr6 &= ~0x2002;
-	while (1) {
-		csr5 = inl (ioaddr + CSR5);
-		if (csr5 == 0xffffffff)
-			break;	/* cannot read csr5, card removed? */
-		csr5_22_20 = csr5 & 0x700000;
-		csr5_19_17 = csr5 & 0x0e0000;
-		if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
-		    (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
-			break;	/* both are stopped or suspended */
-		if (!--attempts) {
-			printk (KERN_INFO "tulip.c: tulip_outl_CSR6 too many attempts,"
-				"csr5=0x%08x\n", csr5);
-			goto out_write;
-		}
-		outl (currcsr6, ioaddr + CSR6);
-		udelay (1);
-	}
-
-out_write:
-	/* now it is safe to change csr6 */
-	outl (newcsr6, ioaddr + CSR6);
-}
-
 
 static void tulip_up(struct net_device *dev)
 {
@@ -264,11 +220,13 @@
 
 	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
 	outl(0x00000001, ioaddr + CSR0);
+	udelay(100);
 
 	/* Deassert reset.
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
 	outl(tp->csr0, ioaddr + CSR0);
+	udelay(100);
 
 	if (tulip_debug > 1)
 		printk(KERN_DEBUG "%s: tulip_up(), irq==%d.\n", dev->name, dev->irq);
@@ -561,7 +519,6 @@
 
 out:
 	dev->trans_start = jiffies;
-	netif_start_queue (dev);
 	spin_unlock_irqrestore (&tp->lock, flags);
 }
 
@@ -786,14 +743,14 @@
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
 		if (tp->mii_cnt)
 			data[0] = phy;
-		else if (tp->flags & HAS_NWAY143)
+		else if (tp->flags & HAS_NWAY)
 			data[0] = 32;
 		else if (tp->chip_id == COMET)
 			data[0] = 1;
 		else
 			return -ENODEV;
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
-		if (data[0] == 32  &&  (tp->flags & HAS_NWAY143)) {
+		if (data[0] == 32  &&  (tp->flags & HAS_NWAY)) {
 			int csr12 = inl(ioaddr + CSR12);
 			int csr14 = inl(ioaddr + CSR14);
 			switch (data[1]) {
@@ -821,7 +778,7 @@
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
-		if (data[0] == 32  &&  (tp->flags & HAS_NWAY143)) {
+		if (data[0] == 32  &&  (tp->flags & HAS_NWAY)) {
 			if (data[1] == 5)
 				tp->to_advertise = data[2];
 		} else {
@@ -1108,9 +1065,13 @@
 	/* Clear the missed-packet counter. */
 	(volatile int)inl(ioaddr + CSR8);
 
-	if (chip_idx == DC21041  &&  inl(ioaddr + CSR9) & 0x8000) {
-		printk(" 21040 compatible mode,");
-		chip_idx = DC21040;
+	if (chip_idx == DC21041) {
+		if (inl(ioaddr + CSR9) & 0x8000) {
+			printk(" 21040 compatible mode,");
+			chip_idx = DC21040;
+		} else {
+			printk(" 21041 mode,");
+		}
 	}
 
 	/* The station address ROM is read byte serially.  The register must
@@ -1307,7 +1268,7 @@
 	dev->do_ioctl = private_ioctl;
 	dev->set_multicast_list = set_rx_mode;
 
-	if ((tp->flags & HAS_NWAY143)  || tp->chip_id == DC21041)
+	if ((tp->flags & HAS_NWAY)  || tp->chip_id == DC21041)
 		tp->link_change = t21142_lnk_change;
 	else if (tp->flags & HAS_PNICNWAY)
 		tp->link_change = pnic_lnk_change;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- v2.3.99-pre2/linux/drivers/net/via-rhine.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/net/via-rhine.c	Thu Mar 23 23:35:35 2000
@@ -27,16 +27,19 @@
 	LK1.1.1:
 	- Justin Guyett: softnet and locking fixes
 	- Jeff Garzik: use PCI interface
-		
-*/
 
-static const char *versionA =
-"via-rhine.c:v1.01-LK1.1.1  3/2/2000  Written by Donald Becker\n";
-static const char *versionB =
-"  http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+	LK1.1.2:
+	- Urban Widmark: minor cleanups, merges from Becker 1.03a/1.04 versions
+
+	LK1.1.3:
+	- Urban Widmark: use PCI DMA interface (with thanks to the eepro100.c code)
+	                 update "Theory of Operation" with softnet/locking changes
+	- Dave Miller: PCI DMA and endian fixups
+	- Jeff Garzik: MOD_xxx race fixes, updated PCI resource allocation
+*/
 
 /* A few user-configurable values.   These may be modified when a driver
-   module is loaded.*/
+   module is loaded. */
 
 static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
 static int max_interrupt_work = 20;
@@ -58,6 +61,7 @@
    The Rhine has a 64 element 8390-like hash table.  */
 static const int multicast_filter_limit = 32;
 
+
 /* Operational parameters that are set at compile time. */
 
 /* Keep the ring sizes a power of two for compile efficiency.
@@ -68,12 +72,21 @@
 #define TX_RING_SIZE	8
 #define RX_RING_SIZE	16
 
+
 /* Operational parameters that usually are not changed. */
+
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (2*HZ)
 
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
+
+#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error  See the last lines of the source file for the proper compile-command.
+#endif
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -91,15 +104,20 @@
 #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
+static const char *versionA __devinitdata =
+"via-rhine.c:v1.03a-LK1.1.3  3/23/2000  Written by Donald Becker\n";
+static const char *versionB __devinitdata =
+"  http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+
+
+
+/* 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
 #undef readb
 #undef readw
 #undef readl
@@ -114,9 +132,6 @@
 #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))
 
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
@@ -186,18 +201,17 @@
 
 The driver runs as two independent, single-threaded flows of control.  One
 is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag.  The other thread is the interrupt handler, which is single
-threaded by the hardware and interrupt handling software.
+dev->priv->lock spinlock. The other thread is the interrupt handler, which 
+is single threaded by the hardware and interrupt handling software.
 
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'lp->tx_full' flag.
+The send packet thread has partial control over the Tx ring. It locks the 
+dev->priv->lock whenever it's queuing a Tx packet. If the next slot in the ring
+is not available it stops the transmit queue by calling netif_stop_queue.
 
 The interrupt handler has exclusive control over the Rx ring and records stats
 from the Tx ring.  After reaping the stats, it marks the Tx queue entry as
-empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
-clears both the tx_full and tbusy flags.
+empty by incrementing the dirty_tx mark. If at least half of the entries in
+the Rx ring are available the transmit queue is woken up if it was stopped.
 
 IV. Notes
 
@@ -231,57 +245,49 @@
 };
 
 enum via_rhine_chips {
-	vt86c100a = 0,
-	vt3043,
+	VT86C100A = 0,
+	VT3043,
 };
 
 struct via_rhine_chip_info {
 	const char *name;
-	u16 flags;
+	u16 pci_flags;
 	int io_size;
+	int drv_flags;
 };
 
 
+enum chip_capability_flags {CanHaveMII=1, };
+
+#if defined(VIA_USE_MEMORY)
+#define RHINE_IOTYPE (PCI_USES_MEM | PCI_USES_MASTER | PCI_ADDR1)
+#else
+#define RHINE_IOTYPE (PCI_USES_IO  | PCI_USES_MASTER | PCI_ADDR0)
+#endif
+
 /* directly indexed by enum via_rhine_chips, above */
 static struct via_rhine_chip_info via_rhine_chip_info[] __devinitdata =
 {
-	{"VIA VT86C100A Rhine-II",
-	 PCI_USES_MEM | PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER,
-	 128,},
-	{"VIA VT3043 Rhine",
-	 PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER,
-	 128,},
+	{ "VIA VT86C100A Rhine-II", RHINE_IOTYPE, 128, CanHaveMII },
+	{ "VIA VT3043 Rhine", RHINE_IOTYPE, 128, CanHaveMII }
 };
 
 static struct pci_device_id via_rhine_pci_tbl[] __devinitdata =
 {
-	{0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt86c100a},
-	{0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt3043},
+	{0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A},
+	{0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT3043},
 	{0,},			/* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl);
 
 
-
-/* A chip capabilities table, matching the entries in pci_tbl[] above. */
-enum chip_capability_flags {CanHaveMII=1, };
-struct chip_info {
-	int io_size;
-	int flags;
-} static cap_tbl[] = {
-	{128, CanHaveMII, },
-	{128, CanHaveMII, },
-};
-
-
-/* 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,
 };
@@ -295,21 +301,19 @@
 	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;
@@ -317,9 +321,11 @@
 
 /* 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. */
@@ -331,32 +337,42 @@
 };
 
 struct netdev_private {
-	/* Descriptor rings first for alignment. */
-	struct rx_desc rx_ring[RX_RING_SIZE];
-	struct tx_desc tx_ring[TX_RING_SIZE];
+	/* Descriptor rings */
+	struct rx_desc *rx_ring;
+	struct tx_desc *tx_ring;
+	dma_addr_t rx_ring_dma;
+	dma_addr_t tx_ring_dma;
+
 	/* The addresses of receive-in-place skbuffs. */
-	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	struct sk_buff *rx_skbuff[RX_RING_SIZE];
+	dma_addr_t rx_skbuff_dma[RX_RING_SIZE];
+
 	/* The saved address of a sent-in-place packet/buffer, for later free(). */
-	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	struct sk_buff *tx_skbuff[TX_RING_SIZE];
+	dma_addr_t tx_skbuff_dma[TX_RING_SIZE];
 	unsigned char *tx_buf[TX_RING_SIZE];	/* Tx bounce buffers */
-	unsigned char *tx_bufs;				/* Tx bounce buffer region. */
+
+	struct pci_dev *pdev;
 	struct net_device_stats stats;
 	struct timer_list timer;	/* Media monitoring timer. */
 	spinlock_t lock;
+
 	/* Frequently used values: keep some adjacent for cache effect. */
 	int chip_id;
 	struct rx_desc *rx_head_desc;
-	unsigned short int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
-	unsigned short int cur_tx, dirty_tx;
+	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
+	unsigned int cur_tx, dirty_tx;
 	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
 	u16 chip_cmd;						/* Current setting for ChipCmd */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
+
 	/* These values are keep track of the transceiver/media in use. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
 	unsigned int duplex_lock:1;
 	unsigned int medialock:1;			/* Do not sense media. */
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	u8 tx_thresh, rx_thresh;
+
 	/* MII transceiver section. */
 	int mii_cnt;						/* MII device addresses. */
 	u16 advertising;					/* NWay media advertisement */
@@ -393,6 +409,9 @@
 	static int did_version = 0;
 	long ioaddr;
 	int io_size;
+	int pci_flags;
+	void *ring;
+	dma_addr_t ring_dma;
 	
 	/* print version once and once only */
 	if (! did_version++) {
@@ -403,44 +422,67 @@
 	card_idx++;
 	option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 	io_size = via_rhine_chip_info[chip_id].io_size;
+	pci_flags = via_rhine_chip_info[chip_id].pci_flags;
 
+	/* this should always be supported */
+	if (!pci_dma_supported(pdev, 0xffffffff)) {
+		printk(KERN_ERR "32-bit PCI DMA addresses not supported by the card!?\n");
+		goto err_out;
+	}
+	
+	/* sanity check */
+	if ((pci_resource_len (pdev, 0) < io_size) ||
+	    (pci_resource_len (pdev, 1) < io_size)) {
+		printk (KERN_ERR "Insufficient PCI resources, aborting\n");
+		goto err_out;
+	}
 
-#ifdef VIA_USE_IO
-	ioaddr = pci_resource_start (pdev, 0);
-#else
-	ioaddr = pci_resource_start (pdev, 1);
-#endif
+	/* allocate pci dma space for rx and tx descriptor rings */
+	ring = pci_alloc_consistent(pdev, 
+				    RX_RING_SIZE * sizeof(struct rx_desc) +
+				    TX_RING_SIZE * sizeof(struct tx_desc),
+				    &ring_dma);
+	if (!ring) {
+		printk(KERN_ERR "Could not allocate DMA memory.\n");
+		goto err_out;
+	}
+
+	ioaddr = pci_resource_start (pdev, pci_flags & PCI_ADDR0 ? 0 : 1);
 
 	if (pci_enable_device (pdev)) {
 		printk (KERN_ERR "unable to init PCI device (card #%d)\n",
 			card_idx);
-		goto err_out;
+		goto err_out_free_dma;
 	}
 	
-	if (via_rhine_chip_info[chip_id].flags & PCI_USES_MASTER)
+	if (pci_flags & PCI_USES_MASTER)
 		pci_set_master (pdev);
 
 	dev = init_etherdev(NULL, sizeof(*np));
 	if (dev == NULL) {
 		printk (KERN_ERR "init_ethernet failed for card #%d\n",
 			card_idx);
-		goto err_out;
+		goto err_out_free_dma;
 	}
 	
-	if (!request_region(pci_resource_start (pdev, 0), io_size, dev->name)) {
+	/* request all PIO and MMIO regions just to make sure
+	 * noone else attempts to use any portion of our I/O space */
+	if (!request_region (pci_resource_start (pdev, 0),
+			     pci_resource_len (pdev, 0), dev->name)) {
 		printk (KERN_ERR "request_region failed for device %s, region 0x%X @ 0x%lX\n",
 			dev->name, io_size,
 			pci_resource_start (pdev, 0));
 		goto err_out_free_netdev;
 	}
-	if (!request_mem_region(pci_resource_start (pdev, 1), io_size, dev->name)) {
+	if (!request_mem_region (pci_resource_start (pdev, 1),
+				 pci_resource_len (pdev, 1), dev->name)) {
 		printk (KERN_ERR "request_mem_region failed for device %s, region 0x%X @ 0x%lX\n",
 			dev->name, io_size,
 			pci_resource_start (pdev, 1));
 		goto err_out_free_pio;
 	}
 
-#ifndef VIA_USE_IO
+#ifndef USE_IO
 	ioaddr = (long) ioremap (ioaddr, io_size);
 	if (!ioaddr) {
 		printk (KERN_ERR "ioremap failed for device %s, region 0x%X @ 0x%X\n",
@@ -469,6 +511,11 @@
 	np = dev->priv;
 	spin_lock_init (&np->lock);
 	np->chip_id = chip_id;
+	np->pdev = pdev;
+	np->rx_ring = ring;
+	np->tx_ring = ring + RX_RING_SIZE * sizeof(struct rx_desc);
+	np->rx_ring_dma = ring_dma;
+	np->tx_ring_dma = ring_dma + RX_RING_SIZE * sizeof(struct rx_desc);
 
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -499,7 +546,7 @@
 	
 	pdev->driver_data = dev;
 
-	if (cap_tbl[np->chip_id].flags & CanHaveMII) {
+	if (via_rhine_chip_info[chip_id].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++) {
@@ -518,18 +565,25 @@
 
 	return 0;
 
-#ifndef VIA_USE_IO
+#ifndef USE_IO
 /* note this is ifdef'd because the ioremap is ifdef'd...
  * so additional exit conditions above this must move
  * release_mem_region outside of the ifdef */
 err_out_free_mmio:
-	release_mem_region(pci_resource_start (pdev, 1), io_size, dev->name));
+	release_mem_region(pci_resource_start (pdev, 1),
+			   pci_resource_len (pdev, 1));
 #endif
 err_out_free_pio:
-	release_region(pci_resource_start (pdev, 0), io_size);
+	release_region(pci_resource_start (pdev, 0),
+		       pci_resource_len (pdev, 0));
 err_out_free_netdev:
 	unregister_netdev (dev);
 	kfree (dev);
+err_out_free_dma:
+	pci_free_consistent(pdev, 
+			    RX_RING_SIZE * sizeof(struct rx_desc) +
+			    TX_RING_SIZE * sizeof(struct tx_desc),
+			    ring, ring_dma);
 err_out:
 	return -ENODEV;
 }
@@ -557,9 +611,12 @@
 
 static void mdio_write(struct net_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]  &&  regnum == 4)
+		np->advertising = value;
 	/* Wait for a previous command to complete. */
 	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
 		;
@@ -578,32 +635,34 @@
 	long ioaddr = dev->base_addr;
 	int i;
 
+	MOD_INC_USE_COUNT;
+
 	/* Reset the chip. */
 	writew(CmdReset, ioaddr + ChipCmd);
 
-	if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev))
-		return -EAGAIN;
+	if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev)) {
+		MOD_DEC_USE_COUNT;
+		return -EBUSY;
+	}
 
 	if (debug > 1)
 		printk(KERN_DEBUG "%s: via_rhine_open() irq %d.\n",
 			   dev->name, dev->irq);
 
-	MOD_INC_USE_COUNT;
-
 	via_rhine_init_ring(dev);
 
-	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
-	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
+	writel(np->rx_ring_dma, ioaddr + RxRingPtr);
+	writel(np->tx_ring_dma, ioaddr + TxRingPtr);
 
 	for (i = 0; i < 6; i++)
 		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;
-	np->rx_thresh = 0x60;				/* Written in via_rhine_set_rx_mode(). */
+	np->rx_thresh = 0x60;			/* Written in via_rhine_set_rx_mode(). */
 
 	if (dev->if_port == 0)
 		dev->if_port = np->default_port;
@@ -666,6 +725,7 @@
 	}
 }
 
+
 static void via_rhine_timer(unsigned long data)
 {
 	struct net_device *dev = (struct net_device *)data;
@@ -677,12 +737,14 @@
 		printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n",
 			   dev->name, readw(ioaddr + IntrStatus));
 	}
+
 	via_rhine_check_duplex(dev);
 
 	np->timer.expires = RUN_AT(next_tick);
 	add_timer(&np->timer);
 }
 
+
 static void via_rhine_tx_timeout (struct net_device *dev)
 {
 	struct netdev_private *np = (struct netdev_private *) dev->priv;
@@ -693,16 +755,17 @@
 		dev->name, readw (ioaddr + IntrStatus),
 		mdio_read (dev, np->phys[0], 1));
 
-	/* Perhaps we should reinitialize the hardware here. */
+	/* XXX Perhaps we should reinitialize the hardware here. */
 	dev->if_port = 0;
+
 	/* Stop and restart the chip's Tx processes . */
+	/* XXX to do */
 
 	/* Trigger an immediate transmit demand. */
+	/* XXX to do */
 
 	dev->trans_start = jiffies;
 	np->stats.tx_errors++;
-
-	netif_start_queue (dev);
 }
 
 
@@ -720,13 +783,13 @@
 
 	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 =
+			cpu_to_le32(np->rx_ring_dma + sizeof(struct rx_desc)*(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 = cpu_to_le32(np->rx_ring_dma);
 
 	/* Fill in the Rx buffers. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
@@ -734,20 +797,25 @@
 		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;
+		skb->dev = dev;                 /* Mark as being used by this device. */
+
+		np->rx_skbuff_dma[i] =
+			pci_map_single(np->pdev, skb->tail, np->rx_buf_sz,
+						   PCI_DMA_FROMDEVICE);
+
+		np->rx_ring[i].addr = cpu_to_le32(np->rx_skbuff_dma[i]);
+		np->rx_ring[i].rx_status = cpu_to_le32(DescOwn);
 	}
 
 	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 =
+			cpu_to_le32(np->tx_ring_dma + sizeof(struct tx_desc)*(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 = cpu_to_le32(np->tx_ring_dma);
 
 	return;
 }
@@ -771,16 +839,24 @@
 
 	if ((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)
+			(np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) {
+			spin_unlock_irqrestore (&np->lock, flags);
 			return 1;
+		}
 		memcpy(np->tx_buf[entry], skb->data, skb->len);
-		np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]);
-	} else
-		np->tx_ring[entry].addr = virt_to_bus(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_skbuff_dma[entry] =
+			pci_map_single(np->pdev, np->tx_buf[entry], skb->len,
+						   PCI_DMA_TODEVICE);
+	} else {
+		np->tx_skbuff_dma[entry] =
+			pci_map_single(np->pdev, skb->data, skb->len,
+						   PCI_DMA_TODEVICE);
+	}
+	np->tx_ring[entry].addr = cpu_to_le32(np->tx_skbuff_dma[entry]);
+
+	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++;
 
@@ -848,7 +924,7 @@
 }
 
 /* This routine is logically part of the interrupt handler, but isolated
-   for clarity and better register allocation. */
+   for clarity. */
 static void via_rhine_tx(struct net_device *dev)
 {
 	struct netdev_private *np = (struct netdev_private *)dev->priv;
@@ -858,15 +934,15 @@
 
 	/* if tx_full is set, they're all dirty, not clean */
 	while (np->dirty_tx != np->cur_tx) {
-		if (np->tx_ring[entry].tx_own) /* transmit request pending */
+		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",
+			printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n",
 				   entry, txstatus);
 		if (txstatus & 0x8000) {
 			if (debug > 1)
-				printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
+				printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
 					   dev->name, txstatus);
 			np->stats.tx_errors++;
 			if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
@@ -877,12 +953,15 @@
 			/* Transmitter restarted in 'abnormal' handler. */
 		} else {
 			np->stats.collisions += (txstatus >> 3) & 15;
-			np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
+			np->stats.tx_bytes += le32_to_cpu(np->tx_ring[entry].desc_length) & 0x7ff;
 			np->stats.tx_packets++;
-        	}
-        	/* Free the original skb. */
-        	dev_kfree_skb_irq(np->tx_skbuff[entry]);
-        	np->tx_skbuff[entry] = NULL;
+		}
+		/* Free the original skb. */
+		pci_unmap_single(np->pdev,
+						 le32_to_cpu(np->tx_ring[entry].addr),
+						 np->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq(np->tx_skbuff[entry]);
+		np->tx_skbuff[entry] = NULL;
 		entry = (++np->dirty_tx) % TX_RING_SIZE;
 	}
 	if ((np->cur_tx - np->dirty_tx) <= TX_RING_SIZE/2)
@@ -896,33 +975,32 @@
 static void via_rhine_rx(struct net_device *dev)
 {
 	struct netdev_private *np = (struct netdev_private *)dev->priv;
-	int entry = (np->dirty_rx = np->cur_rx) % RX_RING_SIZE;
-	int boguscnt = RX_RING_SIZE;
+	int entry = np->cur_rx % RX_RING_SIZE;
+	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
 
 	if (debug > 4) {
-		printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %4.4x.\n",
-			   entry, np->rx_head_desc->rx_length);
+		printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %8.8x.\n",
+			   entry, le32_to_cpu(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 "  via_rhine_rx() status is %4.4x.\n",
+			printk(KERN_DEBUG "  via_rhine_rx() status is %8.8x.\n",
 				   desc_status);
 		if (--boguscnt < 0)
 			break;
 		if ( (desc_status & (RxWholePkt | RxErr)) !=  RxWholePkt) {
 			if ((desc_status & RxWholePkt) !=  RxWholePkt) {
 				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
-					   "multiple buffers, entry %#x length %d status %4.4x!\n",
+					   "multiple buffers, entry %#x length %d status %8.8x!\n",
 					   dev->name, entry, data_size, desc_status);
 				printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
-					   dev->name, np->rx_head_desc,
-					   &np->rx_ring[entry]);
+					   dev->name, np->rx_head_desc, &np->rx_ring[entry]);
 				np->stats.rx_length_errors++;
 			} else if (desc_status & RxErr) {
 				/* There was a error. */
@@ -942,25 +1020,35 @@
 
 			/* Check if the packet is long enough to accept without copying
 			   to a minimally-sized skbuff. */
-			if (pkt_len < rx_copybreak
-				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+			if (pkt_len < rx_copybreak &&
+				(skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
+				pci_dma_sync_single(np->pdev, np->rx_skbuff_dma[entry],
+						    np->rx_buf_sz, PCI_DMA_FROMDEVICE);
 #if ! defined(__alpha__) || USE_IP_COPYSUM		/* Avoid misaligned on Alpha */
-				eth_copy_and_sum(skb, bus_to_virt(desc->addr),
-								 pkt_len, 0);
+				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);
+				skb = np->rx_skbuff[entry];
+				if (skb == NULL) {
+					printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n",
+						   dev->name);
+					break;
+				}
 				np->rx_skbuff[entry] = NULL;
+				skb_put(skb, pkt_len);
+				pci_unmap_single(np->pdev, np->rx_skbuff_dma[entry],
+								 np->rx_buf_sz, PCI_DMA_FROMDEVICE);
 			}
 			skb->protocol = eth_type_trans(skb, dev);
-			np->stats.rx_bytes+=skb->len;
 			netif_rx(skb);
 			dev->last_rx = jiffies;
+			np->stats.rx_bytes += skb->len;
 			np->stats.rx_packets++;
 		}
 		entry = (++np->cur_rx) % RX_RING_SIZE;
@@ -968,19 +1056,21 @@
 	}
 
 	/* Refill the Rx ring buffers. */
-	while (np->dirty_rx != np->cur_rx) {
+	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
 		struct sk_buff *skb;
-		entry = np->dirty_rx++ % RX_RING_SIZE;
+		entry = np->dirty_rx % RX_RING_SIZE;
 		if (np->rx_skbuff[entry] == NULL) {
 			skb = dev_alloc_skb(np->rx_buf_sz);
 			np->rx_skbuff[entry] = skb;
 			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_skbuff_dma[entry] =
+				pci_map_single(np->pdev, skb->tail, np->rx_buf_sz, 
+							   PCI_DMA_FROMDEVICE);
+			np->rx_ring[entry].addr = cpu_to_le32(np->rx_skbuff_dma[entry]);
 		}
-		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. */
@@ -1020,7 +1110,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 > 1) {
+	if ((intr_status & ~( IntrLinkChange | IntrStatsMax |
+						  IntrTxAbort | IntrTxAborted)) && debug > 1) {
 		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
 			   dev->name, intr_status);
 		/* Recovery for other fault sources not known. */
@@ -1076,6 +1167,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;
@@ -1083,12 +1176,11 @@
 		memset(mc_filter, 0, sizeof(mc_filter));
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
 			 i++, mclist = mclist->next) {
-			set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26,
-					mc_filter);
+			set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter);
 		}
 		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);
 }
@@ -1138,13 +1230,17 @@
 
 	/* 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].addr = 0xBADF00D0; /* An invalid address. */
+		np->rx_ring[i].rx_status = 0;
+		np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
 		if (np->rx_skbuff[i]) {
+			pci_unmap_single(np->pdev,
+							 np->rx_skbuff_dma[i],
+							 np->rx_buf_sz, PCI_DMA_FROMDEVICE);
 			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_kfree_skb(np->tx_skbuff[i]);
@@ -1165,14 +1261,19 @@
 	unregister_netdev(dev);
 
 	release_region(pci_resource_start (pdev, 0),
-		       via_rhine_chip_info[np->chip_id].io_size);
+		       pci_resource_len (pdev, 0));
 	release_mem_region(pci_resource_start (pdev, 1),
-		           via_rhine_chip_info[np->chip_id].io_size);
+		           pci_resource_len (pdev, 1));
 
-#ifndef VIA_USE_IO
+#ifndef USE_IO
 	iounmap((char *)(dev->base_addr));
 #endif
 
+	pci_free_consistent(pdev, 
+			    RX_RING_SIZE * sizeof(struct rx_desc) +
+			    TX_RING_SIZE * sizeof(struct tx_desc),
+			    np->rx_ring, np->rx_ring_dma);
+
 	kfree(dev);
 }
 
@@ -1187,7 +1288,15 @@
 
 static int __init via_rhine_init (void)
 {
-	return pci_module_init (&via_rhine_driver);
+	int rc;
+	
+	MOD_INC_USE_COUNT;
+	
+	rc = pci_module_init (&via_rhine_driver);
+	
+	MOD_DEC_USE_COUNT;
+	
+	return rc;
 }
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/wan/Config.in linux/drivers/net/wan/Config.in
--- v2.3.99-pre2/linux/drivers/net/wan/Config.in	Tue Mar 14 19:10:39 2000
+++ linux/drivers/net/wan/Config.in	Mon Mar 20 07:53:52 2000
@@ -55,9 +55,11 @@
 	      if [ "$CONFIG_VENDOR_SANGOMA" != "n" ]; then
 		 int '    Maximum number of cards' CONFIG_WANPIPE_CARDS 1
 		 bool '    WANPIPE Cisco HDLC support' CONFIG_WANPIPE_CHDLC
-		 bool '    WANPIPE Frame Relay support' CONFIG_WANPIPE_FR
+		 if [ "$CONFIG_OBSOLETE" = "y" ]; then
+		    bool '    WANPIPE Frame Relay support' CONFIG_WANPIPE_FR
+		    bool '    WANPIPE X.25 support' CONFIG_WANPIPE_X25
+		 fi
 		 bool '    WANPIPE PPP support' CONFIG_WANPIPE_PPP
-		 bool '    WANPIPE X.25 support' CONFIG_WANPIPE_X25
 	      fi
 	      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 		 dep_tristate '  Cyclom 2X(tm) cards (EXPERIMENTAL)' CONFIG_CYCLADES_SYNC $CONFIG_WAN_ROUTER_DRIVERS
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/parport/ChangeLog linux/drivers/parport/ChangeLog
--- v2.3.99-pre2/linux/drivers/parport/ChangeLog	Sun Mar 19 18:35:30 2000
+++ linux/drivers/parport/ChangeLog	Wed Mar 22 09:37:22 2000
@@ -1,3 +1,12 @@
+2000-03-22  Tim Waugh  <twaugh@redhat.com>
+
+	* init.c (parport_setup): Fix return value.
+
+2000-03-21  Tim Waugh  <twaugh@redhat.com>
+
+	* parport_pc.c (parport_pc_pci_probe): Fix return value; call
+	pci_enable_device.
+
 2000-03-16  Tim Waugh  <twaugh@redhat.com>
 
 	* parport_pc.c (parport_ECP_supported): This seems to trigger on
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/parport/init.c linux/drivers/parport/init.c
--- v2.3.99-pre2/linux/drivers/parport/init.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/parport/init.c	Wed Mar 22 09:37:22 2000
@@ -49,13 +49,13 @@
 	if (!str || !*str || (*str == '0' && !*(str+1))) {
 		/* Disable parport if "parport=0" in cmdline */
 		io[0] = PARPORT_DISABLE;
-		return 0;
+		return 1;
 	}
 
 	if (!strncmp (str, "auto", 4)) {
 		irq[0] = PARPORT_IRQ_AUTO;
 		dma[0] = PARPORT_DMA_AUTO;
-		return 0;
+		return 1;
 	}
 
 	val = simple_strtoul (str, &endptr, 0);
@@ -108,7 +108,7 @@
 	}
 
 	parport_setup_ptr++;
-	return 0;
+	return 1;
 }
 
 __setup ("parport=", parport_setup);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- v2.3.99-pre2/linux/drivers/parport/parport_pc.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/parport/parport_pc.c	Wed Mar 22 09:37:22 2000
@@ -2348,7 +2348,7 @@
 static int __devinit parport_pc_pci_probe (struct pci_dev *dev,
 					   const struct pci_device_id *id)
 {
-	int count, n, i = id->driver_data;
+	int err, count, n, i = id->driver_data;
 	if (i < last_sio)
 		/* This is an onboard Super-IO and has already been probed */
 		return 0;
@@ -2356,6 +2356,9 @@
 	/* This is a PCI card */
 	i -= last_sio;
 	count = 0;
+	if ((err = pci_enable_device (dev)) != 0)
+		return err;
+
 	for (n = 0; n < cards[i].numports; n++) {
 		int lo = cards[i].addr[n].lo;
 		int hi = cards[i].addr[n].hi;
@@ -2370,7 +2373,7 @@
 			count++;
 	}
 
-	return count;
+	return count == 0 ? -ENODEV : 0;
 }
 
 static struct pci_driver parport_pc_pci_driver = {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.3.99-pre2/linux/drivers/pci/pci.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/pci/pci.c	Tue Mar 21 11:02:09 2000
@@ -33,6 +33,16 @@
 LIST_HEAD(pci_root_buses);
 LIST_HEAD(pci_devices);
 
+/**
+ * pci_find_slot - locate PCI device from a given PCI slot
+ * @bus: number of PCI bus on which desired PCI device resides
+ * @devfn:  number of PCI slot in which desired PCI device resides
+ *
+ * Given a PCI bus and slot number, the desired PCI device is
+ * located in system global list of PCI devices.  If the device
+ * is found, a pointer to its data structure is returned.  If no 
+ * device is found, %NULL is returned.
+ */
 struct pci_dev *
 pci_find_slot(unsigned int bus, unsigned int devfn)
 {
@@ -66,6 +76,19 @@
 }
 
 
+/**
+ * pci_find_device - begin or continue searching for a PCI device by vendor/device id
+ * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
+ * @device: PCI device id to match, or %PCI_ANY_ID to match all vendor ids
+ * @from: Previous PCI device found in search, or %NULL for new search.
+ *
+ * Iterates through the list of known PCI devices.  If a PCI device is
+ * found with a matching @vendor and @device, a pointer to its device structure is
+ * returned.  Otherwise, %NULL is returned.
+ *
+ * A new search is initiated by passing %NULL to the @from argument.
+ * Otherwise if @from is not null, searches continue from that point.
+ */
 struct pci_dev *
 pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from)
 {
@@ -73,6 +96,18 @@
 }
 
 
+/**
+ * pci_find_class - begin or continue searching for a PCI device by class
+ * @class: search for a PCI device with this class designation
+ * @from: Previous PCI device found in search, or %NULL for new search.
+ *
+ * Iterates through the list of known PCI devices.  If a PCI device is
+ * found with a matching @class, a pointer to its device structure is
+ * returned.  Otherwise, %NULL is returned.
+ *
+ * A new search is initiated by passing %NULL to the @from argument.
+ * Otherwise if @from is not null, searches continue from that point.
+ */
 struct pci_dev *
 pci_find_class(unsigned int class, const struct pci_dev *from)
 {
@@ -122,7 +157,11 @@
 }
 
 
-/*
+/**
+ * pci_find_parent_resource - return resource region of parent bus of given region
+ * @dev: PCI device structure contains resources to be searched
+ * @res: child resource record for which parent is sought
+ *
  *  For given resource region of given device, return the resource
  *  region of parent bus the given region is contained in or where
  *  it should be allocated from.
@@ -150,7 +189,11 @@
 	return best;
 }
 
-/*
+/**
+ * pci_set_power_state - Set power management state of a device.
+ * @dev: PCI device for which PM is set
+ * @new_state: new power management statement (0 == D0, 3 == D3, etc.)
+ *
  *  Set power management state of a device.  For transitions from state D3
  *  it isn't as straightforward as one could assume since many devices forget
  *  their configuration space during wakeup.  Returns old power state.
@@ -192,7 +235,10 @@
 	return old_state;
 }
 
-/*
+/**
+ * pci_enable_device - Initialize device before it's used by a driver.
+ * @dev: PCI device to be initialized
+ *
  *  Initialize device before it's used by a driver. Ask low-level code
  *  to enable I/O and memory. Wake up the device if it was suspended.
  *  Beware, this function can fail.
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/pcmcia/ti113x.h linux/drivers/pcmcia/ti113x.h
--- v2.3.99-pre2/linux/drivers/pcmcia/ti113x.h	Sun Mar 19 18:35:30 2000
+++ linux/drivers/pcmcia/ti113x.h	Thu Mar 23 15:37:27 2000
@@ -137,16 +137,32 @@
 #ifdef CONFIG_CARDBUS
 
 /*
- * Generic TI open - TI has an extension for the
+ * Generic TI init - TI has an extension for the
  * INTCTL register that sets the PCI CSC interrupt.
  * Make sure we set it correctly at open and init
- * time.
+ * time
+ * - open: disable the PCI CSC interrupt. This makes
+ *   it possible to use the CSC interrupt to probe the
+ *   ISA interrupts.
+ * - init: set the interrupt to match our PCI state.
+ *   This makes us correctly get PCI CSC interrupt
+ *   events.
  */
 static int ti_open(pci_socket_t *socket)
 {
 	u8 new, reg = exca_readb(socket, I365_INTCTL);
 
 	new = reg & ~I365_INTR_ENA;
+	if (new != reg)
+		exca_writeb(socket, I365_INTCTL, new);
+	return 0;
+}
+
+static int ti_intctl(pci_socket_t *socket)
+{
+	u8 new, reg = exca_readb(socket, I365_INTCTL);
+
+	new = reg & ~I365_INTR_ENA;
 	if (socket->cb_irq)
 		new |= I365_INTR_ENA;
 	if (new != reg)
@@ -157,7 +173,7 @@
 static int ti_init(pci_socket_t *socket)
 {
 	yenta_init(socket);
-	ti_open(socket);
+	ti_intctl(socket);
 	return 0;
 }
 
@@ -200,7 +216,7 @@
 	config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket));
 	config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket));
 	config_writeb(socket, TI113X_DEVICE_CONTROL, ti_devctl(socket));
-	ti_open(socket);
+	ti_intctl(socket);
 	return 0;
 }
 
@@ -237,7 +253,7 @@
 	yenta_init(socket);
 
 	config_writeb(socket, TI1250_DIAGNOSTIC, ti_diag(socket));
-	ti_open(socket);
+	ti_intctl(socket);
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/pcmcia/yenta.c linux/drivers/pcmcia/yenta.c
--- v2.3.99-pre2/linux/drivers/pcmcia/yenta.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/pcmcia/yenta.c	Sun Mar 19 20:59:38 2000
@@ -851,22 +851,3 @@
 	yenta_proc_setup
 };
 EXPORT_SYMBOL(yenta_operations);
-
-/*
- * Ricoh cardbus bridge: standard cardbus, except it needs
- * some extra init code to set timings etc.
- */
-struct pci_socket_ops ricoh_operations = {
-	yenta_open,
-	yenta_close,
-	ricoh_init,
-	yenta_suspend,
-	yenta_get_status,
-	yenta_get_socket,
-	yenta_set_socket,
-	yenta_get_io_map,
-	yenta_set_io_map,
-	yenta_get_mem_map,
-	yenta_set_mem_map,
-	yenta_proc_setup
-};
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/pnp/isapnp.c linux/drivers/pnp/isapnp.c
--- v2.3.99-pre2/linux/drivers/pnp/isapnp.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/pnp/isapnp.c	Sun Mar 19 18:19:22 2000
@@ -1208,6 +1208,50 @@
 	return NULL;
 }
 
+static const struct isapnp_card_id *
+isapnp_match_card(const struct isapnp_card_id *ids, struct pci_bus *card)
+{
+	int idx;
+
+	while (ids->vendor || ids->device) {
+		if ((ids->vendor == ISAPNP_ANY_ID || ids->vendor == card->vendor) &&
+		    (ids->device == ISAPNP_ANY_ID || ids->device == card->device)) {
+			for (idx = 0; idx < ISAPNP_CARD_DEVS; idx++) {
+				if (ids->devs[idx].vendor == 0 &&
+				    ids->devs[idx].function == 0)
+					return ids;
+				if (isapnp_find_dev(card,
+						    ids->devs[idx].vendor,
+						    ids->devs[idx].function,
+						    NULL) == NULL)
+					goto __next;
+			}
+			return ids;
+		}
+	      __next:
+		ids++;
+	}
+	return NULL;
+}
+
+int isapnp_probe_cards(const struct isapnp_card_id *ids,
+		       int (*probe)(struct pci_bus *_card,
+		       		    const struct isapnp_card_id *_id))
+{
+	struct pci_bus *card;	
+	const struct isapnp_card_id *id;
+	int count = 0;
+
+	if (ids == NULL || probe == NULL)
+		return -EINVAL;
+	isapnp_for_each_card(card) {
+		id = isapnp_match_card(ids, card);
+		if (id != NULL && probe(card, id) >= 0)
+			count++;
+	}
+	return count;
+}
+
 static unsigned int isapnp_dma_resource_flags(struct isapnp_dma *dma)
 {
 	return dma->flags | IORESOURCE_DMA | IORESOURCE_AUTO;
@@ -2065,6 +2109,8 @@
 
 #endif /* CONFIG_PCI */
 
+EXPORT_SYMBOL(isapnp_cards);
+EXPORT_SYMBOL(isapnp_devices);
 EXPORT_SYMBOL(isapnp_present);
 EXPORT_SYMBOL(isapnp_cfg_begin);
 EXPORT_SYMBOL(isapnp_cfg_end);
@@ -2080,6 +2126,7 @@
 EXPORT_SYMBOL(isapnp_deactivate);
 EXPORT_SYMBOL(isapnp_find_card);
 EXPORT_SYMBOL(isapnp_find_dev);
+EXPORT_SYMBOL(isapnp_probe_cards);
 EXPORT_SYMBOL(isapnp_resource_change);
 
 int __init isapnp_init(void)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sbus/char/envctrl.c linux/drivers/sbus/char/envctrl.c
--- v2.3.99-pre2/linux/drivers/sbus/char/envctrl.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/envctrl.c	Wed Mar 22 21:50:29 2000
@@ -1,4 +1,4 @@
-/* $Id: envctrl.c,v 1.15 2000/02/09 22:33:23 davem Exp $
+/* $Id: envctrl.c,v 1.16 2000/03/22 21:29:23 ecd Exp $
  * envctrl.c: Temperature and Fan monitoring on Machines providing it.
  *
  * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
@@ -1002,7 +1002,6 @@
 	return 0;
 
 out:
-	envctrl_stop();
 	return -ENODEV;
 }
 
@@ -1381,7 +1380,7 @@
 	} else {
 		err = prom_getproperty(node, "cpu-temp-factors",
 				       envctrl.cpu_temp_table, 256);
-		if (err) {
+		if (err < 0) {
 			printk("envctrl: can't read `cpu-temp-factors'\n");
 			goto out;
 		}
@@ -1398,7 +1397,7 @@
 	} else {
 		err = prom_getproperty(node, "cpu-fan-speeds",
 				       envctrl.cpu_fan_speeds, 112);
-		if (err) {
+		if (err < 0) {
 			printk("envctrl: can't read `cpu-fan-speeds'\n");
 			goto out;
 		}
@@ -1415,7 +1414,7 @@
 	} else {
 		err = prom_getproperty(node, "ps-temp-factors",
 				       envctrl.ps_temp_table, 256);
-		if (err) {
+		if (err < 0) {
 			printk("envctrl: can't read `ps-temp-factors'\n");
 			goto out;
 		}
@@ -1432,7 +1431,7 @@
 	} else {
 		err = prom_getproperty(node, "ps-fan-speeds",
 				       envctrl.ps_fan_speeds, 112);
-		if (err) {
+		if (err < 0) {
 			printk("envctrl: can't read `ps-fan-speeds'\n");
 			goto out;
 		}
@@ -1449,8 +1448,6 @@
 out:
 	if (tmp)
 		kfree(tmp);
-
-	envctrl_stop();
 	return err;
 }
 #endif /* U450_SUPPORT */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sbus/char/sunmouse.c linux/drivers/sbus/char/sunmouse.c
--- v2.3.99-pre2/linux/drivers/sbus/char/sunmouse.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/sunmouse.c	Tue Mar 21 23:38:25 2000
@@ -69,7 +69,6 @@
 	unsigned char prev_state;      /* Previous button state */
 	int delta_x;                   /* Current delta-x */
 	int delta_y;                   /* Current delta-y */
-	int present;                   
 	int ready;		       /* set if there if data is available */
 	int active;		       /* set if device is open */
         int vuid_mode;	               /* VUID_NATIVE or VUID_FIRM_EVENT */
@@ -382,8 +381,6 @@
 {
 	if(sunmouse.active++)
 		return 0;
-	if(!sunmouse.present)
-		return -EINVAL;
 	sunmouse.ready = sunmouse.delta_x = sunmouse.delta_y = 0;
 	sunmouse.button_state = 0x80;
 	sunmouse.vuid_mode = VUID_NATIVE;
@@ -555,11 +552,8 @@
 	SUN_MOUSE_MINOR, "sunmouse", &sun_mouse_fops
 };
 
-int __init sun_mouse_init(void)
+void sun_mouse_zsinit(void)
 {
-	if (!sunmouse.present)
-		return -ENODEV;
-
 	printk("Sun Mouse-Systems mouse driver version 1.00\n");
 
 	sunmouse.ready = sunmouse.active = 0;
@@ -568,11 +562,4 @@
 	sunmouse.button_state = 0x80;
 	init_waitqueue_head(&sunmouse.proc_list);
 	sunmouse.byte = 69;
-	return 0;
-}
-
-void
-sun_mouse_zsinit(void)
-{
-	sunmouse.present = 1;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v2.3.99-pre2/linux/drivers/sbus/char/sunserial.c	Wed Dec 29 13:13:18 1999
+++ linux/drivers/sbus/char/sunserial.c	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-/* $Id: sunserial.c,v 1.74 1999/12/15 22:30:23 davem Exp $
+/* $Id: sunserial.c,v 1.75 2000/03/22 02:45:36 davem Exp $
  * serial.c: Serial port driver infrastructure for the Sparc.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -66,6 +66,8 @@
 	}
 	return err;
 }
+
+__initcall(rs_init);
 
 void __init rs_kgdb_hook(int channel)
 {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.3.99-pre2/linux/drivers/scsi/Makefile	Sun Mar 19 18:35:30 2000
+++ linux/drivers/scsi/Makefile	Wed Mar 22 09:39:11 2000
@@ -738,17 +738,11 @@
 
 sim710.o : sim710_d.h
  
-initio.o: ini9100u.c i91uscsi.c
-	$(CC) $(CFLAGS) -c ini9100u.c -o ini9100u.o
-	$(CC) $(CFLAGS) -c i91uscsi.c -o i91uscsi.o
+initio.o: ini9100u.o i91uscsi.o
 	$(LD) -r -o initio.o ini9100u.o i91uscsi.o
-	rm -f ini9100u.o i91uscsi.o
 
 a100u2w.o: inia100.o i60uscsi.o
 	$(LD) -r -o a100u2w.o inia100.o i60uscsi.o
-
-megaraid.o: megaraid.c
-	$(CC) $(CFLAGS) -c megaraid.c
 
 scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
 		scsicam.o scsi_proc.o scsi_error.o scsi_obsolete.o \
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c
--- v2.3.99-pre2/linux/drivers/scsi/advansys.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/advansys.c	Sun Mar 19 18:19:35 2000
@@ -4186,7 +4186,7 @@
 #endif /* ADVANSYS_DEBUG */
 
 #ifdef ADVANSYS_ASSERT
-STATIC int             interrupts_enabled(void);
+STATIC int             advansys_interrupts_enabled(void);
 #endif /* ADVANSYS_ASSERT */
 
 
@@ -7053,7 +7053,7 @@
     Scsi_Device        *device;
     int                ret;
 
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_DBG2(1, "asc_execute_scsi_cmnd: scp %lx, done %lx\n",
         (ulong) scp, (ulong) scp->scsi_done);
 
@@ -7182,7 +7182,7 @@
     }
 
     ASC_DBG(1, "asc_execute_scsi_cmnd: end\n");
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     return ret;
 }
 
@@ -7602,7 +7602,7 @@
     struct Scsi_Host    *shp;
     int                 i;
 
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_DBG2(1, "asc_isr_callback: asc_dvc_varp %lx, qdonep %lx\n",
         (ulong) asc_dvc_varp, (ulong) qdonep);
     ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep);
@@ -7773,7 +7773,7 @@
     struct Scsi_Host    *shp;
     int                 i;
 
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_DBG2(1, "adv_isr_callback: adv_dvc_varp %lx, scsiqp %lx\n",
         (ulong) adv_dvc_varp, (ulong) scsiqp);
     ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
@@ -8422,7 +8422,7 @@
 
     ASC_DBG3(3, "asc_enqueue: ascq %lx, reqp %lx, flag %d\n",
         (ulong) ascq, (ulong) reqp, flag);
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_ASSERT(reqp != NULL);
     ASC_ASSERT(flag == ASC_FRONT || flag == ASC_BACK);
     tid = REQPTID(reqp);
@@ -8475,7 +8475,7 @@
     REQP    reqp;
 
     ASC_DBG2(3, "asc_dequeue: ascq %lx, tid %d\n", (ulong) ascq, tid);
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
     if ((reqp = ascq->q_first[tid]) != NULL) {
         ASC_ASSERT(ascq->q_tidmask & ADV_TID_TO_TIDMASK(tid));
@@ -8524,7 +8524,7 @@
     int     i;
 
     ASC_DBG2(3, "asc_dequeue_list: ascq %lx, tid %d\n", (ulong) ascq, tid);
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_ASSERT((tid == ASC_TID_ALL) || (tid >= 0 && tid <= ADV_MAX_TID));
 
     /*
@@ -8607,7 +8607,7 @@
 
     ASC_DBG2(3, "asc_rmqueue: ascq %lx, reqp %lx\n",
         (ulong) ascq, (ulong) reqp);
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_ASSERT(reqp != NULL);
 
     tid = REQPTID(reqp);
@@ -8675,7 +8675,7 @@
 
     ASC_DBG2(3, "asc_isqueued: ascq %lx, reqp %lx\n",
         (ulong) ascq, (ulong) reqp);
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     ASC_ASSERT(reqp != NULL);
 
     tid = REQPTID(reqp);
@@ -8705,7 +8705,7 @@
     int                     i;
 
     ASC_DBG1(1, "asc_execute_queue: ascq %lx\n", (ulong) ascq);
-    ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+    ASC_ASSERT(advansys_interrupts_enabled() == ASC_FALSE);
     /*
      * Execute queued commands for devices attached to
      * the current board in round-robin fashion.
@@ -10919,12 +10919,12 @@
 
 #ifdef ADVANSYS_ASSERT
 /*
- * interrupts_enabled()
+ * advansys_interrupts_enabled()
  *
  * Return 1 if interrupts are enabled, otherwise return 0.
  */
 STATIC int
-interrupts_enabled(void)
+advansys_interrupts_enabled(void)
 {
     int flags;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v2.3.99-pre2/linux/drivers/scsi/eata.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/scsi/eata.c	Tue Mar 21 14:43:39 2000
@@ -1212,7 +1212,7 @@
 
    ints[0] = i - 1;
    internal_setup(cur, ints);
-   return 0;
+   return 1;
 }
 
 static void add_pci_ports(void) {
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/qlogicisp.c linux/drivers/scsi/qlogicisp.c
--- v2.3.99-pre2/linux/drivers/scsi/qlogicisp.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/qlogicisp.c	Wed Mar 22 00:05:02 2000
@@ -6,6 +6,9 @@
  * Copyright 2000, Jayson C. Vantuyl <vantuyl@csc.smsu.edu>
  *             and Bryon W. Roche    <bryon@csc.smsu.edu>
  *
+ * 64-bit addressing added by Kanoj Sarcar <kanoj@sgi.com>
+ * 			   and Leo Dagum    <dagum@sgi.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, or (at your option) any
@@ -18,6 +21,7 @@
  */
 
 #include <linux/blk.h>
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
@@ -160,6 +164,8 @@
 #define MBOX3		0x76	/* mailbox 3 */
 #define MBOX4		0x78	/* mailbox 4 */
 #define MBOX5		0x7a	/* mailbox 5 */
+#define MBOX6           0x7c    /* mailbox 6 */
+#define MBOX7           0x7e    /* mailbox 7 */
 
 /* mailbox command complete status codes */
 #define MBOX_COMMAND_COMPLETE		0x4000
@@ -177,6 +183,15 @@
 #define REQUEST_QUEUE_WAKEUP		0x8005
 #define EXECUTION_TIMEOUT_RESET		0x8006
 
+#ifdef CONFIG_QL_ISP_A64
+#define IOCB_SEGS                       2
+#define CONTINUATION_SEGS               5
+#define MAX_CONTINUATION_ENTRIES        254
+#else
+#define IOCB_SEGS                       4
+#define CONTINUATION_SEGS               7
+#endif /* CONFIG_QL_ISP_A64 */
+
 struct Entry_header {
 	u_char	entry_type;
 	u_char	entry_cnt;
@@ -185,8 +200,14 @@
 };
 
 /* entry header type commands */
+#ifdef CONFIG_QL_ISP_A64
+#define ENTRY_COMMAND           9
+#define ENTRY_CONTINUATION      0xa
+#else
 #define ENTRY_COMMAND		1
 #define ENTRY_CONTINUATION	2
+#endif /* CONFIG_QL_ISP_A64 */
+
 #define ENTRY_STATUS		3
 #define ENTRY_MARKER		4
 #define ENTRY_EXTENDED_COMMAND	5
@@ -199,6 +220,9 @@
 
 struct dataseg {
 	u_int			d_base;
+#ifdef CONFIG_QL_ISP_A64
+	u_int                   d_base_hi;
+#endif
 	u_int			d_count;
 };
 
@@ -213,7 +237,11 @@
 	u_short			time_out;
 	u_short			segment_cnt;
 	u_char			cdb[12];
-	struct dataseg		dataseg[4];
+#ifdef CONFIG_QL_ISP_A64
+	u_int                   rsvd1;
+	u_int                   rsvd2;
+#endif
+	struct dataseg		dataseg[IOCB_SEGS];
 };
 
 /* command entry control flag definitions */
@@ -240,8 +268,10 @@
 
 struct Continuation_Entry {
 	struct Entry_header	hdr;
+#ifndef CONFIG_QL_ISP_A64
 	u_int			reserved;
-	struct dataseg		dataseg[7];
+#endif
+	struct dataseg		dataseg[CONTINUATION_SEGS];
 };
 
 struct Marker_Entry {
@@ -385,6 +415,11 @@
 #define MBOX_WRITE_FOUR_RAM_WORDS	0x0041
 #define MBOX_EXEC_BIOS_IOCB		0x0042
 
+#ifdef CONFIG_QL_ISP_A64
+#define MBOX_CMD_INIT_REQUEST_QUEUE_64      0x0052
+#define MBOX_CMD_INIT_RESPONSE_QUEUE_64     0x0053
+#endif /* CONFIG_QL_ISP_A64 */
+
 #include "qlogicisp_asm.c"
 
 #define PACKB(a, b)			(((a)<<4)|(b))
@@ -457,6 +492,25 @@
 	PACKB(1, 2),	/* MBOX_RETURN_BIOS_BLOCK_ADDR */
 	PACKB(6, 1),	/* MBOX_WRITE_FOUR_RAM_WORDS */
 	PACKB(2, 3)	/* MBOX_EXEC_BIOS_IOCB */
+#ifdef CONFIG_QL_ISP_A64
+	,PACKB(0, 0),	/* 0x0043 */
+	PACKB(0, 0),	/* 0x0044 */
+	PACKB(0, 0),	/* 0x0045 */
+	PACKB(0, 0),	/* 0x0046 */
+	PACKB(0, 0),	/* 0x0047 */
+	PACKB(0, 0),	/* 0x0048 */
+	PACKB(0, 0),	/* 0x0049 */
+	PACKB(0, 0),	/* 0x004a */
+	PACKB(0, 0),	/* 0x004b */
+	PACKB(0, 0),	/* 0x004c */
+	PACKB(0, 0),	/* 0x004d */
+	PACKB(0, 0),	/* 0x004e */
+	PACKB(0, 0),	/* 0x004f */
+	PACKB(0, 0),	/* 0x0050 */
+	PACKB(0, 0),	/* 0x0051 */
+	PACKB(8, 8),	/* MBOX_CMD_INIT_REQUEST_QUEUE_64 (0x0052) */
+	PACKB(8, 8)	/* MBOX_CMD_INIT_RESPONSE_QUEUE_64 (0x0053) */
+#endif /* CONFIG_QL_ISP_A64 */
 };
 
 #define MAX_MBOX_COMMAND	(sizeof(mbox_param)/sizeof(u_short))
@@ -742,6 +796,7 @@
 	struct Continuation_Entry *cont;
 	struct Scsi_Host *host;
 	struct isp1020_hostdata *hostdata;
+	dma_addr_t	dma_addr;
 
 	ENTER("isp1020_queuecommand");
 
@@ -817,14 +872,18 @@
 
 		/* fill in first four sg entries: */
 		n = sg_count;
-		if (n > 4)
-			n = 4;
+		if (n > IOCB_SEGS)
+			n = IOCB_SEGS;
 		for (i = 0; i < n; i++) {
-			ds[i].d_base  = cpu_to_le32(sg_dma_address(sg));
+			dma_addr = sg_dma_address(sg);
+			ds[i].d_base  = cpu_to_le32((u32) dma_addr);
+#ifdef CONFIG_QL_ISP_A64
+			ds[i].d_base_hi = cpu_to_le32((u32) (dma_addr>>32));
+#endif /* CONFIG_QL_ISP_A64 */
 			ds[i].d_count = cpu_to_le32(sg_dma_len(sg));
 			++sg;
 		}
-		sg_count -= 4;
+		sg_count -= IOCB_SEGS;
 
 		while (sg_count > 0) {
 			++cmd->hdr.entry_cnt;
@@ -841,32 +900,46 @@
 			cont->hdr.entry_cnt  = 0;
 			cont->hdr.sys_def_1  = 0;
 			cont->hdr.flags      = 0;
+#ifndef CONFIG_QL_ISP_A64
 			cont->reserved = 0;
+#endif
 			ds = cont->dataseg;
 			n = sg_count;
-			if (n > 7)
-				n = 7;
+			if (n > CONTINUATION_SEGS)
+				n = CONTINUATION_SEGS;
 			for (i = 0; i < n; ++i) {
-				ds[i].d_base = cpu_to_le32(sg_dma_address(sg));
+				dma_addr = sg_dma_address(sg);
+				ds[i].d_base = cpu_to_le32((u32) dma_addr);
+#ifdef CONFIG_QL_ISP_A64
+				ds[i].d_base_hi = cpu_to_le32((u32)(dma_addr>>32));
+#endif /* CONFIG_QL_ISP_A64 */
 				ds[i].d_count = cpu_to_le32(sg_dma_len(sg));
 				++sg;
 			}
 			sg_count -= n;
 		}
 	} else if (Cmnd->request_bufflen) {
-		Cmnd->SCp.ptr = (char *)(unsigned long)
-			pci_map_single(hostdata->pci_dev,
+		/*Cmnd->SCp.ptr = (char *)(unsigned long)*/
+		dma_addr = pci_map_single(hostdata->pci_dev,
 				       Cmnd->request_buffer,
 				       Cmnd->request_bufflen,
 				       scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+		Cmnd->SCp.ptr = (char *)(unsigned long) dma_addr;
 
 		cmd->dataseg[0].d_base =
-			cpu_to_le32((u32)(long)Cmnd->SCp.ptr);
+			cpu_to_le32((u32) dma_addr);
+#ifdef CONFIG_QL_ISP_A64
+		cmd->dataseg[0].d_base_hi =
+			cpu_to_le32((u32) (dma_addr>>32));
+#endif /* CONFIG_QL_ISP_A64 */
 		cmd->dataseg[0].d_count =
 			cpu_to_le32((u32)Cmnd->request_bufflen);
 		cmd->segment_cnt = cpu_to_le16(1);
 	} else {
 		cmd->dataseg[0].d_base = 0;
+#ifdef CONFIG_QL_ISP_A64
+		cmd->dataseg[0].d_base_hi = 0;
+#endif /* CONFIG_QL_ISP_A64 */
 		cmd->dataseg[0].d_count = 0;
 		cmd->segment_cnt = cpu_to_le16(1); /* Shouldn't this be 0? */
 	}
@@ -983,7 +1056,11 @@
 				     scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
 		else if (Cmnd->request_bufflen)
 			pci_unmap_single(hostdata->pci_dev,
+#ifdef CONFIG_QL_ISP_A64
+					 (dma_addr_t)((long)Cmnd->SCp.ptr),
+#else
 					 (u32)((long)Cmnd->SCp.ptr),
+#endif
 					 Cmnd->request_bufflen,
 					 scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
 
@@ -1612,8 +1689,13 @@
 static int isp1020_load_parameters(struct Scsi_Host *host)
 {
 	int i, k;
+#ifdef CONFIG_QL_ISP_A64
+	u_long queue_addr;
+	u_short param[8];
+#else
 	u_int queue_addr;
 	u_short param[6];
+#endif
 	u_short isp_cfg1, hwrev;
 	unsigned long flags;
 	struct isp1020_hostdata *hostdata =
@@ -1753,13 +1835,20 @@
 	}
 
 	queue_addr = hostdata->res_dma;
-
+#ifdef CONFIG_QL_ISP_A64
+	param[0] = MBOX_CMD_INIT_RESPONSE_QUEUE_64;
+#else
 	param[0] = MBOX_INIT_RES_QUEUE;
+#endif
 	param[1] = RES_QUEUE_LEN + 1;
 	param[2] = (u_short) (queue_addr >> 16);
 	param[3] = (u_short) (queue_addr & 0xffff);
 	param[4] = 0;
 	param[5] = 0;
+#ifdef CONFIG_QL_ISP_A64
+	param[6] = (u_short) (queue_addr >> 48);
+	param[7] = (u_short) (queue_addr >> 32);
+#endif
 
 	isp1020_mbox_command(host, param);
 
@@ -1770,13 +1859,22 @@
 	}
 
 	queue_addr = hostdata->req_dma;
-
+#ifdef CONFIG_QL_ISP_A64
+	param[0] = MBOX_CMD_INIT_REQUEST_QUEUE_64;
+#else
 	param[0] = MBOX_INIT_REQ_QUEUE;
+#endif
 	param[1] = QLOGICISP_REQ_QUEUE_LEN + 1;
 	param[2] = (u_short) (queue_addr >> 16);
 	param[3] = (u_short) (queue_addr & 0xffff);
 	param[4] = 0;
 
+#ifdef CONFIG_QL_ISP_A64
+	param[5] = 0;
+	param[6] = (u_short) (queue_addr >> 48);
+	param[7] = (u_short) (queue_addr >> 32);
+#endif
+
 	isp1020_mbox_command(host, param);
 
 	if (param[0] != MBOX_COMMAND_COMPLETE) {
@@ -1811,6 +1909,8 @@
 		printk("qlogicisp: mbox_command loop timeout #1\n");
 
 	switch(mbox_param[param[0]] >> 4) {
+	      case 8: isp_outw(param[7], host, MBOX7);
+	      case 7: isp_outw(param[6], host, MBOX6);
 	      case 6: isp_outw(param[5], host, MBOX5);
 	      case 5: isp_outw(param[4], host, MBOX4);
 	      case 4: isp_outw(param[3], host, MBOX3);
@@ -1836,6 +1936,8 @@
 		printk("qlogicisp: mbox_command loop timeout #3\n");
 
 	switch(mbox_param[param[0]] & 0xf) {
+	      case 8: param[7] = isp_inw(host, MBOX7);
+	      case 7: param[6] = isp_inw(host, MBOX6);
 	      case 6: param[5] = isp_inw(host, MBOX5);
 	      case 5: param[4] = isp_inw(host, MBOX4);
 	      case 4: param[3] = isp_inw(host, MBOX3);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.3.99-pre2/linux/drivers/scsi/scsi.c	Tue Mar 14 19:10:40 2000
+++ linux/drivers/scsi/scsi.c	Thu Mar 23 11:03:45 2000
@@ -1377,12 +1377,12 @@
  */
 void scsi_release_commandblocks(Scsi_Device * SDpnt)
 {
-	Scsi_Cmnd *SCpnt;
+	Scsi_Cmnd *SCpnt, *SCnext;
 	unsigned long flags;
 
-	spin_lock_irqsave(&device_request_lock, flags);
-	for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
-		SDpnt->device_queue = SCpnt->next;
+ 	spin_lock_irqsave(&device_request_lock, flags);
+	for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) {
+		SDpnt->device_queue = SCnext = SCpnt->next;
 		kfree((char *) SCpnt);
 	}
 	SDpnt->has_cmdblocks = 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.3.99-pre2/linux/drivers/scsi/st.c	Tue Mar 14 19:10:40 2000
+++ linux/drivers/scsi/st.c	Wed Mar 22 15:37:46 2000
@@ -5,14 +5,14 @@
    History:
    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, Michael Schaefer, J"org Weule, and
-   Eric Youngdale.
+   order) Klaus Ehrenfried, Eric Lee Green, Wolfgang Denk, Steve Hirsch,
+   Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Michael Schaefer,
+   J"org Weule, and Eric Youngdale.
 
    Copyright 1992 - 2000 Kai Makisara
    email Kai.Makisara@metla.fi
 
-   Last modified: Mon Mar 13 21:15:29 2000 by makisara@kai.makisara.local
+   Last modified: Sun Mar 19 22:06:54 2000 by makisara@kai.makisara.local
    Some small formal changes - aeb, 950809
 
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
@@ -2621,8 +2621,7 @@
 	return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
 }
 
-/* Functions for reading and writing the medium partition mode page. These
-   seem to work with Wangtek 6200HS and HP C1533A. */
+/* Functions for reading and writing the medium partition mode page. */
 
 #define PART_PAGE   0x11
 #define PART_PAGE_FIXED_LENGTH 8
@@ -2675,6 +2674,11 @@
    The following algorithm is used to accomodate both drives: if the number of
    partition size fields is greater than the maximum number of additional partitions
    in the mode page, the second field is used. Otherwise the first field is used.
+
+   For Seagate DDS drives the page length must be 8 when no partitions is defined
+   and 10 when 1 partition is defined (information from Eric Lee Green). This is
+   is acceptable also to some other old drives and enforced if the first partition
+   size field is used for the first additional partition size.
  */
 static int partition_tape(Scsi_Tape *STp, int size)
 {
@@ -2707,12 +2711,16 @@
 
 	if (size <= 0) {
 		bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
+		if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
+		    bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
                 DEBC(printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n",
                             dev));
 	} else {
 		bp[psdo] = (size >> 8) & 0xff;
 		bp[psdo + 1] = size & 0xff;
 		bp[pgo + 3] = 1;
+		if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8)
+		    bp[pgo + MP_OFF_PAGE_LENGTH] = 8;
                 DEBC(printk(ST_DEB_MSG
                             "st%d: Formatting tape with two partitions (1 = %d MB).\n",
                             dev, size));
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v2.3.99-pre2/linux/drivers/scsi/u14-34f.c	Thu Mar  2 14:36:23 2000
+++ linux/drivers/scsi/u14-34f.c	Tue Mar 21 14:43:39 2000
@@ -943,7 +943,7 @@
 
    ints[0] = i - 1;
    internal_setup(cur, ints);
-   return 0;
+   return 1;
 }
 
 int u14_34f_detect(Scsi_Host_Template *tpnt)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/ac97_codec.c linux/drivers/sound/ac97_codec.c
--- v2.3.99-pre2/linux/drivers/sound/ac97_codec.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/sound/ac97_codec.c	Mon Mar 20 07:53:51 2000
@@ -25,7 +25,7 @@
  * v0.3 Feb 22 2000 Ollie Lho
  *	bug fix for record mask setting
  * v0.2 Feb 10 2000 Ollie Lho
- *	add ac97_read_proc for /proc/driver/vnedor/ac97
+ *	add ac97_read_proc for /proc/driver/{vendor}/ac97
  * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw> 
  *	Isolated from trident.c to support multiple ac97 codec
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/awe_hw.h linux/drivers/sound/awe_hw.h
--- v2.3.99-pre2/linux/drivers/sound/awe_hw.h	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/awe_hw.h	Tue Mar 21 11:10:45 2000
@@ -3,9 +3,9 @@
  *
  * Access routines and definitions for the low level driver for the 
  * Creative AWE32/SB32/AWE64 wave table synth.
- *   version 0.4.3; Mar. 1, 1998
+ *   version 0.4.4; Jan. 4, 2000
  *
- * Copyright (C) 1996-1998 Takashi Iwai
+ * Copyright (C) 1996-2000 Takashi Iwai
  *
  * 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
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/awe_wave.c linux/drivers/sound/awe_wave.c
--- v2.3.99-pre2/linux/drivers/sound/awe_wave.c	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/awe_wave.c	Wed Mar 22 15:37:11 2000
@@ -2,9 +2,9 @@
  * sound/awe_wave.c
  *
  * The low level driver for the AWE32/SB32/AWE64 wave table synth.
- *   version 0.4.3; Feb. 1, 1999
+ *   version 0.4.4; Jan. 4, 2000
  *
- * Copyright (C) 1996-1999 Takashi Iwai
+ * Copyright (C) 1996-2000 Takashi Iwai
  *
  * 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
@@ -26,6 +26,9 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/string.h>
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+#include <linux/isapnp.h>
+#endif
 
 #include "sound_config.h"
 #include "soundmodule.h"
@@ -42,9 +45,6 @@
  * debug message
  */
 
-/* do not allocate buffer at beginning */
-#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
-
 #ifdef AWE_DEBUG_ON
 #define DEBUG(LVL,XXX)	{if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }}
 #define ERRMSG(XXX)	{if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }}
@@ -59,56 +59,56 @@
  * bank and voice record
  */
 
+typedef struct _sf_list sf_list;
+typedef struct _awe_voice_list awe_voice_list;
+typedef struct _awe_sample_list awe_sample_list;
+
 /* soundfont record */
-typedef struct _sf_list {
-	unsigned short sf_id;
-	unsigned short type;
+struct _sf_list {
+	unsigned short sf_id;	/* id number */
+	unsigned short type;	/* lock & shared flags */
 	int num_info;		/* current info table index */
 	int num_sample;		/* current sample table index */
 	int mem_ptr;		/* current word byte pointer */
-	int infos;
-	int samples;
+	awe_voice_list *infos, *last_infos;	/* instruments */
+	awe_sample_list *samples, *last_samples;	/* samples */
 #ifdef AWE_ALLOW_SAMPLE_SHARING
-	int shared;		/* shared index */
-	unsigned char name[AWE_PATCH_NAME_LEN];
+	sf_list *shared;	/* shared list */
+	unsigned char name[AWE_PATCH_NAME_LEN];	/* sharing id */
 #endif
-} sf_list;
+	sf_list *next, *prev;
+};
 
-/* bank record */
-typedef struct _awe_voice_list {
-	int next;	/* linked list with same sf_id */
+/* instrument list */
+struct _awe_voice_list {
+	awe_voice_info v;	/* instrument information */
+	sf_list *holder;	/* parent sf_list of this record */
 	unsigned char bank, instr;	/* preset number information */
 	char type, disabled;	/* type=normal/mapped, disabled=boolean */
-	awe_voice_info v;	/* voice information */
-	int next_instr;	/* preset table list */
-	int next_bank;	/* preset table list */
-} awe_voice_list;
+	awe_voice_list *next;	/* linked list with same sf_id */
+	awe_voice_list *next_instr;	/* instrument list */
+	awe_voice_list *next_bank;	/* hash table list */
+};
 
 /* voice list type */
 #define V_ST_NORMAL	0
 #define V_ST_MAPPED	1
 
-typedef struct _awe_sample_list {
-	int next;	/* linked list with same sf_id */
+/* sample list */
+struct _awe_sample_list {
 	awe_sample_info v;	/* sample information */
-} awe_sample_list;
+	sf_list *holder;	/* parent sf_list of this record */
+	awe_sample_list *next;	/* linked list with same sf_id */
+};
 
 /* sample and information table */
-static int current_sf_id = 0;
-static int locked_sf_id = 0;
-static int max_sfs;
-static sf_list *sflists = NULL;
-
-#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr)
-#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info)
-#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample)
-
-static int max_samples;
-static awe_sample_list *samples = NULL;
-
-static int max_infos;
-static awe_voice_list *infos = NULL;
-
+static int current_sf_id = 0;	/* current number of fonts */
+static int locked_sf_id = 0;	/* locked position */
+static sf_list *sfhead = NULL, *sftail = NULL;	/* linked-lists */
+
+#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0)
+#define awe_free_info() (sftail ? sftail->num_info : 0)
+#define awe_free_sample() (sftail ? sftail->num_sample : 0)
 
 #define AWE_MAX_PRESETS		256
 #define AWE_DEFAULT_PRESET	0
@@ -119,7 +119,7 @@
 #define MAX_LAYERS	AWE_MAX_VOICES
 
 /* preset table index */
-static int preset_table[AWE_MAX_PRESETS];
+static awe_voice_list *preset_table[AWE_MAX_PRESETS];
 
 /*
  * voice table
@@ -143,8 +143,6 @@
 	int main_vol;		/* channel volume (0-127) */
 	int expression_vol;	/* midi expression (0-127) */
 	int chan_press;		/* channel pressure */
-	int vrec;		/* instrument list */
-	int def_vrec;		/* default instrument list */
 	int sustained;		/* sustain status in MIDI */
 	FX_Rec fx;		/* effects */
 	FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
@@ -194,9 +192,9 @@
 static awe_chan_info channels[AWE_MAX_CHANNELS];
 
 
-/*----------------------------------------------------------------
+/*
  * global variables
- *----------------------------------------------------------------*/
+ */
 
 #ifndef AWE_DEFAULT_BASE_ADDR
 #define AWE_DEFAULT_BASE_ADDR	0	/* autodetect */
@@ -206,10 +204,13 @@
 #define AWE_DEFAULT_MEM_SIZE	-1	/* autodetect */
 #endif
 
-#define awe_port	io
-#define awe_mem_size	memsize
 int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */
 int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+static int isapnp = 1;
+#else
+static int isapnp = 0;
+#endif
 
 MODULE_AUTHOR("Takashi Iwai <iwai@ww.uni-erlangen.de>");
 MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver");
@@ -217,6 +218,8 @@
 MODULE_PARM_DESC(io, "base i/o port of Emu8000");
 MODULE_PARM(memsize, "i");
 MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes");
+MODULE_PARM(isapnp, "i");
+MODULE_PARM_DESC(isapnp, "use ISAPnP detection");
 EXPORT_NO_SYMBOLS;
 
 /* DRAM start offset */
@@ -255,7 +258,7 @@
 	0,			/* perc_mode (obsolete) */
 	AWE_MAX_VOICES,		/* nr_voices */
 	0,			/* nr_drums (obsolete) */
-	AWE_MAX_INFOS		/* instr_bank_size */
+	400			/* instr_bank_size */
 };
 
 
@@ -362,8 +365,9 @@
 static void awe_terminate_and_init(int voice, int forced);
 
 /* voice search */
-static int awe_search_instr(int bank, int preset);
-static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist);
+static int awe_search_key(int bank, int preset, int note);
+static awe_voice_list *awe_search_instr(int bank, int preset, int note);
+static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist);
 static void awe_alloc_multi_voices(int ch, int note, int velocity, int key);
 static void awe_alloc_one_voice(int voice, int note, int velocity);
 static int awe_clear_voice(void);
@@ -373,6 +377,7 @@
 static int awe_close_patch(awe_patch_info *patch, const char *addr, int count);
 static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count);
 static int awe_load_info(awe_patch_info *patch, const char *addr, int count);
+static int awe_remove_info(awe_patch_info *patch, const char *addr, int count);
 static int awe_load_data(awe_patch_info *patch, const char *addr, int count);
 static int awe_replace_data(awe_patch_info *patch, const char *addr, int count);
 static int awe_load_map(awe_patch_info *patch, const char *addr, int count);
@@ -381,22 +386,24 @@
 #endif
 /*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/
 static int awe_probe_data(awe_patch_info *patch, const char *addr, int count);
-static int check_patch_opened(int type, char *name);
-static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels);
-static void add_sf_info(int rec);
-static void add_sf_sample(int rec);
-static void purge_old_list(int rec, int next);
-static void add_info_list(int rec);
+static sf_list *check_patch_opened(int type, char *name);
+static int awe_write_wave_data(const char *addr, int offset, awe_sample_list *sp, int channels);
+static int awe_create_sf(int type, char *name);
+static void awe_free_sf(sf_list *sf);
+static void add_sf_info(sf_list *sf, awe_voice_list *rec);
+static void add_sf_sample(sf_list *sf, awe_sample_list *smp);
+static void purge_old_list(awe_voice_list *rec, awe_voice_list *next);
+static void add_info_list(awe_voice_list *rec);
 static void awe_remove_samples(int sf_id);
 static void rebuild_preset_list(void);
-static short awe_set_sample(awe_voice_info *vp);
-static int search_sample_index(int sf, int sample, int level);
+static short awe_set_sample(awe_voice_list *rec);
+static awe_sample_list *search_sample_index(sf_list *sf, int sample);
 
+static int is_identical_holder(sf_list *sf1, sf_list *sf2);
 #ifdef AWE_ALLOW_SAMPLE_SHARING
-static int is_identical_id(int id1, int id2);
-static int is_identical_name(unsigned char *name, int id);
+static int is_identical_name(unsigned char *name, sf_list *p);
 static int is_shared_sf(unsigned char *name);
-static int info_duplicated(awe_voice_list *rec);
+static int info_duplicated(sf_list *sf, awe_voice_list *rec);
 #endif /* allow sharing */
 
 /* lowlevel functions */
@@ -410,7 +417,7 @@
 static int awe_open_dram_for_write(int offset, int channels);
 static void awe_open_dram_for_check(void);
 static void awe_close_dram(void);
-static void awe_write_dram(unsigned short c);
+/*static void awe_write_dram(unsigned short c);*/
 static int awe_detect_base(int addr);
 static int awe_detect(void);
 static void awe_check_dram(void);
@@ -483,9 +490,9 @@
 static int ctrls[AWE_MD_END];
 
 
-/*----------------------------------------------------------------
+/*
  * synth operation table
- *----------------------------------------------------------------*/
+ */
 
 static struct synth_operations awe_operations =
 {
@@ -513,39 +520,32 @@
 };
 
 
-/*================================================================
+/*
  * General attach / unload interface
- *================================================================*/
+ */
 
-static int _attach_awe(void)
+static int __init _attach_awe(void)
 {
 	if (awe_present) return 0; /* for OSS38.. called twice? */
 
 	/* check presence of AWE32 card */
 	if (! awe_detect()) {
-		printk(KERN_WARNING "AWE32: not detected\n");
+		printk(KERN_ERR "AWE32: not detected\n");
 		return 0;
 	}
 
 	/* check AWE32 ports are available */
 	if (awe_check_port()) {
-		printk(KERN_WARNING "AWE32: I/O area already used.\n");
+		printk(KERN_ERR "AWE32: I/O area already used.\n");
 		return 0;
 	}
 
 	/* set buffers to NULL */
-	sflists = NULL;
-	samples = NULL;
-	infos = NULL;
-
-	/* allocate sample tables */
-	INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list);
-	INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list);
-	INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list);
+	sfhead = sftail = NULL;
 
 	my_dev = sound_alloc_synthdev();
 	if (my_dev == -1) {
-		printk(KERN_WARNING "AWE32 Error: too many synthesizers\n");
+		printk(KERN_ERR "AWE32 Error: too many synthesizers\n");
 		return 0;
 	}
 
@@ -570,8 +570,8 @@
 	awe_initialize();
 
 	sprintf(awe_info.name, "AWE32-%s (RAM%dk)",
-		AWEDRV_VERSION, awe_mem_size/1024);
-	printk("<SoundBlaster EMU8000 (RAM%dk)>\n", awe_mem_size/1024);
+		AWEDRV_VERSION, memsize/1024);
+	printk(KERN_INFO "<SoundBlaster EMU8000 (RAM%dk)>\n", memsize/1024);
 
 	awe_present = TRUE;
 
@@ -583,33 +583,18 @@
 
 static void free_tables(void)
 {
-	if(sflists)
-		vfree(sflists);
-	sflists = NULL; max_sfs = 0;
-	if (samples)
-		vfree(samples);
-	samples = NULL; max_samples = 0;
-	if (infos)
-		vfree(infos);
-	infos = NULL; max_infos = 0;
-}
-
-static void *realloc_block(void *buf, int oldsize, int size)
-{
-	void *ptr;
-	if (oldsize == size)
-		return buf;
-	if ((ptr = vmalloc(size)) == NULL)
-		return NULL;
-	if (oldsize && size)
-		memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) );
-	if (buf)
-		vfree(buf);
-	return ptr;
+	if (sftail) {
+		sf_list *p, *prev;
+		for (p = sftail; p; p = prev) {
+			prev = p->prev;
+			awe_free_sf(p);
+		}
+	}
+	sfhead = sftail = NULL;
 }
 
 
-static void _unload_awe(void)
+static void __exit _unload_awe(void)
 {
 	if (awe_present) {
 		awe_reset_samples();
@@ -627,96 +612,6 @@
 	}
 }
 
-/*
- * Linux PnP driver support
- */
-
-#ifdef CONFIG_PNP_DRV
-
-#include <linux/pnp.h>
-
-static int pnp = 1;	/* use PnP as default */
-
-#define AWE_NUM_CHIPS	3
-static unsigned int pnp_ids[AWE_NUM_CHIPS] = {
-	PNP_EISAID('C','T','L',0x0021),
-	PNP_EISAID('C','T','L',0x0022),
-	PNP_EISAID('C','T','L',0x0023),
-};
-static struct pnp_driver pnp_awe[AWE_NUM_CHIPS];
-static int awe_pnp_ok = 0;
-
-static void awe_pnp_config(struct pnp_device *d)
-{
-	struct pnp_resource *r;
-	int port[3];
-	int nio = 0;
-
-	port[0] = port[1] = port[2] = 0;
-	for (r = d->res; r != NULL; r = r->next) {
-		if (r->type == PNP_RES_IO) {
-			if (nio >= 0 && nio < 3)
-				port[nio] = r->start;
-			nio++;
-		}
-	}
-	setup_ports(port[0], port[1], port[2]);
-	DEBUG(0,printk("AWE32: PnP setup ports: %x:%x:%x\n", port[0], port[1], port[2]));
-}
-
-static int awe_pnp_event (struct pnp_device *d, struct pnp_drv_event *e)
-{
-	struct pnp_driver *drv = d->l.k.driver;
-
-	switch (e->type) {
-	case PNP_DRV_ALLOC:
-		drv->flags |= PNP_DRV_INUSE;
-		awe_pnp_ok = 1;
-		awe_pnp_config(d);
-		_attach_awe();
-		break;
-
-	case PNP_DRV_DISABLE:
-	case PNP_DRV_EMERGSTOP:
-		drv->flags &= ~PNP_DRV_INUSE;
-		awe_pnp_ok = 0;
-		_unload_awe();
-		break;
-
-	case PNP_DRV_CONFIG:
-		if (awe_busy) return 1; /* used now */
-		awe_release_region();
-		awe_pnp_config(d);
-		awe_request_region();
-		break;
-		
-	case PNP_DRV_RECONFIG:
-		break;
-	}
-	return 0;
-}
-
-static int awe_initpnp (void)
-{
-	int i;
-	for (i = 0; i < AWE_NUM_CHIPS; i++) {
-		pnp_awe[i].id.type = PNP_HDL_ISA;
-		pnp_awe[i].id.t.isa.id = pnp_ids[i];
-		pnp_awe[i].id.next = NULL;
-		pnp_awe[i].name = "Soundblaster AWE32/AWE64 PnP";
-		pnp_awe[i].event = awe_pnp_event;
-		pnp_register_driver(&pnp_awe[i], 1);
-	}
-	return 0;
-}
-
-static void awe_unload_pnp (void)
-{
-	int i;
-	for (i = 0; i < AWE_NUM_CHIPS; i++)
-		pnp_unregister_driver(&pnp_awe[i]);
-}
-#endif /* PnP support */
 
 /*
  * clear sample tables 
@@ -725,12 +620,8 @@
 static void
 awe_reset_samples(void)
 {
-	int i;
-
 	/* free all bank tables */
-	for (i = 0; i < AWE_MAX_PRESETS; i++)
-		preset_table[i] = -1;
-
+	memset(preset_table, 0, sizeof(preset_table));
 	free_tables();
 
 	current_sf_id = 0;
@@ -767,7 +658,7 @@
 }
 
 /* write 16bit data */
-static inline void
+static void
 awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
 {
 	awe_set_cmd(cmd);
@@ -775,7 +666,7 @@
 }
 
 /* write 32bit data */
-static inline void
+static void
 awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
 {
 	unsigned short addr = awe_ports[port];
@@ -785,7 +676,7 @@
 }
 
 /* read 16bit data */
-static inline unsigned short
+static unsigned short
 awe_peek(unsigned short cmd, unsigned short port)
 {
 	unsigned short k;
@@ -795,7 +686,7 @@
 }
 
 /* read 32bit data */
-static inline unsigned int
+static unsigned int
 awe_peek_dw(unsigned short cmd, unsigned short port)
 {
 	unsigned int k1, k2;
@@ -837,14 +728,16 @@
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout((HZ*(unsigned long)delay + 44099)/44100);
 }
+/*
+static void awe_wait(unsigned short delay)
+{
+	udelay(((unsigned long)delay * 1000000L + 44099) / 44100);
+}
+*/
 #endif /* wait by loop */
 
 /* write a word data */
-static inline void
-awe_write_dram(unsigned short c)
-{
-	awe_poke(AWE_SMLD, c);
-}
+#define awe_write_dram(c)	awe_poke(AWE_SMLD, c)
 
 
 /*
@@ -852,7 +745,7 @@
  *  0x620-623, 0xA20-A23, 0xE20-E23
  */
 
-static int
+static int __init
 awe_check_port(void)
 {
 	if (! port_setuped) return 0;
@@ -861,7 +754,7 @@
 		check_region(awe_ports[3], 4));
 }
 
-static void
+static void __init
 awe_request_region(void)
 {
 	if (! port_setuped) return;
@@ -870,7 +763,7 @@
 	request_region(awe_ports[3], 4, "sound driver (AWE32)");
 }
 
-static void
+static void __exit
 awe_release_region(void)
 {
 	if (! port_setuped) return;
@@ -879,9 +772,11 @@
 	release_region(awe_ports[3], 4);
 }
 
+
 /*
- * AWE32 initialization
+ * initialization of AWE driver
  */
+
 static void
 awe_initialize(void)
 {
@@ -935,7 +830,6 @@
 static void
 awe_init_voice_info(awe_voice_info *vp)
 {
-	vp->sf_id = 0; /* normal mode */
 	vp->sample = 0;
 	vp->rate_offset = 0;
 
@@ -1338,7 +1232,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	/* A voice sample must assigned before calling */
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 
 	parm = (awe_voice_parm_block*)&vp->parm;
@@ -1457,15 +1351,15 @@
 	awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget);
 	awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget);
 
-	/* turn on envelope */
-	awe_poke(AWE_DCYSUSV(voice),
-		 FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
-			  vp->parm.voldcysus));
 	/* set reverb */
 	temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb);
 	temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux;
 	awe_poke_dw(AWE_PTRX(voice), temp);
 	awe_poke_dw(AWE_CPF(voice), ptarget << 16);
+	/* turn on envelope */
+	awe_poke(AWE_DCYSUSV(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+			  vp->parm.voldcysus));
 
 	voices[voice].state = AWE_ST_ON;
 
@@ -1568,7 +1462,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (!IS_PLAYING(voice) && !forced) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 
 	tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF,
@@ -1603,7 +1497,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 
 	/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
@@ -1622,14 +1516,16 @@
 	}
 	if (forced || temp != voices[voice].apan) {
 		voices[voice].apan = temp;
+		if (temp == 0)
+			voices[voice].aaux = 0xff;
+		else
+			voices[voice].aaux = (-temp) & 0xff;
 		addr = vp->loopstart - 1;
 		addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START,
 				  AWE_FX_COARSE_LOOP_START, vp->mode);
 		temp = (temp<<24) | (unsigned int)addr;
 		awe_poke_dw(AWE_PSST(voice), temp);
 		DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr));
-		if (temp == 0) voices[voice].aaux = 0xff;
-		else voices[voice].aaux = (-temp)&0xff;
 	}
 }
 
@@ -1644,7 +1540,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 	awe_poke(AWE_FMMOD(voice),
 		 FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
@@ -1662,7 +1558,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 	awe_poke(AWE_TREMFRQ(voice),
 		 FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
@@ -1680,7 +1576,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 	awe_poke(AWE_FM2FRQ2(voice),
 		 FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
@@ -1700,7 +1596,7 @@
 		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
 		return;
 
 	addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff;
@@ -1708,12 +1604,12 @@
 	awe_poke_dw(AWE_CCCA(voice), addr);
 }
 
-/*================================================================
+/*
  * calculate pitch offset
- *----------------------------------------------------------------
+ *
  * 0xE000 is no pitch offset at 44100Hz sample.
  * Every 4096 is one octave.
- *================================================================*/
+ */
 
 static void
 awe_calc_pitch(int voice)
@@ -1726,9 +1622,9 @@
 	/* search voice information */
 	if ((ap = vp->sample) == NULL)
 			return;
-	if (ap->index < 0) {
+	if (ap->index == 0) {
 		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
-		if (awe_set_sample(ap) < 0)
+		if (awe_set_sample((awe_voice_list*)ap) == 0)
 			return;
 	}
 
@@ -1785,9 +1681,9 @@
 	/* search voice information */
 	if ((ap = vp->sample) == NULL)
 		return;
-	if (ap->index < 0) {
+	if (ap->index == 0) {
 		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
-		if (awe_set_sample(ap) < 0)
+		if (awe_set_sample((awe_voice_list*)ap) == 0)
 			return;
 	}
 	note = freq_to_note(freq);
@@ -1806,13 +1702,13 @@
 #endif /* AWE_HAS_GUS_COMPATIBILITY */
 
 
-/*================================================================
+/*
  * calculate volume attenuation
- *----------------------------------------------------------------
+ *
  * Voice volume is controlled by volume attenuation parameter.
  * So volume becomes maximum when avol is 0 (no attenuation), and
  * minimum when 255 (-96dB or silence).
- *================================================================*/
+ */
 
 static int vol_table[128] = {
 	255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
@@ -1887,9 +1783,9 @@
 		return;
 
 	ap = vp->sample;
-	if (ap->index < 0) {
+	if (ap->index == 0) {
 		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
-		if (awe_set_sample(ap) < 0)
+		if (awe_set_sample((awe_voice_list*)ap) == 0)
 			return;
 	}
 	
@@ -2054,8 +1950,6 @@
 			cp->instr = ctrls[AWE_MD_DEF_PRESET];
 			cp->bank = ctrls[AWE_MD_DEF_BANK];
 		}
-		cp->vrec = -1;
-		cp->def_vrec = -1;
 	}
 
 	cp->bender = 0; /* zero tune skew */
@@ -2092,9 +1986,9 @@
 }
 
 
-/*----------------------------------------------------------------
+/*
  * device open / close
- *----------------------------------------------------------------*/
+ */
 
 /* open device:
  *   reset status of all voices, and clear sample position flag
@@ -2158,13 +2052,13 @@
 			awe_info.nr_voices = awe_max_voices;
 		else
 			awe_info.nr_voices = AWE_MAX_CHANNELS;
-		memcpy((char*)arg, &awe_info + 0, sizeof(awe_info));
+		memcpy((char*)arg, &awe_info, sizeof(awe_info));
 		return 0;
 		break;
 
 	case SNDCTL_SEQ_RESETSAMPLES:
-		awe_reset_samples();
 		awe_reset(dev);
+		awe_reset_samples();
 		return 0;
 		break;
 
@@ -2174,10 +2068,10 @@
 		break;
 
 	case SNDCTL_SYNTH_MEMAVL:
-		return awe_mem_size - awe_free_mem_ptr() * 2;
+		return memsize - awe_free_mem_ptr() * 2;
 
 	default:
-		printk("AWE32: unsupported ioctl %d\n", cmd);
+		printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd);
 		return -EINVAL;
 	}
 }
@@ -2358,18 +2252,46 @@
 }
 
 
-/* search instrument from preset table with the specified bank */
+/* calculate hash key */
 static int
-awe_search_instr(int bank, int preset)
+awe_search_key(int bank, int preset, int note)
 {
-	int i;
+	unsigned int key;
 
-	limitvalue(preset, 0, AWE_MAX_PRESETS-1);
-	for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) {
-		if (infos[i].bank == bank)
-			return i;
+#if 1 /* new hash table */
+	if (bank == AWE_DRUM_BANK)
+		key = preset + note + 128;
+	else
+		key = bank + preset;
+#else
+	key = preset;
+#endif
+	key %= AWE_MAX_PRESETS;
+
+	return (int)key;
+}
+
+
+/* search instrument from hash table */
+static awe_voice_list *
+awe_search_instr(int bank, int preset, int note)
+{
+	awe_voice_list *p;
+	int key, key2;
+
+	key = awe_search_key(bank, preset, note);
+	for (p = preset_table[key]; p; p = p->next_bank) {
+		if (p->instr == preset && p->bank == bank)
+			return p;
 	}
-	return -1;
+	key2 = awe_search_key(bank, preset, 0); /* search default */
+	if (key == key2)
+		return NULL;
+	for (p = preset_table[key2]; p; p = p->next_bank) {
+		if (p->instr == preset && p->bank == bank)
+			return p;
+	}
+	return NULL;
 }
 
 
@@ -2390,7 +2312,6 @@
 awe_set_instr(int dev, int voice, int instr_no)
 {
 	awe_chan_info *cinfo;
-	int def_bank;
 
 	if (! voice_in_range(voice))
 		return -EINVAL;
@@ -2399,26 +2320,8 @@
 		return -EINVAL;
 
 	cinfo = &channels[voice];
-
-	if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice))
-		def_bank = AWE_DRUM_BANK; /* always search drumset */
-	else
-		def_bank = cinfo->bank;
-
-	cinfo->vrec = -1;
-	cinfo->def_vrec = -1;
-	cinfo->vrec = awe_search_instr(def_bank, instr_no);
-	if (def_bank == AWE_DRUM_BANK)	/* search default drumset */
-		cinfo->def_vrec = awe_search_instr(def_bank, ctrls[AWE_MD_DEF_DRUM]);
-	else	/* search default preset */
-		cinfo->def_vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr_no);
-
-	if (cinfo->vrec < 0 && cinfo->def_vrec < 0) {
-		DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
-	}
-
 	cinfo->instr = instr_no;
-	DEBUG(2,printk("AWE32: [program(%d) %d/%d]\n", voice, instr_no, def_bank));
+	DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no));
 
 	return 0;
 }
@@ -2576,7 +2479,7 @@
 	switch (cmd) {
 	case _AWE_DEBUG_MODE:
 		ctrls[AWE_MD_DEBUG_MODE] = p1;
-		printk("AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]);
+		printk(KERN_DEBUG "AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]);
 		break;
 	case _AWE_REVERB_MODE:
 		ctrls[AWE_MD_REVERB_MODE] = p1;
@@ -2590,6 +2493,7 @@
 		      
 	case _AWE_REMOVE_LAST_SAMPLES:
 		DEBUG(0,printk("AWE32: remove last samples\n"));
+		awe_reset(0);
 		if (locked_sf_id > 0)
 			awe_remove_samples(locked_sf_id);
 		break;
@@ -2772,6 +2676,8 @@
 		if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) &&
 		    !ctrls[AWE_MD_TOGGLE_DRUM_BANK])
 			break;
+		if (value < 0 || value > 255)
+			break;
 		cinfo->bank = value;
 		if (cinfo->bank == AWE_DRUM_BANK)
 			DRUM_CHANNEL_ON(cinfo->channel);
@@ -2920,10 +2826,10 @@
 }
 
 
-/*----------------------------------------------------------------
+/*
  * load a sound patch:
  *   three types of patches are accepted: AWE, GUS, and SYSEX.
- *----------------------------------------------------------------*/
+ */
 
 static int
 awe_load_patch(int dev, int format, const char *addr,
@@ -2941,20 +2847,21 @@
 		/* no system exclusive message supported yet */
 		return 0;
 	} else if (format != AWE_PATCH) {
-		printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format);
+		printk(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format);
 		return -EINVAL;
 	}
 	
 	if (count < AWE_PATCH_INFO_SIZE) {
-		printk("AWE32 Error: Patch header too short\n");
+		printk(KERN_WARNING "AWE32 Error: Patch header too short\n");
 		return -EINVAL;
 	}
-	copy_from_user(((char*)&patch) + offs, addr + offs, 
-		       AWE_PATCH_INFO_SIZE - offs);
+	if (copy_from_user(((char*)&patch) + offs, addr + offs, 
+			   AWE_PATCH_INFO_SIZE - offs))
+		return -EFAULT;
 
 	count -= AWE_PATCH_INFO_SIZE;
 	if (count < patch.len) {
-		printk("AWE32: sample: Patch record too short (%d<%d)\n",
+		printk(KERN_WARNING "AWE32: sample: Patch record too short (%d<%d)\n",
 		       count, patch.len);
 		return -EINVAL;
 	}
@@ -2987,6 +2894,9 @@
 	case AWE_PROBE_DATA:
 		rc = awe_probe_data(&patch, addr, count);
 		break;
+	case AWE_REMOVE_INFO:
+		rc = awe_remove_info(&patch, addr, count);
+		break;
 	case AWE_LOAD_CHORUS_FX:
 		rc = awe_load_chorus_fx(&patch, addr, count);
 		break;
@@ -2995,7 +2905,7 @@
 		break;
 
 	default:
-		printk("AWE32 Error: unknown patch format type %d\n",
+		printk(KERN_WARNING "AWE32 Error: unknown patch format type %d\n",
 		       patch.type);
 		rc = -EINVAL;
 	}
@@ -3004,7 +2914,7 @@
 }
 
 
-/* create an sflist record */
+/* create an sf list record */
 static int
 awe_create_sf(int type, char *name)
 {
@@ -3012,40 +2922,43 @@
 
 	/* terminate sounds */
 	awe_reset(0);
-	if (current_sf_id >= max_sfs) {
-		int newsize = max_sfs + AWE_MAX_SF_LISTS;
-		sf_list *newlist = realloc_block(sflists, sizeof(sf_list)*max_sfs,
-						 sizeof(sf_list)*newsize);
-		if (newlist == NULL)
-			return 1;
-		sflists = newlist;
-		max_sfs = newsize;
-	}
-	rec = &sflists[current_sf_id];
+	rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL);
+	if (rec == NULL)
+		return 1; /* no memory */
 	rec->sf_id = current_sf_id + 1;
 	rec->type = type;
-	if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0)
+	if (/*current_sf_id == 0 ||*/ (type & AWE_PAT_LOCKED) != 0)
 		locked_sf_id = current_sf_id + 1;
 	rec->num_info = awe_free_info();
 	rec->num_sample = awe_free_sample();
 	rec->mem_ptr = awe_free_mem_ptr();
-	rec->infos = -1;
-	rec->samples = -1;
+	rec->infos = rec->last_infos = NULL;
+	rec->samples = rec->last_samples = NULL;
+
+	/* add to linked-list */
+	rec->next = NULL;
+	rec->prev = sftail;
+	if (sftail)
+		sftail->next = rec;
+	else
+		sfhead = rec;
+	sftail = rec;
+	current_sf_id++;
 
 #ifdef AWE_ALLOW_SAMPLE_SHARING
-	rec->shared = 0;
+	rec->shared = NULL;
 	if (name)
 		memcpy(rec->name, name, AWE_PATCH_NAME_LEN);
 	else
 		strcpy(rec->name, "*TEMPORARY*");
-	if (current_sf_id > 0 && name && (type & AWE_PAT_SHARED) != 0) {
+	if (current_sf_id > 1 && name && (type & AWE_PAT_SHARED) != 0) {
 		/* is the current font really a shared font? */
 		if (is_shared_sf(rec->name)) {
 			/* check if the shared font is already installed */
-			int i;
-			for (i = current_sf_id; i > 0; i--) {
-				if (is_identical_name(rec->name, i)) {
-					rec->shared = i;
+			sf_list *p;
+			for (p = rec->prev; p; p = p->prev) {
+				if (is_identical_name(rec->name, p)) {
+					rec->shared = p;
 					break;
 				}
 			}
@@ -3053,8 +2966,6 @@
 	}
 #endif /* allow sharing */
 
-	current_sf_id++;
-
 	return 0;
 }
 
@@ -3065,37 +2976,31 @@
 #define ASC_TO_KEY(c) ((c) - 'A' + 1)
 static int is_shared_sf(unsigned char *name)
 {
-	static unsigned char id_head[6] = {
+	static unsigned char id_head[4] = {
 		ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'),
 		AWE_MAJOR_VERSION,
-		AWE_MINOR_VERSION,
-		AWE_TINY_VERSION,
 	};
-	if (memcmp(name, id_head, 6) == 0)
+	if (memcmp(name, id_head, 4) == 0)
 		return TRUE;
 	return FALSE;
 }
 
 /* check if the given name matches to the existing list */
-static int is_identical_name(unsigned char *name, int sf) 
+static int is_identical_name(unsigned char *name, sf_list *p) 
 {
-	char *id = sflists[sf-1].name;
+	char *id = p->name;
 	if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0)
 		return TRUE;
 	return FALSE;
 }
 
 /* check if the given voice info exists */
-static int info_duplicated(awe_voice_list *rec)
+static int info_duplicated(sf_list *sf, awe_voice_list *rec)
 {
-	int j, sf_id;
-	sf_list *sf;
-
 	/* search for all sharing lists */
-	for (sf_id = rec->v.sf_id; sf_id > 0 && sf_id <= current_sf_id; sf_id = sf->shared) {
-		sf = &sflists[sf_id - 1];
-		for (j = sf->infos; j >= 0; j = infos[j].next) {
-			awe_voice_list *p = &infos[j];
+	for (; sf; sf = sf->shared) {
+		awe_voice_list *p;
+		for (p = sf->infos; p; p = p->next) {
 			if (p->type == V_ST_NORMAL &&
 			    p->bank == rec->bank &&
 			    p->instr == rec->instr &&
@@ -3111,6 +3016,29 @@
 #endif /* AWE_ALLOW_SAMPLE_SHARING */
 
 
+/* free sf_list record */
+/* linked-list in this function is not cared */
+static void
+awe_free_sf(sf_list *sf)
+{
+	if (sf->infos) {
+		awe_voice_list *p, *next;
+		for (p = sf->infos; p; p = next) {
+			next = p->next;
+			kfree(p);
+		}
+	}
+	if (sf->samples) {
+		awe_sample_list *p, *next;
+		for (p = sf->samples; p; p = next) {
+			next = p->next;
+			kfree(p);
+		}
+	}
+	kfree(sf);
+}
+
+
 /* open patch; create sf list and set opened flag */
 static int
 awe_open_patch(awe_patch_info *patch, const char *addr, int count)
@@ -3118,13 +3046,14 @@
 	awe_open_parm parm;
 	int shared;
 
-	copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm));
+	if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm)))
+		return -EFAULT;
 	shared = FALSE;
 
 #ifdef AWE_ALLOW_SAMPLE_SHARING
-	if (current_sf_id > 0 && (parm.type & AWE_PAT_SHARED) != 0) {
+	if (sftail && (parm.type & AWE_PAT_SHARED) != 0) {
 		/* is the previous font the same font? */
-		if (is_identical_name(parm.name, current_sf_id)) {
+		if (is_identical_name(parm.name, sftail)) {
 			/* then append to the previous */
 			shared = TRUE;
 			awe_reset(0);
@@ -3135,8 +3064,8 @@
 #endif /* allow sharing */
 	if (! shared) {
 		if (awe_create_sf(parm.type, parm.name)) {
-			printk("AWE32: can't open: failed to alloc new list\n");
-			return -ENOSPC;
+			printk(KERN_ERR "AWE32: can't open: failed to alloc new list\n");
+			return -ENOMEM;
 		}
 	}
 	patch_opened = TRUE;
@@ -3144,28 +3073,30 @@
 }
 
 /* check if the patch is already opened */
-static int
+static sf_list *
 check_patch_opened(int type, char *name)
 {
 	if (! patch_opened) {
 		if (awe_create_sf(type, name)) {
-			printk("AWE32: failed to alloc new list\n");
-			return -ENOSPC;
+			printk(KERN_ERR "AWE32: failed to alloc new list\n");
+			return NULL;
 		}
 		patch_opened = TRUE;
-		return current_sf_id;
+		return sftail;
 	}
-	return current_sf_id;
+	return sftail;
 }
 
 /* close the patch; if no voice is loaded, remove the patch */
 static int
 awe_close_patch(awe_patch_info *patch, const char *addr, int count)
 {
-	if (patch_opened && current_sf_id > 0) {
+	if (patch_opened && sftail) {
 		/* if no voice is loaded, release the current patch */
-		if (sflists[current_sf_id-1].infos == -1)
+		if (sftail->infos == NULL) {
+			awe_reset(0);
 			awe_remove_samples(current_sf_id - 1);
+		}
 	}
 	patch_opened = 0;
 	return 0;
@@ -3176,52 +3107,39 @@
 static int
 awe_unload_patch(awe_patch_info *patch, const char *addr, int count)
 {
-	if (current_sf_id > 0 && current_sf_id > locked_sf_id)
+	if (current_sf_id > 0 && current_sf_id > locked_sf_id) {
+		awe_reset(0);
 		awe_remove_samples(current_sf_id - 1);
+	}
 	return 0;
 }
 
 /* allocate voice info list records */
-static int alloc_new_info(int nvoices)
+static awe_voice_list *
+alloc_new_info(void)
 {
-	int newsize, free_info;
 	awe_voice_list *newlist;
-	free_info = awe_free_info();
-	if (free_info + nvoices >= max_infos) {
-		do {
-			newsize = max_infos + AWE_MAX_INFOS;
-		} while (free_info + nvoices >= newsize);
-		newlist = realloc_block(infos, sizeof(awe_voice_list)*max_infos,
-					sizeof(awe_voice_list)*newsize);
-		if (newlist == NULL) {
-			printk("AWE32: can't alloc info table\n");
-			return -ENOSPC;
-		}
-		infos = newlist;
-		max_infos = newsize;
+	
+	newlist = (awe_voice_list *)kmalloc(sizeof(*newlist), GFP_KERNEL);
+	if (newlist == NULL) {
+		printk(KERN_ERR "AWE32: can't alloc info table\n");
+		return NULL;
 	}
-	return 0;
+	return newlist;
 }
 
 /* allocate sample info list records */
-static int alloc_new_sample(void)
+static awe_sample_list *
+alloc_new_sample(void)
 {
-	int newsize, free_sample;
 	awe_sample_list *newlist;
-	free_sample = awe_free_sample();
-	if (free_sample >= max_samples) {
-		newsize = max_samples + AWE_MAX_SAMPLES;
-		newlist = realloc_block(samples,
-					sizeof(awe_sample_list)*max_samples,
-					sizeof(awe_sample_list)*newsize);
-		if (newlist == NULL) {
-			printk("AWE32: can't alloc sample table\n");
-			return -ENOSPC;
-		}
-		samples = newlist;
-		max_samples = newsize;
+	
+	newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL);
+	if (newlist == NULL) {
+		printk(KERN_ERR "AWE32: can't alloc sample table\n");
+		return NULL;
 	}
-	return 0;
+	return newlist;
 }
 
 /* load voice map */
@@ -3229,35 +3147,33 @@
 awe_load_map(awe_patch_info *patch, const char *addr, int count)
 {
 	awe_voice_map map;
-	awe_voice_list *rec;
-	int p, free_info;
+	awe_voice_list *rec, *p;
+	sf_list *sf;
 
 	/* get the link info */
 	if (count < sizeof(map)) {
-		printk("AWE32 Error: invalid patch info length\n");
+		printk(KERN_WARNING "AWE32 Error: invalid patch info length\n");
 		return -EINVAL;
 	}
-	copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map));
+	if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map)))
+		return -EFAULT;
 	
 	/* check if the identical mapping already exists */
-	p = awe_search_instr(map.map_bank, map.map_instr);
-	for (; p >= 0; p = infos[p].next_instr) {
-		if (p >= 0 && infos[p].type == V_ST_MAPPED &&
-		    infos[p].v.low == map.map_key &&
-		    infos[p].v.start == map.src_instr &&
-		    infos[p].v.end == map.src_bank &&
-		    infos[p].v.fixkey == map.src_key)
+	p = awe_search_instr(map.map_bank, map.map_instr, map.map_key);
+	for (; p; p = p->next_instr) {
+		if (p->type == V_ST_MAPPED &&
+		    p->v.start == map.src_instr &&
+		    p->v.end == map.src_bank &&
+		    p->v.fixkey == map.src_key)
 			return 0; /* already present! */
 	}
 
-	if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0)
-		return -ENOSPC;
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL)
+		return -ENOMEM;
 
-	if (alloc_new_info(1) < 0)
-		return -ENOSPC;
+	if ((rec = alloc_new_info()) == NULL)
+		return -ENOMEM;
 
-	free_info = awe_free_info();
-	rec = &infos[free_info];
 	rec->bank = map.map_bank;
 	rec->instr = map.map_instr;
 	rec->type = V_ST_MAPPED;
@@ -3270,9 +3186,8 @@
 	rec->v.start = map.src_instr;
 	rec->v.end = map.src_bank;
 	rec->v.fixkey = map.src_key;
-	rec->v.sf_id = current_sf_id;
-	add_info_list(free_info);
-	add_sf_info(free_info);
+	add_sf_info(sf, rec);
+	add_info_list(rec);
 
 	return 0;
 }
@@ -3284,25 +3199,28 @@
 {
 #ifdef AWE_ALLOW_SAMPLE_SHARING
 	awe_voice_map map;
-	int p;
+	awe_voice_list *p;
 
 	if (! patch_opened)
 		return -EINVAL;
 
 	/* get the link info */
 	if (count < sizeof(map)) {
-		printk("AWE32 Error: invalid patch info length\n");
+		printk(KERN_WARNING "AWE32 Error: invalid patch info length\n");
 		return -EINVAL;
 	}
-	copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map));
+	if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map)))
+		return -EFAULT;
 	
 	/* check if the identical mapping already exists */
-	p = awe_search_instr(map.src_bank, map.src_instr);
-	for (; p >= 0; p = infos[p].next_instr) {
-		if (p >= 0 && infos[p].type == V_ST_NORMAL &&
-		    is_identical_id(infos[p].v.sf_id, current_sf_id) &&
-		    infos[p].v.low <= map.src_key &&
-		    infos[p].v.high >= map.src_key)
+	if (sftail == NULL)
+		return -EINVAL;
+	p = awe_search_instr(map.src_bank, map.src_instr, map.src_key);
+	for (; p; p = p->next_instr) {
+		if (p->type == V_ST_NORMAL &&
+		    is_identical_holder(p->holder, sftail) &&
+		    p->v.low <= map.src_key &&
+		    p->v.high >= map.src_key)
 			return 0; /* already present! */
 	}
 #endif /* allow sharing */
@@ -3319,12 +3237,40 @@
 		return -EINVAL;
 
 	/* search the specified sample by optarg */
-	if (search_sample_index(current_sf_id, patch->optarg, 0) >= 0)
+	if (search_sample_index(sftail, patch->optarg) != NULL)
 		return 0;
 #endif /* allow sharing */
 	return -EINVAL;
 }
 
+		
+/* remove the present instrument layers */
+static int
+remove_info(sf_list *sf, int bank, int instr)
+{
+	awe_voice_list *prev, *next, *p;
+	int removed = 0;
+
+	prev = NULL;
+	for (p = sf->infos; p; prev = p, p = next) {
+		next = p->next;
+		if (p->type == V_ST_NORMAL &&
+		    p->bank == bank && p->instr == instr) {
+			/* remove this layer */
+			if (prev)
+				prev->next = next;
+			else
+				sf->infos = next;
+			if (p == sf->last_infos)
+				sf->last_infos = prev;
+			sf->num_info--;
+			removed++;
+			kfree(p);
+		}
+	}
+	return removed;
+}
+
 /* load voice information data */
 static int
 awe_load_info(awe_patch_info *patch, const char *addr, int count)
@@ -3333,125 +3279,154 @@
 	awe_voice_rec_hdr hdr;
 	int i;
 	int total_size;
+	sf_list *sf;
+	awe_voice_list *rec;
 
 	if (count < AWE_VOICE_REC_SIZE) {
-		printk("AWE32 Error: invalid patch info length\n");
+		printk(KERN_WARNING "AWE32 Error: invalid patch info length\n");
 		return -EINVAL;
 	}
 
 	offset = AWE_PATCH_INFO_SIZE;
-	copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE);
+	if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE))
+		return -EFAULT;
 	offset += AWE_VOICE_REC_SIZE;
 
 	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
-		printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices);
+		printk(KERN_WARNING "AWE32 Error: Invalid voice number %d\n", hdr.nvoices);
 		return -EINVAL;
 	}
 	total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices;
 	if (count < total_size) {
-		printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
+		printk(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
 		       count, hdr.nvoices);
 		return -EINVAL;
 	}
 
-	if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
-		return -ENOSPC;
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL)
+		return -ENOMEM;
 
-#if 0 /* it looks like not so useful.. */
-	/* check if the same preset already exists in the info list */
-	for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) {
-		if (infos[i].disabled) continue;
-		if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) {
-			/* in exclusive mode, do skip loading this */
-			if (hdr.write_mode == AWE_WR_EXCLUSIVE)
-				return 0;
-			/* in replace mode, disable the old data */
-			else if (hdr.write_mode == AWE_WR_REPLACE)
-				infos[i].disabled = TRUE;
+	switch (hdr.write_mode) {
+	case AWE_WR_EXCLUSIVE:
+		/* exclusive mode - if the instrument already exists,
+		   return error */
+		for (rec = sf->infos; rec; rec = rec->next) {
+			if (rec->type == V_ST_NORMAL &&
+			    rec->bank == hdr.bank &&
+			    rec->instr == hdr.instr)
+				return -EINVAL;
 		}
+		break;
+	case AWE_WR_REPLACE:
+		/* replace mode - remoe the instrument if it already exists */
+		remove_info(sf, hdr.bank, hdr.instr);
+		break;
 	}
-	if (hdr.write_mode == AWE_WR_REPLACE)
-		rebuild_preset_list();
-#endif
-
-	if (alloc_new_info(hdr.nvoices) < 0)
-		return -ENOSPC;
 
+	/* append new layers */
 	for (i = 0; i < hdr.nvoices; i++) {
-		int rec = awe_free_info();
-
-		infos[rec].bank = hdr.bank;
-		infos[rec].instr = hdr.instr;
-		infos[rec].type = V_ST_NORMAL;
-		infos[rec].disabled = FALSE;
+		rec = alloc_new_info();
+		if (rec == NULL)
+			return -ENOMEM;
+
+		rec->bank = hdr.bank;
+		rec->instr = hdr.instr;
+		rec->type = V_ST_NORMAL;
+		rec->disabled = FALSE;
 
 		/* copy awe_voice_info parameters */
-		copy_from_user(&infos[rec].v, addr + offset, AWE_VOICE_INFO_SIZE);
+		if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) {
+			kfree(rec);
+			return -EFAULT;
+		}
 		offset += AWE_VOICE_INFO_SIZE;
-		infos[rec].v.sf_id = current_sf_id;
 #ifdef AWE_ALLOW_SAMPLE_SHARING
-		if (sflists[current_sf_id-1].shared) {
-			if (info_duplicated(&infos[rec]))
+		if (sf && sf->shared) {
+			if (info_duplicated(sf, rec)) {
+				kfree(rec);
 				continue;
+			}
 		}
 #endif /* allow sharing */
-		if (infos[rec].v.mode & AWE_MODE_INIT_PARM)
-			awe_init_voice_parm(&infos[rec].v.parm);
-		awe_set_sample(&infos[rec].v);
+		if (rec->v.mode & AWE_MODE_INIT_PARM)
+			awe_init_voice_parm(&rec->v.parm);
+		add_sf_info(sf, rec);
+		awe_set_sample(rec);
 		add_info_list(rec);
-		add_sf_info(rec);
 	}
 
 	return 0;
 }
 
 
+/* remove instrument layers */
+static int
+awe_remove_info(awe_patch_info *patch, const char *addr, int count)
+{
+	unsigned char bank, instr;
+	sf_list *sf;
+
+	if (! patch_opened || (sf = sftail) == NULL) {
+		printk(KERN_WARNING "AWE32: remove_info: patch not opened\n");
+		return -EINVAL;
+	}
+
+	bank = ((unsigned short)patch->optarg >> 8) & 0xff;
+	instr = (unsigned short)patch->optarg & 0xff;
+	if (! remove_info(sf, bank, instr))
+		return -EINVAL;
+	return 0;
+}
+
+
 /* load wave sample data */
 static int
 awe_load_data(awe_patch_info *patch, const char *addr, int count)
 {
 	int offset, size;
-	int rc, free_sample;
-	awe_sample_info tmprec, *rec;
+	int rc;
+	awe_sample_info tmprec;
+	awe_sample_list *rec;
+	sf_list *sf;
 
-	if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
-		return -ENOSPC;
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL)
+		return -ENOMEM;
 
 	size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
 	offset = AWE_PATCH_INFO_SIZE;
-	copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE);
+	if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE))
+		return -EFAULT;
 	offset += AWE_SAMPLE_INFO_SIZE;
 	if (size != tmprec.size) {
-		printk("AWE32: load: sample size differed (%d != %d)\n",
+		printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n",
 		       tmprec.size, size);
 		return -EINVAL;
 	}
 
-	if (search_sample_index(current_sf_id, tmprec.sample, 0) >= 0) {
+	if (search_sample_index(sf, tmprec.sample) != NULL) {
 #ifdef AWE_ALLOW_SAMPLE_SHARING
 		/* if shared sample, skip this data */
-		if (sflists[current_sf_id-1].type & AWE_PAT_SHARED)
+		if (sf->type & AWE_PAT_SHARED)
 			return 0;
 #endif /* allow sharing */
 		DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample));
 		return -EINVAL;
 	}
 
-	if (alloc_new_sample() < 0)
-		return -ENOSPC;
+	if ((rec = alloc_new_sample()) == NULL)
+		return -ENOMEM;
 
-	free_sample = awe_free_sample();
-	rec = &samples[free_sample].v;
-	*rec = tmprec;
+	memcpy(&rec->v, &tmprec, sizeof(tmprec));
 
-	if (rec->size > 0)
-		if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0)
+	if (rec->v.size > 0) {
+		if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) {
+			kfree(rec);
 			return rc;
+		}
+		sf->mem_ptr += rc;
+	}
 
-	rec->sf_id = current_sf_id;
-
-	add_sf_sample(free_sample);
-
+	add_sf_sample(sf, rec);
 	return 0;
 }
 
@@ -3462,55 +3437,57 @@
 {
 	int offset;
 	int size;
-	int rc, i;
+	int rc;
 	int channels;
 	awe_sample_info cursmp;
 	int save_mem_ptr;
+	sf_list *sf;
+	awe_sample_list *rec;
 
-	if (! patch_opened) {
-		printk("AWE32: replace: patch not opened\n");
+	if (! patch_opened || (sf = sftail) == NULL) {
+		printk(KERN_WARNING "AWE32: replace: patch not opened\n");
 		return -EINVAL;
 	}
 
 	size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
 	offset = AWE_PATCH_INFO_SIZE;
-	copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE);
+	if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE))
+		return -EFAULT;
 	offset += AWE_SAMPLE_INFO_SIZE;
 	if (cursmp.size == 0 || size != cursmp.size) {
-		printk("AWE32: replace: illegal sample size (%d!=%d)\n",
+		printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n",
 		       cursmp.size, size);
 		return -EINVAL;
 	}
 	channels = patch->optarg;
 	if (channels <= 0 || channels > AWE_NORMAL_VOICES) {
-		printk("AWE32: replace: illegal channels %d\n", channels);
+		printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels);
 		return -EINVAL;
 	}
 
-	for (i = sflists[current_sf_id-1].samples;
-	     i >= 0; i = samples[i].next) {
-		if (samples[i].v.sample == cursmp.sample)
+	for (rec = sf->samples; rec; rec = rec->next) {
+		if (rec->v.sample == cursmp.sample)
 			break;
 	}
-	if (i < 0) {
-		printk("AWE32: replace: cannot find existing sample data %d\n",
+	if (rec == NULL) {
+		printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n",
 		       cursmp.sample);
 		return -EINVAL;
 	}
 		
-	if (samples[i].v.size != cursmp.size) {
-		printk("AWE32: replace: exiting size differed (%d!=%d)\n",
-		       samples[i].v.size, cursmp.size);
+	if (rec->v.size != cursmp.size) {
+		printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n",
+		       rec->v.size, cursmp.size);
 		return -EINVAL;
 	}
 
 	save_mem_ptr = awe_free_mem_ptr();
-	sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start;
-	memcpy(&samples[i].v, &cursmp, sizeof(cursmp));
-	if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0)
+	sftail->mem_ptr = rec->v.start - awe_mem_start;
+	memcpy(&rec->v, &cursmp, sizeof(cursmp));
+	rec->v.sf_id = current_sf_id;
+	if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0)
 		return rc;
-	sflists[current_sf_id-1].mem_ptr = save_mem_ptr;
-	samples[i].v.sf_id = current_sf_id;
+	sftail->mem_ptr = save_mem_ptr;
 
 	return 0;
 }
@@ -3521,28 +3498,11 @@
 static const char *readbuf_addr;
 static int readbuf_offs;
 static int readbuf_flags;
-#ifdef MALLOC_LOOP_DATA
-static unsigned short *readbuf_loop;
-static int readbuf_loopstart, readbuf_loopend;
-#endif
 
 /* initialize read buffer */
 static int
 readbuf_init(const char *addr, int offset, awe_sample_info *sp)
 {
-#ifdef MALLOC_LOOP_DATA
-	readbuf_loop = NULL;
-	readbuf_loopstart = sp->loopstart;
-	readbuf_loopend = sp->loopend;
-	if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
-		int looplen = sp->loopend - sp->loopstart;
-		readbuf_loop = vmalloc(looplen * 2);
-		if (readbuf_loop == NULL) {
-			printk("AWE32: can't malloc temp buffer\n");
-			return -ENOSPC;
-		}
-	}
-#endif
 	readbuf_addr = addr;
 	readbuf_offs = offset;
 	readbuf_flags = sp->mode_flags;
@@ -3557,59 +3517,31 @@
 	/* read from user buffer */
 	if (readbuf_flags & AWE_SAMPLE_8BITS) {
 		unsigned char cc;
-		get_user(cc, (unsigned char*)&(readbuf_addr)[readbuf_offs + pos]);
-		c = cc << 8; /* convert 8bit -> 16bit */
+		get_user(cc, (unsigned char*)(readbuf_addr + readbuf_offs + pos));
+		c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */
 	} else {
-		get_user(c, (unsigned short*)&(readbuf_addr)[readbuf_offs + pos * 2]);
+		get_user(c, (unsigned short*)(readbuf_addr + readbuf_offs + pos * 2));
 	}
 	if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
 		c ^= 0x8000; /* unsigned -> signed */
-#ifdef MALLOC_LOOP_DATA
-	/* write on cache for reverse loop */
-	if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
-		if (pos >= readbuf_loopstart && pos < readbuf_loopend)
-			readbuf_loop[pos - readbuf_loopstart] = c;
-	}
-#endif
 	return c;
 }
 
-#ifdef MALLOC_LOOP_DATA
-/* read from cache */
-static unsigned short
-readbuf_word_cache(int pos)
-{
-	if (pos >= readbuf_loopstart && pos < readbuf_loopend)
-		return readbuf_loop[pos - readbuf_loopstart];
-	return 0;
-}
-
-static void
-readbuf_end(void)
-{
-	if (readbuf_loop)
-		vfree(readbuf_loop);
-	readbuf_loop = NULL;
-}
-
-#else
-
 #define readbuf_word_cache	readbuf_word
 #define readbuf_end()		/**/
 
-#endif
-
 /*----------------------------------------------------------------*/
 
 #define BLANK_LOOP_START	8
 #define BLANK_LOOP_END		40
 #define BLANK_LOOP_SIZE		48
 
-/* loading onto memory */
+/* loading onto memory - return the actual written size */
 static int 
-awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels)
+awe_write_wave_data(const char *addr, int offset, awe_sample_list *list, int channels)
 {
 	int i, truesize, dram_offset;
+	awe_sample_info *sp = &list->v;
 	int rc;
 
 	/* be sure loop points start < end */
@@ -3621,11 +3553,11 @@
 
 	/* compute true data size to be loaded */
 	truesize = sp->size;
-	if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)
+	if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))
 		truesize += sp->loopend - sp->loopstart;
 	if (sp->mode_flags & AWE_SAMPLE_NO_BLANK)
 		truesize += BLANK_LOOP_SIZE;
-	if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) {
+	if (awe_free_mem_ptr() + truesize >= memsize/2) {
 		DEBUG(-1,printk("AWE32 Error: Sample memory full\n"));
 		return -ENOSPC;
 	}
@@ -3686,13 +3618,12 @@
 		}
 	}
 
-	sflists[current_sf_id-1].mem_ptr += truesize;
 	awe_close_dram();
 
 	/* initialize FM */
 	awe_init_fm();
 
-	return 0;
+	return truesize;
 }
 
 
@@ -3728,33 +3659,36 @@
 	struct patch_info patch;
 	awe_voice_info *rec;
 	awe_sample_info *smp;
+	awe_voice_list *vrec;
+	awe_sample_list *smprec;
 	int sizeof_patch;
-	int note, free_sample, free_info;
-	int rc;
+	int note, rc;
+	sf_list *sf;
 
 	sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */
 	if (size < sizeof_patch) {
-		printk("AWE32 Error: Patch header too short\n");
+		printk(KERN_WARNING "AWE32 Error: Patch header too short\n");
 		return -EINVAL;
 	}
-	copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs);
+	if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs))
+		return -EFAULT;
 	size -= sizeof_patch;
 	if (size < patch.len) {
-		printk("AWE32 Warning: Patch record too short (%d<%d)\n",
+		printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n",
 		       size, patch.len);
 		return -EINVAL;
 	}
-	if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0)
-		return -ENOSPC;
-	if (alloc_new_sample() < 0)
-		return -ENOSPC;
-	if (alloc_new_info(1))
-		return -ENOSPC;
-
-	free_sample = awe_free_sample();
-	smp = &samples[free_sample].v;
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL)
+		return -ENOMEM;
+	if ((smprec = alloc_new_sample()) == NULL)
+		return -ENOMEM;
+	if ((vrec = alloc_new_info()) == NULL) {
+		kfree(smprec);
+		return -ENOMEM;
+	}
 
-	smp->sample = free_sample;
+	smp = &smprec->v;
+	smp->sample = sf->num_sample;
 	smp->start = 0;
 	smp->end = patch.len;
 	smp->loopstart = patch.loop_start;
@@ -3786,17 +3720,15 @@
 	smp->checksum_flag = 0;
 	smp->checksum = 0;
 
-	if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0)
+	if ((rc = awe_write_wave_data(addr, sizeof_patch, smprec, -1)) < 0)
 		return rc;
-
-	smp->sf_id = current_sf_id;
-	add_sf_sample(free_sample);
+	sf->mem_ptr += rc;
+	add_sf_sample(sf, smprec);
 
 	/* set up voice info */
-	free_info = awe_free_info();
-	rec = &infos[free_info].v;
+	rec = &vrec->v;
 	awe_init_voice_info(rec);
-	rec->sample = free_sample; /* the last sample */
+	rec->sample = sf->num_info; /* the last sample */
 	rec->rate_offset = calc_rate_offset(patch.base_freq);
 	note = freq_to_note(patch.base_note);
 	rec->root = note / 100;
@@ -3831,8 +3763,8 @@
 		release += calc_gus_envelope_time
 			(patch.env_rate[5], patch.env_offset[4],
 			 patch.env_offset[5]);
-		rec->parm.volatkhld = (calc_parm_attack(attack) << 8) |
-			calc_parm_hold(hold);
+		rec->parm.volatkhld = (calc_parm_hold(hold) << 8) |
+			calc_parm_attack(attack);
 		rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
 			calc_parm_decay(decay);
 		rec->parm.volrelease = 0x8000 | calc_parm_decay(release);
@@ -3860,17 +3792,16 @@
 	/* scale_freq, scale_factor, volume, and fractions not implemented */
 
 	/* append to the tail of the list */
-	infos[free_info].bank = ctrls[AWE_MD_GUS_BANK];
-	infos[free_info].instr = patch.instr_no;
-	infos[free_info].disabled = FALSE;
-	infos[free_info].type = V_ST_NORMAL;
-	infos[free_info].v.sf_id = current_sf_id;
+	vrec->bank = ctrls[AWE_MD_GUS_BANK];
+	vrec->instr = patch.instr_no;
+	vrec->disabled = FALSE;
+	vrec->type = V_ST_NORMAL;
 
-	add_info_list(free_info);
-	add_sf_info(free_info);
+	add_sf_info(sf, vrec);
+	add_info_list(vrec);
 
 	/* set the voice index */
-	awe_set_sample(rec);
+	awe_set_sample(vrec);
 
 	return 0;
 }
@@ -3881,96 +3812,100 @@
  * sample and voice list handlers
  */
 
-/* append this to the sf list */
-static void add_sf_info(int rec)
+/* append this to the current sf list */
+static void add_sf_info(sf_list *sf, awe_voice_list *rec)
 {
-	int sf_id = infos[rec].v.sf_id;
-	if (sf_id <= 0) return;
-	sf_id--;
-	if (sflists[sf_id].infos < 0)
-		sflists[sf_id].infos = rec;
-	else {
-		int i, prev;
-		prev = sflists[sf_id].infos;
-		while ((i = infos[prev].next) >= 0)
-			prev = i;
-		infos[prev].next = rec;
-	}
-	infos[rec].next = -1;
-	sflists[sf_id].num_info++;
+	if (sf == NULL)
+		return;
+	rec->holder = sf;
+	rec->v.sf_id = sf->sf_id;
+	if (sf->last_infos)
+		sf->last_infos->next = rec;
+	else
+		sf->infos = rec;
+	sf->last_infos = rec;
+	rec->next = NULL;
+	sf->num_info++;
 }
 
 /* prepend this sample to sf list */
-static void add_sf_sample(int rec)
+static void add_sf_sample(sf_list *sf, awe_sample_list *rec)
 {
-	int sf_id = samples[rec].v.sf_id;
-	if (sf_id <= 0) return;
-	sf_id--;
-	samples[rec].next = sflists[sf_id].samples;
-	sflists[sf_id].samples = rec;
-	sflists[sf_id].num_sample++;
+	if (sf == NULL)
+		return;
+	rec->holder = sf;
+	rec->v.sf_id = sf->sf_id;
+	if (sf->last_samples)
+		sf->last_samples->next = rec;
+	else
+		sf->samples = rec;
+	sf->last_samples = rec;
+	rec->next = NULL;
+	sf->num_sample++;
 }
 
 /* purge the old records which don't belong with the same file id */
-static void purge_old_list(int rec, int next)
+static void purge_old_list(awe_voice_list *rec, awe_voice_list *next)
 {
-	infos[rec].next_instr = next;
-	if (infos[rec].bank == AWE_DRUM_BANK) {
+	rec->next_instr = next;
+	if (rec->bank == AWE_DRUM_BANK) {
 		/* remove samples with the same note range */
-		int cur, *prevp = &infos[rec].next_instr;
-		int low = infos[rec].v.low;
-		int high = infos[rec].v.high;
-		for (cur = next; cur >= 0; cur = infos[cur].next_instr) {
-			if (infos[cur].v.low == low &&
-			    infos[cur].v.high == high &&
-			    ! is_identical_id(infos[cur].v.sf_id, infos[rec].v.sf_id))
-				*prevp = infos[cur].next_instr;
-			prevp = &infos[cur].next_instr;
+		awe_voice_list *cur, *prev = rec;
+		int low = rec->v.low;
+		int high = rec->v.high;
+		for (cur = next; cur; cur = cur->next_instr) {
+			if (cur->v.low == low &&
+			    cur->v.high == high &&
+			    ! is_identical_holder(cur->holder, rec->holder))
+				prev->next_instr = cur->next_instr;
+			else
+				prev = cur;
 		}
 	} else {
-		if (! is_identical_id(infos[next].v.sf_id, infos[rec].v.sf_id))
-			infos[rec].next_instr = -1;
+		if (! is_identical_holder(next->holder, rec->holder))
+			/* remove all samples */
+			rec->next_instr = NULL;
 	}
 }
 
 /* prepend to top of the preset table */
-static void add_info_list(int rec)
+static void add_info_list(awe_voice_list *rec)
 {
-	int *prevp, cur;
-	int instr;
-	int bank;
+	awe_voice_list *prev, *cur;
+	int key;
 
-	if (infos[rec].disabled)
+	if (rec->disabled)
 		return;
 
-	instr = infos[rec].instr;
-	bank = infos[rec].bank;
-	limitvalue(instr, 0, AWE_MAX_PRESETS-1);
-	prevp = &preset_table[instr];
-	cur = *prevp;
-	while (cur >= 0) {
+	key = awe_search_key(rec->bank, rec->instr, rec->v.low);
+	prev = NULL;
+	for (cur = preset_table[key]; cur; cur = cur->next_bank) {
 		/* search the first record with the same bank number */
-		if (infos[cur].bank == bank) {
+		if (cur->instr == rec->instr && cur->bank == rec->bank) {
 			/* replace the list with the new record */
-			infos[rec].next_bank = infos[cur].next_bank;
-			*prevp = rec;
+			rec->next_bank = cur->next_bank;
+			if (prev)
+				prev->next_bank = rec;
+			else
+				preset_table[key] = rec;
 			purge_old_list(rec, cur);
 			return;
 		}
-		prevp = &infos[cur].next_bank;
-		cur = infos[cur].next_bank;
+		prev = cur;
 	}
 
 	/* this is the first bank record.. just add this */
-	infos[rec].next_instr = -1;
-	infos[rec].next_bank = preset_table[instr];
-	preset_table[instr] = rec;
+	rec->next_instr = NULL;
+	rec->next_bank = preset_table[key];
+	preset_table[key] = rec;
 }
 
 /* remove samples later than the specified sf_id */
 static void
 awe_remove_samples(int sf_id)
 {
+	sf_list *p, *prev;
+
 	if (sf_id <= 0) {
 		awe_reset_samples();
 		return;
@@ -3979,6 +3914,20 @@
 	if (current_sf_id <= sf_id)
 		return;
 
+	for (p = sftail; p; p = prev) {
+		if (p->sf_id <= sf_id)
+			break;
+		prev = p->prev;
+		awe_free_sf(p);
+	}
+	sftail = p;
+	if (sftail) {
+		sf_id = sftail->sf_id;
+		sftail->next = NULL;
+	} else {
+		sf_id = 0;
+		sfhead = NULL;
+	}
 	current_sf_id = sf_id;
 	if (locked_sf_id > sf_id)
 		locked_sf_id = sf_id;
@@ -3989,33 +3938,36 @@
 /* rebuild preset search list */
 static void rebuild_preset_list(void)
 {
-	int i, j;
+	sf_list *p;
+	awe_voice_list *rec;
 
-	for (i = 0; i < AWE_MAX_PRESETS; i++)
-		preset_table[i] = -1;
+	memset(preset_table, 0, sizeof(preset_table));
 
-	for (i = 0; i < current_sf_id; i++) {
-		for (j = sflists[i].infos; j >= 0; j = infos[j].next)
-			add_info_list(j);
+	for (p = sfhead; p; p = p->next) {
+		for (rec = p->infos; rec; rec = rec->next)
+			add_info_list(rec);
 	}
 }
 
 /* compare the given sf_id pair */
-static int is_identical_id(int id1, int id2)
+static int is_identical_holder(sf_list *sf1, sf_list *sf2)
 {
-	if (id1 == id2)
-		return TRUE;
-	if (id1 <= 0 || id2 <= 0) /* this must not happen.. */
+	if (sf1 == NULL || sf2 == NULL)
 		return FALSE;
+	if (sf1 == sf2)
+		return TRUE;
 #ifdef AWE_ALLOW_SAMPLE_SHARING
 	{
 		/* compare with the sharing id */
-		int i;
-		if (id1 < id2) { /* make sure id1 > id2 */
-			int tmp; tmp = id1; id1 = id2; id2 = tmp;
-		}
-		for (i = sflists[id1-1].shared; i > 0 && i <= current_sf_id; i = sflists[i-1].shared) {
-			if (i == id2)
+		sf_list *p;
+		int counter = 0;
+		if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */
+			sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp;
+		}
+		for (p = sf1->shared; p; p = p->shared) {
+			if (counter++ > current_sf_id)
+				break; /* strange sharing loop.. quit */
+			if (p == sf2)
 				return TRUE;
 		}
 	}
@@ -4024,73 +3976,81 @@
 }
 
 /* search the sample index matching with the given sample id */
-static int search_sample_index(int sf, int sample, int level)
+static awe_sample_list *
+search_sample_index(sf_list *sf, int sample)
 {
-	int i;
-
-	if (sf <= 0 || sf > current_sf_id)
-		return -1; /* this must not happen */
-
-	for (i = sflists[sf-1].samples; i >= 0; i = samples[i].next) {
-		if (samples[i].v.sample == sample)
-			return i;
-	}
+	awe_sample_list *p;
 #ifdef AWE_ALLOW_SAMPLE_SHARING
-	if ((i = sflists[sf-1].shared) > 0 && i <= current_sf_id) { /* search recursively */
-		if (level > current_sf_id)
-			return -1; /* strange sharing loop.. quit */
-		return search_sample_index(i, sample, level + 1);
+	int counter = 0;
+	while (sf) {
+		for (p = sf->samples; p; p = p->next) {
+			if (p->v.sample == sample)
+				return p;
+		}
+		sf = sf->shared;
+		if (counter++ > current_sf_id)
+			break; /* strange sharing loop.. quit */
+	}
+#else
+	if (sf) {
+		for (p = sf->samples; p; p = p->next) {
+			if (p->v.sample == sample)
+				return p;
+		}
 	}
 #endif
-	return -1;
+	return NULL;
 }
 
 /* search the specified sample */
+/* non-zero = found */
 static short
-awe_set_sample(awe_voice_info *vp)
+awe_set_sample(awe_voice_list *rec)
 {
-	int i;
+	awe_sample_list *smp;
+	awe_voice_info *vp = &rec->v;
 
-	vp->index = -1;
-	if ((i = search_sample_index(vp->sf_id, vp->sample, 0)) < 0)
-		return -1;
+	vp->index = 0;
+	if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL)
+		return 0;
 
 	/* set the actual sample offsets */
-	vp->start += samples[i].v.start;
-	vp->end += samples[i].v.end;
-	vp->loopstart += samples[i].v.loopstart;
-	vp->loopend += samples[i].v.loopend;
+	vp->start += smp->v.start;
+	vp->end += smp->v.end;
+	vp->loopstart += smp->v.loopstart;
+	vp->loopend += smp->v.loopend;
 	/* copy mode flags */
-	vp->mode = samples[i].v.mode_flags;
-	/* set index */
-	vp->index = i;
+	vp->mode = smp->v.mode_flags;
+	/* set flag */
+	vp->index = 1;
 
-	return i;
+	return 1;
 }
 
 
-/*----------------------------------------------------------------
+/*
  * voice allocation
- *----------------------------------------------------------------*/
+ */
 
 /* look for all voices associated with the specified note & velocity */
 static int
-awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist)
+awe_search_multi_voices(awe_voice_list *rec, int note, int velocity,
+			awe_voice_info **vlist)
 {
 	int nvoices;
 
 	nvoices = 0;
-	for (; rec >= 0; rec = infos[rec].next_instr) {
-		if (note >= infos[rec].v.low &&
-		    note <= infos[rec].v.high &&
-		    velocity >= infos[rec].v.vellow &&
-		    velocity <= infos[rec].v.velhigh) {
-			if (infos[rec].type == V_ST_MAPPED) {
+	for (; rec; rec = rec->next_instr) {
+		if (note >= rec->v.low &&
+		    note <= rec->v.high &&
+		    velocity >= rec->v.vellow &&
+		    velocity <= rec->v.velhigh) {
+			if (rec->type == V_ST_MAPPED) {
 				/* mapper */
-				vlist[0] = &infos[rec].v;
+				vlist[0] = &rec->v;
 				return -1;
 			}
-			vlist[nvoices++] = &infos[rec].v;
+			vlist[nvoices++] = &rec->v;
 			if (nvoices >= AWE_MAX_VOICES)
 				break;
 		}
@@ -4103,29 +4063,45 @@
    the note number if necessary.
    */
 static int
-really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level)
+really_alloc_voices(int bank, int instr, int *note, int velocity, awe_voice_info **vlist)
 {
 	int nvoices;
+	awe_voice_list *vrec;
+	int level = 0;
 
-	nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
-	if (nvoices == 0)
-		nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist);
-	if (nvoices < 0) { /* mapping */
-		int preset = vlist[0]->start;
-		int bank = vlist[0]->end;
-		int key = vlist[0]->fixkey;
-		if (level > 5) {
-			printk("AWE32: too deep mapping level\n");
-			return 0;
-		}
-		vrec = awe_search_instr(bank, preset);
-		if (bank == AWE_DRUM_BANK)
-			def_vrec = awe_search_instr(bank, 0);
-		else
-			def_vrec = awe_search_instr(0, preset);
-		if (key >= 0)
-			*note = key;
-		return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1);
+	for (;;) {
+		vrec = awe_search_instr(bank, instr, *note);
+		nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+		if (nvoices == 0) {
+			if (bank == AWE_DRUM_BANK)
+				/* search default drumset */
+				vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note);
+			else
+				/* search default preset */
+				vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note);
+			nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+		}
+		if (nvoices == 0) {
+			if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0)
+				/* search default drumset */
+				vrec = awe_search_instr(bank, 0, *note);
+			else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0)
+				/* search default preset */
+				vrec = awe_search_instr(0, instr, *note);
+			nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+		}
+		if (nvoices < 0) { /* mapping */
+			int key = vlist[0]->fixkey;
+			instr = vlist[0]->start;
+			bank = vlist[0]->end;
+			if (level++ > 5) {
+				printk(KERN_ERR "AWE32: too deep mapping level\n");
+				return 0;
+			}
+			if (key >= 0)
+				*note = key;
+		} else
+			break;
 	}
 
 	return nvoices;
@@ -4135,15 +4111,17 @@
 static void
 awe_alloc_multi_voices(int ch, int note, int velocity, int key)
 {
-	int i, v, nvoices;
+	int i, v, nvoices, bank;
 	awe_voice_info *vlist[AWE_MAX_VOICES];
 
-	if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
-		awe_set_instr(0, ch, channels[ch].instr);
+	if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch))
+		bank = AWE_DRUM_BANK; /* always search drumset */
+	else
+		bank = channels[ch].bank;
 
 	/* check the possible voices; note may be changeable if mapped */
-	nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec,
-				      &note, velocity, vlist, 0);
+	nvoices = really_alloc_voices(bank, channels[ch].instr,
+				      &note, velocity, vlist);
 
 	/* set the voices */
 	current_alloc_time++;
@@ -4169,78 +4147,62 @@
 }
 
 
-/* search the best voice from the specified status condition */
+/* search an empty voice.
+   if no empty voice is found, at least terminate a voice
+   */
 static int
-search_best_voice(int condition)
+awe_clear_voice(void)
 {
-	int i, time, best;
-	int vtarget = 0xffff, min_vtarget = 0xffff;
+	enum {
+		OFF=0, RELEASED, SUSTAINED, PLAYING, END
+	};
+	struct voice_candidate_t {
+		int best;
+		int time;
+		int vtarget;
+	} candidate[END];
+	int i, type, vtarget;
+
+	vtarget = 0xffff;
+	for (type = OFF; type < END; type++) {
+		candidate[type].best = -1;
+		candidate[type].time = current_alloc_time + 1;
+		candidate[type].vtarget = vtarget;
+	}
 
-	best = -1;
-	time = current_alloc_time + 1;
 	for (i = 0; i < awe_max_voices; i++) {
-		if (! (voices[i].state & condition))
+		if (voices[i].state & AWE_ST_OFF)
+			type = OFF;
+		else if (voices[i].state & AWE_ST_RELEASED)
+			type = RELEASED;
+		else if (voices[i].state & AWE_ST_SUSTAINED)
+			type = SUSTAINED;
+		else if (voices[i].state & ~AWE_ST_MARK)
+			type = PLAYING;
+		else
 			continue;
 #ifdef AWE_CHECK_VTARGET
 		/* get current volume */
 		vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff;
 #endif
-		if (best < 0 || vtarget < min_vtarget ||
-		    (vtarget == min_vtarget && voices[i].time < time)) {
-			best = i;
-			time = voices[i].time;
-			min_vtarget = vtarget;
+		if (candidate[type].best < 0 ||
+		    vtarget < candidate[type].vtarget ||
+		    (vtarget == candidate[type].vtarget &&
+		     voices[i].time < candidate[type].time)) {
+			candidate[type].best = i;
+			candidate[type].time = voices[i].time;
+			candidate[type].vtarget = vtarget;
 		}
 	}
-	/* clear voice */
-	if (best >= 0) {
-		if (voices[best].state != AWE_ST_OFF)
-			awe_terminate(best);
-		awe_voice_init(best, TRUE);
-	}
-
-	return best;
-}
-
-/* search an empty voice.
-   if no empty voice is found, at least terminate a voice
-   */
-static int
-awe_clear_voice(void)
-{
-	int best;
 
-	/* looking for the oldest empty voice */
-	if ((best = search_best_voice(AWE_ST_OFF)) >= 0)
-		return best;
-	if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0)
-		return best;
-	/* looking for the oldest sustained voice */
-	if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0)
-		return best;
-
-	if (MULTI_LAYER_MODE() && ctrls[AWE_MD_CHN_PRIOR]) {
-		int ch = -1;
-		int time = current_alloc_time + 1;
-		int i;
-		/* looking for the voices from high channel (except drum ch) */
-		for (i = 0; i < awe_max_voices; i++) {
-			if (IS_DRUM_CHANNEL(voices[i].ch)) continue;
-			if (voices[i].ch < ch) continue;
-			if (voices[i].state != AWE_ST_MARK &&
-			    (voices[i].ch > ch || voices[i].time < time)) {
-				best = i;
-				time = voices[i].time;
-				ch = voices[i].ch;
-			}
+	for (type = OFF; type < END; type++) {
+		if ((i = candidate[type].best) >= 0) {
+			if (voices[i].state != AWE_ST_OFF)
+				awe_terminate(i);
+			awe_voice_init(i, TRUE);
+			return i;
 		}
 	}
-	if (best < 0)
-		best = search_best_voice(~AWE_ST_MARK);
-
-	if (best >= 0)
-		return best;
-
 	return 0;
 }
 
@@ -4251,16 +4213,17 @@
 static void
 awe_alloc_one_voice(int voice, int note, int velocity)
 {
-	int ch, nvoices;
+	int ch, nvoices, bank;
 	awe_voice_info *vlist[AWE_MAX_VOICES];
 
 	ch = voices[voice].ch;
-	if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
-		awe_set_instr(0, ch, channels[ch].instr);
+	if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice))
+		bank = AWE_DRUM_BANK; /* always search drumset */
+	else
+		bank = voices[voice].cinfo->bank;
 
-	nvoices = really_alloc_voices(voices[voice].cinfo->vrec,
-				      voices[voice].cinfo->def_vrec,
-				      &note, velocity, vlist, 0);
+	nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr,
+				      &note, velocity, vlist);
 	if (nvoices > 0) {
 		voices[voice].time = ++current_alloc_time;
 		voices[voice].sample = vlist[0]; /* use the first one */
@@ -4271,9 +4234,9 @@
 }
 
 
-/*----------------------------------------------------------------
+/*
  * sequencer2 functions
- *----------------------------------------------------------------*/
+ */
 
 /* search an empty voice; used by sequencer2 */
 static int
@@ -4331,14 +4294,14 @@
 	awe_mixer_ioctl,
 };
 
-static void attach_mixer(void)
+static void __init attach_mixer(void)
 {
 	if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) {
 		mixer_devs[my_mixerdev] = &awe_mixer_operations;
 	}
 }
 
-static void unload_mixer(void)
+static void __exit unload_mixer(void)
 {
 	if (my_mixerdev >= 0)
 		sound_unload_mixerdev(my_mixerdev);
@@ -4352,7 +4315,7 @@
 	if (((cmd >> 8) & 0xff) != 'M')
 		return -EINVAL;
 
-	level = (int) *(int *)arg;
+	level = *(int*)arg;
 	level = ((level & 0xff) + (level >> 8)) / 2;
 	DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level));
 
@@ -4408,13 +4371,13 @@
 		level = 0;
 		break;
 	}
-	return *(int *)arg = level;
+	return *(int*)arg = level;
 }
 #endif /* CONFIG_AWE32_MIXER */
 
 
 /*
- * initialization of AWE32
+ * initialization of Emu8000
  */
 
 /* intiailize audio channels */
@@ -4636,7 +4599,7 @@
 {
 #ifndef AWE_ALWAYS_INIT_FM
 	/* if no extended memory is on board.. */
-	if (awe_mem_size <= 0)
+	if (memsize <= 0)
 		return;
 #endif
 	DEBUG(3,printk("AWE32: initializing FM\n"));
@@ -4731,7 +4694,8 @@
 			awe_poke_dw(AWE_CCCA(vidx[i]), 0);
 			voices[vidx[i]].state = AWE_ST_OFF;
 		}
-		return -ENOSPC;
+		printk("awe: not ready to write..\n");
+		return -EPERM;
 	}
 
 	/* set address to write */
@@ -4784,13 +4748,13 @@
 }
 
 
-/*================================================================
+/*
  * detect presence of AWE32 and check memory size
- *================================================================*/
+ */
 
 /* detect emu8000 chip on the specified address; from VV's guide */
 
-static int
+static int __init
 awe_detect_base(int addr)
 {
 	setup_ports(addr, 0, 0);
@@ -4804,16 +4768,79 @@
 	return 1;
 }
 	
-static int
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+static struct {
+	unsigned short vendor;
+	unsigned short function;
+	char *name;
+} isapnp_awe_list[] __initdata = {
+	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0021), "AWE32 WaveTable"},
+	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0022), "AWE64 WaveTable"},
+	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0023), "AWE64 Gold WaveTable"},
+	{0,}
+};
+
+static struct pci_dev *idev = NULL;
+
+static int __init awe_probe_isapnp(int *port)
+{
+	int i;
+
+	for (i = 0; isapnp_awe_list[i].vendor != 0; i++) {
+		while ((idev = isapnp_find_dev(NULL,
+		                               isapnp_awe_list[i].vendor,
+		                               isapnp_awe_list[i].function,
+		                               idev))) {
+			if (idev->prepare(idev) < 0)
+				continue;
+			if (idev->activate(idev) < 0 ||
+			    !idev->resource[0].start) {
+				idev->deactivate(idev);
+				idev->deactivate(idev);
+				continue;
+			}
+			*port = idev->resource[0].start;
+			break;
+		}
+		if (!idev)
+			continue;
+		printk(KERN_INFO "ISAPnP reports %s at i/o %#x\n",
+		       isapnp_awe_list[i].name, *port);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static void __exit awe_deactivate_isapnp(void)
+{
+#if 1
+	if (idev) {
+		idev->deactivate(idev);
+		idev = NULL;
+	}
+#endif
+}
+
+#endif
+
+static int __init
 awe_detect(void)
 {
 	int base;
 
-	if (port_setuped) /* already initialized by PnP */
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+	if (isapnp) {
+		if (awe_probe_isapnp(&io) < 0) {
+			printk(KERN_ERR "AWE32: No ISAPnP cards found\n");
+			return 0;
+		}
+		setup_ports(io, 0, 0);
 		return 1;
+	}
+#endif /* isapnp */
 
-	if (awe_port) /* use default i/o port value */
-		setup_ports(awe_port, 0, 0);
+	if (io) /* use default i/o port value */
+		setup_ports(io, 0, 0);
 	else { /* probe it */
 		for (base = 0x620; base <= 0x680; base += 0x20)
 			if (awe_detect_base(base))
@@ -4826,36 +4853,36 @@
 }
 
 
-/*================================================================
+/*
  * check dram size on AWE board
- *================================================================*/
+ */
 
 /* any three numbers you like */
 #define UNIQUE_ID1	0x1234
 #define UNIQUE_ID2	0x4321
 #define UNIQUE_ID3	0xFFFF
 
-static void
+static void __init
 awe_check_dram(void)
 {
 	if (awe_present) /* already initialized */
 		return;
 
-	if (awe_mem_size >= 0) { /* given by config file or module option */
-		awe_mem_size *= 1024; /* convert to Kbytes */
+	if (memsize >= 0) { /* given by config file or module option */
+		memsize *= 1024; /* convert to Kbytes */
 		return;
 	}
 
 	awe_open_dram_for_check();
 
-	awe_mem_size = 0;
+	memsize = 0;
 
 	/* set up unique two id numbers */
 	awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
 	awe_poke(AWE_SMLD, UNIQUE_ID1);
 	awe_poke(AWE_SMLD, UNIQUE_ID2);
 
-	while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
+	while (memsize < AWE_MAX_DRAM_SIZE) {
 		awe_wait(5);
 		/* read a data on the DRAM start address */
 		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
@@ -4864,33 +4891,35 @@
 			break;
 		if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
 			break;
-		awe_mem_size += 512;  /* increment 512kbytes */
+		memsize += 512;  /* increment 512kbytes */
 		/* Write a unique data on the test address;
 		 * if the address is out of range, the data is written on
 		 * 0x200000(=AWE_DRAM_OFFSET).  Then the two id words are
 		 * broken by this data.
 		 */
-		awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
+		awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + memsize*512L);
 		awe_poke(AWE_SMLD, UNIQUE_ID3);
 		awe_wait(5);
 		/* read a data on the just written DRAM address */
-		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L);
+		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + memsize*512L);
 		awe_peek(AWE_SMLD); /* discard stale data  */
 		if (awe_peek(AWE_SMLD) != UNIQUE_ID3)
 			break;
 	}
 	awe_close_dram();
 
-	DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size));
+	DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", memsize));
 
 	/* convert to Kbytes */
-	awe_mem_size *= 1024;
+	memsize *= 1024;
 }
 
 
-/*================================================================
+/*----------------------------------------------------------------*/
+
+/*
  * chorus and reverb controls; from VV's guide
- *================================================================*/
+ */
 
 /* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
 static char chorus_defined[AWE_CHORUS_NUMBERS];
@@ -4909,15 +4938,16 @@
 awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count)
 {
 	if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) {
-		printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg);
+		printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg);
 		return -EINVAL;
 	}
 	if (count < sizeof(awe_chorus_fx_rec)) {
-		printk("AWE32 Error: too short chorus fx parameters\n");
+		printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n");
 		return -EINVAL;
 	}
-	copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
-		       sizeof(awe_chorus_fx_rec));
+	if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
+			   sizeof(awe_chorus_fx_rec)))
+		return -EFAULT;
 	chorus_defined[patch->optarg] = TRUE;
 	return 0;
 }
@@ -5016,15 +5046,16 @@
 awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count)
 {
 	if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) {
-		printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg);
+		printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg);
 		return -EINVAL;
 	}
 	if (count < sizeof(awe_reverb_fx_rec)) {
-		printk("AWE32 Error: too short reverb fx parameters\n");
+		printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n");
 		return -EINVAL;
 	}
-	copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
-		       sizeof(awe_reverb_fx_rec));
+	if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
+			   sizeof(awe_reverb_fx_rec)))
+		return -EFAULT;
 	reverb_defined[patch->optarg] = TRUE;
 	return 0;
 }
@@ -5047,9 +5078,9 @@
 	awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]);
 }
 
-/*================================================================
+/*
  * treble/bass equalizer control
- *================================================================*/
+ */
 
 static unsigned short bass_parm[12][3] = {
 	{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
@@ -5113,15 +5144,17 @@
 }
 
 
+/*----------------------------------------------------------------*/
+
 #ifdef CONFIG_AWE32_MIDIEMU
 
-/*================================================================
+/*
  * Emu8000 MIDI Emulation
- *================================================================*/
+ */
 
-/*================================================================
+/*
  * midi queue record
- *================================================================*/
+ */
 
 /* queue type */
 enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, };
@@ -5149,9 +5182,9 @@
 } ConvTable;
 
 
-/*================================================================
+/*
  * prototypes
- *================================================================*/
+ */
 
 static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int));
 static void awe_midi_close(int dev);
@@ -5183,9 +5216,9 @@
 #define numberof(ary)	(sizeof(ary)/sizeof(ary[0]))
 
 
-/*================================================================
+/*
  * OSS Midi device record
- *================================================================*/
+ */
 
 static struct midi_operations awe_midi_operations =
 {
@@ -5204,7 +5237,7 @@
 
 static int my_mididev = -1;
 
-static void attach_midiemu(void)
+static void __init attach_midiemu(void)
 {
 	if ((my_mididev = sound_alloc_mididev()) < 0)
 		printk ("Sound: Too many midi devices detected\n");
@@ -5212,7 +5245,7 @@
 		midi_devs[my_mididev] = &awe_midi_operations;
 }
 
-static void unload_midiemu(void)
+static void __exit unload_midiemu(void)
 {
 	if (my_mididev >= 0)
 		sound_unload_mididev(my_mididev);
@@ -5632,9 +5665,9 @@
 }
 
 
-/*================================================================
+/*
  * RPN events
- *================================================================*/
+ */
 
 static void midi_rpn_event(MidiStatus *st)
 {
@@ -5686,10 +5719,10 @@
 }
 
 
-/*================================================================
+/*
  * system exclusive message
  * GM/GS/XG macros are accepted
- *================================================================*/
+ */
 
 static void midi_system_exclusive(MidiStatus *st)
 {
@@ -5784,6 +5817,8 @@
 }
 
 
+/*----------------------------------------------------------------*/
+
 /*
  * convert NRPN/control values
  */
@@ -6006,9 +6041,9 @@
 static int num_gs_effects = numberof(gs_effects);
 
 
-/*================================================================
+/*
  * NRPN events: accept as AWE32/SC88 specific controls
- *================================================================*/
+ */
 
 static void midi_nrpn_event(MidiStatus *st)
 {
@@ -6026,9 +6061,9 @@
 }
 
 
-/*----------------------------------------------------------------
+/*
  * XG control effects; still experimental
- *----------------------------------------------------------------*/
+ */
 
 /* cutoff: quarter semitone step, max=255 */
 static unsigned short xg_cutoff(int val)
@@ -6071,45 +6106,42 @@
 
 #endif /* CONFIG_AWE32_MIDIEMU */
 
-/* new type interface */
-static int __init attach_awe(void)
+
+/*----------------------------------------------------------------*/
+
+/*
+ * device / lowlevel (module) interface
+ */
+
+int __init attach_awe(void)
 {
-#ifdef CONFIG_PNP_DRV
-	if (pnp) {
-		awe_initpnp();
-		if (awe_pnp_ok)
-			return 0;
-	}
-#endif /* pnp */
-	
-	_attach_awe();
-	
-	return 0;
-}									
+	return _attach_awe() ? 0 : -ENODEV;
+}
 
-static void __exit unload_awe(void)								
+void __exit unload_awe(void)
 {
-#ifdef CONFIG_PNP_DRV
-	if (pnp)
-		awe_unload_pnp();
-#endif
-	
 	_unload_awe();
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+	if (isapnp)
+		awe_deactivate_isapnp();
+#endif /* isapnp */
 }
 
+
 module_init(attach_awe);
 module_exit(unload_awe);
 
 #ifndef MODULE
 static int __init setup_awe(char *str)
 {
-	/* io, memsize */
-	int ints[3];
+	/* io, memsize, isapnp */
+	int ints[4];
 
 	str = get_options(str, ARRAY_SIZE(ints), ints);
 
 	io = ints[1];
 	memsize = ints[2];
+	isapnp = ints[3];
 
 	return 1;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/awe_wave.h linux/drivers/sound/awe_wave.h
--- v2.3.99-pre2/linux/drivers/sound/awe_wave.h	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/awe_wave.h	Tue Mar 21 11:10:45 2000
@@ -2,7 +2,7 @@
  * sound/awe_config.h
  *
  * Configuration of AWE32/SB32/AWE64 wave table synth driver.
- *   version 0.4.3; Mar. 1, 1998
+ *   version 0.4.4; Jan. 4, 2000
  *
  * Copyright (C) 1996-1998 Takashi Iwai
  *
@@ -68,20 +68,10 @@
 /*#define AWE_DEFAULT_MEM_SIZE	512*/	/* kbytes */
 
 /*
- * maximum size of soundfont list table
+ * AWE driver version number
  */
-
-#define AWE_MAX_SF_LISTS 16
-
-/*
- * chunk size of sample and voice tables
- */
-
-#define AWE_MAX_SAMPLES 400
-#define AWE_MAX_INFOS 800
-
 #define AWE_MAJOR_VERSION	0
 #define AWE_MINOR_VERSION	4
-#define AWE_TINY_VERSION	3
+#define AWE_TINY_VERSION	4
 #define AWE_VERSION_NUMBER	((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION)
-#define AWEDRV_VERSION		"0.4.3"
+#define AWEDRV_VERSION		"0.4.4"
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.3.99-pre2/linux/drivers/sound/sb.h	Tue Mar  7 14:32:26 2000
+++ linux/drivers/sound/sb.h	Mon Mar 20 07:40:59 2000
@@ -151,6 +151,7 @@
 int sb_dsp_init (struct address_info *hw_config);
 void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
 int sb_mixer_init(sb_devc *devc);
+void sb_mixer_unload(sb_devc *devc);
 void sb_mixer_set_stereo (sb_devc *devc, int mode);
 void smw_mixer_init(sb_devc *devc);
 void sb_dsp_midi_init (sb_devc *devc);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.3.99-pre2/linux/drivers/sound/sb_card.c	Tue Mar 14 19:10:40 2000
+++ linux/drivers/sound/sb_card.c	Wed Mar 22 15:37:02 2000
@@ -29,6 +29,10 @@
  * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup
  *  Alessandro Zummo <azummo@ita.flashnet.it>
  *
+ * 13-03-2000 Added some more cards, thanks to Torsten Werner.
+ *  Removed joystick and wavetable code, there are better places for them.
+ *  Code cleanup plus some fixes. 
+ *
  */
 
 #include <linux/config.h>
@@ -145,10 +149,7 @@
 static struct address_info cfg_mpu;
 
 struct pci_dev 	*sb_dev 	= NULL, 
-		*wss_dev	= NULL, 
-		*jp_dev		= NULL,
-		*mpu_dev	= NULL, 
-		*wt_dev		= NULL;
+		*mpu_dev	= NULL;
 /*
  *    Note DMA2 of -1 has the right meaning in the SB16 driver as well
  *    as here. It will cause either an error if it is needed or a fallback
@@ -166,10 +167,9 @@
 #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
 static int isapnp	= 1;
 static int isapnpjump	= 0;
-static int nosbwave	= 0;	/* This option will be removed when the new awe_wave driver will be
-				   in the kernel tree */
+static int activated	= 1;
 #else
-int isapnp	= 0;
+static int isapnp	= 0;
 #endif
 
 MODULE_DESCRIPTION("Soundblaster driver");
@@ -187,10 +187,8 @@
 #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
 MODULE_PARM(isapnp,	"i");
 MODULE_PARM(isapnpjump,	"i");
-MODULE_PARM(nosbwave,	"i");
 MODULE_PARM_DESC(isapnp,	"When set to 0, Plug & Play support will be disabled");
 MODULE_PARM_DESC(isapnpjump,	"Jumps to a specific slot in the driver's PnP table. Use the source, Luke.");
-MODULE_PARM_DESC(nosbwave,	"Disable SB AWE 32/64 Wavetable initialization. Use this option with the new awe_wave driver.");
 #endif
 
 MODULE_PARM_DESC(io,		"Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
@@ -213,12 +211,13 @@
 {
 	int err;
 
+	/* Device already active? Let's use it */
+
 	if(dev->active)
 	{
-		printk(KERN_INFO "sb: %s %s already in use\n", devname, resname);
-		return(NULL);
+		activated = 0;
+		return(dev);
 	}
-
 	if((err = dev->activate(dev)) < 0)
 	{
 		printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err);
@@ -324,42 +323,6 @@
 	else
 		printk(KERN_ERR "sb: CMI8330 panic: mpu not found\n");
 
-
-	/*  @P@:Gameport
-	 */
-
-	if((jp_dev = isapnp_find_dev(bus,
-		ISAPNP_VENDOR('@','P','@'), ISAPNP_FUNCTION(0x0001), NULL)))
-	{
-		jp_dev->prepare(jp_dev);
-
-		if((jp_dev = activate_dev("CMI8330", "gameport", jp_dev)))
-			show_base("CMI8330", "gameport", &jp_dev->resource[0]);
-	}
-	else
-		printk(KERN_ERR "sb: CMI8330 panic: gameport not found\n");
-
-	/*  @@@0001:OPL3 
-	 */
-
-#if defined(CONFIG_SOUND_YM3812) || defined(CONFIG_SOUND_YM3812_MODULE)
-	if((wss_dev = isapnp_find_dev(bus,
-		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL)))
-	{
-		wss_dev->prepare(wss_dev);
-
-		/* Let's disable IRQ and DMA for WSS device */
-
-		wss_dev->irq_resource[0].flags = 0;
-		wss_dev->dma_resource[0].flags = 0;
-
-		if((wss_dev = activate_dev("CMI8330", "opl3", wss_dev)))
-			show_base("CMI8330", "opl3", &wss_dev->resource[1]);
-	}
-	else
-		printk(KERN_ERR "sb: CMI8330 panic: opl3 not found\n");
-#endif
-
 	printk(KERN_INFO "sb: CMI8330 mail reports to Alessandro Zummo <azummo@ita.flashnet.it>\n");
 
 	return(sb_dev);
@@ -391,7 +354,6 @@
 		}
 
 		if(!sb_dev) return(NULL);
-
 	}
 	else
 		printk(KERN_ERR "sb: DT0197H panic: sb base not found\n");
@@ -413,169 +375,100 @@
 	else
 		printk(KERN_ERR "sb: DT0197H panic: mpu not found\n");
 
-
-	/*  @P@:Gameport
-	 */
-
-	if((jp_dev = isapnp_find_dev(bus,
-				ISAPNP_VENDOR('@','P','@'), ISAPNP_FUNCTION(0x0001), NULL)))
-	{
-		jp_dev->prepare(jp_dev);
-
-		if((jp_dev = activate_dev("DT0197H", "gameport", jp_dev)))
-			show_base("DT0197H", "gameport", &jp_dev->resource[0]);
-	}
-	else
-		printk(KERN_ERR "sb: DT0197H panic: gameport not found\n");
-
-	/*  @H@0001:OPL3 
-	 */
-
-#if defined(CONFIG_SOUND_YM3812) || defined(CONFIG_SOUND_YM3812_MODULE)
-	if((wss_dev = isapnp_find_dev(bus,
-				ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), NULL)))
-	{
-		wss_dev->prepare(wss_dev);
-
-		/* Let's disable IRQ and DMA for WSS device */
-
-		wss_dev->irq_resource[0].flags = 0;
-		wss_dev->dma_resource[0].flags = 0;
-
-		if((wss_dev = activate_dev("DT0197H", "opl3", wss_dev)))
-			show_base("DT0197H", "opl3", &wss_dev->resource[0]);
-	}
-	else
-		printk(KERN_ERR "sb: DT0197H panic: opl3 not found\n");
-#endif
-
 	printk(KERN_INFO "sb: DT0197H mail reports to Torsten Werner <twerner@intercomm.de>\n");
 
 	return(sb_dev);
 }
 
-/* Specific support for awe will be dropped when:
- * a) The new awe_wawe driver with PnP support will be introduced in the kernel
- * b) The joystick driver will support PnP - a little patch is available from me....hint, hint :-)
- */
-
-static struct pci_dev *sb_init_awe(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config)
+static struct pci_dev *sb_init_als(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config)
 {
-	/*  CTL0042:Audio SB64
-	 *  CTL0031:Audio SB32
-	 *  CTL0045:Audio SB64
+	/* 
+	 * ALS100
+	 * very similar to both ones above above
+	 */
+
+	/*  @@@0001:Soundblaster.
 	 */
 
-	if(	(sb_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), NULL)) || 
-		(sb_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), NULL)) ||
-		(sb_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), NULL))	)
+	if((sb_dev = isapnp_find_dev(bus,
+				ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL)))
 	{
 		sb_dev->prepare(sb_dev);
 		
-		if((sb_dev = activate_dev("AWE", "sb", sb_dev)))
+		if((sb_dev = activate_dev("ALS100", "sb", sb_dev)))
 		{
 			hw_config->io_base 	= sb_dev->resource[0].start;
 			hw_config->irq 		= sb_dev->irq_resource[0].start;
-			hw_config->dma 		= sb_dev->dma_resource[0].start;
-			hw_config->dma2 	= sb_dev->dma_resource[1].start;
-
-			mpu_config->io_base	= sb_dev->resource[1].start;
+			hw_config->dma 		= sb_dev->dma_resource[1].start;
+			hw_config->dma2 	= sb_dev->dma_resource[0].start;
 
-			show_base("AWE", "sb",		&sb_dev->resource[0]);
-			show_base("AWE", "mpu", 	&sb_dev->resource[1]);
-			show_base("AWE", "opl3",	&sb_dev->resource[2]);
+			show_base("ALS100", "sb", &sb_dev->resource[0]);
 		}
-		else
-			return(NULL);
-	}
-	else
-		printk(KERN_ERR "sb: AWE panic: sb base not found\n");
-
-
-	/*  CTL7002:Game SB64
-	 *  CTL7001:Game SB32
-	 */
 
-	if(	(jp_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x7002), NULL)) ||
-		(jp_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x7001), NULL)) )
-	{
-		jp_dev->prepare(jp_dev);
-		
-		if((jp_dev = activate_dev("AWE", "gameport", jp_dev)))
-			show_base("AWE", "gameport", &jp_dev->resource[0]);
+		if(!sb_dev) return(NULL);
 	}
 	else
-		printk(KERN_ERR "sb: AWE panic: gameport not found\n");
+		printk(KERN_ERR "sb: ALS100 panic: sb base not found\n");
 
-
-	/*  CTL0022:WaveTable SB64
-	 *  CTL0021:WaveTable SB32
-	 *  CTL0023:WaveTable Sb64
+	/*  @X@0001:mpu
 	 */
 
-	if( nosbwave == 0 &&
-   	( ( wt_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0023), NULL)) ||
-	  ( wt_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0022), NULL)) ||
-	  ( wt_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0021), NULL)) ))
+	if((mpu_dev = isapnp_find_dev(bus,
+				ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL)))
 	{
-		wt_dev->prepare(wt_dev);
-		
-		if((wt_dev = activate_dev("AWE", "wavetable", wt_dev)))
+		mpu_dev->prepare(mpu_dev);
+
+		if((mpu_dev = activate_dev("ALS100", "mpu", mpu_dev)))
 		{
-			show_base("AWE", "wavetable", &wt_dev->resource[0]);
-			show_base("AWE", "wavetable", &wt_dev->resource[1]);
-			show_base("AWE", "wavetable", &wt_dev->resource[2]);
+			show_base("ALS100", "mpu", &mpu_dev->resource[0]);
+			mpu_config->io_base = mpu_dev->resource[0].start;
 		}
 	}
 	else
-		printk(KERN_ERR "sb: AWE panic: wavetable not found\n");
+		printk(KERN_ERR "sb: ALS100 panic: mpu not found\n");
 
-	printk(KERN_INFO "sb: AWE mail reports to Alessandro Zummo <azummo@ita.flashnet.it>\n");
+	printk(KERN_INFO "sb: ALS100 mail reports to Torsten Werner <twerner@intercomm.de>\n");
 
 	return(sb_dev);
 }
 
-#define SBF_DEV	0x01
+#define SBF_DEV	0x01 /* Please notice that cards without this flag are on the top in the list */
 
 
 static struct { unsigned short vendor, function, flags; struct pci_dev * (*initfunc)(struct pci_bus *, struct pci_dev *, struct address_info *, struct address_info *); char *name; }
-isapnp_sb_list[] __initdata = {
+sb_isapnp_list[] __initdata = {
+	{ISAPNP_VENDOR('C','M','I'), ISAPNP_FUNCTION(0x0001), 0,	&sb_init_cmi,		"CMI 8330 SoundPRO" },
+	{ISAPNP_VENDOR('R','W','B'), ISAPNP_FUNCTION(0x1688), 0,	&sb_init_diamond,	"Diamond DT0197H" },
+	{ISAPNP_VENDOR('A','L','S'), ISAPNP_FUNCTION(0x0001), 0,	&sb_init_als,		"ALS 100" },
 	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), SBF_DEV,	&sb_init_generic,	"Sound Blaster 16" },
 	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), SBF_DEV,	&sb_init_generic,	"Sound Blaster 16" },
 	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), SBF_DEV,	&sb_init_generic,	"Sound Blaster 16" },
 	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), SBF_DEV,	&sb_init_generic,	"Sound Blaster 16" },
 	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), SBF_DEV,	&sb_init_generic,	"Sound Blaster 16" },
 	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), SBF_DEV,	&sb_init_generic,	"Sound Blaster 16" },
-	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0,	&sb_init_awe,		"Sound Blaster 32" },
-	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0039), 0,	&sb_init_awe,		"Sound Blaster AWE 32" },
-	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x009D), 0,	&sb_init_awe,		"Sound Blaster AWE 64" },
-	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x00C5), 0,	&sb_init_awe,		"Sound Blaster AWE 64" },
-	{ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x00E4), 0,	&sb_init_awe,		"Sound Blaster AWE 64" },
 	{ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), SBF_DEV,	&sb_init_ess,		"ESS 1688" },
 	{ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), SBF_DEV,	&sb_init_ess,		"ESS 1868" },
 	{ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), SBF_DEV,	&sb_init_ess,		"ESS 1868" },
 	{ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), SBF_DEV,	&sb_init_ess,		"ESS 1869" },
 	{ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), SBF_DEV,	&sb_init_ess,		"ESS 1878" },
 	{ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), SBF_DEV,	&sb_init_ess,		"ESS 1879" },
-	{ISAPNP_VENDOR('C','M','I'), ISAPNP_FUNCTION(0x0001), 0,	&sb_init_cmi,		"CMI 8330 SoundPRO" },
-	{ISAPNP_VENDOR('R','W','B'), ISAPNP_FUNCTION(0x1688), 0,	&sb_init_diamond,	"Diamond DT0197H" },
 	{0}
 };
 
-static int __init sb_init_isapnp(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, struct pci_dev *card, int slot)
+static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, struct pci_dev *card, int slot)
 {
 	struct pci_dev *idev = NULL;
 
 	/* You missed the init func? That's bad. */
-	if(isapnp_sb_list[slot].initfunc)
+	if(sb_isapnp_list[slot].initfunc)
 	{
-		char *busname = bus->name[0] ? bus->name : isapnp_sb_list[slot].name;
+		char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name;
 
 		printk(KERN_INFO "sb: %s detected\n", busname); 
 
 		/* Initialize this baby. */
 
-		if((idev = isapnp_sb_list[slot].initfunc(bus, card, hw_config, mpu_config)))
+		if((idev = sb_isapnp_list[slot].initfunc(bus, card, hw_config, mpu_config)))
 		{
 			/* We got it. */
 
@@ -600,12 +493,12 @@
    Should this be fixed? - azummo
 */
 
-int __init sb_probe_isapnp(struct address_info *hw_config, struct address_info *mpu_config) 
+int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config) 
 {
 	int i;
 
-	/* Count entries in isapnp_sb_list */
-	for (i = 0; isapnp_sb_list[i].vendor != 0; i++);
+	/* Count entries in sb_isapnp_list */
+	for (i = 0; sb_isapnp_list[i].vendor != 0; i++);
 
 	/* Check and adjust isapnpjump */
 	if( isapnpjump < 0 || isapnpjump > ( i - 1 ) )
@@ -614,18 +507,18 @@
 		isapnpjump = 0;
 	}
 	
-	for (i = isapnpjump; isapnp_sb_list[i].vendor != 0; i++) {
+	for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) {
 
-		if(!(isapnp_sb_list[i].flags & SBF_DEV))
+		if(!(sb_isapnp_list[i].flags & SBF_DEV))
 		{
 			struct pci_bus *bus = NULL;
 				
 			while ((bus = isapnp_find_card(
-					isapnp_sb_list[i].vendor,
-					isapnp_sb_list[i].function,
+					sb_isapnp_list[i].vendor,
+					sb_isapnp_list[i].function,
 					bus))) {
 	
-				if(sb_init_isapnp(hw_config, mpu_config, bus, NULL, i))
+				if(sb_isapnp_init(hw_config, mpu_config, bus, NULL, i))
 					return 0;
 			}
 		}
@@ -635,18 +528,18 @@
 	 *  that matches any entry marked with SBF_DEV in the table.
 	 */
 
-	for (i = isapnpjump; isapnp_sb_list[i].vendor != 0; i++) {
+	for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) {
 
-		if(isapnp_sb_list[i].flags & SBF_DEV)
+		if(sb_isapnp_list[i].flags & SBF_DEV)
 		{
 			struct pci_dev *card = NULL;
 
 			while ((card = isapnp_find_dev(NULL,
-					isapnp_sb_list[i].vendor,
-					isapnp_sb_list[i].function,
+					sb_isapnp_list[i].vendor,
+					sb_isapnp_list[i].function,
 					card))) {
 
-				if(sb_init_isapnp(hw_config, mpu_config, card->bus, card, i))
+				if(sb_isapnp_init(hw_config, mpu_config, card->bus, card, i))
 					return 0;
 			}
 		}
@@ -665,7 +558,7 @@
 	*/
 
 #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE			
-	if(isapnp && (sb_probe_isapnp(&cfg, &cfg_mpu) < 0) ) {
+	if(isapnp && (sb_isapnp_probe(&cfg, &cfg_mpu) < 0) ) {
 		printk(KERN_NOTICE "sb_card: No ISAPnP cards found, trying standard ones...\n");
 		isapnp = 0;
 	}
@@ -707,11 +600,13 @@
 		unload_sbmpu(&cfg_mpu);
 	SOUND_LOCK_END;
 
-	if(sb_dev)	sb_dev->deactivate(sb_dev);
-	if(jp_dev)	jp_dev->deactivate(jp_dev);
-	if(wt_dev)	wt_dev->deactivate(wt_dev);
-	if(mpu_dev)	mpu_dev->deactivate(mpu_dev);
-	if(wss_dev)	wss_dev->deactivate(wss_dev);
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE			
+	if(activated)
+	{
+		if(sb_dev) 	sb_dev->deactivate(sb_dev);
+		if(mpu_dev) 	mpu_dev->deactivate(mpu_dev);
+	}
+#endif
 }
 
 module_init(init_sb);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.3.99-pre2/linux/drivers/sound/sb_common.c	Sun Mar 19 18:35:30 2000
+++ linux/drivers/sound/sb_common.c	Mon Mar 20 07:40:59 2000
@@ -908,13 +908,10 @@
 		}
 		if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI))
 		{
-			extern int sbmixnum;
-			
 			if (devc->irq > 0)
 				free_irq(devc->irq, devc);
 
-			sound_unload_mixerdev(devc->my_mixerdev);
-			sbmixnum--;
+			sb_mixer_unload(devc);
 			/* We don't have to do this bit any more the UART401 is its own
 				master  -- Krzysztof Halasa */
 			/* But we have to do it, if UART401 is not detected */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.3.99-pre2/linux/drivers/sound/sb_mixer.c	Thu Mar  2 14:36:23 2000
+++ linux/drivers/sound/sb_mixer.c	Mon Mar 20 07:40:59 2000
@@ -740,3 +740,9 @@
 	sb_mixer_reset(devc);
 	return 1;
 }
+
+void sb_mixer_unload(sb_devc *devc)
+{
+	sound_unload_mixerdev(devc->my_mixerdev);
+	sbmixnum--;
+}
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/Config.in linux/drivers/usb/Config.in
--- v2.3.99-pre2/linux/drivers/usb/Config.in	Sun Mar 19 18:35:30 2000
+++ linux/drivers/usb/Config.in	Tue Mar 21 12:31:12 2000
@@ -43,6 +43,7 @@
    dep_tristate '  USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB
    dep_tristate '  USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	   dep_tristate '  USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB
       dep_tristate '  USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB m
       if [ "$CONFIG_USB_STORAGE" != "n" ]; then
          bool '    USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/Makefile linux/drivers/usb/Makefile
--- v2.3.99-pre2/linux/drivers/usb/Makefile	Sun Mar 19 18:35:30 2000
+++ linux/drivers/usb/Makefile	Tue Mar 21 12:31:12 2000
@@ -74,6 +74,7 @@
 obj-$(CONFIG_USB_CPIA)		+= cpia.o
 obj-$(CONFIG_USB_IBMCAM)	+= ibmcam.o
 obj-$(CONFIG_USB_DC2XX)		+= dc2xx.o
+obj-$(CONFIG_USB_MDC800)   += mdc800.o
 obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 obj-$(CONFIG_USB_DABUSB)	+= dabusb.o
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/hid-debug.h linux/drivers/usb/hid-debug.h
--- v2.3.99-pre2/linux/drivers/usb/hid-debug.h	Sat Feb 12 11:22:11 2000
+++ linux/drivers/usb/hid-debug.h	Sun Mar 19 18:29:40 2000
@@ -105,6 +105,7 @@
     {0, 0x35, "Tap"},
     {0, 0x39, "TabletFunctionKey"},
     {0, 0x3a, "ProgramChangeKey"},
+    {0, 0x3c, "Invert"},
     {0, 0x42, "TipSwitch"},
     {0, 0x43, "SecondaryTipSwitch"},
     {0, 0x44, "BarrelSwitch"},
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/hid.c linux/drivers/usb/hid.c
--- v2.3.99-pre2/linux/drivers/usb/hid.c	Fri Mar 10 16:40:44 2000
+++ linux/drivers/usb/hid.c	Sun Mar 19 18:29:40 2000
@@ -802,6 +802,11 @@
 
 				case 0x30: /* TipPressure */
 
+					if (!test_bit(BTN_TOUCH, input->keybit)) {
+						device->quirks |= HID_QUIRK_NOTOUCH;
+						set_bit(EV_KEY, input->evbit);
+						set_bit(BTN_TOUCH, input->keybit);
+					}
 					usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; 
 					usage->code = ABS_PRESSURE;
 					clear_bit(usage->code, bit);
@@ -817,10 +822,18 @@
 					}
 					break;
 
+				case 0x3c: /* Invert */
+
+					usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+					usage->code = BTN_TOOL_RUBBER;
+					clear_bit(usage->code, bit);
+					break;
+
 				case 0x33: /* Touch */
 				case 0x42: /* TipSwitch */
 				case 0x43: /* TipSwitch2 */
 
+					device->quirks &= ~HID_QUIRK_NOTOUCH;
 					usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
 					usage->code = BTN_TOUCH;
 					clear_bit(usage->code, bit);
@@ -930,7 +943,7 @@
 	}
 }
 
-static void hid_process_event(struct input_dev *input, int flags, struct hid_usage *usage, __s32 value)
+static void hid_process_event(struct input_dev *input, int *quirks, struct hid_field *field, struct hid_usage *usage, __s32 value)
 {
 	hid_dump_input(usage, value);
 
@@ -941,9 +954,30 @@
 		return;
 	}
 
+	if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
+		*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
+		if (value) {
+			input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
+			return;
+		}
+		input_event(input, usage->type, usage->code, 0);
+		input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
+		int a = field->logical_minimum;
+		int b = field->logical_maximum;
+		input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
+	}
+
 	input_event(input, usage->type, usage->code, value);
 
-	if ((flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
+	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
 		input_event(input, usage->type, usage->code, 0);
 }
 
@@ -986,19 +1020,21 @@
 			} else {
 				if (value[n] == field->value[n]) continue;
 			}
-			hid_process_event(&dev->input, field->flags, &field->usage[n], value[n]);
+			hid_process_event(&dev->input, &dev->quirks, field, &field->usage[n], value[n]);
 
 		} else {
 
 			if (field->value[n] >= min && field->value[n] <= max			/* non-NULL value */
 				&& field->usage[field->value[n] - min].hid			/* nonzero usage */
 				&& search(value, field->value[n], count))
-					hid_process_event(&dev->input, field->flags, &field->usage[field->value[n] - min], 0);
+					hid_process_event(&dev->input, &dev->quirks, field,
+						&field->usage[field->value[n] - min], 0);
 
 			if (value[n] >= min && value[n] <= max					/* non-NULL value */
 				&& field->usage[value[n] - min].hid				/* nonzero usage */
 				&& search(field->value, value[n], count))
-					hid_process_event(&dev->input, field->flags, &field->usage[value[n] - min], 1);
+					hid_process_event(&dev->input, &dev->quirks,
+						field, &field->usage[value[n] - min], 1);
 		}
 	}
 
@@ -1261,14 +1297,18 @@
 
 #define USB_VENDOR_ID_WACOM		0x056a
 #define USB_DEVICE_ID_WACOM_GRAPHIRE	0x0010
-#define USB_DEVICE_ID_WACOM_INTUOS	0x0021
+#define USB_DEVICE_ID_WACOM_INTUOS	0x0020
 
 struct hid_blacklist {
 	__u16 idVendor;
 	__u16 idProduct;
 } hid_blacklist[] = {
-	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS },
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1},
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2},
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3},
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4},
 	{ 0, 0 }
 };
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/hid.h linux/drivers/usb/hid.h
--- v2.3.99-pre2/linux/drivers/usb/hid.h	Thu Mar  2 14:36:23 2000
+++ linux/drivers/usb/hid.h	Sun Mar 19 18:29:40 2000
@@ -179,6 +179,13 @@
 #define HID_FEATURE_REPORT	2
 
 /*
+ * HID device quirks.
+ */
+
+#define HID_QUIRK_INVERT	0x01
+#define HID_QUIRK_NOTOUCH	0x02
+
+/*
  * This is the global enviroment of the parser. This information is
  * persistent for main-items. The global enviroment can be saved and
  * restored with PUSH/POP statements.
@@ -285,6 +292,7 @@
 	struct urb urb;							/* USB URB structure */
 	struct urb urbout;						/* Output URB */
 	struct input_dev input;						/* input device structure */
+	int quirks;							/* Various nasty tricks the device can pull on us */
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/keybdev.c linux/drivers/usb/keybdev.c
--- v2.3.99-pre2/linux/drivers/usb/keybdev.c	Sat Feb 26 22:31:50 2000
+++ linux/drivers/usb/keybdev.c	Sun Mar 19 18:29:40 2000
@@ -58,7 +58,8 @@
 
 #endif
 
-struct input_handler keybdev_handler;
+static struct input_handler keybdev_handler;
+static int keybdev_alt = 0;
 
 void keybdev_ledfunc(unsigned int led)
 {
@@ -93,14 +94,22 @@
 		handle_scancode(0x1d, down);
 		handle_scancode(0x45, down);
 	} else if (code >= 96) {
-		handle_scancode(0xe0, 1);
-		handle_scancode(keybdev_x86_e0s[code - 96], down);
-		if (code == 99) {
+		if (code == 99 && keybdev_alt) {
+			 handle_scancode(84, down);
+		} else {
 			handle_scancode(0xe0, 1);
-			handle_scancode(0x37, down);
+			handle_scancode(keybdev_x86_e0s[code - 96], down);
+			if (code == 99) {
+				handle_scancode(0xe0, 1);
+				handle_scancode(0x37, down);
+			}
 		}
+	} else if (code == 84) {
+		handle_scancode(43, down);
 	} else handle_scancode(code, down);
 
+	if (code == 56 || code == 100) keybdev_alt = down;
+
 #elif CONFIG_ADB_KEYBOARD
 
 	if (code < 128 && keybdev_mac_codes[code]) 
@@ -152,7 +161,7 @@
 	kfree(handle);
 }
 	
-struct input_handler keybdev_handler = {
+static struct input_handler keybdev_handler = {
 	event:		keybdev_event,
 	connect:	keybdev_connect,
 	disconnect:	keybdev_disconnect,
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/mdc800.c linux/drivers/usb/mdc800.c
--- v2.3.99-pre2/linux/drivers/usb/mdc800.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/mdc800.c	Tue Mar 21 12:31:12 2000
@@ -0,0 +1,931 @@
+/*
+ * copyright (C) 1999/2000 by Henning Zabel <henning@uni-paderborn.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.
+ *
+ * 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.
+ */
+
+
+/*
+ *	USB-Kernel Driver for the Mustek MDC800 Digital Camera
+ *	(c) 1999/2000 Henning Zabel <henning@uni-paderborn.de>
+ *
+ *
+ *	The driver brings the USB functions of the MDC800 to Linux.
+ * To use the Camera you must support the USB Protocoll of the camera
+ * to the Kernel Node.
+ * The Driver uses a misc device Node. Create it with :
+ * mknod /dev/mustek c 10 171
+ *
+ * The driver supports only one camera.
+ *
+ * version 0.7.1
+ * The Init und Exit Module Function are updated.
+ * (01/03/2000)
+ *
+ * version 0.7.0
+ * Rewrite of the driver : The driver now uses URB's. The old stuff
+ * has been removed.
+ *
+ * version 0.6.0
+ * Rewrite of this driver: The Emulation of the rs232 protocoll
+ * has been removed from the driver. A special executeCommand function
+ * for this driver is included to gphoto.
+ * The driver supports two kind of communication to bulk endpoints.
+ * Either with the dev->bus->ops->bulk... or with callback function.
+ * (09/11/1999)
+ *
+ * version 0.5.0:
+ *	first Version that gets a version number. Most of the needed
+ * functions work.
+ * (20/10/1999)
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+
+#include <linux/usb.h>
+
+#define VERSION 		"0.7.1"
+#define RELEASE_DATE "(01/03/2000)"
+
+/* Vendor and Product Information */
+#define MDC800_VENDOR_ID 	0x055f
+#define MDC800_PRODUCT_ID	0xa800
+
+/* Timeouts (msec) */
+#define TO_READ_FROM_IRQ 			4000
+#define TO_GET_READY				2000
+#define TO_DOWNLOAD_GET_READY			1500
+#define TO_DOWNLOAD_GET_BUSY			1500
+#define TO_WRITE_GET_READY			3000
+#define TO_DEFAULT_COMMAND			5000
+
+/* Minor Number of the device (create with mknod /dev/mustek c 10 171) */
+#define MDC800_DEVICE_MINOR 171
+
+
+/**************************************************************************
+	Data and structs
+***************************************************************************/
+
+
+typedef enum {
+	NOT_CONNECTED, READY, WORKING, DOWNLOAD
+} mdc800_state;
+
+
+/* Data for the driver */
+struct mdc800_data
+{
+	struct usb_device *	dev;			// Device Data
+	mdc800_state 		state;
+
+	unsigned int		endpoint [4];
+
+	purb_t			irq_urb;
+	wait_queue_head_t	irq_wait;
+	char*			irq_urb_buffer;
+
+	int			camera_busy;		 // is camera busy ?
+	int 			camera_request_ready; // Status to synchronize with irq
+	char 			camera_response [8];	 // last Bytes send after busy
+
+	purb_t   		write_urb;
+	char*			write_urb_buffer;
+	wait_queue_head_t	write_wait;
+
+
+	purb_t	   		download_urb;
+	char*			download_urb_buffer;
+	wait_queue_head_t	download_wait;
+	int			download_left;		// Bytes left to download ?
+
+
+	/* Device Data */
+	char			out [64];		// Answer Buffer
+	int 			out_ptr;		// Index to the first not readen byte
+	int			out_count;		// Bytes in the buffer
+
+	int			open;			// Camera device open ?
+	int			rw_lock;		// Block read <-> write
+
+	char 			in [8];			// Command Input Buffer
+	int  			in_count;
+
+	int			pic_index;		// Cache for the Imagesize (-1 for nothing cached )
+	int			pic_len;
+};
+
+
+/* Specification of the Endpoints */
+static struct usb_endpoint_descriptor mdc800_ed [4] =
+{
+	{ 0,0, 0x01, 0x02,  8, 0,0,0 },
+	{ 0,0, 0x82, 0x03,  8, 0,0,0 },
+	{ 0,0, 0x03, 0x02, 64, 0,0,0 },
+	{ 0,0, 0x84, 0x02, 64, 0,0,0 }
+};
+
+
+/* The Variable used by the driver */
+static struct mdc800_data* mdc800=0;
+
+
+/***************************************************************************
+	The USB Part of the driver
+****************************************************************************/
+
+static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b)
+{
+	return (
+		   ( a->bEndpointAddress == b->bEndpointAddress )
+		&& ( a->bmAttributes     == b->bmAttributes     )
+		&& ( a->wMaxPacketSize   == b->wMaxPacketSize   )
+	);
+}
+
+
+/*
+ * Checks wether the camera responds busy
+ */
+static int mdc800_isBusy (char* ch)
+{
+	int i=0;
+	while (i<8)
+	{
+		if (ch [i] != (char)0x99)
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+
+/*
+ * Checks wether the Camera is ready
+ */
+static int mdc800_isReady (char *ch)
+{
+	int i=0;
+	while (i<8)
+	{
+		if (ch [i] != (char)0xbb)
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+
+
+/*
+ * USB IRQ Handler for InputLine
+ */
+static void mdc800_usb_irq (struct urb *urb)
+{
+	int data_received=0, wake_up;
+	unsigned char* b=urb->transfer_buffer;
+	struct mdc800_data* mdc800=urb->context;
+
+	if (urb->status >= 0)
+	{
+
+		//dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+		if (mdc800_isBusy (b))
+		{
+			if (!mdc800->camera_busy)
+			{
+				mdc800->camera_busy=1;
+				dbg ("gets busy");
+			}
+		}
+		else
+		{
+			if (mdc800->camera_busy && mdc800_isReady (b))
+			{
+				mdc800->camera_busy=0;
+				dbg ("gets ready");
+			}
+		}
+		if (!(mdc800_isBusy (b) || mdc800_isReady (b)))
+		{
+			/* Store Data in camera_answer field */
+			dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+			memcpy (mdc800->camera_response,b,8);
+			data_received=1;
+		}
+	}
+	wake_up= ( mdc800->camera_request_ready > 0 )
+				&&
+				(
+					((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
+				||
+					((mdc800->camera_request_ready == 2) && data_received)
+				||
+					((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
+				||
+					(urb->status < 0)
+				);
+
+	if (wake_up)
+	{
+		mdc800->camera_request_ready=0;
+		wake_up_interruptible (&mdc800->irq_wait);
+	}
+}
+
+
+/*
+ * Waits a while until the irq responds that camera is ready
+ *
+ *  mode : 0: Wait for camera gets ready
+ *         1: Wait for receiving data
+ *         2: Wait for camera gets busy
+ *
+ * msec: Time to wait
+ */
+static int mdc800_usb_waitForIRQ (int mode, int msec)
+{
+	mdc800->camera_request_ready=1+mode;
+
+	interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000);
+
+	if (mdc800->camera_request_ready>0)
+	{
+		mdc800->camera_request_ready=0;
+		err ("timeout waiting for camera.");
+		return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * The write_urb callback function
+ */
+static void mdc800_usb_write_notify (struct urb *urb)
+{
+	struct mdc800_data* mdc800=urb->context;
+
+	if (urb->status != 0)
+	{
+		err ("writing command fails (status=%i)", urb->status);
+	}
+	mdc800->state=READY;
+	wake_up_interruptible (&mdc800->write_wait);
+}
+
+
+/*
+ * The download_urb callback function
+ */
+static void mdc800_usb_download_notify (struct urb *urb)
+{
+	struct mdc800_data* mdc800=urb->context;
+
+	if (urb->status == 0)
+	{
+		/* Fill output buffer with these data */
+		memcpy (mdc800->out,  urb->transfer_buffer, 64);
+		mdc800->out_count=64;
+		mdc800->out_ptr=0;
+		mdc800->download_left-=64;
+		if (mdc800->download_left == 0)
+		{
+			mdc800->state=READY;
+		}
+	}
+	else
+	{
+		err ("request bytes fails (status:%i)", urb->status);
+		mdc800->state=READY;
+	}
+	wake_up_interruptible (&mdc800->download_wait);
+}
+
+
+/***************************************************************************
+	Probing for the Camera
+ ***************************************************************************/
+
+static struct usb_driver mdc800_usb_driver;
+
+/*
+ * Callback to search the Mustek MDC800 on the USB Bus
+ */
+static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum )
+{
+	int 										i,j;
+	struct usb_interface_descriptor	*intf_desc;
+	int irq_interval=0;
+
+	dbg ("(mdc800_usb_probe) called.");
+
+	if (mdc800->dev != 0)
+	{
+		warn ("only one Mustek MDC800 is supported.");
+		return 0;
+	}
+
+	if (dev->descriptor.idVendor != MDC800_VENDOR_ID)
+		return 0;
+	if (dev->descriptor.idProduct != MDC800_PRODUCT_ID)
+		return 0;
+
+	if (dev->descriptor.bNumConfigurations != 1)
+	{
+		err ("probe fails -> wrong Number of Configuration");
+		return 0;
+	}
+	intf_desc=&dev->actconfig->interface[ifnum].altsetting[0];
+
+	if (
+			( intf_desc->bInterfaceClass != 0xff )
+		||	( intf_desc->bInterfaceSubClass != 0 )
+		|| ( intf_desc->bInterfaceProtocol != 0 )
+		|| ( intf_desc->bNumEndpoints != 4)
+	)
+	{
+		err ("probe fails -> wrong Interface");
+		return 0;
+	}
+
+	/* Check the Endpoints */
+	for (i=0; i<4; i++)
+	{
+		mdc800->endpoint[i]=-1;
+		for (j=0; j<4; j++)
+		{
+			if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i]))
+			{
+				mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ;
+				if (i==1)
+				{
+					irq_interval=intf_desc->endpoint [j].bInterval;
+				}
+
+				continue;
+			}
+		}
+		if (mdc800->endpoint[i] == -1)
+		{
+			err ("probe fails -> Wrong Endpoints.");
+			return 0;
+		}
+	}
+
+
+	usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800);
+	if (usb_set_interface (dev, ifnum, 0) < 0)
+	{
+		err ("MDC800 Configuration fails.");
+		return 0;
+	}
+
+	info ("Found Mustek MDC800 on USB.");
+
+	mdc800->dev=dev;
+	mdc800->state=READY;
+
+	/* Setup URB Structs */
+	FILL_INT_URB (
+		mdc800->irq_urb,
+		mdc800->dev,
+		usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]),
+		mdc800->irq_urb_buffer,
+		8,
+		mdc800_usb_irq,
+		mdc800,
+		irq_interval
+	);
+
+	FILL_BULK_URB (
+		mdc800->write_urb,
+		mdc800->dev,
+		usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]),
+		mdc800->write_urb_buffer,
+		8,
+		mdc800_usb_write_notify,
+		mdc800
+	);
+
+	FILL_BULK_URB (
+		mdc800->download_urb,
+		mdc800->dev,
+		usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]),
+		mdc800->download_urb_buffer,
+		64,
+		mdc800_usb_download_notify,
+		mdc800
+	);
+
+	return mdc800;
+}
+
+
+/*
+ * Disconnect USB device (maybe the MDC800)
+ */
+static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr)
+{
+	struct mdc800_data* mdc800=(struct mdc800_data*) ptr;
+
+	dbg ("(mdc800_usb_disconnect) called");
+
+	if (mdc800->state == NOT_CONNECTED)
+		return;
+
+	mdc800->state=NOT_CONNECTED;
+	mdc800->open=0;
+	mdc800->rw_lock=0;
+
+	usb_unlink_urb (mdc800->irq_urb);
+	usb_unlink_urb (mdc800->write_urb);
+	usb_unlink_urb (mdc800->download_urb);
+
+	usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]);
+
+	mdc800->dev=0;
+	info ("Mustek MDC800 disconnected from USB.");
+}
+
+
+/***************************************************************************
+	The Misc device Part (file_operations)
+****************************************************************************/
+
+/*
+ * This Function calc the Answersize for a command.
+ */
+static int mdc800_getAnswerSize (char command)
+{
+	switch ((unsigned char) command)
+	{
+		case 0x2a:
+		case 0x49:
+		case 0x51:
+		case 0x0d:
+		case 0x20:
+		case 0x07:
+		case 0x01:
+		case 0x25:
+		case 0x00:
+			return 8;
+
+		case 0x05:
+		case 0x3e:
+			return mdc800->pic_len;
+
+		case 0x09:
+			return 4096;
+
+		default:
+			return 0;
+	}
+}
+
+
+/*
+ * Init the device: (1) alloc mem (2) Increase MOD Count ..
+ */
+static int mdc800_device_open (struct inode* inode, struct file *file)
+{
+	int retval=0;
+	if (mdc800->state == NOT_CONNECTED)
+		return -EBUSY;
+
+	if (mdc800->open)
+		return -EBUSY;
+
+	mdc800->rw_lock=0;
+	mdc800->in_count=0;
+	mdc800->out_count=0;
+	mdc800->out_ptr=0;
+	mdc800->pic_index=0;
+	mdc800->pic_len=-1;
+	mdc800->download_left=0;
+
+	mdc800->camera_busy=0;
+	mdc800->camera_request_ready=0;
+
+	retval=0;
+	if (usb_submit_urb (mdc800->irq_urb))
+	{
+		err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
+		return -EIO;
+	}
+
+	MOD_INC_USE_COUNT;
+	mdc800->open=1;
+
+	dbg ("Mustek MDC800 device opened.");
+	return 0;
+}
+
+
+/*
+ * Close the Camera and release Memory
+ */
+static int mdc800_device_release (struct inode* inode, struct file *file)
+{
+	int retval=0;
+	dbg ("Mustek MDC800 device closed.");
+
+	if (mdc800->open && (mdc800->state != NOT_CONNECTED))
+	{
+		mdc800->open=0;
+		usb_unlink_urb (mdc800->irq_urb);
+		usb_unlink_urb (mdc800->write_urb);
+		usb_unlink_urb (mdc800->download_urb);
+	}
+	else
+	{
+		retval=-EIO;
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	return retval;
+}
+
+
+/*
+ * The Device read callback Function
+ */
+static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos)
+{
+	int   left=len, sts=len; /* single transfer size */
+	char* ptr=buf;
+
+	if (mdc800->state == NOT_CONNECTED)
+		return -EBUSY;
+
+	if (!mdc800->open || mdc800->rw_lock)
+		return -EBUSY;
+	mdc800->rw_lock=1;
+
+	while (left)
+	{
+		if (signal_pending (current)) {
+			mdc800->rw_lock=0;
+			return -EINTR;
+		}
+
+		sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
+
+		if (sts <= 0)
+		{
+			/* Too less Data in buffer */
+			if (mdc800->state == DOWNLOAD)
+			{
+				mdc800->out_count=0;
+				mdc800->out_ptr=0;
+
+				/* Download -> Request new bytes */
+				if (usb_submit_urb (mdc800->download_urb))
+				{
+					err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
+					mdc800->state=READY;
+					mdc800->rw_lock=0;
+					return len-left;
+				}
+				interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000);
+				if (mdc800->download_urb->status != 0)
+				{
+					err ("requesting bytes fails (status=%i)",mdc800->download_urb->status);
+					mdc800->state=READY;
+					mdc800->rw_lock=0;
+					return len-left;
+				}
+			}
+			else
+			{
+				/* No more bytes -> that's an error*/
+				mdc800->rw_lock=0;
+				return -EIO;
+			}
+		}
+		else
+		{
+			/* memcpy Bytes */
+			memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts);
+			ptr+=sts;
+			left-=sts;
+			mdc800->out_ptr+=sts;
+		}
+	}
+
+	mdc800->rw_lock=0;
+	return len-left;
+}
+
+
+/*
+ * The Device write callback Function
+ * If a 8Byte Command is received, it will be send to the camera.
+ * After this the driver initiates the request for the answer or
+ * just waits until the camera becomes ready.
+ */
+static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
+{
+	int i=0;
+
+	if (mdc800->state != READY)
+		return -EBUSY;
+
+	if (!mdc800->open || mdc800->rw_lock)
+		return -EBUSY;
+	mdc800->rw_lock=1;
+
+	while (i<len)
+	{
+		if (signal_pending (current)) {
+			mdc800->rw_lock=0;
+			return -EINTR;
+		}
+
+		/* check for command start */
+		if (buf [i] == (char) 0x55)
+		{
+			mdc800->in_count=0;
+			mdc800->out_count=0;
+			mdc800->out_ptr=0;
+			mdc800->download_left=0;
+		}
+
+		/* save command byte */
+		if (mdc800->in_count < 8)
+		{
+			mdc800->in[mdc800->in_count]=buf[i];
+			mdc800->in_count++;
+		}
+		else
+		{
+			err ("Command is to long !\n");
+			mdc800->rw_lock=0;
+			return -EIO;
+		}
+
+		/* Command Buffer full ? -> send it to camera */
+		if (mdc800->in_count == 8)
+		{
+			int answersize;
+
+			mdc800_usb_waitForIRQ (0,TO_GET_READY);
+
+			answersize=mdc800_getAnswerSize (mdc800->in[1]);
+
+			mdc800->state=WORKING;
+			memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8);
+			if (usb_submit_urb (mdc800->write_urb))
+			{
+				err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
+				mdc800->rw_lock=0;
+				mdc800->state=READY;
+				return -EIO;
+			}
+			interruptible_sleep_on_timeout (&mdc800->write_wait, TO_DEFAULT_COMMAND*HZ/1000);
+			if (mdc800->state == WORKING)
+			{
+				usb_unlink_urb (mdc800->write_urb);
+				mdc800->state=READY;
+				mdc800->rw_lock=0;
+				return -EIO;
+			}
+
+			switch ((unsigned char) mdc800->in[1])
+			{
+				case 0x05: /* Download Image */
+				case 0x3e: /* Take shot in Fine Mode (WCam Mode) */
+					if (mdc800->pic_len < 0)
+					{
+						err ("call 0x07 before 0x05,0x3e");
+						mdc800->state=READY;
+						mdc800->rw_lock=0;
+						return -EIO;
+					}
+					mdc800->pic_len=-1;
+
+				case 0x09: /* Download Thumbnail */
+					mdc800->download_left=answersize+64;
+					mdc800->state=DOWNLOAD;
+					mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY);
+					break;
+
+
+				default:
+					if (answersize)
+					{
+
+						if (!mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
+						{
+							err ("requesting answer from irq fails");
+							mdc800->state=READY;
+							mdc800->rw_lock=0;
+							return -EIO;
+						}
+
+						/* Write dummy data, (this is ugly but part of the USB Protokoll */
+						/* if you use endpoint 1 as bulk and not as irq */
+						memcpy (mdc800->out, mdc800->camera_response,8);
+
+						/* This is the interpreted answer */
+						memcpy (&mdc800->out[8], mdc800->camera_response,8);
+
+						mdc800->out_ptr=0;
+						mdc800->out_count=16;
+
+						/* Cache the Imagesize, if command was getImageSize */
+						if (mdc800->in [1] == (char) 0x07)
+						{
+							mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2];
+
+							dbg ("cached imagesize = %i",mdc800->pic_len);
+						}
+
+					}
+					else
+					{
+						if (!mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
+						{
+							err ("Command Timeout.");
+							mdc800->rw_lock=0;
+							mdc800->state=READY;
+							return -EIO;
+						}
+					}
+					mdc800->state=READY;
+					break;
+			}
+		}
+		i++;
+	}
+	mdc800->rw_lock=0;
+	return i;
+}
+
+
+/***************************************************************************
+	Init and Cleanup this driver (Structs and types)
+****************************************************************************/
+
+
+/*
+ * USB Driver Struct for this device
+ */
+static struct usb_driver mdc800_usb_driver =
+{
+	"mdc800",
+	mdc800_usb_probe,
+	mdc800_usb_disconnect,
+	{ 0,0 },
+	0,
+	0
+};
+
+
+/* File Operations of this drivers */
+static struct file_operations mdc800_device_ops =
+{
+	0,					/* llseek */
+	mdc800_device_read,
+	mdc800_device_write,
+	0,					/* readdir */
+	0,					/* poll */
+	0,					/* ioctl, this can be used to detect USB ! */
+	0,					/* mmap */
+	mdc800_device_open,
+	0,					/* flush */
+	mdc800_device_release,
+	0,					/* async */
+	0,					/* fasync */
+	0,					/* check_media_change */
+//	0,					/* revalidate */
+//	0					/* lock */
+};
+
+
+/*
+ * The Misc Device Configuration Struct
+ */
+static struct miscdevice mdc800_device =
+{
+	MDC800_DEVICE_MINOR,
+	"USB Mustek MDC800 Camera",
+	&mdc800_device_ops
+};
+
+
+/************************************************************************
+	Init and Cleanup this driver (Main Functions)
+*************************************************************************/
+
+#define try(A)           if ((A) == 0) goto cleanup_on_fail;
+#define try_free_mem(A)  if (A != 0) { kfree (A); A=0; }
+#define try_free_urb(A)  if (A != 0) { usb_free_urb (A); A=0; }
+
+int __init usb_mdc800_init (void)
+{
+	/* Allocate Memory */
+	try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
+
+	mdc800->dev=0;
+	mdc800->open=0;
+	mdc800->state=NOT_CONNECTED;
+	memset(mdc800, 0, sizeof(struct mdc800_data));
+
+	init_waitqueue_head (&mdc800->irq_wait);
+	init_waitqueue_head (&mdc800->write_wait);
+	init_waitqueue_head (&mdc800->download_wait);
+
+	try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
+	try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
+	try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL));
+
+	try (mdc800->irq_urb=usb_alloc_urb (0));
+	try (mdc800->download_urb=usb_alloc_urb (0));
+	try (mdc800->write_urb=usb_alloc_urb (0));
+
+	/* Register the driver */
+	if (usb_register (&mdc800_usb_driver) < 0)
+		goto cleanup_on_fail;
+	if (misc_register (&mdc800_device) < 0)
+		goto cleanup_on_misc_register_fail;
+
+	info ("Mustek Digital Camera Driver " VERSION " (MDC800)");
+	info (RELEASE_DATE " Henning Zabel <henning@uni-paderborn.de>");
+
+	return 0;
+
+	/* Clean driver up, when something fails */
+
+cleanup_on_misc_register_fail:
+	usb_deregister (&mdc800_usb_driver);
+
+cleanup_on_fail:
+
+	if (mdc800 != 0)
+	{
+		err ("can't alloc memory!");
+
+		try_free_mem (mdc800->download_urb_buffer);
+		try_free_mem (mdc800->write_urb_buffer);
+		try_free_mem (mdc800->irq_urb_buffer);
+
+		try_free_urb (mdc800->write_urb);
+		try_free_urb (mdc800->download_urb);
+		try_free_urb (mdc800->irq_urb);
+
+		kfree (mdc800);
+	}
+	mdc800=0;
+	return -1;
+}
+
+
+void __exit usb_mdc800_cleanup (void)
+{
+	usb_deregister (&mdc800_usb_driver);
+	misc_deregister (&mdc800_device);
+
+	usb_free_urb (mdc800->irq_urb);
+	usb_free_urb (mdc800->download_urb);
+	usb_free_urb (mdc800->write_urb);
+
+	kfree (mdc800->irq_urb_buffer);
+	kfree (mdc800->write_urb_buffer);
+	kfree (mdc800->download_urb_buffer);
+
+	kfree (mdc800);
+	mdc800=0;
+}
+
+
+MODULE_AUTHOR ("Henning Zabel <henning@uni-paderborn.de>");
+MODULE_DESCRIPTION ("USB Driver for Mustek MDC800 Digital Camera");
+
+module_init (usb_mdc800_init);
+module_exit (usb_mdc800_cleanup);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/mousedev.c linux/drivers/usb/mousedev.c
--- v2.3.99-pre2/linux/drivers/usb/mousedev.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/mousedev.c	Sun Mar 19 18:29:40 2000
@@ -95,8 +95,8 @@
 						break;
 					case ABS_Y:
 						size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
-						list->dy += (value * CONFIG_MOUSEDEV_SCREEN_Y - list->oldy) / size;
-						list->oldy += list->dy * size;
+						list->dy -= (value * CONFIG_MOUSEDEV_SCREEN_Y - list->oldy) / size;
+						list->oldy -= list->dy * size;
 						break;
 				}
 				break;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
--- v2.3.99-pre2/linux/drivers/usb/pegasus.c	Tue Mar 14 19:10:40 2000
+++ linux/drivers/usb/pegasus.c	Tue Mar 21 12:30:20 2000
@@ -16,12 +16,9 @@
 #include <linux/usb.h>
 
 
-static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
+static const char *version = __FILE__ ": v0.3.5 2000/03/21 Written by Petko Manolov (petkan@spct.net)\n";
 
 
-#define	ADMTEK_VENDOR_ID		0x07a6
-#define	ADMTEK_DEVICE_ID_PEGASUS	0x0986
-
 #define	PEGASUS_MTU		1500
 #define PEGASUS_MAX_MTU		1536
 #define	PEGASUS_TX_TIMEOUT	(HZ*5)
@@ -38,6 +35,13 @@
 	unsigned char		ALIGN(intr_buff[8]);
 };
 
+struct usb_eth_dev {
+	char	*name;
+	__u16	vendor;
+	__u16	device;
+	void	*private;
+};
+
 static int loopback = 0;
 static int multicast_filter_limit = 32;
 
@@ -46,6 +50,16 @@
 MODULE_PARM(loopback, "i");
 
 
+static struct usb_eth_dev usb_dev_id[] = {
+	{ "D-Link DSB-650TX", 0x2001, 0x4001, NULL },
+	{ "Linksys USB100TX", 0x066b, 0x2203, NULL },
+	{ "SMC 202 USB Ethernet", 0x0707, 0x0200, NULL },
+	{ "ADMtek AN986 (Pegasus) USB Ethernet", 0x07a6, 0x0986, NULL },
+	{ "Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL },
+	{ NULL, 0, 0, NULL }
+};
+
+
 #define pegasus_get_registers(dev, indx, size, data)\
 	usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
 #define pegasus_set_registers(dev, indx, size, data)\
@@ -164,8 +178,8 @@
 		return 4;
 
 	if ((partmedia & 0x1f) != 1) {
-		err("party FAIL %x", partmedia);
-		return 5;
+		warn("party FAIL %x", partmedia);
+		/* return 5;	FIXME */ 
 	}
 
 	data[0] = 0xc9;
@@ -374,13 +388,25 @@
 	netif_wake_queue(net);
 }
 
+static int check_device_ids( __u16 vendor, __u16 product )
+{
+	int i=0;
+	
+	while ( usb_dev_id[i].name ) {
+		if ( (usb_dev_id[i].vendor == vendor) && 
+			(usb_dev_id[i].device == product) )
+			return 0;
+		i++;
+	}
+	return	1;
+}
+
 static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
 {
 	struct net_device *net;
 	struct pegasus *pegasus;
 
-	if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
-		dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+	if ( check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct) ) {
 		return NULL;
 	}
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/serial/Makefile-keyspan_pda_fw linux/drivers/usb/serial/Makefile-keyspan_pda_fw
--- v2.3.99-pre2/linux/drivers/usb/serial/Makefile-keyspan_pda_fw	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/serial/Makefile-keyspan_pda_fw	Mon Mar 20 08:28:25 2000
@@ -5,7 +5,7 @@
 
 all: keyspan_pda_fw.h
 
-%.asm: %.s
+%.asm: %.S
 	gcc -x assembler-with-cpp -P -E -o $@ $<
 
 %.hex: %.asm
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/serial/keyspan_pda.S linux/drivers/usb/serial/keyspan_pda.S
--- v2.3.99-pre2/linux/drivers/usb/serial/keyspan_pda.S	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/serial/keyspan_pda.S	Mon Mar 20 08:28:25 2000
@@ -0,0 +1,1124 @@
+/*  $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
+ * 
+ *  Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
+ *  the EzUSB microcontroller.
+ * 
+ *  (C) Copyright 2000 Brian Warner <warner@lothar.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.
+ * 
+ *  "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
+ *  company.
+ * 
+ *  This serial adapter is basically an EzUSB chip and an RS-232 line driver
+ *  in a little widget that has a DB-9 on one end and a USB plug on the other.
+ *  It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
+ *  as a baud-rate generator. The wiring is:
+ *   PC0/RxD0 <- rxd (DB9 pin 2)         PC4 <- dsr pin 6
+ *   PC1/TxD0 -> txd pin 3               PC5 <- ri  pin 9
+ *   PC2      -> rts pin 7               PC6 <- dcd pin 1
+ *   PC3      <- cts pin 8               PC7 -> dtr pin 4
+ *   PB1 -> line driver standby
+ *
+ *  The EzUSB register constants below come from their excellent documentation
+ *  and sample code (which used to be available at www.anchorchips.com, but
+ *  that has now been absorbed into Cypress' site and the CD-ROM contents
+ *  don't appear to be available online anymore). If we get multiple
+ *  EzUSB-based drivers into the kernel, it might be useful to pull them out
+ *  into a separate .h file.
+ * 
+ * THEORY OF OPERATION:
+ *
+ *   There are two 256-byte ring buffers, one for tx, one for rx.
+ *
+ *   EP2out is pure tx data. When it appears, the data is copied into the tx
+ *   ring and serial transmission is started if it wasn't already running. The
+ *   "tx buffer empty" interrupt may kick off another character if the ring
+ *   still has data. If the host is tx-blocked because the ring filled up,
+ *   it will request a "tx unthrottle" interrupt. If sending a serial character
+ *   empties the ring below the desired threshold, we set a bit that will send
+ *   up the tx unthrottle message as soon as the rx buffer becomes free.
+ *
+ *   EP2in (interrupt) is used to send both rx chars and rx status messages
+ *   (only "tx unthrottle" at this time) back up to the host. The first byte
+ *   of the rx message indicates data (0) or status msg (1). Status messages
+ *   are sent before any data.
+ *
+ *   Incoming serial characters are put into the rx ring by the serial
+ *   interrupt, and the EP2in buffer sent if it wasn't already in transit.
+ *   When the EP2in buffer returns, the interrupt prompts us to send more
+ *   rx chars (or status messages) if they are pending.
+ *
+ *   Device control happens through "vendor specific" control messages on EP0.
+ *   All messages are destined for the "Interface" (with the index always 0,
+ *   so that if their two-port device might someday use similar firmware, we
+ *   can use index=1 to refer to the second port). The messages defined are:
+ *
+ *    bRequest = 0 : set baud/bits/parity
+ *               1 : unused
+ *               2 : reserved for setting HW flow control (CTSRTS)
+ *               3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
+ *               4 : set break (on/off)
+ *               5 : reserved for requesting interrupts on pin state change
+ *               6 : query buffer room or chars in tx buffer
+ *               7 : request tx unthrottle interrupt
+ *
+ *  The host-side driver is set to recognize the device ID values stashed in
+ *  serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
+ *  start it running. This firmware will use EzUSB's "renumeration" trick by
+ *  simulating a bus disconnect, then reconnect with a different device ID
+ *  (encoded in the desc_device descriptor below). The host driver then
+ *  recognizes the new device ID and glues it to the real serial driver code.
+ *
+ * USEFUL DOCS:
+ *  EzUSB Technical Reference Manual: <http://www.anchorchips.com>
+ *  8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
+ *   basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
+ *   use totally different registers!
+ *  USB 1.1 spec: www.usb.org
+ *
+ * HOW TO BUILD:
+ *  gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
+ *  as31 -l keyspan_pda.asm
+ *  mv keyspan_pda.obj keyspan_pda.hex
+ *  perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
+ * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
+ * a bit to make it build.
+ *
+ * THANKS:
+ *  Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
+ *  AnchorChips, for making such an incredibly useful little microcontroller.
+ *  KeySpan, for making a handy, cheap ($40) widget that was so easy to take
+ *           apart and trace with an ohmmeter.
+ *
+ * TODO:
+ *  lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
+ *  control. Interrupting host upon change in DCD, etc, counting transitions.
+ *  Need to find a safe device id to use (the one used by the Keyspan firmware
+ *  under Windows would be ideal.. can anyone figure out what it is?). Parity.
+ *  More baud rates. Oh, and the string-descriptor-length silicon bug
+ *  workaround should be implemented, but I'm lazy, and the consequence is
+ *  that the device name strings that show up in your kernel log will have
+ *  lots of trailing binary garbage in them (appears as ????). Device strings
+ *  should be made more accurate.
+ *
+ * Questions, bugs, patches to Brian.
+ *
+ *  -Brian Warner <warner@lothar.com>
+ *
+ */
+	
+#define HIGH(x) (((x) & 0xff00) / 256)
+#define LOW(x) ((x) & 0xff)
+
+#define dpl1 0x84
+#define dph1 0x85
+#define dps 0x86
+
+;;; our bit assignments
+#define TX_RUNNING 0
+#define DO_TX_UNTHROTTLE 1
+	
+	;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
+#define STACK #0x60-1
+
+#define EXIF 0x91
+#define EIE 0xe8
+	.flag EUSB, EIE.0
+	.flag ES0, IE.4
+
+#define EP0CS #0x7fb4
+#define EP0STALLbit #0x01
+#define IN0BUF #0x7f00
+#define IN0BC #0x7fb5
+#define OUT0BUF #0x7ec0
+#define OUT0BC #0x7fc5		
+#define IN2BUF #0x7e00
+#define IN2BC #0x7fb9
+#define IN2CS #0x7fb8
+#define OUT2BC #0x7fc9
+#define OUT2CS #0x7fc8
+#define OUT2BUF #0x7dc0
+#define IN4BUF #0x7d00
+#define IN4BC #0x7fbd
+#define IN4CS #0x7fbc
+#define OEB #0x7f9d
+#define OUTB #0x7f97
+#define OEC #0x7f9e
+#define OUTC #0x7f98
+#define PINSC #0x7f9b
+#define PORTCCFG #0x7f95
+#define IN07IRQ #0x7fa9
+#define OUT07IRQ #0x7faa
+#define IN07IEN #0x7fac
+#define OUT07IEN #0x7fad
+#define USBIRQ #0x7fab
+#define USBIEN #0x7fae
+#define USBBAV #0x7faf
+#define USBCS #0x7fd6
+#define SUDPTRH #0x7fd4
+#define SUDPTRL #0x7fd5
+#define SETUPDAT #0x7fe8
+		
+	;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
+
+	.org 0
+	ljmp start
+	;; interrupt vectors
+	.org 23H
+	ljmp serial_int
+	.byte 0
+	
+	.org 43H
+	ljmp USB_Jump_Table
+	.byte 0			; filled in by the USB core
+
+;;; local variables. These are not initialized properly: do it by hand.
+	.org 30H
+rx_ring_in:	.byte 0
+rx_ring_out:	.byte 0
+tx_ring_in:	.byte 0
+tx_ring_out:	.byte 0
+tx_unthrottle_threshold:	.byte 0
+		
+	.org 0x100H		; wants to be on a page boundary
+USB_Jump_Table:
+	ljmp	ISR_Sudav	; Setup Data Available
+	.byte 0
+	ljmp	0		; Start of Frame
+	.byte 0
+	ljmp	0		; Setup Data Loading
+	.byte 0
+	ljmp	0		; Global Suspend
+	.byte 	0
+	ljmp	0		; USB Reset  	
+	.byte	0
+	ljmp	0		; Reserved
+	.byte	0
+	ljmp	0		; End Point 0 In
+	.byte	0
+	ljmp	0		; End Point 0 Out
+	.byte	0
+	ljmp	0		; End Point 1 In
+	.byte	0
+	ljmp	0		; End Point 1 Out
+	.byte	0
+	ljmp	ISR_Ep2in
+	.byte	0
+	ljmp	ISR_Ep2out
+	.byte	0
+
+
+	.org 0x200
+		
+start:	mov SP,STACK-1 ; set stack
+	;; clear local variables
+	clr a
+	mov tx_ring_in, a
+	mov tx_ring_out, a
+	mov rx_ring_in, a
+	mov rx_ring_out, a
+	mov tx_unthrottle_threshold, a
+	clr TX_RUNNING
+	clr DO_TX_UNTHROTTLE
+	
+	;; clear fifo with "fe"
+	mov r1, 0
+	mov a, #0xfe
+	mov dptr, #tx_ring
+clear_tx_ring_loop:
+	movx @dptr, a
+	inc dptr
+	djnz r1, clear_tx_ring_loop
+
+	mov a, #0xfd
+	mov dptr, #rx_ring
+clear_rx_ring_loop:
+	movx @dptr, a
+	inc dptr
+	djnz r1, clear_rx_ring_loop
+
+;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
+	;; set OEB.1
+	mov a, #02H
+	mov dptr,OEB
+	movx @dptr,a
+	;; clear PB1
+	mov a, #00H
+	mov dptr,OUTB
+	movx @dptr,a
+	;; set OEC.[127]
+	mov a, #0x86
+	mov dptr,OEC
+	movx @dptr,a
+	;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
+	mov dptr, PORTCCFG
+	mov a, #0x03
+	movx @dptr, a
+	
+	;; set up interrupts, autovectoring
+	mov dptr, USBBAV
+	movx a,@dptr
+	setb acc.0		; AVEN bit to 0
+	movx @dptr, a
+
+	mov a,#0x01		; enable SUDAV:	setup data available (for ep0)
+	mov dptr, USBIRQ
+	movx @dptr, a		; clear SUDAVI
+	mov dptr, USBIEN
+	movx @dptr, a
+	
+	mov dptr, IN07IEN
+	mov a,#0x04		; enable IN2 int
+	movx @dptr, a
+	
+	mov dptr, OUT07IEN
+	mov a,#0x04		; enable OUT2 int
+	movx @dptr, a
+	mov dptr, OUT2BC
+	movx @dptr, a		; arm OUT2
+
+	mov a, #0x84		; turn on RTS, DTR
+	mov dptr,OUTC
+	movx @dptr, a
+	;; setup the serial port. 9600 8N1.
+	mov a,#01010011		; mode 1, enable rx, clear int
+	mov SCON, a
+	;;  using timer2, in 16-bit baud-rate-generator mode
+	;;   (xtal 12MHz, internal fosc 24MHz)
+	;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
+	;;  57600: 0xFFF2.F, say 0xFFF3
+	;;   9600: 0xFFB1.E, say 0xFFB2
+	;;    300: 0xF63C
+#define BAUD 9600
+#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
+#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
+#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
+		
+	mov T2CON, #030h	; rclk=1,tclk=1,cp=0,tr2=0(enable later)
+	mov r3, #5
+	acall set_baud
+	setb TR2
+	mov SCON, #050h
+	
+#if 0
+	mov r1, #0x40
+	mov a, #0x41
+send:	
+	mov SBUF, a
+	inc a
+	anl a, #0x3F
+	orl a, #0x40
+;	xrl a, #0x02
+wait1:	
+	jnb TI, wait1
+	clr TI
+	djnz r1, send
+;done:	sjmp done
+
+#endif
+	
+	setb EUSB
+	setb EA
+	setb ES0
+	;acall dump_stat
+
+	;; hey, what say we RENUMERATE! (TRM p.62)
+	mov a, #0
+	mov dps, a
+	mov dptr, USBCS
+	mov a, #0x02		; DISCON=0, DISCOE=0, RENUM=1
+	movx @dptr, a
+	;; now presence pin is floating, simulating disconnect. wait 0.5s
+	mov r1, #46
+renum_wait1:
+	mov r2, #0
+renum_wait2:
+	mov r3, #0
+renum_wait3:
+	djnz r3, renum_wait3
+	djnz r2, renum_wait2
+	djnz r1, renum_wait1	; wait about n*(256^2) 6MHz clocks
+	mov a, #0x06		; DISCON=0, DISCOE=1, RENUM=1
+	movx @dptr, a
+	;; we are back online. the host device will now re-query us
+	
+	
+main:	sjmp main
+
+	
+
+ISR_Sudav:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	mov a,EXIF
+	clr acc.4
+	mov EXIF,a		; clear INT2 first
+	mov dptr, USBIRQ	; clear USB int
+	mov a,#01h
+	movx @dptr,a
+
+	;; get request type
+	mov dptr, SETUPDAT
+	movx a, @dptr
+	mov r1, a		; r1 = bmRequestType
+	inc dptr
+	movx a, @dptr
+	mov r2, a		; r2 = bRequest
+	inc dptr
+	movx a, @dptr
+	mov r3, a		; r3 = wValueL
+	inc dptr
+	movx a, @dptr
+	mov r4, a		; r4 = wValueH
+
+	;; main switch on bmRequest.type: standard or vendor
+	mov a, r1
+	anl a, #0x60
+	cjne a, #0x00, setup_bmreq_type_not_standard
+	;; standard request: now main switch is on bRequest
+	ljmp setup_bmreq_is_standard
+	
+setup_bmreq_type_not_standard:	
+	;; a still has bmreq&0x60
+	cjne a, #0x40, setup_bmreq_type_not_vendor
+	;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
+	;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
+	cjne r2, #0x00, setup_ctrl_not_00
+	;; 00 is set baud, wValue[0] has baud rate index
+	lcall set_baud		; index in r3, carry set if error
+	jc setup_bmreq_type_not_standard__do_stall
+	ljmp setup_done_ack
+setup_bmreq_type_not_standard__do_stall:
+	ljmp setup_stall
+setup_ctrl_not_00:
+	cjne r2, #0x01, setup_ctrl_not_01
+	;; 01 is reserved for set bits (parity). TODO
+	ljmp setup_stall
+setup_ctrl_not_01:
+	cjne r2, #0x02, setup_ctrl_not_02
+	;; 02 is set HW flow control. TODO
+	ljmp setup_stall
+setup_ctrl_not_02:
+	cjne r2, #0x03, setup_ctrl_not_03
+	;; 03 is control pins (RTS, DTR).
+	ljmp control_pins	; will jump to setup_done_ack,
+				;  or setup_return_one_byte
+setup_ctrl_not_03:
+	cjne r2, #0x04, setup_ctrl_not_04
+	;; 04 is send break (really "turn break on/off"). TODO
+	cjne r3, #0x00, setup_ctrl_do_break_on
+	;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
+	mov dptr, PORTCCFG
+	movx a, @dptr
+	orl a, #0x02
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_ctrl_do_break_on:
+	;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
+	mov dptr, OUTC
+	movx a, @dptr
+	anl a, #0xfd		; ~0x02
+	movx @dptr, a
+	mov dptr, PORTCCFG
+	movx a, @dptr
+	anl a, #0xfd		; ~0x02
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_ctrl_not_04:
+	cjne r2, #0x05, setup_ctrl_not_05
+	;; 05 is set desired interrupt bitmap. TODO
+	ljmp setup_stall
+setup_ctrl_not_05:
+	cjne r2, #0x06, setup_ctrl_not_06
+	;; 06 is query room
+	cjne r3, #0x00, setup_ctrl_06_not_00
+	;; 06, wValue[0]=0 is query write_room
+	mov a, tx_ring_out
+	setb c
+	subb a, tx_ring_in	; out-1-in = 255 - (in-out)
+	ljmp setup_return_one_byte
+setup_ctrl_06_not_00:
+	cjne r3, #0x01, setup_ctrl_06_not_01
+	;; 06, wValue[0]=1 is query chars_in_buffer
+	mov a, tx_ring_in
+	clr c
+	subb a, tx_ring_out	; in-out
+	ljmp setup_return_one_byte
+setup_ctrl_06_not_01:	
+	ljmp setup_stall
+setup_ctrl_not_06:
+	cjne r2, #0x07, setup_ctrl_not_07
+	;; 07 is request tx unthrottle interrupt
+	mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
+	ljmp setup_done_ack
+setup_ctrl_not_07:
+	ljmp setup_stall
+	
+setup_bmreq_type_not_vendor:
+	ljmp setup_stall
+
+
+setup_bmreq_is_standard:	
+	cjne r2, #0x00, setup_breq_not_00
+	;; 00:	Get_Status (sub-switch on bmRequestType: device, ep, int)
+	cjne r1, #0x80, setup_Get_Status_not_device
+	;; Get_Status(device)
+	;;  are we self-powered? no. can we do remote wakeup? no
+	;;   so return two zero bytes. This is reusable
+setup_return_two_zero_bytes:
+	mov dptr, IN0BUF
+	clr a
+	movx @dptr, a
+	inc dptr
+	movx @dptr, a
+	mov dptr, IN0BC
+	mov a, #2
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_Get_Status_not_device:
+	cjne r1, #0x82, setup_Get_Status_not_endpoint
+	;; Get_Status(endpoint)
+	;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
+	;; for now: cheat. TODO
+	sjmp setup_return_two_zero_bytes
+setup_Get_Status_not_endpoint:
+	cjne r1, #0x81, setup_Get_Status_not_interface
+	;; Get_Status(interface): return two zeros
+	sjmp setup_return_two_zero_bytes
+setup_Get_Status_not_interface:	
+	ljmp setup_stall
+	
+setup_breq_not_00:
+	cjne r2, #0x01, setup_breq_not_01
+	;; 01:	Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
+	cjne r3, #0x00, setup_Clear_Feature_not_stall
+	;; Clear_Feature(stall). should clear a stall bit. TODO
+	ljmp setup_stall
+setup_Clear_Feature_not_stall:
+	cjne r3, #0x01, setup_Clear_Feature_not_rwake
+	;; Clear_Feature(remote wakeup). ignored.
+	ljmp setup_done_ack
+setup_Clear_Feature_not_rwake:
+	ljmp setup_stall
+	
+setup_breq_not_01:
+	cjne r2, #0x03, setup_breq_not_03
+	;; 03:	Set_Feature (sub-switch on wValueL: stall, remote wakeup)
+	cjne r3, #0x00, setup_Set_Feature_not_stall
+	;; Set_Feature(stall). Should set a stall bit. TODO
+	ljmp setup_stall
+setup_Set_Feature_not_stall:
+	cjne r3, #0x01, setup_Set_Feature_not_rwake
+	;; Set_Feature(remote wakeup). ignored.
+	ljmp setup_done_ack
+setup_Set_Feature_not_rwake:
+	ljmp setup_stall
+	
+setup_breq_not_03:	
+	cjne r2, #0x06, setup_breq_not_06
+	;; 06:	Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
+	cjne r4, #0x01, setup_Get_Descriptor_not_device
+	;; Get_Descriptor(device)
+	mov dptr, SUDPTRH
+	mov a, #HIGH(desc_device)
+	movx @dptr, a
+	mov dptr, SUDPTRL
+	mov a, #LOW(desc_device)
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_Get_Descriptor_not_device:
+	cjne r4, #0x02, setup_Get_Descriptor_not_config
+	;; Get_Descriptor(config[n])
+	cjne r3, #0x00, setup_stall; only handle n==0
+	;; Get_Descriptor(config[0])
+	mov dptr, SUDPTRH
+	mov a, #HIGH(desc_config1)
+	movx @dptr, a
+	mov dptr, SUDPTRL
+	mov a, #LOW(desc_config1)
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_Get_Descriptor_not_config:
+	cjne r4, #0x03, setup_Get_Descriptor_not_string
+	;; Get_Descriptor(string[wValueL])
+	;;  if (wValueL >= maxstrings) stall
+	mov a, #((desc_strings_end-desc_strings)/2)
+	clr c
+	subb a,r3		; a=4, r3 = 0..3 . if a<=0 then stall
+	jc  setup_stall
+	jz  setup_stall
+	mov a, r3
+	add a, r3		; a = 2*wValueL
+	mov dptr, #desc_strings
+	add a, dpl
+	mov dpl, a
+	mov a, #0
+	addc a, dph
+	mov dph, a		; dph = desc_strings[a]. big endian! (handy)
+	;; it looks like my adapter uses a revision of the EZUSB that
+	;; contains "rev D errata number 8", as hinted in the EzUSB example
+	;; code. I cannot find an actual errata description on the Cypress
+	;; web site, but from the example code it looks like this bug causes
+	;; the length of string descriptors to be read incorrectly, possibly
+	;; sending back more characters than the descriptor has. The workaround
+	;; is to manually send out all of the data. The consequence of not
+	;; using the workaround is that the strings gathered by the kernel
+	;; driver are too long and are filled with trailing garbage (including
+	;; leftover strings). Writing this out by hand is a nuisance, so for
+	;; now I will just live with the bug.
+	movx a, @dptr
+	mov r1, a
+	inc dptr
+	movx a, @dptr
+	mov r2, a
+	mov dptr, SUDPTRH
+	mov a, r1
+	movx @dptr, a
+	mov dptr, SUDPTRL
+	mov a, r2
+	movx @dptr, a
+	;; done
+	ljmp setup_done_ack
+	
+setup_Get_Descriptor_not_string:
+	ljmp setup_stall
+	
+setup_breq_not_06:
+	cjne r2, #0x08, setup_breq_not_08
+	;; Get_Configuration. always 1. return one byte.
+	;; this is reusable
+	mov a, #1
+setup_return_one_byte:	
+	mov dptr, IN0BUF
+	movx @dptr, a
+	mov a, #1
+	mov dptr, IN0BC
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_breq_not_08:
+	cjne r2, #0x09, setup_breq_not_09
+	;; 09: Set_Configuration. ignored.
+	ljmp setup_done_ack
+setup_breq_not_09:
+	cjne r2, #0x0a, setup_breq_not_0a
+	;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
+	;;  since we only have one interface, ignore wIndexL, return a 0
+	mov a, #0
+	ljmp setup_return_one_byte
+setup_breq_not_0a:
+	cjne r2, #0x0b, setup_breq_not_0b
+	;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
+	ljmp setup_done_ack
+setup_breq_not_0b:
+	ljmp setup_stall
+
+		
+setup_done_ack:	
+	;; now clear HSNAK
+	mov dptr, EP0CS
+	mov a, #0x02
+	movx @dptr, a
+	sjmp setup_done
+setup_stall:	
+	;; unhandled. STALL
+	;EP0CS |= bmEPSTALL
+	mov dptr, EP0CS
+	movx a, @dptr
+	orl a, EP0STALLbit
+	movx @dptr, a
+	sjmp setup_done
+	
+setup_done:	
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+;;; ==============================================================
+	
+set_baud:			; baud index in r3
+	;; verify a < 10
+	mov a, r3
+	jb ACC.7, set_baud__badbaud
+	clr c
+	subb a, #10
+	jnc set_baud__badbaud
+	mov a, r3
+	rl a			; a = index*2
+	add a, #LOW(baud_table)
+	mov dpl, a
+	mov a, #HIGH(baud_table)
+	addc a, #0
+	mov dph, a
+	;; TODO: shut down xmit/receive
+	;; TODO: wait for current xmit char to leave
+	;; TODO: shut down timer to avoid partial-char glitch
+	movx a,@dptr		; BAUD_HIGH
+	mov RCAP2H, a
+	mov TH2, a
+	inc dptr
+	movx a,@dptr		; BAUD_LOW
+	mov RCAP2L, a
+	mov TL2, a
+	;; TODO: restart xmit/receive
+	;; TODO: reenable interrupts, resume tx if pending
+	clr c			; c=0: success
+	ret
+set_baud__badbaud:
+	setb c			; c=1: failure
+	ret
+	
+;;; ==================================================
+control_pins:
+	cjne r1, #0x41, control_pins_in
+control_pins_out:
+	mov a, r3 ; wValue[0] holds new bits:	b7 is new DTR, b2 is new RTS
+	xrl a, #0xff		; 1 means active, 0V, +12V ?
+	anl a, #0x84
+	mov r3, a
+	mov dptr, OUTC
+	movx a, @dptr		; only change bits 7 and 2
+	anl a, #0x7b		; ~0x84
+	orl a, r3
+	movx @dptr, a		; other pins are inputs, bits ignored
+	ljmp setup_done_ack
+control_pins_in:
+	mov dptr, PINSC
+	movx a, @dptr
+	xrl a, #0xff
+	ljmp setup_return_one_byte
+
+;;; ========================================
+	
+ISR_Ep2in:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	mov a,EXIF
+	clr acc.4
+	mov EXIF,a		; clear INT2 first
+	mov dptr, IN07IRQ	; clear USB int
+	mov a,#04h
+	movx @dptr,a
+
+	;; do stuff
+	lcall start_in
+	
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+ISR_Ep2out:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	mov a,EXIF
+	clr acc.4
+	mov EXIF,a		; clear INT2 first
+	mov dptr, OUT07IRQ	; clear USB int
+	mov a,#04h
+	movx @dptr,a
+
+	;; do stuff
+
+	;; copy data into buffer. for now, assume we will have enough space
+	mov dptr, OUT2BC	; get byte count
+	movx a,@dptr
+	mov r1, a
+	clr a
+	mov dps, a
+	mov dptr, OUT2BUF	; load DPTR0 with source
+	mov dph1, #HIGH(tx_ring)	; load DPTR1 with target
+	mov dpl1, tx_ring_in
+OUT_loop:
+	movx a,@dptr		; read
+	inc dps			; switch to DPTR1: target
+	inc dpl1		; target = tx_ring_in+1
+	movx @dptr,a		; store
+	mov a,dpl1
+	cjne a, tx_ring_out, OUT_no_overflow
+	sjmp OUT_overflow
+OUT_no_overflow:	
+	inc tx_ring_in		; tx_ring_in++
+	inc dps			; switch to DPTR0: source
+	inc dptr
+	djnz r1, OUT_loop
+	sjmp OUT_done
+OUT_overflow:
+	;; signal overflow
+	;; fall through
+OUT_done:	
+	;; ack
+	mov dptr,OUT2BC
+	movx @dptr,a
+
+	;; start tx
+	acall maybe_start_tx
+	;acall dump_stat
+	
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+dump_stat:
+	;; fill in EP4in with a debugging message:
+	;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
+	;;   tx_active
+	;;   tx_ring[0..15]
+	;;   0xfc
+	;;   rx_ring[0..15]
+	clr a
+	mov dps, a
+	
+	mov dptr, IN4CS
+	movx a, @dptr
+	jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
+	mov dptr, IN4BUF
+	
+	mov a, tx_ring_in
+	movx @dptr, a
+	inc dptr
+	mov a, tx_ring_out
+	movx @dptr, a
+	inc dptr
+
+	mov a, rx_ring_in
+	movx @dptr, a
+	inc dptr
+	mov a, rx_ring_out
+	movx @dptr, a
+	inc dptr
+	
+	clr a
+	jnb TX_RUNNING, dump_stat__no_tx_running
+	inc a
+dump_stat__no_tx_running:
+	movx @dptr, a
+	inc dptr
+	;; tx_ring[0..15]
+	inc dps
+	mov dptr, #tx_ring	; DPTR1: source
+	mov r1, #16
+dump_stat__tx_ring_loop:
+	movx a, @dptr
+	inc dptr
+	inc dps
+	movx @dptr, a
+	inc dptr
+	inc dps
+	djnz r1, dump_stat__tx_ring_loop
+	inc dps
+	
+	mov a, #0xfc
+	movx @dptr, a
+	inc dptr
+	
+	;; rx_ring[0..15]
+	inc dps
+	mov dptr, #rx_ring	; DPTR1: source
+	mov r1, #16
+dump_stat__rx_ring_loop:
+	movx a, @dptr
+	inc dptr
+	inc dps
+	movx @dptr, a
+	inc dptr
+	inc dps
+	djnz r1, dump_stat__rx_ring_loop
+	
+	;; now send it
+	clr a
+	mov dps, a
+	mov dptr, IN4BC
+	mov a, #38
+	movx @dptr, a
+dump_stat__done:	
+	ret
+		
+;;; ============================================================
+	
+maybe_start_tx:
+	;; make sure the tx process is running.
+	jb TX_RUNNING, start_tx_done
+start_tx:
+	;; is there work to be done?
+	mov a, tx_ring_in
+	cjne a,tx_ring_out, start_tx__work
+	ret			; no work
+start_tx__work:	
+	;; tx was not running. send the first character, setup the TI int
+	inc tx_ring_out		; [++tx_ring_out]
+	mov dph, #HIGH(tx_ring)
+	mov dpl, tx_ring_out
+	movx a, @dptr
+	mov sbuf, a
+	setb TX_RUNNING
+start_tx_done:
+	;; can we unthrottle the host tx process?
+	;;  step 1: do we care?
+	mov a, #0
+	cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
+	;; nope
+start_tx_really_done:
+	ret
+start_tx__maybe_unthrottle_tx:
+	;;  step 2: is there now room?
+	mov a, tx_ring_out
+	setb c
+	subb a, tx_ring_in
+	;; a is now write_room. If thresh >= a, we can unthrottle
+	clr c
+	subb a, tx_unthrottle_threshold
+	jc start_tx_really_done	; nope
+	;; yes, we can unthrottle. remove the threshold and mark a request
+	mov tx_unthrottle_threshold, #0
+	setb DO_TX_UNTHROTTLE
+	;; prod rx, which will actually send the message when in2 becomes free
+	ljmp start_in
+	
+
+serial_int:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	jnb TI, serial_int__not_tx
+	;; tx finished. send another character if we have one
+	clr TI			; clear int
+	clr TX_RUNNING
+	lcall start_tx
+serial_int__not_tx:
+	jnb RI, serial_int__not_rx
+	lcall get_rx_char
+	clr RI			; clear int
+serial_int__not_rx:	
+	;; return
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+get_rx_char:
+	mov dph, #HIGH(rx_ring)
+	mov dpl, rx_ring_in
+	inc dpl			; target = rx_ring_in+1
+	mov a, sbuf
+	movx @dptr, a
+	;; check for overflow before incrementing rx_ring_in
+	mov a, dpl
+	cjne a, rx_ring_out, get_rx_char__no_overflow
+	;; signal overflow
+	ret
+get_rx_char__no_overflow:	
+	inc rx_ring_in
+	;; kick off USB INpipe
+	acall start_in
+	ret
+
+start_in:
+	;; check if the inpipe is already running.
+	mov dptr, IN2CS
+	movx a, @dptr
+	jb acc.1, start_in__done; int will handle it
+	jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
+	;; see if there is any work to do. a serial interrupt might occur
+	;; during this sequence?
+	mov a, rx_ring_in
+	cjne a, rx_ring_out, start_in__have_work
+	ret			; nope
+start_in__have_work:	
+	;; now copy as much data as possible into the pipe. 63 bytes max.
+	clr a
+	mov dps, a
+	mov dph, #HIGH(rx_ring)	; load DPTR0 with source
+	inc dps
+	mov dptr, IN2BUF	; load DPTR1 with target
+	movx @dptr, a		; in[0] signals that rest of IN is rx data
+	inc dptr
+	inc dps
+	;; loop until we run out of data, or we have copied 64 bytes
+	mov r1, #1		; INbuf size counter
+start_in__loop:
+	mov a, rx_ring_in
+	cjne a, rx_ring_out, start_in__still_copying
+	sjmp start_in__kick
+start_in__still_copying:
+	inc rx_ring_out
+	mov dpl, rx_ring_out
+	movx a, @dptr
+	inc dps
+	movx @dptr, a		; write into IN buffer
+	inc dptr
+	inc dps
+	inc r1
+	cjne r1, #64, start_in__loop; loop
+start_in__kick:
+	;; either we ran out of data, or we copied 64 bytes. r1 has byte count
+	;; kick off IN
+	mov dptr, IN2BC
+	mov a, r1
+	jz start_in__done
+	movx @dptr, a
+	;; done
+start_in__done:
+	;acall dump_stat
+	ret
+start_in__do_tx_unthrottle:
+	;; special sequence: send a tx unthrottle message
+	clr DO_TX_UNTHROTTLE
+	clr a
+	mov dps, a
+	mov dptr, IN2BUF
+	mov a, #1
+	movx @dptr, a
+	inc dptr
+	mov a, #2
+	movx @dptr, a
+	mov dptr, IN2BC
+	movx @dptr, a
+	ret
+	
+putchar:
+	clr TI
+	mov SBUF, a
+putchar_wait:
+	jnb TI, putchar_wait
+	clr TI
+	ret
+
+	
+baud_table:			; baud_high, then baud_low
+	;; baud[0]: 110
+	.byte BAUD_HIGH(110)
+	.byte BAUD_LOW(110)
+	;; baud[1]: 300
+	.byte BAUD_HIGH(300)
+	.byte BAUD_LOW(300)
+	;; baud[2]: 1200
+	.byte BAUD_HIGH(1200)
+	.byte BAUD_LOW(1200)
+	;; baud[3]: 2400
+	.byte BAUD_HIGH(2400)
+	.byte BAUD_LOW(2400)
+	;; baud[4]: 4800
+	.byte BAUD_HIGH(4800)
+	.byte BAUD_LOW(4800)
+	;; baud[5]: 9600
+	.byte BAUD_HIGH(9600)
+	.byte BAUD_LOW(9600)
+	;; baud[6]: 19200
+	.byte BAUD_HIGH(19200)
+	.byte BAUD_LOW(19200)
+	;; baud[7]: 38400
+	.byte BAUD_HIGH(38400)
+	.byte BAUD_LOW(38400)
+	;; baud[8]: 57600
+	.byte BAUD_HIGH(57600)
+	.byte BAUD_LOW(57600)
+	;; baud[9]: 115200
+	.byte BAUD_HIGH(115200)
+	.byte BAUD_LOW(115200)
+
+desc_device:
+	.byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
+	.byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
+;;; The "real" device id, which must match the host driver, is that
+;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
+	
+desc_config1:
+	.byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
+	.byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
+	.byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
+	.byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
+
+desc_strings:
+	.word string_langids, string_mfg, string_product, string_serial
+desc_strings_end:
+
+string_langids:	.byte string_langids_end-string_langids
+	.byte 3
+	.word 0
+string_langids_end:
+
+	;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
+	;; *that* is a pain in the ass to encode. And they are little-endian
+	;; too. Use this perl snippet to get the bytecodes:
+	/* while (<>) {
+	    @c = split(//);
+	    foreach $c (@c) {
+	     printf("0x%02x, 0x00, ", ord($c));
+	    }
+	   }
+	*/
+
+string_mfg:	.byte string_mfg_end-string_mfg
+	.byte 3
+;	.byte "ACME usb widgets"
+	.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
+string_mfg_end:
+	
+string_product:	.byte string_product_end-string_product
+	.byte 3
+;	.byte "ACME USB serial widget"
+	.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
+string_product_end:
+	
+string_serial:	.byte string_serial_end-string_serial
+	.byte 3
+;	.byte "47"
+	.byte 0x34, 0x00, 0x37, 0x00
+string_serial_end:
+		
+;;; ring buffer memory
+	;; tx_ring_in+1 is where the next input byte will go
+	;; [tx_ring_out] has been sent
+	;; if tx_ring_in == tx_ring_out, theres no work to do
+	;; there are (tx_ring_in - tx_ring_out) chars to be written
+	;; dont let _in lap _out
+	;;   cannot inc if tx_ring_in+1 == tx_ring_out
+	;;  write [tx_ring_in+1] then tx_ring_in++
+	;;   if (tx_ring_in+1 == tx_ring_out), overflow
+	;;   else tx_ring_in++
+	;;  read/send [tx_ring_out+1], then tx_ring_out++
+
+	;; rx_ring_in works the same way
+	
+	.org 0x1000
+tx_ring:
+	.skip 0x100		; 256 bytes
+rx_ring:
+	.skip 0x100		; 256 bytes
+	
+	
+	.END
+	
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/serial/usb-serial.c linux/drivers/usb/serial/usb-serial.c
--- v2.3.99-pre2/linux/drivers/usb/serial/usb-serial.c	Sun Mar 19 18:35:31 2000
+++ linux/drivers/usb/serial/usb-serial.c	Sun Mar 19 18:15:06 2000
@@ -14,6 +14,16 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (03/19/2000) gkh
+ *	Fixed oops that could happen when device was removed while a program
+ *	was talking to the device.
+ *	Removed the static urbs and now all urbs are created and destroyed
+ *	dynamically.
+ *	Reworked the internal interface. Now everything is based on the 
+ *	usb_serial_port structure instead of the larger usb_serial structure.
+ *	This fixes the bug that a multiport device could not have more than
+ *	one port open at one time.
+ *
  * (03/17/2000) gkh
  *	Added config option for debugging messages.
  *	Added patch for keyspan pda from Brian Warner.
@@ -220,10 +230,9 @@
 static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
 
 static struct usb_driver usb_serial_driver = {
-	"serial",
-	usb_serial_probe,
-	usb_serial_disconnect,
-	{ NULL, NULL }
+	name:		"serial",
+	probe:		usb_serial_probe,
+	disconnect:	usb_serial_disconnect,
 };
 
 static int			serial_refcount;
@@ -233,11 +242,50 @@
 static struct usb_serial	*serial_table[SERIAL_TTY_MINORS] = {NULL, };
 
 
+static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
+{
+	if (!serial) {
+		dbg("%s - serial == NULL", function);
+		return -1;
+	}
+	if (serial->magic != USB_SERIAL_MAGIC) {
+		dbg("%s - bad magic number for serial", function);
+		return -1;
+	}
+	if (!serial->type) {
+		dbg("%s - serial->type == NULL!", function);
+		return -1;
+	}
 
-static struct usb_serial *get_serial_by_minor (int minor)
+	return 0;
+}
+
+
+static inline int port_paranoia_check (struct usb_serial_port *port, const char *function)
 {
-	dbg("get_serial_by_minor %d", minor);
+	if (!port) {
+		dbg("%s - port == NULL", function);
+		return -1;
+	}
+	if (port->magic != USB_SERIAL_PORT_MAGIC) {
+		dbg("%s - bad magic number for port", function);
+		return -1;
+	}
+	if (!port->serial) {
+		dbg("%s - port->serial == NULL", function);
+		return -1;
+	}
+	if (!port->tty) {
+		dbg("%s - port->tty == NULL", function);
+		return -1;
+	}
 
+	return 0;
+}
+
+
+static struct usb_serial *get_serial_by_minor (int minor)
+{
 	return serial_table[minor];
 }
 
@@ -267,6 +315,7 @@
 			return NULL;
 		}
 		memset(serial, 0, sizeof(struct usb_serial));
+		serial->magic = USB_SERIAL_MAGIC;
 		serial_table[i] = serial;
 		*minor = i;
 		dbg("minor base = %d", *minor);
@@ -337,6 +386,8 @@
 static int serial_open (struct tty_struct *tty, struct file * filp)
 {
 	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int portNumber;
 	
 	dbg("serial_open");
 
@@ -346,180 +397,182 @@
 	/* get the serial object associated with this tty pointer */
 	serial = get_serial_by_minor (MINOR(tty->device));
 
-	/* do some sanity checking that we really have a device present */
-	if (!serial) {
-		dbg("serial == NULL!");
-		return (-ENODEV);
-	}
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
-		return (-ENODEV);
+	if (serial_paranoia_check (serial, "serial_open")) {
+		return -ENODEV;
 	}
 
-	/* make the tty driver remember our serial object, and us it */
-	tty->driver_data = serial;
-	serial->tty = tty;
+	/* set up our port structure */
+	portNumber = MINOR(tty->device) - serial->minor;
+	port = &serial->port[portNumber];
+	port->number = portNumber;
+	port->serial = serial;
+	port->magic = USB_SERIAL_PORT_MAGIC;
+	
+	/* make the tty driver remember our port object, and us it */
+	tty->driver_data = port;
+	port->tty = tty;
 	 
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->open) {
-		return (serial->type->open(tty, filp));
+		return (serial->type->open(port, filp));
 	} else {
-		return (generic_serial_open(tty, filp));
+		return (generic_open(port, filp));
 	}
 }
 
 
 static void serial_close(struct tty_struct *tty, struct file * filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port;	
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
 
 	dbg("serial_close");
 
-	if (!serial) {
-		dbg("serial == NULL!");
+	if (port_paranoia_check (port, "serial_close")) {
 		return;
 	}
 
-	port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_close port %d", port);
-	
-	/* do some sanity checking that we really have a device present */
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_close")) {
 		return;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not opened");
+
+	dbg("serial_close port %d", port->number);
+	
+	if (!port->active) {
+		dbg ("port not opened");
 		return;
 	}
 
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->close) {
-		serial->type->close(tty, filp);
+		serial->type->close(port, filp);
 	} else {
-		generic_serial_close(tty, filp);
+		generic_close(port, filp);
 	}
 }	
 
 
 static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
 	
-	dbg("serial_write port %d, %d byte(s)", port, count);
-
-	/* do some sanity checking that we really have a device present */
-	if (!serial) {
-		dbg("serial == NULL!");
-		return (-ENODEV);
+	dbg("serial_write");
+	
+	if (port_paranoia_check (port, "serial_write")) {
+		return -ENODEV;
 	}
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
-		return (-ENODEV);
+	
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_write")) {
+		return -ENODEV;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not opened");
-		return (-EINVAL);
+	
+	dbg("serial_write port %d, %d byte(s)", port->number, count);
+
+	if (!port->active) {
+		dbg ("port not opened");
+		return -EINVAL;
 	}
 	
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->write) {
-		return (serial->type->write(tty, from_user, buf, count));
+		return (serial->type->write(port, from_user, buf, count));
 	} else {
-		return (generic_serial_write(tty, from_user, buf, count));
+		return (generic_write(port, from_user, buf, count));
 	}
 }
 
 
 static int serial_write_room (struct tty_struct *tty) 
 {
-	struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_write_room port %d", port);
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
 	
-	/* do some sanity checking that we really have a device present */
-	if (!serial) {
-		dbg("serial == NULL!");
-		return (-ENODEV);
+	dbg("serial_write_room");
+	
+	if (port_paranoia_check (port, "serial_write")) {
+		return -ENODEV;
 	}
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
-		return (-ENODEV);
+	
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_write")) {
+		return -ENODEV;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
-		return (-EINVAL);
+	
+	dbg("serial_write_room port %d", port->number);
+	
+	if (!port->active) {
+		dbg ("port not open");
+		return -EINVAL;
 	}
 	
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->write_room) {
-		return (serial->type->write_room(tty));
+		return (serial->type->write_room(port));
 	} else {
-		return (generic_write_room(tty));
+		return (generic_write_room(port));
 	}
 }
 
 
 static int serial_chars_in_buffer (struct tty_struct *tty) 
 {
-	struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_chars_in_buffer port %d", port);
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
 	
-	/* do some sanity checking that we really have a device present */
-	if (!serial) {
-		dbg("serial == NULL!");
-		return (-ENODEV);
+	dbg("serial_chars_in_buffer");
+	
+	if (port_paranoia_check (port, "serial_chars_in_buffer")) {
+		return -ENODEV;
 	}
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
-		return (-ENODEV);
+	
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_chars_in_buffer")) {
+		return -ENODEV;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
-		return (-EINVAL);
+	
+	if (!port->active) {
+		dbg ("port not open");
+		return -EINVAL;
 	}
 	
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->chars_in_buffer) {
-		return (serial->type->chars_in_buffer(tty));
+		return (serial->type->chars_in_buffer(port));
 	} else {
-		return (generic_chars_in_buffer(tty));
+		return (generic_chars_in_buffer(port));
 	}
 }
 
 
 static void serial_throttle (struct tty_struct * tty)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_throttle port %d", port);
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
 	
-	/* do some sanity checking that we really have a device present */
-	if (!serial) {
-		dbg("serial == NULL!");
+	dbg("serial_throttle");
+	
+	if (port_paranoia_check (port, "serial_throttle")) {
 		return;
 	}
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
+	
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_throttle")) {
 		return;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
+	
+	dbg("serial_throttle port %d", port->number);
+	
+	if (!port->active) {
+		dbg ("port not open");
 		return;
 	}
 
 	/* pass on to the driver specific version of this function */
 	if (serial->type->throttle) {
-		serial->type->throttle(tty);
-	} else {
-		generic_throttle(tty);
+		serial->type->throttle(port);
 	}
 
 	return;
@@ -528,30 +581,30 @@
 
 static void serial_unthrottle (struct tty_struct * tty)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_unthrottle port %d", port);
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
 	
-	/* do some sanity checking that we really have a device present */
-	if (!serial) {
-		dbg("serial == NULL!");
+	dbg("serial_unthrottle");
+	
+	if (port_paranoia_check (port, "serial_unthrottle")) {
 		return;
 	}
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
+	
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_unthrottle")) {
 		return;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
+	
+	dbg("serial_unthrottle port %d", port->number);
+	
+	if (!port->active) {
+		dbg ("port not open");
 		return;
 	}
 
 	/* pass on to the driver specific version of this function */
 	if (serial->type->unthrottle) {
-		serial->type->unthrottle(tty);
-	} else {
-		generic_unthrottle(tty);
+		serial->type->unthrottle(port);
 	}
 
 	return;
@@ -560,70 +613,62 @@
 
 static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port;	
-
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
+	
 	dbg("serial_ioctl");
-
-	if (!serial) {
-		dbg("serial == NULL!");
+	
+	if (port_paranoia_check (port, "serial_ioctl")) {
 		return -ENODEV;
 	}
 
-	port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_ioctl port %d", port);
-	
-	/* do some sanity checking that we really have a device present */
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_ioctl")) {
 		return -ENODEV;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
+	
+	dbg("serial_ioctl port %d", port->number);
+	
+	if (!port->active) {
+		dbg ("port not open");
 		return -ENODEV;
 	}
 
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->ioctl) {
-		return (serial->type->ioctl(tty, file, cmd, arg));
+		return (serial->type->ioctl(port, file, cmd, arg));
 	} else {
-		return (generic_ioctl (tty, file, cmd, arg));
+		return -ENOIOCTLCMD;
 	}
 }
 
 
 static void serial_set_termios (struct tty_struct *tty, struct termios * old)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port;	
-
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
+	
 	dbg("serial_set_termios");
-
-	if (!serial) {
-		dbg("serial == NULL!");
+	
+	if (port_paranoia_check (port, "serial_set_termios")) {
 		return;
 	}
 
-	port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_set_termios port %d", port);
-	
-	/* do some sanity checking that we really have a device present */
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_set_termios")) {
 		return;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
+
+	dbg("serial_set_termios port %d", port->number);
+
+	if (!port->active) {
+		dbg ("port not open");
 		return;
 	}
 
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->set_termios) {
-		serial->type->set_termios(tty, old);
-	} else {
-		generic_set_termios (tty, old);
+		serial->type->set_termios(port, old);
 	}
 	
 	return;
@@ -632,32 +677,31 @@
 
 static void serial_break (struct tty_struct *tty, int break_state)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port;	
-
-	if (!serial) {
-		dbg("serial == NULL!");
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial;
+	
+	dbg("serial_break");
+	
+	if (port_paranoia_check (port, "serial_break")) {
 		return;
 	}
 
-	port = MINOR(tty->device) - serial->minor;
-
-	dbg("serial_break port %d", port);
-	
-	/* do some sanity checking that we really have a device present */
-	if (!serial->type) {
-		dbg("serial->type == NULL!");
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "serial_break")) {
 		return;
 	}
-	if (!serial->port[port].active) {
-		dbg ("device not open");
+
+	dbg("serial_break port %d", port->number);
+
+	if (!port->active) {
+		dbg ("port not open");
 		return;
 	}
 
 	/* pass on to the driver specific version of this function if it is
            available */
 	if (serial->type->break_ctl) {
-		serial->type->break_ctl(tty, break_state);
+		serial->type->break_ctl(port, break_state);
 	}
 }
 
@@ -666,13 +710,9 @@
 /*****************************************************************************
  * Connect Tech's White Heat specific driver functions
  *****************************************************************************/
-static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp)
+static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
-
-	dbg("whiteheat_serial_open port %d", portNumber);
+	dbg("whiteheat_open port %d", port->number);
 
 	if (port->active) {
 		dbg ("device already open");
@@ -681,7 +721,7 @@
 	port->active = 1;
  
 	/*Start reading from the device*/
-	if (usb_submit_urb(&port->read_urb))
+	if (usb_submit_urb(port->read_urb))
 		dbg("usb_submit_urb(read bulk) failed");
 
 	/* Need to do device specific setup here (control lines, baud rate, etc.) */
@@ -691,36 +731,30 @@
 }
 
 
-static void whiteheat_serial_close(struct tty_struct *tty, struct file * filp)
+static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
-
-	dbg("whiteheat_serial_close port %d", portNumber);
+	dbg("whiteheat_close port %d", port->number);
 	
 	/* Need to change the control lines here */
 	/* FIXME */
 	
 	/* shutdown our bulk reads and writes */
-	usb_unlink_urb (&port->write_urb);
-	usb_unlink_urb (&port->read_urb);
+	usb_unlink_urb (port->write_urb);
+	usb_unlink_urb (port->read_urb);
 	port->active = 0;
 }
 
 
-static void whiteheat_set_termios (struct tty_struct *tty, struct termios *old_termios)
+static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-	unsigned int cflag = tty->termios->c_cflag;
+	unsigned int cflag = port->tty->termios->c_cflag;
 
-	dbg("whiteheat_set_termios port %d", port);
+	dbg("whiteheat_set_termios port %d", port->number);
 
 	/* check that they really want us to change something */
 	if (old_termios) {
 		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
 			dbg("nothing to change...");
 			return;
 		}
@@ -732,12 +766,9 @@
 	return;
 }
 
-static void whiteheat_throttle (struct tty_struct * tty)
+static void whiteheat_throttle (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("whiteheat_throttle port %d", port);
+	dbg("whiteheat_throttle port %d", port->number);
 
 	/* Change the control signals */
 	/* FIXME!!! */
@@ -746,12 +777,9 @@
 }
 
 
-static void whiteheat_unthrottle (struct tty_struct * tty)
+static void whiteheat_unthrottle (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("whiteheat_unthrottle port %d", port);
+	dbg("whiteheat_unthrottle port %d", port->number);
 
 	/* Change the control signals */
 	/* FIXME!!! */
@@ -837,13 +865,9 @@
 /******************************************************************************
  * Handspring Visor specific driver functions
  ******************************************************************************/
-static int visor_serial_open (struct tty_struct *tty, struct file *filp)
+static int visor_open (struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
-
-	dbg("visor_serial_open port %d", portNumber);
+	dbg("visor_open port %d", port->number);
 
 	if (port->active) {
 		dbg ("device already open");
@@ -853,23 +877,22 @@
 	port->active = 1;
 
 	/*Start reading from the device*/
-	if (usb_submit_urb(&port->read_urb))
+	if (usb_submit_urb(port->read_urb))
 		dbg("usb_submit_urb(read bulk) failed");
 
 	return (0);
 }
 
-static void visor_serial_close(struct tty_struct *tty, struct file * filp)
+
+static void visor_close (struct usb_serial_port *port, struct file * filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 	unsigned char *transfer_buffer =  kmalloc (0x12, GFP_KERNEL);
 	
-	dbg("visor_serial_close port %d", portNumber);
+	dbg("visor_close port %d", port->number);
 			 
 	if (!transfer_buffer) {
-		err("visor_serial_close: kmalloc(%d) failed.", 0x12);
+		err("visor_close: kmalloc(%d) failed.", 0x12);
 	} else {
 		/* send a shutdown message to the device */
 		usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION,
@@ -877,33 +900,27 @@
 	}
 
 	/* shutdown our bulk reads and writes */
-	usb_unlink_urb (&port->write_urb);
-	usb_unlink_urb (&port->read_urb);
+	usb_unlink_urb (port->write_urb);
+	usb_unlink_urb (port->read_urb);
 	port->active = 0;
 }
 
 
-static void visor_throttle (struct tty_struct * tty)
+static void visor_throttle (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("visor_throttle port %d", port);
+	dbg("visor_throttle port %d", port->number);
 
-	usb_unlink_urb (&serial->port[port].read_urb);
+	usb_unlink_urb (port->read_urb);
 
 	return;
 }
 
 
-static void visor_unthrottle (struct tty_struct * tty)
+static void visor_unthrottle (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
-	int port = MINOR(tty->device) - serial->minor;
+	dbg("visor_unthrottle port %d", port->number);
 
-	dbg("visor_unthrottle port %d", port);
-
-	if (usb_unlink_urb (&serial->port[port].read_urb))
+	if (usb_unlink_urb (port->read_urb))
 		dbg("usb_submit_urb(read bulk) failed");
 
 	return;
@@ -988,17 +1005,15 @@
 
 #include "ftdi_sio.h"
 
-static int  ftdi_sio_serial_open (struct tty_struct *tty, struct file *filp)
+static int  ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 	char buf[1]; /* Needed for the usb_control_msg I think */
 
-	dbg("ftdi_sio_serial_open port %d", portNumber);
+	dbg("ftdi_sio_open port %d", port->number);
 
 	if (port->active) {
-		dbg ("device already open");
+		dbg ("port already open");
 		return -EINVAL;
 	}
 	port->active = 1;
@@ -1069,21 +1084,19 @@
 	}
 	
 	/*Start reading from the device*/
-	if (usb_submit_urb(&port->read_urb))
+	if (usb_submit_urb(port->read_urb))
 		dbg("usb_submit_urb(read bulk) failed");
 
 	return (0);
 }
 
 
-static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp)
+static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 	char buf[1];
 
-	dbg("ftdi_sio_serial_close port %d", portNumber);
+	dbg("ftdi_sio_close port %d", port->number);
 	
 	/* FIXME - might be able to do both simultaneously */
 	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
@@ -1104,8 +1117,8 @@
 	/* FIXME Should I flush the device here? - not doing it for now */
 
 	/* shutdown our bulk reads and writes */
-	usb_unlink_urb (&port->write_urb);
-	usb_unlink_urb (&port->read_urb);
+	usb_unlink_urb (port->write_urb);
+	usb_unlink_urb (port->read_urb);
 	port->active = 0;
 }
 
@@ -1116,15 +1129,13 @@
    B1 0
    B2..7 length of message excluding byte 0
 */
-static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user, 
-				  const unsigned char *buf, int count)
+static int ftdi_sio_write (struct usb_serial_port *port, int from_user, 
+			   const unsigned char *buf, int count)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 	const int data_offset = 1;
 
-	dbg("ftdi_sio_serial_write port %d, %d bytes", portNumber, count);
+	dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count);
 
 	if (count == 0) {
 		dbg("write request of 0 bytes");
@@ -1133,9 +1144,9 @@
 
 	/* only do something if we have a bulk out endpoint */
 	if (serial->num_bulk_out) {
-		unsigned char *first_byte = port->write_urb.transfer_buffer;
+		unsigned char *first_byte = port->write_urb->transfer_buffer;
 
-		if (port->write_urb.status == -EINPROGRESS) {
+		if (port->write_urb->status == -EINPROGRESS) {
 			dbg ("already writing");
 			return 0;
 		}
@@ -1148,16 +1159,16 @@
 
 		/* Copy in the data to send */
 		if (from_user) {
-			copy_from_user(port->write_urb.transfer_buffer + data_offset , 
+			copy_from_user(port->write_urb->transfer_buffer + data_offset , 
 				       buf, count - data_offset );
 		}
 		else {
-			memcpy(port->write_urb.transfer_buffer + data_offset,
+			memcpy(port->write_urb->transfer_buffer + data_offset,
 			       buf, count - data_offset );
 		}  
 
 		/* Write the control byte at the front of the packet*/
-		first_byte = port->write_urb.transfer_buffer;
+		first_byte = port->write_urb->transfer_buffer;
 		*first_byte = 1 | ((count-data_offset) << 2) ; 
 
 #ifdef DEBUG
@@ -1181,9 +1192,9 @@
 
 #endif
 		/* send the data out the bulk port */
-		port->write_urb.transfer_buffer_length = count;
+		port->write_urb->transfer_buffer_length = count;
 
-		if (usb_submit_urb(&port->write_urb))
+		if (usb_submit_urb(port->write_urb))
 			dbg("usb_submit_urb(write bulk) failed");
 
 		dbg("write returning: %d", count - data_offset);
@@ -1197,14 +1208,24 @@
 
 static void ftdi_sio_read_bulk_callback (struct urb *urb)
 { /* ftdi_sio_serial_buld_callback */
-	struct usb_serial *serial = (struct usb_serial *)urb->context;
-       	struct tty_struct *tty = serial->tty; 
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty;
        	unsigned char *data = urb->transfer_buffer;
 	const int data_offset = 2;
 	int i;
 
 	dbg("ftdi_sio_read_bulk_callback");
 
+	if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
+		return;
+	}
+	
 	if (urb->status) {
 		dbg("nonzero read bulk status received: %d", urb->status);
 		return;
@@ -1227,6 +1248,7 @@
 	
 
 	if (urb->actual_length > data_offset) {
+		tty = port->tty;
 		for (i = data_offset ; i < urb->actual_length ; ++i) {
 			tty_insert_flip_char(tty, data[i], 0);
 	  	}
@@ -1241,14 +1263,14 @@
 } /* ftdi_sio_serial_read_bulk_callback */
 
 
-static void ftdi_sio_set_termios (struct tty_struct *tty, struct termios *old_termios)
+static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-	unsigned int cflag = tty->termios->c_cflag;
+	struct usb_serial *serial = port->serial;
+	unsigned int cflag = port->tty->termios->c_cflag;
 	__u16 urb_value; /* Will hold the new flags */
 	char buf[1]; /* Perhaps I should dynamically alloc this? */
-	dbg("ftdi_sio_set_termios port %d", port);
+	
+	dbg("ftdi_sio_set_termios port %d", port->number);
 
 	/* FIXME - we should keep the old termios really */
 	/* FIXME -For this cut I don't care if the line is really changing or 
@@ -1312,14 +1334,14 @@
 
 
 /*FIXME - the beginnings of this implementation - not even hooked into the driver yet */
-static int ftdi_sio_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
+	struct usb_serial *serial = port->serial;
 	__u16 urb_value=0; /* Will hold the new flags */
 	char buf[1];
 	int  ret, mask;
-	dbg("ftdi_sio_ioctl port %d", port);
+	
+	dbg("ftdi_sio_ioctl port %d", port->number);
 
 	/* Based on code from acm.c */
 	switch (cmd) {
@@ -1408,8 +1430,9 @@
 
 static void keyspan_pda_rx_interrupt (struct urb *urb)
 {
-	struct usb_serial *serial = (struct usb_serial *) urb->context;
-	struct tty_struct *tty = serial->tty;
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
 	int i;
 
@@ -1417,11 +1440,21 @@
 	if (urb->status)
 		return;
 	
-	/* see if the message is data or a status interrupt */
+	if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+	
+ 	/* see if the message is data or a status interrupt */
 	switch (data[0]) {
 	case 0:
 		/* rest of message is rx data */
 		if (urb->actual_length) {
+			tty = serial->port[0].tty;
 			for (i = 1; i < urb->actual_length ; ++i) {
 				tty_insert_flip_char(tty, data[i], 0);
 			}
@@ -1435,6 +1468,7 @@
 		case 1: /* modemline change */
 			break;
 		case 2: /* tx unthrottle interrupt */
+			tty = serial->port[0].tty;
 			serial->tx_throttled = 0;
 			wake_up(&serial->write_wait); /* wake up writer */
 			wake_up(&tty->write_wait); /* them too */
@@ -1451,11 +1485,8 @@
 }
 
 
-static void keyspan_pda_rx_throttle (struct tty_struct *tty)
+static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
 	/* stop receiving characters. We just turn off the URB request, and
 	   let chars pile up in the device. If we're doing hardware
 	   flowcontrol, the device will signal the other end when its buffer
@@ -1463,19 +1494,16 @@
 	   send an XOFF, although it might make sense to foist that off
 	   upon the device too. */
 
-	dbg("keyspan_pda_rx_throttle port %d", port);
-	usb_unlink_urb(&serial->port[port].read_urb);
+	dbg("keyspan_pda_rx_throttle port %d", port->number);
+	usb_unlink_urb(port->read_urb);
 }
 
 
-static void keyspan_pda_rx_unthrottle (struct tty_struct *tty)
+static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
 	/* just restart the receive interrupt URB */
-	dbg("keyspan_pda_rx_unthrottle port %d", port);
-	if (usb_submit_urb(&serial->port[port].read_urb))
+	dbg("keyspan_pda_rx_unthrottle port %d", port->number);
+	if (usb_submit_urb(port->read_urb))
 		dbg(" usb_submit_urb(read urb) failed");
 	return;
 }
@@ -1516,9 +1544,9 @@
 }
 
 
-static void keyspan_pda_break_ctl (struct tty_struct *tty, int break_state)
+static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+	struct usb_serial *serial = port->serial;
 	int value;
 	if (break_state == -1)
 		value = 1; /* start break */
@@ -1535,11 +1563,11 @@
 }
 
 
-static void keyspan_pda_set_termios (struct tty_struct *tty,
+static void keyspan_pda_set_termios (struct usb_serial_port *port, 
 				     struct termios *old_termios)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	unsigned int cflag = tty->termios->c_cflag;
+	struct usb_serial *serial = port->serial;
+	unsigned int cflag = port->tty->termios->c_cflag;
 
 	/* cflag specifies lots of stuff: number of stop bits, parity, number
 	   of data bits, baud. What can the device actually handle?:
@@ -1611,10 +1639,10 @@
 }
 
 
-static int keyspan_pda_ioctl(struct tty_struct *tty, struct file *file,
+static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file,
 			     unsigned int cmd, unsigned long arg)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+	struct usb_serial *serial = port->serial;
 	int rc;
 	unsigned int value;
 	unsigned char status, mask;
@@ -1677,11 +1705,10 @@
 	return -ENOIOCTLCMD;
 }
 
-static int keyspan_pda_write(struct tty_struct * tty, int from_user, 
+static int keyspan_pda_write(struct usb_serial_port *port, int from_user, 
 			     const unsigned char *buf, int count)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
+	struct usb_serial *serial = port->serial;
 	int request_unthrottle = 0;
 	int rc = 0;
 	DECLARE_WAITQUEUE(wait, current);
@@ -1704,7 +1731,7 @@
 	   the TX urb is in-flight (wait until it completes)
 	   the device is full (wait until it says there is room)
 	*/
-	while (serial->port[port].write_urb.status == -EINPROGRESS) {
+	while (port->write_urb->status == -EINPROGRESS) {
 		if (0 /* file->f_flags & O_NONBLOCK */) {
 			rc = -EAGAIN;
 			goto err;
@@ -1746,8 +1773,7 @@
 	remove_wait_queue(&serial->write_wait, &wait);
 	set_current_state(TASK_RUNNING);
 
-	count = (count > serial->port[port].bulk_out_size) ? 
-		serial->port[port].bulk_out_size : count;
+	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 	if (count > serial->tx_room) {
 		unsigned char room;
 		/* Looks like we might overrun the Tx buffer. Ask the device
@@ -1784,17 +1810,15 @@
 	if (count) {
 		/* now transfer data */
 		if (from_user) {
-			copy_from_user(serial->port[port].write_urb.transfer_buffer,
-				       buf, count);
+			copy_from_user(port->write_urb->transfer_buffer, buf, count);
 		}
 		else {
-			memcpy (serial->port[port].write_urb.transfer_buffer, 
-				buf, count);
+			memcpy (port->write_urb->transfer_buffer, buf, count);
 		}  
 		/* send the data out the bulk port */
-		serial->port[port].write_urb.transfer_buffer_length = count;
+		port->write_urb->transfer_buffer_length = count;
 		
-		if (usb_submit_urb(&serial->port[port].write_urb))
+		if (usb_submit_urb(port->write_urb))
 			dbg(" usb_submit_urb(write bulk) failed");
 	}
 	else {
@@ -1828,11 +1852,22 @@
 
 static void keyspan_pda_write_bulk_callback (struct urb *urb)
 {
-	struct usb_serial *serial = (struct usb_serial *) urb->context;
-	struct tty_struct *tty = serial->tty; 
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty;
+
+	if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
 
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+	
 	wake_up_interruptible(&serial->write_wait);
 
+	tty = port->tty;
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && 
 	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
@@ -1841,9 +1876,9 @@
 }
 
 
-static int keyspan_pda_write_room (struct tty_struct *tty)
+static int keyspan_pda_write_room (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
+	struct usb_serial *serial = port->serial;
 
 	/* used by n_tty.c for processing of tabs and such. Giving it our
 	   conservative guess is probably good enough, but needs testing by
@@ -1853,9 +1888,9 @@
 }
 
 
-static int keyspan_pda_chars_in_buffer (struct tty_struct *tty) 
+static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
+	struct usb_serial *serial = port->serial;
 	
 	/* when throttled, return at least WAKEUP_CHARS to tell select() (via
 	   n_tty.c:normal_poll() ) that we're not writeable. */
@@ -1865,11 +1900,9 @@
 }
 
 
-static int keyspan_pda_serial_open (struct tty_struct *tty, struct file *filp)
+static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 	unsigned char room;
 	int rc;
 
@@ -1901,33 +1934,30 @@
 
 	/* the normal serial device seems to always turn on DTR and RTS here,
 	   so do the same */
-	if (tty->termios->c_cflag & CBAUD)
+	if (port->tty->termios->c_cflag & CBAUD)
 		keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) );
 	else
 		keyspan_pda_set_modem_info(serial, 0);
 
 	/*Start reading from the device*/
-	if (usb_submit_urb(&port->read_urb))
+	if (usb_submit_urb(port->read_urb))
 		dbg(" usb_submit_urb(read int) failed");
 
 	return (0);
 }
 
 
-static void keyspan_pda_serial_close(struct tty_struct *tty, 
-				     struct file *filp)
+static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 
 	/* the normal serial device seems to always shut off DTR and RTS now */
-	if (tty->termios->c_cflag & HUPCL)
+	if (port->tty->termios->c_cflag & HUPCL)
 		keyspan_pda_set_modem_info(serial, 0);
 
 	/* shutdown our bulk reads and writes */
-	usb_unlink_urb (&port->write_urb);
-	usb_unlink_urb (&port->read_urb);
+	usb_unlink_urb (port->write_urb);
+	usb_unlink_urb (port->read_urb);
 	port->active = 0;
 }
 
@@ -1971,7 +2001,7 @@
 	intin = serial->port[0].interrupt_in_endpoint;
 
 	/* set up the receive interrupt urb */
-	FILL_INT_URB(&serial->port[0].read_urb, serial->dev,
+	FILL_INT_URB(serial->port[0].read_urb, serial->dev,
 		     usb_rcvintpipe(serial->dev, intin->bEndpointAddress),
 		     serial->port[0].interrupt_in_buffer,
 		     intin->wMaxPacketSize,
@@ -1990,13 +2020,11 @@
 /*****************************************************************************
  * generic devices specific driver functions
  *****************************************************************************/
-static int generic_serial_open (struct tty_struct *tty, struct file *filp)
+static int generic_open (struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 
-	dbg("generic_serial_open port %d", portNumber);
+	dbg("generic_open port %d", port->number);
 
 	if (port->active) {
 		dbg ("device already open");
@@ -2007,7 +2035,7 @@
 	/* if we have a bulk interrupt, start reading from it */
 	if (serial->num_bulk_in) {
 		/*Start reading from the device*/
-		if (usb_submit_urb(&port->read_urb))
+		if (usb_submit_urb(port->read_urb))
 			dbg("usb_submit_urb(read bulk) failed");
 	}
 
@@ -2015,33 +2043,29 @@
 }
 
 
-static void generic_serial_close(struct tty_struct *tty, struct file * filp)
+static void generic_close (struct usb_serial_port *port, struct file * filp)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 
-	dbg("generic_serial_close port %d", portNumber);
+	dbg("generic_close port %d", port->number);
 	
 	/* shutdown any bulk reads that might be going on */
 	if (serial->num_bulk_out) {
-		usb_unlink_urb (&port->write_urb);
+		usb_unlink_urb (port->write_urb);
 	}
 	if (serial->num_bulk_in) {
-		usb_unlink_urb (&port->read_urb);
+		usb_unlink_urb (port->read_urb);
 	}
 
 	port->active = 0;
 }
 
 
-static int generic_serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
 {
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 
-	dbg("generic_serial_write port %d", portNumber);
+	dbg("generic_serial_write port %d", port->number);
 
 	if (count == 0) {
 		dbg("write request of 0 bytes");
@@ -2050,7 +2074,7 @@
 
 	/* only do something if we have a bulk out endpoint */
 	if (serial->num_bulk_out) {
-		if (port->write_urb.status == -EINPROGRESS) {
+		if (port->write_urb->status == -EINPROGRESS) {
 			dbg ("already writing");
 			return (0);
 		}
@@ -2058,16 +2082,16 @@
 		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 
 		if (from_user) {
-			copy_from_user(port->write_urb.transfer_buffer, buf, count);
+			copy_from_user(port->write_urb->transfer_buffer, buf, count);
 		}
 		else {
-			memcpy (port->write_urb.transfer_buffer, buf, count);
+			memcpy (port->write_urb->transfer_buffer, buf, count);
 		}  
 
 		/* send the data out the bulk port */
-		port->write_urb.transfer_buffer_length = count;
+		port->write_urb->transfer_buffer_length = count;
 
-		if (usb_submit_urb(&port->write_urb))
+		if (usb_submit_urb(port->write_urb))
 			dbg("usb_submit_urb(write bulk) failed");
 
 		return (count);
@@ -2078,17 +2102,15 @@
 } 
 
 
-static int generic_write_room (struct tty_struct *tty) 
+static int generic_write_room (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 	int room;
 
-	dbg("generic_write_room port %d", portNumber);
+	dbg("generic_write_room port %d", port->number);
 	
 	if (serial->num_bulk_out) {
-		if (port->write_urb.status == -EINPROGRESS)
+		if (port->write_urb->status == -EINPROGRESS)
 			room = 0;
 		else
 			room = port->bulk_out_size;
@@ -2100,16 +2122,14 @@
 }
 
 
-static int generic_chars_in_buffer (struct tty_struct *tty) 
+static int generic_chars_in_buffer (struct usb_serial_port *port)
 {
-	struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
-	int portNumber = MINOR(tty->device) - serial->minor;
-	struct usb_serial_port *port = &serial->port[portNumber];
+	struct usb_serial *serial = port->serial;
 
-	dbg("generic_chars_in_buffer port %d", portNumber);
+	dbg("generic_chars_in_buffer port %d", port->number);
 	
 	if (serial->num_bulk_out) {
-		if (port->write_urb.status == -EINPROGRESS) {
+		if (port->write_urb->status == -EINPROGRESS) {
 			return (port->bulk_out_size);
 		}
 	}
@@ -2118,43 +2138,25 @@
 }
 
 
-static void generic_throttle (struct tty_struct *tty) 
-{
-	/* do nothing for the generic device */
-	return;
-}
-
-
-static void generic_unthrottle (struct tty_struct *tty) 
-{
-	/* do nothing for the generic device */
-	return;
-}
-
-
-static int generic_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
-{
-	/* generic driver doesn't support any ioctls yet */
-	return -ENOIOCTLCMD;
-}
-
-
-static void generic_set_termios (struct tty_struct *tty, struct termios * old)
-{
-	/* generic driver doesn't really care about setting any line settings */
-	return;
-}
-
-
 static void generic_read_bulk_callback (struct urb *urb)
 {
-	struct usb_serial *serial = (struct usb_serial *)urb->context;
-       	struct tty_struct *tty = serial->tty; 
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty;
        	unsigned char *data = urb->transfer_buffer;
 	int i;
 
 	dbg("generic_read_bulk_callback");
 
+	if (port_paranoia_check (port, "generic_read_bulk_callback")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "generic_read_bulk_callback")) {
+		return;
+	}
+	
 	if (urb->status) {
 		dbg("nonzero read bulk status received: %d", urb->status);
 		return;
@@ -2169,7 +2171,8 @@
 		printk ("\n");
 	}
 #endif
-	
+
+	tty = port->tty;
 	if (urb->actual_length) {
 		for (i = 0; i < urb->actual_length ; ++i) {
 			 tty_insert_flip_char(tty, data[i], 0);
@@ -2187,16 +2190,27 @@
 
 static void generic_write_bulk_callback (struct urb *urb)
 {
-	struct usb_serial *serial = (struct usb_serial *) urb->context; 
-       	struct tty_struct *tty = serial->tty; 
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty;
 
 	dbg("generic_write_bulk_callback");
 
+	if (port_paranoia_check (port, "generic_write_bulk_callback")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "generic_write_bulk_callback")) {
+		return;
+	}
+	
 	if (urb->status) {
 		dbg("nonzero write bulk status received: %d", urb->status);
 		return;
 	}
 
+	tty = port->tty;
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
 
@@ -2206,7 +2220,6 @@
 }
 
 
-
 static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
 {
 	struct usb_serial *serial = NULL;
@@ -2331,6 +2344,11 @@
 				/* set up the endpoint information */
 				for (i = 0; i < num_bulk_in; ++i) {
 					port = &serial->port[i];
+					port->read_urb = usb_alloc_urb (0);
+					if (!port->read_urb) {
+						err("No free urbs available");
+						goto probe_error;
+					}
 					buffer_size = bulk_in_endpoint[i]->wMaxPacketSize;
 					port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
 					if (!port->bulk_in_buffer) {
@@ -2338,16 +2356,21 @@
 						goto probe_error;
 					}
 					if (serial->type->read_bulk_callback) {
-						FILL_BULK_URB(&port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
-								port->bulk_in_buffer, buffer_size, serial->type->read_bulk_callback, serial);
+						FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+								port->bulk_in_buffer, buffer_size, serial->type->read_bulk_callback, port);
 					} else {
-						FILL_BULK_URB(&port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
-								port->bulk_in_buffer, buffer_size, generic_read_bulk_callback, serial);
+						FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+								port->bulk_in_buffer, buffer_size, generic_read_bulk_callback, port);
 					}
 				}
 
 				for (i = 0; i < num_bulk_out; ++i) {
 					port = &serial->port[i];
+					port->write_urb = usb_alloc_urb(0);
+					if (!port->write_urb) {
+						err("No free urbs available");
+						goto probe_error;
+					}
 					port->bulk_out_size = bulk_out_endpoint[i]->wMaxPacketSize;
 					port->bulk_out_buffer = kmalloc (port->bulk_out_size, GFP_KERNEL);
 					if (!port->bulk_out_buffer) {
@@ -2355,25 +2378,31 @@
 						goto probe_error;
 					}
 					if (serial->type->write_bulk_callback) {
-						FILL_BULK_URB(&port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
-								port->bulk_out_buffer, port->bulk_out_size, serial->type->write_bulk_callback, serial);
+						FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+								port->bulk_out_buffer, port->bulk_out_size, serial->type->write_bulk_callback, port);
 					} else {
-						FILL_BULK_URB(&port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
-								port->bulk_out_buffer, port->bulk_out_size, generic_write_bulk_callback, serial);
+						FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+								port->bulk_out_buffer, port->bulk_out_size, generic_write_bulk_callback, port);
 					}
 				}
 
 #if 0 /* use this code when WhiteHEAT is up and running */
 				for (i = 0; i < num_interrupt_in; ++i) {
+					port = &serial->port[i];
+					port->control_urb = usb_alloc_urb(0);
+					if (!port->control_urb) {
+						err("No free urbs available");
+						goto probe_error;
+					}
 					buffer_size = interrupt_in_endpoint[i]->wMaxPacketSize;
-					serial->interrupt_in_buffer[i] = kmalloc (buffer_size, GFP_KERNEL);
-					if (!serial->interrupt_in_buffer[i]) {
+					port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+					if (!port->interrupt_in_buffer) {
 						err("Couldn't allocate interrupt_in_buffer");
 						goto probe_error;
 					}
-					FILL_INT_URB(&serial->control_urb[i], dev, usb_rcvintpipe (dev, interrupt_in_endpoint[i]->bEndpointAddress),
-							serial->interrupt_in_buffer[i], buffer_size, serial_control_irq,
-							serial, interrupt_in_endpoint[i]->bInterval);
+					FILL_INT_URB(port->control_urb, dev, usb_rcvintpipe (dev, interrupt_in_endpoint[i]->bEndpointAddress),
+							port->interrupt_in_buffer, buffer_size, serial_control_irq,
+							port, interrupt_in_endpoint[i]->bInterval);
 				}
 #endif
 
@@ -2395,15 +2424,27 @@
 
 probe_error:
 	if (serial) {
-		for (i = 0; i < num_bulk_in; ++i)
+		for (i = 0; i < num_bulk_in; ++i) {
+			port = &serial->port[i];
+			if (port->read_urb)
+				usb_free_urb (port->read_urb);
 			if (serial->port[i].bulk_in_buffer[i])
 				kfree (serial->port[i].bulk_in_buffer);
-		for (i = 0; i < num_bulk_out; ++i)
+		}
+		for (i = 0; i < num_bulk_out; ++i) {
+			port = &serial->port[i];
+			if (port->write_urb)
+				usb_free_urb (port->write_urb);
 			if (serial->port[i].bulk_out_buffer)
 				kfree (serial->port[i].bulk_out_buffer);
-		for (i = 0; i < num_interrupt_in; ++i)
+		}
+		for (i = 0; i < num_interrupt_in; ++i) {
+			port = &serial->port[i];
+			if (port->control_urb)
+				usb_free_urb (port->control_urb);
 			if (serial->port[i].interrupt_in_buffer)
 				kfree (serial->port[i].interrupt_in_buffer);
+		}
 		
 		/* return the minor range that this device had */
 		return_serial (serial);
@@ -2422,27 +2463,33 @@
 	int i;
 
 	if (serial) {
-		/* need to stop any transfers...*/
-		for (i = 0; i < serial->num_ports; ++i) {
-			port = &serial->port[i];
-			usb_unlink_urb (&port->write_urb);
-			usb_unlink_urb (&port->read_urb);
-			port->active = 0;
-		}
+		for (i = 0; i < serial->num_ports; ++i)
+			serial->port[i].active = 0;
 
-		/* free up any memory that we allocated */
 		for (i = 0; i < serial->num_bulk_in; ++i) {
 			port = &serial->port[i];
+			if (port->read_urb) {
+				usb_unlink_urb (port->read_urb);
+				usb_free_urb (port->read_urb);
+			}
 			if (port->bulk_in_buffer)
 				kfree (port->bulk_in_buffer);
 		}
 		for (i = 0; i < serial->num_bulk_out; ++i) {
 			port = &serial->port[i];
+			if (port->write_urb) {
+				usb_unlink_urb (port->write_urb);
+				usb_free_urb (port->write_urb);
+			}
 			if (port->bulk_out_buffer)
 				kfree (port->bulk_out_buffer);
 		}
 		for (i = 0; i < serial->num_interrupt_in; ++i) {
 			port = &serial->port[i];
+			if (port->control_urb) {
+				usb_unlink_urb (port->control_urb);
+				usb_free_urb (port->control_urb);
+			}
 			if (port->interrupt_in_buffer)
 				kfree (port->interrupt_in_buffer);
 		}
@@ -2468,7 +2515,7 @@
 static struct tty_driver serial_tty_driver = {
 	magic:			TTY_DRIVER_MAGIC,
 	driver_name:		"usb",
-	name:			"ttyUSB",
+	name:			"ttyUSB%d",
 	major:			SERIAL_TTY_MAJOR,
 	minor_start:		0,
 	num:			SERIAL_TTY_MINORS,
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/serial/usb-serial.h linux/drivers/usb/serial/usb-serial.h
--- v2.3.99-pre2/linux/drivers/usb/serial/usb-serial.h	Sun Mar 19 18:35:31 2000
+++ linux/drivers/usb/serial/usb-serial.h	Sun Mar 19 18:15:06 2000
@@ -52,8 +52,12 @@
 
 #define MAX_NUM_PORTS	8	/* The maximum number of ports one device can grab at once */
 
+#define USB_SERIAL_MAGIC	0x6702	/* magic number for usb_serial struct */
+#define USB_SERIAL_PORT_MAGIC	0x7301	/* magic number for usb_serial_port struct */
+
 
 struct usb_serial_port {
+	int			magic;
 	struct usb_serial	*serial;	/* pointer back to the owner of this port */
 	struct tty_struct *	tty;		/* the coresponding tty for this device */
 	unsigned char		minor;
@@ -63,21 +67,21 @@
 	struct usb_endpoint_descriptor * interrupt_in_endpoint;
 	__u8			interrupt_in_interval;
 	unsigned char *		interrupt_in_buffer;
-	struct urb		control_urb;
+	struct urb *		control_urb;
 
 	unsigned char *		bulk_in_buffer;
-	struct urb		read_urb;
+	struct urb *		read_urb;
 
 	unsigned char *		bulk_out_buffer;
 	int			bulk_out_size;
-	struct urb		write_urb;
+	struct urb *		write_urb;
 	void *			private;	/* data private to the specific driver */
 };
 
 struct usb_serial {
+	int				magic;
 	struct usb_device *		dev;
 	struct usb_serial_device_type *	type;
-	struct tty_struct *		tty;			/* the coresponding tty for this device */
 	unsigned char			minor;
 	unsigned char			num_ports;		/* the number of ports this device has */
 	char				num_interrupt_in;	/* number of interrupt in endpoints we have */
@@ -121,16 +125,16 @@
 	int (*startup) (struct usb_serial *serial);	/* return 0 to continue initialization, anything else to abort */
 	
 	/* serial function calls */
-	int  (*open)(struct tty_struct * tty, struct file * filp);
-	void (*close)(struct tty_struct * tty, struct file * filp);
-	int  (*write)(struct tty_struct * tty, int from_user,const unsigned char *buf, int count);
-	int  (*write_room)(struct tty_struct *tty);
-	int  (*ioctl)(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
-	void (*set_termios)(struct tty_struct *tty, struct termios * old);
-	void (*break_ctl)(struct tty_struct *tty, int break_state);
-	int  (*chars_in_buffer)(struct tty_struct *tty);
-	void (*throttle)(struct tty_struct * tty);
-	void (*unthrottle)(struct tty_struct * tty);
+	int  (*open)		(struct usb_serial_port *port, struct file * filp);
+	void (*close)		(struct usb_serial_port *port, struct file * filp);
+	int  (*write)		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+	int  (*write_room)	(struct usb_serial_port *port);
+	int  (*ioctl)		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+	void (*set_termios)	(struct usb_serial_port *port, struct termios * old);
+	void (*break_ctl)	(struct usb_serial_port *port, int break_state);
+	int  (*chars_in_buffer)	(struct usb_serial_port *port);
+	void (*throttle)	(struct usb_serial_port *port);
+	void (*unthrottle)	(struct usb_serial_port *port);
 	
 	void (*read_bulk_callback)(struct urb *urb);
 	void (*write_bulk_callback)(struct urb *urb);
@@ -140,15 +144,11 @@
 /* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
 /* need to always compile these in, as some of the other devices use these functions as their own. */
 /* if a driver does not provide a function pointer, the generic function will be called. */
-static int  generic_serial_open		(struct tty_struct *tty, struct file *filp);
-static void generic_serial_close	(struct tty_struct *tty, struct file *filp);
-static int  generic_serial_write	(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
-static int  generic_write_room		(struct tty_struct *tty);
-static int  generic_ioctl		(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
-static void generic_set_termios		(struct tty_struct *tty, struct termios * old);
-static int  generic_chars_in_buffer	(struct tty_struct *tty);
-static void generic_throttle		(struct tty_struct *tty);
-static void generic_unthrottle		(struct tty_struct *tty);
+static int  generic_open		(struct usb_serial_port *port, struct file *filp);
+static void generic_close		(struct usb_serial_port *port, struct file *filp);
+static int  generic_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int  generic_write_room		(struct usb_serial_port *port);
+static int  generic_chars_in_buffer	(struct usb_serial_port *port);
 static void generic_read_bulk_callback	(struct urb *urb);
 static void generic_write_bulk_callback	(struct urb *urb);
 
@@ -172,11 +172,11 @@
 
 #ifdef CONFIG_USB_SERIAL_WHITEHEAT
 /* function prototypes for the Connect Tech WhiteHEAT serial converter */
-static int  whiteheat_serial_open	(struct tty_struct *tty, struct file *filp);
-static void whiteheat_serial_close	(struct tty_struct *tty, struct file *filp);
-static void whiteheat_set_termios	(struct tty_struct *tty, struct termios * old);
-static void whiteheat_throttle		(struct tty_struct *tty);
-static void whiteheat_unthrottle	(struct tty_struct *tty);
+static int  whiteheat_open		(struct usb_serial_port *port, struct file *filp);
+static void whiteheat_close		(struct usb_serial_port *port, struct file *filp);
+static void whiteheat_set_termios	(struct usb_serial_port *port, struct termios * old);
+static void whiteheat_throttle		(struct usb_serial_port *port);
+static void whiteheat_unthrottle	(struct usb_serial_port *port);
 static int  whiteheat_startup		(struct usb_serial *serial);
 
 /* All of the device info needed for the Connect Tech WhiteHEAT */
@@ -193,6 +193,7 @@
 	num_interrupt_in:	NUM_DONT_CARE,
 	num_bulk_in:		NUM_DONT_CARE,
 	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
 	startup:		whiteheat_startup	
 };
 static struct usb_serial_device_type whiteheat_device = {
@@ -206,8 +207,8 @@
 	num_bulk_in:		NUM_DONT_CARE,
 	num_bulk_out:		NUM_DONT_CARE,
 	num_ports:		4,
-	open:			whiteheat_serial_open,
-	close:			whiteheat_serial_close,
+	open:			whiteheat_open,
+	close:			whiteheat_close,
 	throttle:		whiteheat_throttle,
 	unthrottle:		whiteheat_unthrottle,
 	set_termios:		whiteheat_set_termios,
@@ -269,11 +270,11 @@
 
 
 /* function prototypes for a handspring visor */
-static int  visor_serial_open		(struct tty_struct *tty, struct file *filp);
-static void visor_serial_close		(struct tty_struct *tty, struct file *filp);
-static void visor_throttle		(struct tty_struct *tty);
-static void visor_unthrottle		(struct tty_struct *tty);
-static int  visor_startup		(struct usb_serial *serial);
+static int  visor_open		(struct usb_serial_port *port, struct file *filp);
+static void visor_close		(struct usb_serial_port *port, struct file *filp);
+static void visor_throttle	(struct usb_serial_port *port);
+static void visor_unthrottle	(struct usb_serial_port *port);
+static int  visor_startup	(struct usb_serial *serial);
 
 /* All of the device info needed for the Handspring Visor */
 static __u16	handspring_vendor_id	= HANDSPRING_VENDOR_ID;
@@ -289,8 +290,8 @@
 	num_bulk_in:		2,
 	num_bulk_out:		2,
 	num_ports:		2,
-	open:			visor_serial_open,
-	close:			visor_serial_close,
+	open:			visor_open,
+	close:			visor_close,
 	throttle:		visor_throttle,
 	unthrottle:		visor_unthrottle,
 	startup:		visor_startup,
@@ -300,12 +301,12 @@
 
 #ifdef CONFIG_USB_SERIAL_FTDI_SIO
 /* function prototypes for a FTDI serial converter */
-static int  ftdi_sio_serial_open	(struct tty_struct *tty, struct file *filp);
-static void ftdi_sio_serial_close	(struct tty_struct *tty, struct file *filp);
-static int  ftdi_sio_serial_write	(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static int  ftdi_sio_open		(struct usb_serial_port *port, struct file *filp);
+static void ftdi_sio_close		(struct usb_serial_port *port, struct file *filp);
+static int  ftdi_sio_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
 static void ftdi_sio_read_bulk_callback	(struct urb *urb);
-static void ftdi_sio_set_termios		(struct tty_struct *tty, struct termios * old);
-static int ftdi_sio_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
+static void ftdi_sio_set_termios	(struct usb_serial_port *port, struct termios * old);
+static int  ftdi_sio_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
 
 /* All of the device info needed for the FTDI SIO serial converter */
 static __u16	ftdi_vendor_id		= FTDI_VENDOR_ID;
@@ -321,9 +322,10 @@
 	num_bulk_in:		1,
 	num_bulk_out:		1,
 	num_ports:		1,
-	open:			ftdi_sio_serial_open,
-	close:			ftdi_sio_serial_close,
-	write:			ftdi_sio_serial_write,
+	open:			ftdi_sio_open,
+	close:			ftdi_sio_close,
+	write:			ftdi_sio_write,
+	ioctl:			ftdi_sio_ioctl,
 	read_bulk_callback:	ftdi_sio_read_bulk_callback,
 	set_termios:		ftdi_sio_set_termios
 };
@@ -332,28 +334,28 @@
 
 #ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
 /* function prototypes for a Keyspan PDA serial converter */
-static int  keyspan_pda_serial_open	(struct tty_struct *tty, 
+static int  keyspan_pda_open		(struct usb_serial_port *port,
 					 struct file *filp);
-static void keyspan_pda_serial_close	(struct tty_struct *tty, 
+static void keyspan_pda_close		(struct usb_serial_port *port,
 					 struct file *filp);
 static int  keyspan_pda_startup		(struct usb_serial *serial);
-static void keyspan_pda_rx_throttle	(struct tty_struct *tty);
-static void keyspan_pda_rx_unthrottle	(struct tty_struct *tty);
+static void keyspan_pda_rx_throttle	(struct usb_serial_port *port);
+static void keyspan_pda_rx_unthrottle	(struct usb_serial_port *port);
 static int  keyspan_pda_setbaud		(struct usb_serial *serial, int baud);
-static int  keyspan_pda_write_room	(struct tty_struct *tty);
-static int  keyspan_pda_write		(struct tty_struct *tty,
+static int  keyspan_pda_write_room	(struct usb_serial_port *port);
+static int  keyspan_pda_write		(struct usb_serial_port *port,
 					 int from_user,
 					 const unsigned char *buf,
 					 int count);
 static void keyspan_pda_write_bulk_callback (struct urb *urb);
-static int  keyspan_pda_chars_in_buffer (struct tty_struct *tty);
-static int  keyspan_pda_ioctl		(struct tty_struct *tty,
+static int  keyspan_pda_chars_in_buffer (struct usb_serial_port *port);
+static int  keyspan_pda_ioctl		(struct usb_serial_port *port,
 					 struct file *file,
 					 unsigned int cmd,
 					 unsigned long arg);
-static void keyspan_pda_set_termios	(struct tty_struct *tty,
+static void keyspan_pda_set_termios	(struct usb_serial_port *port,
 					 struct termios *old);
-static void keyspan_pda_break_ctl	(struct tty_struct *tty,
+static void keyspan_pda_break_ctl	(struct usb_serial_port *port,
 					 int break_state);
 static int  keyspan_pda_fake_startup	(struct usb_serial *serial);
 
@@ -385,8 +387,8 @@
 	num_bulk_in:		0,
 	num_bulk_out:		1,
 	num_ports:		1,
-	open:			keyspan_pda_serial_open,
-	close:			keyspan_pda_serial_close,
+	open:			keyspan_pda_open,
+	close:			keyspan_pda_close,
 	write:			keyspan_pda_write,
 	write_room:		keyspan_pda_write_room,
 	write_bulk_callback: 	keyspan_pda_write_bulk_callback,
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- v2.3.99-pre2/linux/drivers/usb/uhci.c	Sun Mar 19 18:35:31 2000
+++ linux/drivers/usb/uhci.c	Thu Mar 23 14:59:08 2000
@@ -1315,8 +1315,8 @@
 	0x00,			/*  __u16 bcdDevice; */
 	0x00,
 	0x00,			/*  __u8  iManufacturer; */
-	0x00,			/*  __u8  iProduct; */
-	0x00,			/*  __u8  iSerialNumber; */
+	0x02,			/*  __u8  iProduct; */
+	0x01,			/*  __u8  iSerialNumber; */
 	0x01			/*  __u8  bNumConfigurations; */
 };
 
@@ -1617,7 +1617,13 @@
 			memcpy (data, root_hub_config_des, len);
 			OK(len);
 		case 0x03:	/* string descriptors */
-			stat = -EPIPE;
+			len = usb_root_hub_string (wValue & 0xff,
+				uhci->io_addr, "UHCI",
+				data, wLength);
+			if (len > 0) {
+				OK (min (leni, len));
+			} else 
+				stat = -EPIPE;
 		}
 		break;
 	case RH_GET_DESCRIPTOR | RH_CLASS:
@@ -1985,6 +1991,15 @@
 {
 	int retval;
 	struct uhci *uhci;
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
 
 	uhci = alloc_uhci(io_addr, io_size);
 	if (!uhci)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- v2.3.99-pre2/linux/drivers/usb/usb-ohci.c	Sun Mar 19 18:35:31 2000
+++ linux/drivers/usb/usb-ohci.c	Thu Mar 23 15:02:18 2000
@@ -262,8 +262,14 @@
 	urb_print (urb, "SUB", usb_pipein (pipe));
 #endif
 	
+	/* a request to the virtual root hub */
 	if (usb_pipedevice (pipe) == ohci->rh.devnum) 
-		return rh_submit_urb (urb); /* a request to the virtual root hub */
+		return rh_submit_urb (urb);
+	
+	/* when controller's hung, permit only hub cleanup attempts
+	 * such as powering down ports */
+	if (ohci->disabled)
+		return -ESHUTDOWN;
 
 	/* every endpoint has a ed, locate and fill it */
 	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
@@ -333,10 +339,11 @@
 								(le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
 	}	
 	
-	td_submit_urb (urb); /* fill the TDs and link it to the ed */
-						
 	if (ed->state != ED_OPER)  /* link the ed into a chain if is not already */
 		ep_link (ohci, ed);
+	
+	td_submit_urb (urb); /* fill the TDs and link it to the ed */
+
 	spin_unlock_irqrestore (&usb_ed_lock, flags);
 	
 	urb->status = USB_ST_URB_PENDING; 
@@ -1122,8 +1129,8 @@
 	0x00,       /*  __u16 bcdDevice; */
  	0x00,
 	0x00,       /*  __u8  iManufacturer; */
-	0x00,       /*  __u8  iProduct; */
-	0x00,       /*  __u8  iSerialNumber; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
 	0x01        /*  __u8  bNumConfigurations; */
 };
 
@@ -1222,13 +1229,16 @@
 
 	urb_t * urb = (urb_t *) ptr;
 	ohci_t * ohci = urb->dev->bus->hcpriv;
+
+	if (ohci->disabled)
+	    return;
 	
 	if(ohci->rh.send) { 
 		len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
 		if (len > 0) {
 			urb->actual_length = len;
 #ifdef DEBUG
-			urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+			urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
 #endif
 			if (urb->complete) urb->complete (urb);
 		}
@@ -1379,27 +1389,49 @@
 					len = min (leni, min (sizeof (root_hub_config_des), wLength));
 					data_buf = root_hub_config_des; OK(len);
 				case (0x03): /* string descriptors */
+					len = usb_root_hub_string (wValue & 0xff,
+						(int) ohci->regs, "OHCI",
+						data, wLength);
+					if (len > 0) {
+						data_buf = data;
+						OK (min (leni, len));
+					}
+					// else fallthrough
 				default: 
 					status = TD_CC_STALL;
 			}
 			break;
 		
 		case RH_GET_DESCRIPTOR | RH_CLASS:
-			*(__u8 *)  (data_buf+1) = 0x29;
-			put_unaligned(cpu_to_le32 (readl (&ohci->regs->roothub.a)),
-				      (__u32 *) (data_buf + 2));
-	 		*(__u8 *)  data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */
-				 
-			len = min (leni, min(*(__u8 *) data_buf, wLength));
-			*(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */
-			if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */
-				*(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff; 
-				*(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16; 
-			} else {
-				put_unaligned(cpu_to_le32 (readl(&ohci->regs->roothub.b)),
-					      (__u32 *) (data_buf + 7));
+		    {
+			    __u32 temp = readl (&ohci->regs->roothub.a);
+
+			    data_buf [0] = 9;		// min length;
+			    data_buf [1] = 0x29;
+			    data_buf [2] = temp & RH_A_NDP;
+			    data_buf [3] = 0;
+			    if (temp & RH_A_PSM) 	/* per-port power switching? */
+				data_buf [3] |= 0x1;
+			    if (temp & RH_A_NOCP)	/* no overcurrent reporting? */
+				data_buf [3] |= 0x10;
+			    else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */
+				data_buf [3] |= 0x8;
+
+			    datab [1] = 0;
+			    data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+			    temp = readl (&ohci->regs->roothub.b);
+			    data_buf [7] = temp & RH_B_DR;
+			    if (data_buf [2] < 7) {
+				data_buf [8] = 0xff;
+			    } else {
+				data_buf [0] += 2;
+				data_buf [8] = (temp & RH_B_DR) >> 8;
+				data_buf [10] = data_buf [9] = 0xff;
+			    }
+				
+			    len = min (leni, min (data_buf [0], wLength));
+			    OK (len);
 			}
-			OK (len); 
  
 		case RH_GET_CONFIGURATION: 	*(__u8 *) data_buf = 0x01; OK (1);
 
@@ -1413,7 +1445,8 @@
 	dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) ));
 
 	len = min(len, leni);
-	memcpy (data, data_buf, len);
+	if (data != data_buf)
+	    memcpy (data, data_buf, len);
   	urb->actual_length = len;
 	urb->status = cc_to_error [status];
 	
@@ -1472,6 +1505,7 @@
 		}	
 		udelay (1);
 	}	 
+	ohci->disabled = 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1502,7 +1536,7 @@
 	writel (0x628, &ohci->regs->lsthresh);
 
 	/* Choose the interrupts we care about now, others later on demand */
-	mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO;
+	mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
 	
 	writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */
 	writel (mask, &ohci->regs->intrenable);
@@ -1550,6 +1584,11 @@
 
 	dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
 	
+	if (ints & OHCI_INTR_UE) {
+		ohci->disabled++;
+		err ("OHCI Unrecoverable Error, controller disabled");
+	}
+  
 	if (ints & OHCI_INTR_WDH) {
 		writel (OHCI_INTR_WDH, &regs->intrdisable);	
 		dl_done_list (ohci, dl_reverse_done_list (ohci));
@@ -1650,16 +1689,24 @@
 static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base)
 {
 	ohci_t * ohci;
-	dbg("USB HC found: irq= %d membase= %lx", irq, (unsigned long) mem_base);
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
+		(unsigned long)	mem_base, bufp);
     
 	ohci = hc_alloc_ohci (mem_base);
 	if (!ohci) {
 		return -ENOMEM;
 	}
-	
+
 	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
 	list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
-	
+
 	hc_reset (ohci);
 	writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
 	wait_ms (10);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/usb-ohci.h linux/drivers/usb/usb-ohci.h
--- v2.3.99-pre2/linux/drivers/usb/usb-ohci.h	Fri Jan 28 15:09:08 2000
+++ linux/drivers/usb/usb-ohci.h	Tue Mar 21 12:37:01 2000
@@ -212,31 +212,53 @@
 	} roothub;
 } __attribute((aligned(32)));
 
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
 /*
- * cmdstatus register */
-#define OHCI_CLF  0x02
-#define OHCI_BLF  0x04
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */
+#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */
+#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */
+#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */
+#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */
+#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */
+#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */
+#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */
+#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#	define OHCI_USB_RESET	(0 << 6)
+#	define OHCI_USB_RESUME	(1 << 6)
+#	define OHCI_USB_OPER	(2 << 6)
+#	define OHCI_USB_SUSPEND	(3 << 6)
 
 /*
- * Interrupt register masks
+ * HcCommandStatus (cmdstatus) register masks
  */
-#define OHCI_INTR_SO	(1)
-#define OHCI_INTR_WDH	(1 << 1)
-#define OHCI_INTR_SF	(1 << 2)
-#define OHCI_INTR_RD	(1 << 3)
-#define OHCI_INTR_UE	(1 << 4)
-#define OHCI_INTR_FNO	(1 << 5)
-#define OHCI_INTR_RHSC	(1 << 6)
-#define OHCI_INTR_OC	(1 << 30)
-#define OHCI_INTR_MIE	(1 << 31)
+#define OHCI_HCR	(1 << 0)	/* host controller reset */
+#define OHCI_CLF  	(1 << 1)	/* control list filled */
+#define OHCI_BLF  	(1 << 2)	/* bulk list filled */
+#define OHCI_OCR  	(1 << 3)	/* ownership change request */
+#define OHCI_SOC  	(3 << 16)	/* scheduling overrun count */
 
 /*
- * Control register masks
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
  */
-#define OHCI_USB_RESET		0
-#define OHCI_USB_RESUME     (1 << 6)
-#define OHCI_USB_OPER		(2 << 6)
-#define OHCI_USB_SUSPEND	(3 << 6)
+#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */
+#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */
+#define OHCI_INTR_SF	(1 << 2)	/* start frame */
+#define OHCI_INTR_RD	(1 << 3)	/* resume detect */
+#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */
+#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */
+#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */
+#define OHCI_INTR_OC	(1 << 30)	/* ownership change */
+#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */
+
 
 
 /* Virtual Root HUB */
@@ -248,6 +270,9 @@
 	int interval;
 	struct timer_list rh_int_timer;
 };
+
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
  
 /* destination of request */
 #define RH_INTERFACE               0x01
@@ -261,8 +286,8 @@
 #define RH_GET_STATUS           0x0080
 #define RH_CLEAR_FEATURE        0x0100
 #define RH_SET_FEATURE          0x0300
-#define RH_SET_ADDRESS			0x0500
-#define RH_GET_DESCRIPTOR		0x0680
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
 #define RH_SET_DESCRIPTOR       0x0700
 #define RH_GET_CONFIGURATION	0x0880
 #define RH_SET_CONFIGURATION	0x0900
@@ -282,6 +307,7 @@
 #define RH_PORT_RESET              0x04
 #define RH_PORT_POWER              0x08
 #define RH_PORT_LOW_SPEED          0x09
+
 #define RH_C_PORT_CONNECTION       0x10
 #define RH_C_PORT_ENABLE           0x11
 #define RH_C_PORT_SUSPEND          0x12
@@ -298,29 +324,44 @@
 #define RH_ACK                     0x01
 #define RH_REQ_ERR                 -1
 #define RH_NACK                    0x00
- 
-/* Root-Hub Register info */
 
-#define RH_PS_CCS            0x00000001   
-#define RH_PS_PES            0x00000002   
-#define RH_PS_PSS            0x00000004   
-#define RH_PS_POCI           0x00000008   
-#define RH_PS_PRS            0x00000010  
-#define RH_PS_PPS            0x00000100   
-#define RH_PS_LSDA           0x00000200    
-#define RH_PS_CSC            0x00010000 
-#define RH_PS_PESC           0x00020000   
-#define RH_PS_PSSC           0x00040000    
-#define RH_PS_OCIC           0x00080000    
-#define RH_PS_PRSC           0x00100000   
-
-/* Root hub status bits */
-#define RH_HS_LPS	     0x00000001
-#define RH_HS_OCI	     0x00000002
-#define RH_HS_DRWE	     0x00008000
-#define RH_HS_LPSC	     0x00010000
-#define RH_HS_OCIC	     0x00020000
-#define RH_HS_CRWE	     0x80000000
+
+/* OHCI ROOT HUB REGISTER MASKS */
+ 
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS            0x00000001   	/* current connect status */
+#define RH_PS_PES            0x00000002   	/* port enable status*/
+#define RH_PS_PSS            0x00000004   	/* port suspend status */
+#define RH_PS_POCI           0x00000008   	/* port over current indicator */
+#define RH_PS_PRS            0x00000010  	/* port reset status */
+#define RH_PS_PPS            0x00000100   	/* port power status */
+#define RH_PS_LSDA           0x00000200    	/* low speed device attached */
+#define RH_PS_CSC            0x00010000 	/* connect status change */
+#define RH_PS_PESC           0x00020000   	/* port enable status change */
+#define RH_PS_PSSC           0x00040000    	/* port suspend status change */
+#define RH_PS_OCIC           0x00080000    	/* over current indicator change */
+#define RH_PS_PRSC           0x00100000   	/* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS	     0x00000001		/* local power status */
+#define RH_HS_OCI	     0x00000002		/* over current indicator */
+#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */
+#define RH_HS_LPSC	     0x00010000		/* local power status change */
+#define RH_HS_OCIC	     0x00020000		/* over current indicator change */
+#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR		0x0000ffff		/* device removable flags */
+#define RH_B_PPCM	0xffff0000		/* port power control mask */
+
+/* roothub.a masks */
+#define	RH_A_NDP	(0xff << 0)		/* number of downstream ports */
+#define	RH_A_PSM	(1 << 8)		/* power switching mode */
+#define	RH_A_NPS	(1 << 9)		/* no power switching */
+#define	RH_A_DT		(1 << 10)		/* device type (mbz) */
+#define	RH_A_OCPM	(1 << 11)		/* over current protection mode */
+#define	RH_A_NOCP	(1 << 12)		/* no over current protection */
+#define	RH_A_POTPGT	(0xff << 24)		/* power on to power good time */
 
 #define min(a,b) (((a)<(b))?(a):(b))  
  
@@ -347,23 +388,25 @@
 
 
 typedef struct ohci {
-	struct ohci_hcca hcca;					/* hcca */                
+	struct ohci_hcca hcca;		/* hcca */
 
 	int irq;
-	struct ohci_regs * regs;					/* OHCI controller's memory */	
-    struct list_head ohci_hcd_list;         /* list of all ohci_hcd */           
+	int disabled;			/* e.g. got a UE, we're hung */
+
+	struct ohci_regs * regs;	/* OHCI controller's memory */
+	struct list_head ohci_hcd_list;	/* list of all ohci_hcd */
 
 	struct ohci * next; 		// chain of uhci device contexts
 	struct list_head urb_list; 	// list of all pending urbs
 	spinlock_t urb_list_lock; 	// lock to keep consistency 
   
-	int ohci_int_load[32];                  /* load of the 32 Interrupt Chains (for load ballancing)*/     
+	int ohci_int_load[32];		/* load of the 32 Interrupt Chains (for load balancing)*/
 	ed_t * ed_rm_list[2];     /* lists of all endpoints to be removed */
 	ed_t * ed_bulktail;       /* last endpoint of bulk list */
 	ed_t * ed_controltail;    /* last endpoint of control list */
  	ed_t * ed_isotail;        /* last endpoint of iso list */
 	int intrstatus;
-	__u32 hc_control;						/* copy of the hc control reg */  
+	__u32 hc_control;		/* copy of the hc control reg */
 	struct usb_bus * bus;    
 	struct usb_device * dev[128];
 	struct virt_root_hub rh;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/usb-storage.c linux/drivers/usb/usb-storage.c
--- v2.3.99-pre2/linux/drivers/usb/usb-storage.c	Sun Mar 19 18:35:31 2000
+++ linux/drivers/usb/usb-storage.c	Thu Mar 23 15:15:18 2000
@@ -3,20 +3,19 @@
  * (c) 1999 Michael Gee (michael@linuxspecific.com)
  * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
- * Further reference:
- *	This driver is based on the 'USB Mass Storage Class' document. This
- *	describes in detail the protocol used to communicate with such
- *      devices.  Clearly, the designers had SCSI and ATAPI commands in mind 
- *      when they created this document.  The commands are all very similar 
- *      to commands in the SCSI-II and ATAPI specifications.
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices.  Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document.  The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
  *
- *	It is important to note that in a number of cases this class exhibits
- *	class-specific exemptions from the USB specification. Notably the
- *	usage of NAK, STALL and ACK differs from the norm, in that they are
- *	used to communicate wait, failed and OK on commands.
- *	Also, for certain devices, the interrupt endpoint is used to convey
- *	status of a command.
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
  *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
  */
 
 #include <linux/module.h>
@@ -40,7 +39,6 @@
 #include "usb-storage.h"
 #include "usb-storage-debug.h"
 
-
 /*
  * This is the size of the structure Scsi_Host_Template.  We create
  * an instance of this structure in this file and this is a check
@@ -75,34 +73,47 @@
 typedef int (*trans_reset)(struct us_data*);
 typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
 
+/* we allocate one of these for every device that we remember */
 struct us_data {
 	struct us_data	        *next;	         /* next device */
 	struct usb_device	*pusb_dev;       /* this usb_device */
 	unsigned int		flags;		 /* from filter initially */
+
+	/* information about the device -- only good if device is attached */
 	__u8			ifnum;		 /* interface number */
 	__u8			ep_in;		 /* in endpoint */
 	__u8			ep_out;		 /* out ....... */
 	__u8			ep_int;		 /* interrupt . */
-	__u8			subclass;	 /* as in overview */
-	__u8			protocol;	 /* .............. */
-	trans_cmnd              transport;	 /* protocol specific do cmd */
-	trans_reset             transport_reset; /* .......... device reset */
+	__u8			subclass;
+	__u8			protocol;
+
+	/* function pointers for this device */
+	trans_cmnd              transport;	 /* transport function */
+	trans_reset             transport_reset; /* transport device reset */
 	proto_cmnd              proto_handler;   /* protocol handler */
+
+	/* SCSI interfaces */
 	GUID(guid);				 /* unique dev id */
 	struct Scsi_Host	*host;		 /* our dummy host data */
 	Scsi_Host_Template	htmplt;  	 /* own host template */
 	int			host_number;	 /* to find us */
 	int			host_no;	 /* allocated by scsi */
 	Scsi_Cmnd		*srb;		 /* current srb */
+	
+	/* thread information */
 	Scsi_Cmnd		*queue_srb;	 /* the single queue slot */
 	int			action;		 /* what to do */
+	int			pid;		 /* control thread */
+
+	/* interrupt info for CBI devices */
 	struct semaphore	ip_waitq;	 /* for CBI interrupts */
 	__u16			ip_data;	 /* interrupt data */
 	int			ip_wanted;	 /* needed */
-	int			pid;		 /* control thread */
-	struct semaphore	*notify;	 /* wait for thread to begin */
 	void			*irq_handle;	 /* for USB int requests */
 	unsigned int		irqpipe;	 /* pipe for release_irq */
+
+	/* mutual exclusion structures */
+	struct semaphore	notify;          /* wait for thread to begin */
 	struct semaphore        sleeper;         /* to sleep on */
 	struct semaphore        queue_exclusion; /* to protect data structs */
 };
@@ -116,6 +127,7 @@
 #define US_ACT_DEVICE_RESET	3
 #define US_ACT_BUS_RESET	4
 #define US_ACT_HOST_RESET	5
+#define US_ACT_EXIT		6
 
 /* The list of structures and the protective lock for them */
 static struct us_data *us_list;
@@ -254,11 +266,6 @@
 	case MODE_SENSE:
 		return srb->cmnd[4];
 
-		/* FIXME: this needs to come out when the other
-		 * fix is in place */
-	case READ_CAPACITY:
-		return 8;
-
 		/* FIXME: these should be removed and tested */
 	case LOG_SENSE:
 	case MODE_SENSE_10:
@@ -791,9 +798,8 @@
 				 us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
 
 	/* check the return code for the command */
+	US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
 	if (result < 0) {
-		US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
-
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
@@ -820,15 +826,10 @@
 
 	/* STATUS STAGE */
 
-	/* go to sleep until we get this interrup */
-	/* FIXME: this should be changed to use a timeout -- or let the
-	 * device reset routine up() this for us to unjam us
-	 */
+	/* go to sleep until we get this interrupt */
 	down(&(us->ip_waitq));
 	
-	/* FIXME: currently this code is unreachable, but the idea is
-	 * necessary.  See above comment.
-	 */
+	/* if we were woken up by a reset instead of the actual interrupt */
 	if (us->ip_wanted) {
 		US_DEBUGP("Did not get interrupt on CBI\n");
 		us->ip_wanted = 0;
@@ -837,8 +838,9 @@
 	
 	US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
 	
-	/* UFI gives us ASC and ASCQ, like a request sense */
-	/* REQUEST_SENSE and INQUIRY don't affect the sense data, so we
+	/* UFI gives us ASC and ASCQ, like a request sense
+	 *
+	 * REQUEST_SENSE and INQUIRY don't affect the sense data, so we
 	 * ignore the information for those commands
 	 */
 	if (us->subclass == US_SC_UFI) {
@@ -1114,29 +1116,27 @@
 static int us_release(struct Scsi_Host *psh)
 {
 	struct us_data *us = (struct us_data *)psh->hostdata[0];
-	struct us_data *prev;
 	unsigned long flags;
+	int result;
 
+	/* lock the data structures */
+	spin_lock_irqsave(&us_list_spinlock, flags);
+
+	US_DEBUGP("us_release() called for host %s\n", us->htmplt.name);
+
+	/* release the interrupt handler, if necessary */
 	if (us->irq_handle) {
-		usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
+		US_DEBUGP("-- releasing irq\n");
+		result = usb_release_irq(us->pusb_dev, us->irq_handle, 
+					 us->irqpipe);
+		US_DEBUGP("-- usb_release_irq() returned %d\n", result);
 		us->irq_handle = NULL;
 	}
 
-	/* FIXME: release the interface claim here? */
-
-	/* FIXME: we need to move this elsewhere -- 
-	 * the remove function only gets called to remove the module
-	 */
-	spin_lock_irqsave(&us_list_spinlock, flags);
-	if (us_list == us)
-		us_list = us->next;
-	else {
-		prev = us_list;
-		while (prev->next != us)
-			prev = prev->next;
-		prev->next = us->next;
-	}
+	/* lock the data structures */
 	spin_unlock_irqrestore(&us_list_spinlock, flags);
+
+	/* we always have a successful release */
 	return 0;
 }
 
@@ -1153,7 +1153,7 @@
 {
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
-	US_DEBUGP("Command wakeup\n");
+	US_DEBUGP("us_queuecommand() called\n");
 	srb->host_scribble = (unsigned char *)us;
 
 	/* get exclusive access to the structures we want */
@@ -1180,9 +1180,11 @@
 /* FIXME: this doesn't do anything right now */
 static int us_bus_reset( Scsi_Cmnd *srb )
 {
-	//  struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
 	US_DEBUGP("Bus reset requested\n");
+	if (us->ip_wanted)
+		up(&(us->ip_waitq));
 	//  us->transport_reset(us);
 	return SUCCESS;
 }
@@ -1364,12 +1366,9 @@
 
 	unlock_kernel();
 
-	up(us->notify);
+	up(&(us->notify));
 
 	for(;;) {
-		siginfo_t info;
-		int unsigned long signr;
-
 		US_DEBUGP("*** thread sleeping.\n");
 		down(&(us->sleeper));
 		down(&(us->queue_exclusion));
@@ -1378,7 +1377,7 @@
 				/* take the command off the queue */
 		action = us->action;
 		us->action = 0;
-		us->srb = us-> queue_srb;
+		us->srb = us->queue_srb;
      	
 		/* release the queue lock as fast as possible */
 		up(&(us->queue_exclusion));
@@ -1433,7 +1432,8 @@
 				us->proto_handler(us->srb, us);
 			}
       
-			US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result);
+			US_DEBUGP("scsi cmd done, result=0x%x\n", 
+				  us->srb->result);
 			us->srb->scsi_done(us->srb);
 			us->srb = NULL;
 			break;
@@ -1452,19 +1452,12 @@
 
 		} /* end switch on action */
 
-		/* FIXME: we ignore TERM and KILL... is this right? */
-		if (signal_pending(current)) {
-			/* sending SIGUSR1 makes us print out some info */
-			spin_lock_irq(&current->sigmask_lock);
-			signr = dequeue_signal(&current->blocked, &info);
-			spin_unlock_irq(&current->sigmask_lock);
-		} /* if (singal_pending(current)) */
+		/* exit if we get a signal to exit */
+		if (action == US_ACT_EXIT)
+			break;
 	} /* for (;;) */
   
-	//  MOD_DEC_USE_COUNT;
-
 	printk("usb_stor_control_thread exiting\n");
-
 	return 0;
 }	
 
@@ -1495,14 +1488,12 @@
 
 	/* FIXME: this isn't quite right... */
 	/* We make an exception for the shuttle E-USB */
-	if (dev->descriptor.idVendor == 0x04e6 &&
-	    dev->descriptor.idProduct == 0x0001) {
-		protocol = US_PR_CB;
-		subclass = US_SC_8070;	    /* an assumption */
-	} else if (dev->descriptor.bDeviceClass != 0 ||
-		   altsetting->bInterfaceClass != USB_CLASS_MASS_STORAGE ||
-		   altsetting->bInterfaceSubClass < US_SC_MIN ||
-		   altsetting->bInterfaceSubClass > US_SC_MAX) {
+	if (!(dev->descriptor.idVendor == 0x04e6 &&
+	      dev->descriptor.idProduct == 0x0001) &&
+	    !(dev->descriptor.bDeviceClass == 0 &&
+	      altsetting->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
+	      altsetting->bInterfaceSubClass >= US_SC_MIN &&
+	      altsetting->bInterfaceSubClass <= US_SC_MAX)) {
 		/* if it's not a mass storage, we go no further */
 		return NULL;
 	}
@@ -1510,7 +1501,43 @@
 	/* At this point, we know we've got a live one */
 	US_DEBUGP("USB Mass Storage device detected\n");
 
+	/* Determine subclass and protocol, or copy from the interface */
+	/* FIXME: this isn't quite right */
+	if (dev->descriptor.idVendor == 0x04e6 &&
+	    dev->descriptor.idProduct == 0x0001) {
+		protocol = US_PR_CB;
+		subclass = US_SC_8070;	    /* an assumption */
+	} else {
+		subclass = altsetting->bInterfaceSubClass;
+		protocol = altsetting->bInterfaceProtocol;
+	}
+
+	/* shuttle E-USB */	
+	/* FIXME: all we should need to do here is determine the protocol */
+	if (dev->descriptor.idVendor == 0x04e6 &&
+	    dev->descriptor.idProduct == 0x0001) {
+		__u8 qstat[2];
+		
+		result = usb_control_msg(ss->pusb_dev, 
+					 usb_rcvctrlpipe(dev,0),
+					 1, 0xC0,
+					 0, ss->ifnum,
+					 qstat, 2, HZ*5);
+		US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
+		init_MUTEX_LOCKED(&(ss->ip_waitq));
+		ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+		result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
+					 CBI_irq, 255, (void *)ss, 
+					 &ss->irq_handle);
+		if (result < 0)
+			return NULL;
+		
+		/* FIXME: what is this?? */
+		down(&(ss->ip_waitq));
+	}
+			
 	/*
+	 * Find the endpoints we need
 	 * We are expecting a minimum of 2 endpoints - in and out (bulk).
 	 * An optional interrupt is OK (necessary for CBI protocol).
 	 * We will ignore any others.
@@ -1519,6 +1546,7 @@
 		/* is it an BULK endpoint? */
 		if ((altsetting->endpoint[i].bmAttributes & 
 		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+			/* BULK in or out? */
 			if (altsetting->endpoint[i].bEndpointAddress & 
 			    USB_DIR_IN)
 				ep_in = altsetting->endpoint[i].bEndpointAddress &
@@ -1542,36 +1570,14 @@
 	result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
 	US_DEBUGP("Result from usb_set_interface is %d\n", result);
 	if (result == -EPIPE) {
+		US_DEBUGP("-- clearing stall on control interface\n");
 		usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
 	} else if (result != 0) {
 		/* it's not a stall, but another error -- time to bail */
+		US_DEBUGP("-- unknown error.  rejecting device\n");
 		return NULL;
 	}
 
-	/* shuttle E-USB */	
-	/* FIXME: all we should need to do here is determine the protocol */
-	if (dev->descriptor.idVendor == 0x04e6 &&
-	    dev->descriptor.idProduct == 0x0001) {
-		__u8 qstat[2];
-		
-		result = usb_control_msg(ss->pusb_dev, 
-					 usb_rcvctrlpipe(dev,0),
-					 1, 0xC0,
-					 0, ss->ifnum,
-					 qstat, 2, HZ*5);
-		US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
-		init_MUTEX_LOCKED(&(ss->ip_waitq));
-		ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
-		result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
-					 CBI_irq, 255, (void *)ss, 
-					 &ss->irq_handle);
-		if (result < 0)
-			return NULL;
-		
-		/* FIXME: what is this?? */
-		down(&(ss->ip_waitq));
-	}
-		
 	/* Do some basic sanity checks, and bail if we find a problem */
 	if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
 		US_DEBUGP("Problems with device\n");
@@ -1638,8 +1644,8 @@
 			US_DEBUGP("Allocating IRQ for CBI transport\n");
 			ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
 			result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
-						 CBI_irq, 255, 
-						 (void *)ss, &ss->irq_handle);
+						 CBI_irq, 255, (void *)ss, 
+						 &(ss->irq_handle));
 			US_DEBUGP("usb_request_irq returned %d\n", result);
 		}
 	} else { 
@@ -1656,23 +1662,12 @@
 
 		/* Initialize the mutexes only when the struct is new */
 		init_MUTEX_LOCKED(&(ss->sleeper));
+		init_MUTEX_LOCKED(&(ss->notify));
 		init_MUTEX(&(ss->queue_exclusion));
 
-		/*
-		 * If we've allready determined the subclass and protocol, 
-		 * use that.  Otherwise, use the interface ones.  This 
-		 * allows us to support devices which are compliant but 
-		 * don't announce it.  Note that this information is 
-		 * maintained in the us_data struct so we only have to do 
-		 * this for new devices.
-		 */
-		if (subclass) {
-			ss->subclass = subclass;
-			ss->protocol = protocol;
-		} else {
-			ss->subclass = altsetting->bInterfaceSubClass;
-			ss->protocol = altsetting->bInterfaceProtocol;
-		}
+		/* copy over the subclass and protocol data */
+		ss->subclass = subclass;
+		ss->protocol = protocol;
 
 		/* copy over the endpoint data */
 		ss->ep_in = ep_in;
@@ -1762,9 +1757,9 @@
 			US_DEBUGP("Allocating IRQ for CBI transport\n");
 			ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
 			result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
-						 CBI_irq, 255, 
-						 (void *)ss, &ss->irq_handle);
-			US_DEBUGP("usb_request_irq returned %d", result);
+						 CBI_irq, 255, (void *)ss,
+						 &(ss->irq_handle));
+			US_DEBUGP("usb_request_irq returned %d\n", result);
 		}
 		
 		/*
@@ -1786,23 +1781,18 @@
 		(struct us_data *)ss->htmplt.proc_dir = ss; 
 		
 		/* start up our thread */
-		{
-			DECLARE_MUTEX_LOCKED(sem);
-			
-			ss->notify = &sem;
-			ss->pid = kernel_thread(usb_stor_control_thread, ss,
-						CLONE_FS | CLONE_FILES |
-						CLONE_SIGHAND);
-			if (ss->pid < 0) {
-				printk(KERN_WARNING USB_STORAGE 
-				       "Unable to start control thread\n");
-				kfree(ss);
-				return NULL;
-			}
-			
-	       		/* wait for it to start */
-			down(&sem);
+		ss->pid = kernel_thread(usb_stor_control_thread, ss,
+					CLONE_FS | CLONE_FILES |
+					CLONE_SIGHAND);
+		if (ss->pid < 0) {
+			printk(KERN_WARNING USB_STORAGE 
+			       "Unable to start control thread\n");
+			kfree(ss);
+			return NULL;
 		}
+		
+		/* wait for it to start */
+		down(&(ss->notify));
 			
 		/* now register - our detect function will be called */
 		ss->htmplt.module = &__this_module;
@@ -1829,18 +1819,26 @@
 static void storage_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct us_data *ss = ptr;
+	int result;
 
-	if (!ss)
+	US_DEBUGP("storage_disconnect() called\n");
+ 
+	/* this is the odd case -- we disconnected but weren't using it */
+	if (!ss) {
+		US_DEBUGP("-- device was not in use\n");
 		return;
-
-	/* FIXME: we need mututal exclusion and resource freeing here */
+	}
 
 	/* release the IRQ, if we have one */
 	if (ss->irq_handle) {
-		usb_release_irq(ss->pusb_dev, ss->irq_handle, ss->irqpipe);
+		US_DEBUGP("-- releasing irq handle\n");
+		result = usb_release_irq(ss->pusb_dev, ss->irq_handle, 
+					 ss->irqpipe);
+		US_DEBUGP("-- usb_release_irq() returned %d\n", result);
 		ss->irq_handle = NULL;
 	}
 
+	/* mark the device as gone */
 	ss->pusb_dev = NULL;
 }
 
@@ -1851,11 +1849,16 @@
 
 int __init usb_stor_init(void)
 {
+	/* 
+	 * Check to see if the host template is a different size from
+	 * what we're expected -- people have updated this in the past
+	 * and forgotten about this driver.
+	 */
 	if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
-		printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
-		printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n", 
+		printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE bad\n");
+		printk(KERN_ERR 
+		       "usb-storage: expected %d bytes, got %d bytes\n", 
 		       SCSI_HOST_TEMPLATE_SIZE, sizeof(my_host_template)) ;
-
 		return -1 ;
 	}
 
@@ -1863,6 +1866,7 @@
 	if (usb_register(&storage_driver) < 0)
 		return -1;
 
+	/* we're all set */
 	printk(KERN_INFO "USB Mass Storage support registered.\n");
 	return 0;
 }
@@ -1870,17 +1874,35 @@
 void __exit usb_stor_exit(void)
 {
 	static struct us_data *ptr;
-
+	static struct us_data *next;
+	unsigned long flags;
+	
+	/*
+	 * deregister the driver -- this eliminates races with probes and
+	 * disconnects 
+	 */
+	usb_deregister(&storage_driver) ;
+	
+	/* lock access to the data structures */
+	spin_lock_irqsave(&us_list_spinlock, flags);
+	
 	/* unregister all the virtual hosts */
 	for (ptr = us_list; ptr != NULL; ptr = ptr->next)
-	  scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt));
-
-	/* free up the data structures */
-
+		scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt));
+	
 	/* kill the threads */
+	/* FIXME: we can do this by sending them a signal to die */
 
-	/* deregister the driver */
-	usb_deregister(&storage_driver) ;
+	/* free up the data structures */
+	/* FIXME: we need to eliminate the host structure also */
+	while (ptr) {
+		next = ptr->next;
+		kfree(ptr);
+		ptr = next;
+	}
+	
+	/* unlock the data structures */
+	spin_unlock_irqrestore(&us_list_spinlock, flags);
 }
 
 module_init(usb_stor_init) ;
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- v2.3.99-pre2/linux/drivers/usb/usb-uhci.c	Sun Mar 19 18:35:31 2000
+++ linux/drivers/usb/usb-uhci.c	Thu Mar 23 14:59:56 2000
@@ -1002,8 +1002,11 @@
 		urb_priv = urb->hcpriv;
 
 		switch (usb_pipetype (urb->pipe)) {
-		case PIPE_ISOCHRONOUS:
+
 		case PIPE_INTERRUPT:
+			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+
+		case PIPE_ISOCHRONOUS:
 			uhci_clean_iso_step1(s, urb_priv);
 			uhci_wait_ms(1);
 			uhci_clean_iso_step2(s, urb_priv);
@@ -1131,8 +1134,10 @@
 		urb_priv = (urb_priv_t*)urb->hcpriv;
 
 		switch (usb_pipetype (urb->pipe)) {
-		case PIPE_ISOCHRONOUS:
 		case PIPE_INTERRUPT:
+			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));		
+
+		case PIPE_ISOCHRONOUS:
 			uhci_clean_iso_step1 (s, urb_priv);
 			break;
 
@@ -1617,8 +1622,8 @@
 	0x00,			/*  __u16 bcdDevice; */
 	0x00,
 	0x00,			/*  __u8  iManufacturer; */
-	0x00,			/*  __u8  iProduct; */
-	0x00,			/*  __u8  iSerialNumber; */
+	0x02,			/*  __u8  iProduct; */
+	0x01,			/*  __u8  iSerialNumber; */
 	0x01			/*  __u8  bNumConfigurations; */
 };
 
@@ -1915,8 +1920,14 @@
 			len = min (leni, min (sizeof (root_hub_config_des), wLength));
 			memcpy (data, root_hub_config_des, len);
 			OK (len);
-		case (0x03):	/*string descriptors */
-			stat = -EPIPE;
+		case (0x03):	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+				uhci->io_addr, "UHCI",
+				data, wLength);
+			if (len > 0) {
+				OK (min (leni, len));
+			} else 
+				stat = -EPIPE;
 		}
 		break;
 
@@ -2597,6 +2608,15 @@
 	uhci_t *s;
 	struct usb_bus *bus;
 	struct pm_dev *pmdev;
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
 
 	s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
 	if (!s)
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
--- v2.3.99-pre2/linux/drivers/usb/usb.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/usb.c	Thu Mar 23 14:59:08 2000
@@ -1184,6 +1184,54 @@
 	dev->actconfig = NULL;
 }
 
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+	int retval;
+
+	for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+		*utf++ = *ascii++ & 0x7f;
+		*utf++ = 0;
+	}
+	return retval;
+}
+
+/*
+ * root_hub_string is used by each host controller's root hub code,
+ * so that they're identified consistently throughout the system.
+ */
+int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+	char buf [20];
+
+	// assert (len > (2 * (sizeof (buf) + 1)));
+	// assert (strlen (type) ~== 4);
+
+	// language ids
+	if (id == 0) {
+		*data++ = 4; *data++ = 3;	/* 4 bytes data */
+		*data++ = 0; *data++ = 0;	/* some language id */
+		return 4;
+
+	// serial number
+	} else if (id == 1) {
+		sprintf (buf, "%x", serial);
+
+	// product description
+	} else if (id == 2) {
+		sprintf (buf, "USB %s Root Hub", type);
+
+	// id 3 == vendor description
+
+	// unsupported IDs --> "stall"
+	} else
+	    return 0;
+
+	data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+	data [1] = 3;
+	return data [0];
+}
+
 /*
  * __usb_get_extra_descriptor() finds a descriptor of specific type in the
  * extra field of the interface and endpoint descriptor structs.
@@ -1824,6 +1872,7 @@
 EXPORT_SYMBOL(usb_driver_release_interface);
 
 EXPORT_SYMBOL(usb_init_root_hub);
+EXPORT_SYMBOL(usb_root_hub_string);
 EXPORT_SYMBOL(usb_new_device);
 EXPORT_SYMBOL(usb_connect);
 EXPORT_SYMBOL(usb_disconnect);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/wacom.c linux/drivers/usb/wacom.c
--- v2.3.99-pre2/linux/drivers/usb/wacom.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/wacom.c	Sun Mar 19 18:29:40 2000
@@ -1,5 +1,5 @@
 /*
- *  wacom.c  Version 0.4
+ *  wacom.c  Version 0.5
  *
  *  Copyright (c) 2000 Vojtech Pavlik		<vojtech@suse.cz>
  *  Copyright (c) 2000 Andreas Bach Aaen	<abach@stofanet.dk>
@@ -14,8 +14,10 @@
  *      v0.1 (vp)  - Initial release
  *      v0.2 (aba) - Support for all buttons / combinations
  *      v0.3 (vp)  - Support for Intuos added
- *      v0.4 (sm)  - Support for more Intuos models, menustrip,
- *                   relative mode, proximity.
+ *	v0.4 (sm)  - Support for more Intuos models, menustrip
+ *			relative mode, proximity.
+ *	v0.5 (vp)  - Big cleanup, nifty features removed,
+ * 			they belong in userspace
  */
 
 /*
@@ -50,16 +52,14 @@
 /*
  * Wacom Graphire packet:
  *
- * The input report:
- * 
  * byte 0: report ID (2)
- * byte 1: bit7 	mouse/pen/rubber near
- *         bit5-6	0 - pen, 1 - rubber, 2 - mouse
- * 	   bit4		1 ?
- *         bit3		0 ?
- *         bit2		mouse middle button / pen button2
- *         bit1		mouse right button / pen button1
- *         bit0		mouse left button / pen tip / rubber
+ * byte 1: bit7		pointer in range
+ *	   bit5-6	pointer type 0 - pen, 1 - rubber, 2 - mouse
+ *	   bit4		1 ?
+ *	   bit3		0 ?
+ *	   bit2		mouse middle button / pen button2
+ *	   bit1		mouse right button / pen button1
+ *	   bit0		mouse left button / touch
  * byte 2: X low bits
  * byte 3: X high bits
  * byte 4: Y low bits
@@ -69,115 +69,58 @@
  * 
  * There are also two single-byte feature reports (2 and 3).
  *
- * Resolution:
- * X: 0 - 10206
- * Y: 0 - 7422
- * 
- * (0,0) is upper left corner
- *
- * Wacom Intuos Status packet:
+ * Wacom Intuos status packet:
  *
- *  byte 0: report ID (2)
- *  byte 1: bit7	1 (Sync Byte)
- *          bit6	Pointer Near
- *          bit5	0 - first proximity report
- *          bit4 	0 ?
- *          bit3	0 ?
- *          bit2	pen button2
- *          bit1	pen button1
- *          bit0	0 ?
- *  byte 2: X high bits
- *  byte 3: X low bits
- *  byte 4: Y high bits
- *  byte 5: Y low bits
- *  byte 6: bits 0-7:  pressure (bits 2-9)
- *  byte 7: bits 6-7:  pressure (bits 0-1)
- *  byte 7: bits 0-5:  X tilt  (bits 1-6)
- *  byte 8: bit    7:  X tilt  (bit  0)
- *  byte 8: bits 0-6:  Y tilt  (bits 0-6)
- *  byte 9: bits 4-7:  Proximity
+ * byte 0: report ID (2)
+ * byte 1: bit7		1 - sync bit 
+ *	   bit6		pointer in range
+ *	   bit5		pointer type report
+ *	   bit4		0 ?
+ *	   bit3		0 ?
+ *	   bit2		pen button2
+ *	   bit1		pen button1
+ *	   bit0		0 ?
+ * byte 2: X high bits
+ * byte 3: X low bits
+ * byte 4: Y high bits
+ * byte 5: Y low bits
+ * byte 6: bits 0-7: pressure	(bits 2-9)
+ * byte 7: bits 6-7: pressure	(bits 0-1)
+ * byte 7: bits 0-5: X tilt	(bits 1-6)
+ * byte 8: bit    7: X tilt	(bit  0)
+ * byte 8: bits 0-6: Y tilt	(bits 0-6)
+ * byte 9: bits 4-7: distance
  */
 
-#define USB_VENDOR_ID_WACOM		0x056a
-#define USB_DEVICE_ID_WACOM_GRAPHIRE	0x0010
-#define USB_DEVICE_ID_WACOM_INTUOS45	0x0020 /* Guess */
-#define USB_DEVICE_ID_WACOM_INTUOS68	0x0021
-#define USB_DEVICE_ID_WACOM_INTUOS912	0x0022 /* Guess */
-#define USB_DEVICE_ID_WACOM_INTUOS1212	0x0023
-#define USB_DEVICE_ID_WACOM_INTUOS1218	0x0024 /* Guess */
-
-#define USB_TOOL_ID_WACOM_PEN	        0x0022
-#define USB_TOOL_ID_WACOM_ERASER        0x00fa
-#define USB_TOOL_ID_WACOM_STROKE_PEN    0x0032
-#define USB_TOOL_ID_WACOM_INKING_PEN    0x0012
-#define USB_TOOL_ID_WACOM_AIRBRUSH      0x0112
-#define USB_TOOL_ID_WACOM_MOUSE4D       0x0094
-#define USB_TOOL_ID_WACOM_LENS_CURSOR   0x0096
-
-#define INTUOS_PEN_MODE_ABS             0x00
-#define INTUOS_PEN_MODE_REL             0x01
-#define INTUOS_PEN_MODE_QUICKPOINT      0x02
-
-#define INTUOS_PRESSURE_MODE_SOFT       0x00
-#define INTUOS_PRESSURE_MODE_MED        0x01
-#define INTUOS_PRESSURE_MODE_FIRM       0x02
-
-#define INTUOS_MENUSTRIP_Y_ZONE         1400
-#define INTUOS_MENUSTRIP_BTN_YMIN        270
-#define INTUOS_MENUSTRIP_BTN_YMAX       1070
-#define INTUOS_MENUSTRIP_F1_XMIN          40
-#define INTUOS_MENUSTRIP_F7_XMIN        8340
-#define INTUOS_MENUSTRIP_F12_XMIN      15300
+#define USB_VENDOR_ID_WACOM	0x056a
 
-#define INTUOS_MENUSTRIP_BTN_WIDTH      1300
-
-#define INTUOS_MENUSTRIP_F7_IX_OFFSET      6 /* offset into wacom_fkeys */
-#define INTUOS_MENUSTRIP_F12_IX_OFFSET    11
+struct wacom_features {
+	char *name;
+	int idProduct;
+	int pktlen;
+	int x_max;
+	int y_max;
+	int pressure_max;
+	int distance_max;
+	void (*irq)(struct urb *urb);
+	unsigned long evbit;
+	unsigned long relbit;
+	unsigned long absbit;
+	unsigned long btnbit;
+	unsigned long digibit;
+};
 
 struct wacom {
-        signed char data[10];
+	signed char data[10];
 	struct input_dev dev;
 	struct urb irq;
-
-        int last_x, last_y;
-	unsigned int tool, device;
-	unsigned int ymax, menustrip_touch;
-	unsigned int pen_mode;
-	unsigned int pressure_mode;
+	struct wacom_features *features;
+	int tool;
 };
 
-static int wacom_fkeys[16] = { KEY_F1,  KEY_F2,  KEY_F3,  KEY_F4,
-			       KEY_F5,  KEY_F6,  KEY_F7,  KEY_F8,
-			       KEY_F9,  KEY_F10, KEY_F11, KEY_F12,
-			       KEY_F13, KEY_F14, KEY_F15, KEY_F16};
-
-#define INTUOS_EXTENTS_MAX_X              0x00
-#define INTUOS_EXTENTS_MAX_Y              0x01
-#define INTUOS_EXTENTS_HAS_F7             0x02
-#define INTUOS_EXTENTS_HAS_F12            0x03
-#define INTUOS_EXTENTS_PEN_MODE           0x04
-#define INTUOS_EXTENTS_HAS_QUICKPOINT     0x05
-#define INTUOS_EXTENTS_HAS_PRESSURE_MODE  0x06
-#define INTUOS_EXTENTS_PRESSURE_MODE      0x07
-
-#define WACOM_TRUE   1
-#define WACOM_FALSE  0
-
-static int intuos_extents[5][8] = {
-	{ 12700, 10360, WACOM_FALSE, WACOM_FALSE,  8340, WACOM_FALSE, WACOM_FALSE,     0},  /* Intuos 4x5   */
-	{ 20320, 15040,  WACOM_TRUE, WACOM_FALSE, 15300, WACOM_FALSE,  WACOM_TRUE, 18360},  /* Intuos 6x8   */
-	{ 30480, 23060,  WACOM_TRUE,  WACOM_TRUE, 22280,  WACOM_TRUE,  WACOM_TRUE, 26640},  /* Intuos 9x12  */
-	{ 30480, 30480,  WACOM_TRUE,  WACOM_TRUE, 22280,  WACOM_TRUE,  WACOM_TRUE, 26640},  /* Intuos 12x12 */
-	{ 47720, 30480,  WACOM_TRUE,  WACOM_TRUE, 29260,  WACOM_TRUE,  WACOM_TRUE, 33620}}; /* Intuos 12x18 */
-
-static char intuos_names[5][12] = {
-	{"Intuos 4x5  "}, {"Intuos 6x8  "},
-	{"Intuos 9x12 "}, {"Intuos 12x12"},
-	{"Intuos 12x18"}};
-
 static void wacom_graphire_irq(struct urb *urb)
 {
-        struct wacom *wacom = urb->context;
+	struct wacom *wacom = urb->context;
 	unsigned char *data = wacom->data;
 	struct input_dev *dev = &wacom->dev;
 
@@ -186,285 +129,135 @@
 	if (data[0] != 2)
 		dbg("received unknown report #%d", data[0]);
 
-        if ( data[1] & 0x80 ) {
-	        input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
-	        input_report_abs(dev, ABS_Y, 7422 - (data[4] | ((__u32)data[5] << 8)));
+	if ( data[1] & 0x80 ) {
+		input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
+		input_report_abs(dev, ABS_Y, data[4] | ((__u32)data[5] << 8));
 	}
 
 	switch ((data[1] >> 5) & 3) {
 
-	case 0:	/* Pen */
-		input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80);
-		input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
-		input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
-		input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
-		input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
-		break;
-
-	case 1: /* Rubber */
-		input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
-		input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
-		input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
-		input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
-		input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
-		break;
-
-	case 2: /* Mouse */
-		input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24);
-		input_report_btn(dev, BTN_LEFT, data[1] & 0x01);
-		input_report_btn(dev, BTN_RIGHT, data[1] & 0x02);
-		input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04);
-		input_report_abs(dev, ABS_DISTANCE, data[7]);
-		input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
-		break;
-	}
-}
+		case 0:	/* Pen */
+			input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80);
+			break;
 
-static void intuos_menustrip( unsigned int x, unsigned int y, struct wacom *wacom )
-{
-	struct input_dev *dev = &wacom->dev;
-	unsigned int local_x = x;
-	unsigned int local_y = y - ( wacom->ymax - INTUOS_MENUSTRIP_Y_ZONE );
-	unsigned int fkey_index ;
-
-	/* Ensure we are in the vertical strip for the buttons */
-	if ( (local_y > INTUOS_MENUSTRIP_BTN_YMIN) && (local_y < INTUOS_MENUSTRIP_BTN_YMAX) ) {
-
-		/* Handle Pressure Mode */
-		if ( intuos_extents[wacom->device][INTUOS_EXTENTS_HAS_PRESSURE_MODE] ) {
-			int pressure_mode = ( (local_x - intuos_extents[wacom->device][INTUOS_EXTENTS_PRESSURE_MODE])
-					      / INTUOS_MENUSTRIP_BTN_WIDTH );
-			if ( ( pressure_mode >= INTUOS_PRESSURE_MODE_SOFT ) &&
-			     ( pressure_mode <= INTUOS_PRESSURE_MODE_FIRM ) ) {
-				wacom->pressure_mode = pressure_mode;
-				return;
-			}
-		}
-
-		/* Handle Pen Mode */
-		{
-			int pen_mode = ( (local_x - intuos_extents[wacom->device][INTUOS_EXTENTS_PEN_MODE])
-					 / INTUOS_MENUSTRIP_BTN_WIDTH );
-			if ( ( pen_mode == INTUOS_PEN_MODE_ABS ) ||
-			     ( pen_mode == INTUOS_PEN_MODE_REL ) ||
-			     ( ( pen_mode == INTUOS_PEN_MODE_QUICKPOINT ) &&
-			       ( intuos_extents[wacom->device][INTUOS_EXTENTS_HAS_QUICKPOINT] ) ) ) {
-				wacom->pen_mode = pen_mode;
-				return;
-			}
-		}
-
-		/* Handle Function Keys */
-		if ( local_x > INTUOS_MENUSTRIP_F12_XMIN ) {
-			fkey_index = INTUOS_MENUSTRIP_F12_IX_OFFSET
-				+ ( (local_x - INTUOS_MENUSTRIP_F12_XMIN) / INTUOS_MENUSTRIP_BTN_WIDTH );
-			fkey_index = ( fkey_index > 16 ) ? 16 : fkey_index; /* Ensure in range */
-		}
-		else if ( local_x > INTUOS_MENUSTRIP_F7_XMIN ) {
-			fkey_index = INTUOS_MENUSTRIP_F7_IX_OFFSET
-				+ ( (local_x - INTUOS_MENUSTRIP_F7_XMIN) / INTUOS_MENUSTRIP_BTN_WIDTH );
-			fkey_index = ( fkey_index > 11 ) ? 11 : fkey_index;
-		}
-		else {
-			fkey_index = ( (local_x - INTUOS_MENUSTRIP_F1_XMIN) / INTUOS_MENUSTRIP_BTN_WIDTH );
-			fkey_index = ( fkey_index > 6 ) ? 6 : fkey_index;
-		}
-		input_report_key(dev, wacom_fkeys[fkey_index], 1);
-		input_report_key(dev, wacom_fkeys[fkey_index], 0);
+		case 1: /* Rubber */
+			input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+			break;
 
-		return;
+		case 2: /* Mouse */
+			input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24);
+			input_report_btn(dev, BTN_LEFT, data[1] & 0x01);
+			input_report_btn(dev, BTN_RIGHT, data[1] & 0x02);
+			input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04);
+			input_report_abs(dev, ABS_DISTANCE, data[7]);
+			input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
+			return;
 	}
+
+	input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
+
+	input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
+	input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
+	input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
 }
-		
+
 static void wacom_intuos_irq(struct urb *urb)
 {
-        struct wacom *wacom = urb->context;
+	struct wacom *wacom = urb->context;
 	unsigned char *data = wacom->data;
 	struct input_dev *dev = &wacom->dev;
 	unsigned int t;
-	int x, y;
 
 	if (urb->status) return;
 
 	if (data[0] != 2)
 		dbg("received unknown report #%d", data[0]);
 
-	if ( ((data[1] >> 5) & 0x3) == 0x2 ) /* First record, feature report */
-		{
-			wacom->tool = (((char)data[2] << 4) | (char)data[3] >> 4) & 0xff ;
-			/* Report tool type */
-			switch ( wacom->tool )	{
-			case USB_TOOL_ID_WACOM_PEN: 
-				input_report_btn(dev, BTN_TOOL_PEN, 1);
-				break;
-			case USB_TOOL_ID_WACOM_ERASER: 
-				input_report_btn(dev, BTN_TOOL_RUBBER, 1);
-				break;
-			case USB_TOOL_ID_WACOM_STROKE_PEN: 
-				input_report_btn(dev, BTN_TOOL_BRUSH, 1);
-				break;
-			case USB_TOOL_ID_WACOM_INKING_PEN: 
-				input_report_btn(dev, BTN_TOOL_PENCIL, 1);
-				break;
-			case USB_TOOL_ID_WACOM_AIRBRUSH: 
-				input_report_btn(dev, BTN_TOOL_AIRBRUSH, 1);
-				break;
-			case USB_TOOL_ID_WACOM_MOUSE4D: 
-			case USB_TOOL_ID_WACOM_LENS_CURSOR: 
-				input_report_btn(dev, BTN_TOOL_MOUSE, 1);
-				break;
-			default:
-				break;
-			}			
-			return;
-		}
+	if (((data[1] >> 5) & 0x3) == 0x2) {				/* Enter report */
 
-	if ( ( data[1] | data[2] | data[3] | data[4] | data[5] | data[6]
-	       | data[7] | data[8] | data[9] ) == 0x80 ) { /* exit report */
-		switch ( wacom->tool ) {
-		case USB_TOOL_ID_WACOM_PEN: 
-			input_report_btn(dev, BTN_TOOL_PEN, 0);
-			break;
-		case USB_TOOL_ID_WACOM_ERASER: 
-			input_report_btn(dev, BTN_TOOL_RUBBER, 0);
-			break;
-		case USB_TOOL_ID_WACOM_STROKE_PEN: 
-			input_report_btn(dev, BTN_TOOL_BRUSH, 0);
-			break;
-		case USB_TOOL_ID_WACOM_INKING_PEN: 
-			input_report_btn(dev, BTN_TOOL_PENCIL, 0);
-			break;
-		case USB_TOOL_ID_WACOM_AIRBRUSH: 
-			input_report_btn(dev, BTN_TOOL_AIRBRUSH, 0);
-			break;
-		case USB_TOOL_ID_WACOM_MOUSE4D: 
-		case USB_TOOL_ID_WACOM_LENS_CURSOR: 
-			input_report_btn(dev, BTN_TOOL_MOUSE, 0);
-			break;
-		default:
-			break;
+		switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
+			case 0x012: wacom->tool = BTN_TOOL_PENCIL;	break;	/* Inking pen */
+			case 0x022: wacom->tool = BTN_TOOL_PEN;		break;	/* Pen */
+			case 0x032: wacom->tool = BTN_TOOL_BRUSH;	break;	/* Stroke pen */
+			case 0x094: wacom->tool = BTN_TOOL_MOUSE;	break;	/* Mouse 4D */
+			case 0x096: wacom->tool = BTN_TOOL_LENS;	break;	/* Lens cursor */
+			case 0x0fa: wacom->tool = BTN_TOOL_RUBBER;	break;	/* Eraser */
+			case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH;	break;	/* Airbrush */
+			default:    wacom->tool = BTN_TOOL_PEN;		break;	/* Unknown tool */
 		}			
+		input_report_btn(dev, wacom->tool, 1);
 		return;
 	}
 
-	x = (((unsigned int) data[2]) << 8) | data[3] ;
-	y = wacom->dev.absmax[ABS_Y] - ((((unsigned int) data[4]) << 8) | data[5]);
-
-	t = (((unsigned int) data[6]) << 2) | ((data[7] & 0xC0) >> 6);
-
-	/* Handle touch near menustrip */
-	if ( y > ( wacom->dev.absmax[ABS_Y] - INTUOS_MENUSTRIP_Y_ZONE ) ) {
-		if ( t > 10 ) /* Touch */
-			wacom->menustrip_touch = 1;
-		if ( (wacom->menustrip_touch) && (t <= 10) ) { /* Pen Up */
-			intuos_menustrip( x, y, wacom );
-			wacom->menustrip_touch = 0;
-		}
+	if ((data[1] | data[2] | data[3] | data[4] | data[5] |
+	     data[6] | data[7] | data[8] | data[9]) == 0x80) {		/* Exit report */
+		input_report_btn(dev, wacom->tool, 0);
 		return;
 	}
-	else
-		wacom->menustrip_touch = 0;
 
-	switch ( wacom->pen_mode ) {
-	case INTUOS_PEN_MODE_ABS:
-	        input_report_abs(dev, ABS_X, x);
-		input_report_abs(dev, ABS_Y, y);
-		break;
-	case INTUOS_PEN_MODE_REL:
-	        input_report_rel(dev, REL_X, ( x - wacom->last_x) / 8 );
-	        input_report_rel(dev, REL_Y, - ( y - wacom->last_y) / 8 );
-		break;
-	default: break;
-	}
-
-	wacom->last_x = x;
-	wacom->last_y = y;
-
-	input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
-	input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
+	input_report_abs(dev, ABS_X, ((__u32)data[2] << 8) | data[3]);
+	input_report_abs(dev, ABS_Y, ((__u32)data[4] << 8) | data[5]);
+	input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
+	input_report_abs(dev, ABS_DISTANCE, data[9] >> 4);
+	input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+	input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
 
+	input_report_btn(dev, BTN_STYLUS, data[1] & 2);
+	input_report_btn(dev, BTN_STYLUS2, data[1] & 4);
 	input_report_btn(dev, BTN_TOUCH, t > 10);
-	input_report_abs(dev, ABS_PRESSURE, t);
-
-	input_report_abs(dev, ABS_DISTANCE, ( data[9] & 0xf0 ) >> 4 );
+}
 
-	input_report_abs(dev, ABS_TILT_X, ((((unsigned int) data[7]) & 0x3f) << 1) | ((data[8] & 0x80) >> 7));
-	input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+#define WACOM_INTUOS_TOOLS	(BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS))
 
-}
+struct wacom_features wacom_features[] = {
+	{ "Graphire",     0x10,  8, 10206,  7422,  511, 32, wacom_graphire_irq,
+		BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 },
+	{ "Intuos 4x5",   0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Intuos 6x8",   0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Intuos 9x12",  0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ NULL , 0 }
+};
 
 static void *wacom_probe(struct usb_device *dev, unsigned int ifnum)
 {
 	struct usb_endpoint_descriptor *endpoint;
 	struct wacom *wacom;
-	char *name;
+	int i;
 
-	if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM ||
-	    (dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_GRAPHIRE &&
-	     dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS45 &&
-	     dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS68 &&
-	     dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS912 &&
-	     dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS1212 &&
-	     dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS1218))
-		return NULL;
+	if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM) return NULL;
+	for (i = 0; wacom_features[i].idProduct && wacom_features[i].idProduct != dev->descriptor.idProduct; i++);
+	if (!wacom_features[i].idProduct) return NULL;
 
 	endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
 
 	if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL;
 	memset(wacom, 0, sizeof(struct wacom));
 
-	if ( dev->descriptor.idProduct == USB_DEVICE_ID_WACOM_GRAPHIRE ) {
-		wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
-		wacom->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
-		wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE);
-		wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
-		wacom->dev.relbit[0] |= BIT(REL_WHEEL);
-		wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
-
-		wacom->dev.absmax[ABS_X] = 10206;
-		wacom->dev.absmax[ABS_Y] = 7422;
-		wacom->dev.absmax[ABS_PRESSURE] = 511;
-		wacom->dev.absmax[ABS_DISTANCE] = 32;
+	wacom->features = wacom_features + i;
 
-		FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
-			     wacom->data, 8, wacom_graphire_irq, wacom, endpoint->bInterval);
+	wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | wacom->features->evbit;
+	wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE) | wacom->features->absbit;
+	wacom->dev.relbit[0] |= wacom->features->relbit;
+	wacom->dev.keybit[LONG(BTN_LEFT)] |= wacom->features->btnbit;
+	wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) |
+		BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | wacom->features->digibit;
+
+	wacom->dev.absmax[ABS_X] = wacom->features->x_max;
+	wacom->dev.absmax[ABS_Y] = wacom->features->y_max;
+	wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max;
+	wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max;
+	wacom->dev.absmax[ABS_TILT_X] = 127;
+	wacom->dev.absmax[ABS_TILT_Y] = 127;
 
-		name = "Graphire";
-	}
-	else { /* Intuos */
-
-		wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
-		wacom->dev.keybit[LONG(KEY_F1)]  |= BIT(KEY_F1)  | BIT(KEY_F2)  | BIT(KEY_F3)  | BIT(KEY_F4) | BIT(KEY_F5);
-		wacom->dev.keybit[LONG(KEY_F6)]  |= BIT(KEY_F6)  | BIT(KEY_F7)  | BIT(KEY_F8);
-		wacom->dev.keybit[LONG(KEY_F9)]  |= BIT(KEY_F9)  | BIT(KEY_F10) | BIT(KEY_F11) | BIT(KEY_F12);
-		wacom->dev.keybit[LONG(KEY_F13)] |= BIT(KEY_F13) | BIT(KEY_F14) | BIT(KEY_F15) | BIT(KEY_F16);
-		wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
-		wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_BRUSH);
-		wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_MOUSE);
-		wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
-		wacom->dev.absbit[0] |= BIT(ABS_TILT_X) | BIT(ABS_TILT_Y);
-		wacom->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
-
-		wacom->dev.absmax[ABS_PRESSURE] = 1023;
-		wacom->dev.absmax[ABS_DISTANCE] = 15;
-		wacom->dev.absmax[ABS_TILT_X] = 127;
-		wacom->dev.absmax[ABS_TILT_Y] = 127;
-
-		wacom->device = dev->descriptor.idProduct - USB_DEVICE_ID_WACOM_INTUOS45;
-
-		wacom->dev.absmax[ABS_X] = intuos_extents[wacom->device][INTUOS_EXTENTS_MAX_X];
-		wacom->dev.absmax[ABS_Y] = intuos_extents[wacom->device][INTUOS_EXTENTS_MAX_Y];
-
-		wacom->ymax = intuos_extents[wacom->device][INTUOS_EXTENTS_MAX_Y];
-		wacom->pen_mode = INTUOS_PEN_MODE_ABS;
-		wacom->pressure_mode = INTUOS_PRESSURE_MODE_SOFT;
-
-		name = intuos_names[wacom->device];
-
-		FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
-			     wacom->data, 10, wacom_intuos_irq, wacom, endpoint->bInterval);
-
-	}
+	FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+		     wacom->data, wacom->features->pktlen, wacom->features->irq, wacom, endpoint->bInterval);
 
 	if (usb_submit_urb(&wacom->irq)) {
 		kfree(wacom);
@@ -473,7 +266,7 @@
 
 	input_register_device(&wacom->dev);
 
-	printk(KERN_INFO "input%d: Wacom %s\n", wacom->dev.number, name);
+	printk(KERN_INFO "input%d: Wacom %s on usb%d\n", wacom->dev.number, wacom->features->name, dev->devnum);
 
 	return wacom;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/Makefile linux/drivers/video/Makefile
--- v2.3.99-pre2/linux/drivers/video/Makefile	Fri Mar 10 16:40:45 2000
+++ linux/drivers/video/Makefile	Thu Mar 23 10:07:42 2000
@@ -16,7 +16,8 @@
 # All of the (potential) objects that export symbols.
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
-export-objs    := fbmem.o fbcmap.o fbcon.o fbmon.o fbcon-afb.o fbcon-ilbm.o \
+export-objs    := fbmem.o fbcmap.o fbcon.o fbmon.o modedb.o \
+		  fbcon-afb.o fbcon-ilbm.o \
 		  fbcon-vga.o fbcon-iplan2p2.o fbcon-iplan2p4.o \
 		  fbcon-iplan2p8.o fbcon-vga-planes.o fbcon-cfb16.o \
 		  fbcon-cfb2.o fbcon-cfb24.o fbcon-cfb32.o fbcon-cfb4.o \
@@ -46,6 +47,7 @@
 obj-$(CONFIG_FONT_ACORN_8x8)      += font_acorn_8x8.o
 
 obj-$(CONFIG_FB)                  += fbmem.o fbcmap.o modedb.o fbcon.o fonts.o fbmon.o 
+obj-$(CONFIG_FB_COMPAT_XPMAC)     += macmodes.o
 
 obj-$(CONFIG_FB_ACORN)            += acornfb.o
 obj-$(CONFIG_FB_AMIGA)            += amifb.o
@@ -64,9 +66,9 @@
 obj-$(CONFIG_FB_CYBER2000)        += cyber2000fb.o
 obj-$(CONFIG_FB_SGIVW)            += sgivwfb.o
 obj-$(CONFIG_FB_3DFX)             += tdfxfb.o
-obj-$(CONFIG_FB_MAC)              += macfb.o macmodes.o
+obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HP300)            += hpfb.o
-obj-$(CONFIG_FB_OF)               += offb.o macmodes.o
+obj-$(CONFIG_FB_OF)               += offb.o
 obj-$(CONFIG_FB_IMSTT)            += imsttfb.o
 obj-$(CONFIG_FB_RETINAZ3)         += retz3fb.o
 obj-$(CONFIG_FB_CLGEN)            += clgenfb.o fbgen.o
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/amifb.c linux/drivers/video/amifb.c
--- v2.3.99-pre2/linux/drivers/video/amifb.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/video/amifb.c	Thu Mar 23 10:07:42 2000
@@ -1765,37 +1765,11 @@
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	memset(&var, 0, sizeof(var));
 
-#ifdef MODULE
-	var.xres = ami_modedb[defmode].xres;
-	var.yres = ami_modedb[defmode].yres;
-	var.xres_virtual = ami_modedb[defmode].xres;
-	var.yres_virtual = ami_modedb[defmode].yres;
-	var.xoffset = 0;
-	var.yoffset = 0;
-	var.bits_per_pixel = 4;
-	var.activate |= FB_ACTIVATE_TEST;
-	var.pixclock = ami_modedb[defmode].pixclock;
-	var.left_margin = ami_modedb[defmode].left_margin;
-	var.right_margin = ami_modedb[defmode].right_margin;
-	var.upper_margin = ami_modedb[defmode].upper_margin;
-	var.lower_margin = ami_modedb[defmode].lower_margin;
-	var.hsync_len = ami_modedb[defmode].hsync_len;
-	var.vsync_len = ami_modedb[defmode].vsync_len;
-	var.sync = ami_modedb[defmode].sync;
-	var.vmode = ami_modedb[defmode].vmode;
-	err = fb_info.fbops->fb_set_var(&var, -1, &fb_info);
-	var.activate &= ~FB_ACTIVATE_TEST;
-	if (err) {
-		err = -EINVAL;
-		goto amifb_error;
-	}
-#else
 	if (!fb_find_mode(&var, &fb_info, mode_option, ami_modedb,
 			  NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) {
 		err = -EINVAL;
 		goto amifb_error;
 	}
-#endif
 
 	round_down_bpp = 0;
 	chipptr = chipalloc(videomemorysize+
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/cgthreefb.c linux/drivers/video/cgthreefb.c
--- v2.3.99-pre2/linux/drivers/video/cgthreefb.c	Wed Dec 29 13:13:20 1999
+++ linux/drivers/video/cgthreefb.c	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-/* $Id: cgthreefb.c,v 1.8 1999/11/19 09:57:08 davem Exp $
+/* $Id: cgthreefb.c,v 1.9 2000/03/19 04:20:44 anton Exp $
  * cgthreefb.c: CGthree frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
@@ -114,9 +114,9 @@
 	u8 tmp;
 
 	spin_lock_irqsave(&fb->lock, flags);
-	tmp = sbus_readl(&fb->s.cg3.regs->control);
+	tmp = sbus_readb(&fb->s.cg3.regs->control);
 	tmp &= ~CG3_CR_ENABLE_VIDEO;
-	sbus_writel(tmp, &fb->s.cg3.regs->control);
+	sbus_writeb(tmp, &fb->s.cg3.regs->control);
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
@@ -126,9 +126,9 @@
 	u8 tmp;
 
 	spin_lock_irqsave(&fb->lock, flags);
-	tmp = sbus_readl(&fb->s.cg3.regs->control);
+	tmp = sbus_readb(&fb->s.cg3.regs->control);
 	tmp |= CG3_CR_ENABLE_VIDEO;
-	sbus_writel(tmp, &fb->s.cg3.regs->control);
+	sbus_writeb(tmp, &fb->s.cg3.regs->control);
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/macmodes.c linux/drivers/video/macmodes.c
--- v2.3.99-pre2/linux/drivers/video/macmodes.c	Wed Aug 18 10:10:06 1999
+++ linux/drivers/video/macmodes.c	Thu Mar 23 10:07:42 2000
@@ -3,15 +3,22 @@
  *
  *	Copyright (C) 1998 Geert Uytterhoeven
  *
+ *      2000 - Removal of OpenFirmware dependencies by:
+ *      - Ani Joshi
+ *      - Brad Douglas <brad@neruo.com>
+ *
  *  This file is subject to the terms and conditions of the GNU General Public
  *  License. See the file COPYING in the main directory of this archive for
  *  more details.
  */
 
-#include <linux/tty.h>
+#include <linux/errno.h>
 #include <linux/fb.h>
 #include <linux/string.h>
 
+#include <asm/vc_ioctl.h>
+
+#include <video/fbcon.h>
 #include <video/macmodes.h>
 
 
@@ -181,6 +188,107 @@
     { -1,    VMODE_640_480_60 },	/* catch-all, must be last */
 };
 
+struct fb_info *console_fb_info = NULL;
+struct vc_mode display_info;
+static u16 palette_red[16];
+static u16 palette_green[16];
+static u16 palette_blue[16];
+static struct fb_cmap palette_cmap = {
+    0, 16, palette_red, palette_green, palette_blue, NULL
+};
+
+
+int console_getmode(struct vc_mode *);
+int console_setmode(struct vc_mode *, int);
+int console_setcmap(int, unsigned char *, unsigned char *, unsigned char *);
+int console_powermode(int);
+int mac_var_to_vmode(const struct fb_var_screeninfo *, int *, int *);
+
+
+int console_getmode(struct vc_mode *mode)
+{
+    *mode = display_info;
+    return 0;
+}
+
+
+int console_setmode(struct vc_mode *mode, int doit)
+{
+    struct fb_var_screeninfo var;
+    int cmode, err;
+
+    if (!console_fb_info)
+        return -EOPNOTSUPP;
+
+    switch(mode->depth) {
+        case 0:
+        case 8:
+            cmode = CMODE_8;
+            break;
+        case 16:
+            cmode = CMODE_16;
+            break;
+        case 24:
+        case 32:
+            cmode = CMODE_32;
+            break;
+        default:
+            return -EINVAL;
+    }
+
+    if ((err = mac_vmode_to_var(mode->mode, cmode, &var)))
+        return err;
+
+    var.activate = FB_ACTIVATE_TEST;
+    err = console_fb_info->fbops->fb_set_var(&var, fg_console,
+                                             console_fb_info);
+    if (err || !doit)
+        return err;
+    else {
+        int unit;
+
+        var.activate = FB_ACTIVATE_NOW;
+        for (unit = 0; unit < MAX_NR_CONSOLES; unit++)
+            if (fb_display[unit].conp &&
+                (GET_FB_IDX(console_fb_info->node) == con2fb_map[unit]))
+                console_fb_info->fbops->fb_set_var(&var, unit,
+                                                   console_fb_info);
+    }
+
+    return 0;
+}
+
+
+int console_setcmap(int n_entries, unsigned char *red, unsigned char *green,
+                    unsigned char *blue)
+{
+    int i, j, n = 0, err;
+
+    if (!console_fb_info)
+        return -EOPNOTSUPP;
+
+    for (i = 0; i < n_entries; i += n) {
+        n = n_entries - i;
+        if (n > 16)
+            n = 16;
+        palette_cmap.start = 1;
+        palette_cmap.len = n;
+
+        for (j = 0; j < n; j++) {
+            palette_cmap.red[j] = (red[i+j] << 8) | red[i+j];
+            palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
+            palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j];
+        }
+        err = console_fb_info->fbops->fb_set_cmap(&palette_cmap, 1,
+                                                  fg_console,
+                                                  console_fb_info);
+        if (err)
+            return err;
+    }
+
+    return 0;
+}
+
 
     /*
      *  Convert a MacOS vmode/cmode pair to a frame buffer video mode structure
@@ -252,6 +360,17 @@
     var->sync = mode->sync;
     var->vmode = mode->vmode;
     return 0;
+}
+
+
+int console_powermode(int mode)
+{
+    if (mode == VC_POWERMODE_INQUIRY)
+        return 0;
+    if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN)
+        return -EINVAL;
+    /* Not Supported */
+    return -ENXIO;
 }
 
 
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/modedb.c linux/drivers/video/modedb.c
--- v2.3.99-pre2/linux/drivers/video/modedb.c	Mon Oct 11 15:38:15 1999
+++ linux/drivers/video/modedb.c	Thu Mar 23 10:07:42 2000
@@ -8,6 +8,7 @@
  *  more details.
  */
 
+#include <linux/module.h>
 #include <linux/tty.h>
 #include <linux/fb.h>
 #include <linux/console_struct.h>
@@ -253,7 +254,7 @@
     }
 }
 
-static int __init PROC_CONSOLE(const struct fb_info *info)
+static int PROC_CONSOLE(const struct fb_info *info)
 {
 	int fgc;
 	
@@ -275,8 +276,8 @@
 	return MINOR(current->tty->device) - 1;
 }
 
-static int __init try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
-			   const struct fb_videomode *mode, unsigned int bpp)
+int __fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
+		  const struct fb_videomode *mode, unsigned int bpp)
 {
     int err;
 
@@ -391,20 +392,22 @@
 		if ((name_matches(db[j], name, namelen) ||
 		     (res_specified && res_matches(db[j], xres, yres))) &&
 		    (!i || db[j].refresh == refresh) &&
-		    try_mode(var, info, &db[j], bpp))
+		    __fb_try_mode(var, info, &db[j], bpp))
 		    return 2-i;
 	}
     }
 
     DPRINTK("Trying default video mode\n");
-    if (try_mode(var, info, default_mode, default_bpp))
+    if (__fb_try_mode(var, info, default_mode, default_bpp))
 	return 3;
 
     DPRINTK("Trying all modes\n");
     for (i = 0; i < dbsize; i++)
-	if (try_mode(var, info, &db[i], default_bpp))
+	if (__fb_try_mode(var, info, &db[i], default_bpp))
 	    return 4;
 
     DPRINTK("No valid mode found\n");
     return 0;
 }
+
+EXPORT_SYMBOL(__fb_try_mode);
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/offb.c linux/drivers/video/offb.c
--- v2.3.99-pre2/linux/drivers/video/offb.c	Fri Mar 10 16:40:46 2000
+++ linux/drivers/video/offb.c	Thu Mar 23 10:07:42 2000
@@ -94,15 +94,6 @@
 static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
 			    u_long arg, int con, struct fb_info *info);
 
-#ifdef CONFIG_FB_COMPAT_XPMAC
-int console_getmode(struct vc_mode *);
-int console_setmode(struct vc_mode *, int);
-int console_setcmap(int, unsigned char *, unsigned char *, unsigned char *);
-int console_powermode(int);
-struct fb_info *console_fb_info = NULL;
-struct vc_mode display_info;
-#endif /* CONFIG_FB_COMPAT_XPMAC */
-
 extern boot_infos_t *boot_infos;
 
 static int offb_init_driver(struct device_node *);
@@ -898,103 +889,3 @@
 	fb_set_cmap(fb_default_cmap(size), 1, offb_setcolreg, info);
     }
 }
-
-
-#ifdef CONFIG_FB_COMPAT_XPMAC
-
-    /*
-     *  Backward compatibility mode for Xpmac
-     */
-
-int console_getmode(struct vc_mode *mode)
-{
-    *mode = display_info;
-    return 0;
-}
-
-int console_setmode(struct vc_mode *mode, int doit)
-{
-    struct fb_var_screeninfo var;
-    int cmode, err;
-
-    if (!console_fb_info)
-	return -EOPNOTSUPP;
-    switch (mode->depth) {
-	case 8:
-	case 0:		/* default */
-	    cmode = CMODE_8;
-	    break;
-	case 16:
-	    cmode = CMODE_16;
-	    break;
-	case 24:
-	case 32:
-	    cmode = CMODE_32;
-	    break;
-	default:
-	    return -EINVAL;
-    }
-    if ((err = mac_vmode_to_var(mode->mode, cmode, &var)))
-	return err;
-    var.activate = FB_ACTIVATE_TEST;
-    err = console_fb_info->fbops->fb_set_var(&var, fg_console,
-					     console_fb_info);
-    if (err || !doit)
-	return err;
-    else {
-	int unit;
-	var.activate = FB_ACTIVATE_NOW;
-	for (unit = 0; unit < MAX_NR_CONSOLES; unit++)
-	    if (fb_display[unit].conp &&
-		(GET_FB_IDX(console_fb_info->node) == con2fb_map[unit]))
-		    console_fb_info->fbops->fb_set_var(&var, unit,
-						       console_fb_info);
-    }
-    return 0;
-}
-
-static u16 palette_red[16];
-static u16 palette_green[16];                                                 
-static u16 palette_blue[16];
-
-static struct fb_cmap palette_cmap = {
-    0, 16, palette_red, palette_green, palette_blue, NULL
-};
-
-int console_setcmap(int n_entries, unsigned char *red, unsigned char *green,
-		    unsigned char *blue)
-{
-    int i, j, n, err;
-
-    if (!console_fb_info)
-	return -EOPNOTSUPP;
-    for (i = 0; i < n_entries; i += n) {
-	n = n_entries-i;
-	if (n > 16)
-	    n = 16;
-	palette_cmap.start = i;
-	palette_cmap.len = n;
-	for (j = 0; j < n; j++) {
-	    palette_cmap.red[j]   = (red[i+j] << 8) | red[i+j];
-	    palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
-	    palette_cmap.blue[j]  = (blue[i+j] << 8) | blue[i+j];
-	}
-	err = console_fb_info->fbops->fb_set_cmap(&palette_cmap, 1, fg_console,
-						  console_fb_info);
-	if (err)
-	    return err;
-    }
-    return 0;
-}
-
-int console_powermode(int mode)
-{
-    if (mode == VC_POWERMODE_INQUIRY)
-	return 0;
-    if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN)
-	return -EINVAL;
-    /* Not supported */
-    return -ENXIO;
-}
-
-#endif /* CONFIG_FB_COMPAT_XPMAC */
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/video/riva/fbdev.c linux/drivers/video/riva/fbdev.c
--- v2.3.99-pre2/linux/drivers/video/riva/fbdev.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/video/riva/fbdev.c	Mon Mar 20 07:53:52 2000
@@ -521,7 +521,7 @@
 	rinfo->base1_region_size = pci_resource_len (pd, 1);
 
 	assert (rinfo->base0_region_size >= 0x00800000);	/* from GGI */
-	assert (rinfo->base0_region_size >= 0x01000000);	/* from GGI */
+	assert (rinfo->base1_region_size >= 0x01000000);	/* from GGI */
 
 	rinfo->ctrl_base_phys = rinfo->pd->resource[0].start;
 	rinfo->fb_base_phys = rinfo->pd->resource[1].start;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/adfs/inode.c linux/fs/adfs/inode.c
--- v2.3.99-pre2/linux/fs/adfs/inode.c	Sat Feb 26 22:31:51 2000
+++ linux/fs/adfs/inode.c	Tue Mar 21 11:30:08 2000
@@ -59,7 +59,7 @@
 	return block_read_full_page(page, adfs_get_block);
 }
 
-static int adfs_prepare_write(struct page *page, unsigned int from, unsigned int to)
+static int adfs_prepare_write(struct file *file, struct page *page, unsigned int from, unsigned int to)
 {
 	return cont_prepare_write(page, from, to, adfs_get_block,
 		&((struct inode *)page->mapping->host)->u.adfs_i.mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/affs/file.c linux/fs/affs/file.c
--- v2.3.99-pre2/linux/fs/affs/file.c	Sat Feb 26 22:31:51 2000
+++ linux/fs/affs/file.c	Tue Mar 21 11:30:08 2000
@@ -299,7 +299,7 @@
 {
 	return block_read_full_page(page,affs_get_block);
 }
-static int affs_prepare_write(struct page *page, unsigned from, unsigned to)
+static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,affs_get_block,
 		&((struct inode*)page->mapping->host)->u.affs_i.mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/bfs/file.c linux/fs/bfs/file.c
--- v2.3.99-pre2/linux/fs/bfs/file.c	Fri Mar 10 16:40:46 2000
+++ linux/fs/bfs/file.c	Tue Mar 21 11:30:08 2000
@@ -137,7 +137,7 @@
 	return block_read_full_page(page, bfs_get_block);
 }
 
-static int bfs_prepare_write(struct page *page, unsigned from, unsigned to)
+static int bfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return block_prepare_write(page, from, to, bfs_get_block);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.3.99-pre2/linux/fs/binfmt_aout.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/binfmt_aout.c	Wed Mar 22 22:15:57 2000
@@ -252,7 +252,6 @@
 static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct exec ex;
-	struct file * file;
 	int fd;
 	unsigned long error;
 	unsigned long fd_offset;
@@ -263,7 +262,7 @@
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
 	     N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
 	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
-	    bprm->dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+	    bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
 		return -ENOEXEC;
 	}
 
@@ -304,26 +303,32 @@
  	current->flags &= ~PF_FORKNOEXEC;
 #ifdef __sparc__
 	if (N_MAGIC(ex) == NMAGIC) {
+		loff_t pos = fd_offset;
 		/* Fuck me plenty... */
+		/* <AOL></AOL> */
 		error = do_brk(N_TXTADDR(ex), ex.a_text);
-		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
-			  ex.a_text, 0);
+		bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
+			  ex.a_text, &pos);
 		error = do_brk(N_DATADDR(ex), ex.a_data);
-		read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
-			  ex.a_data, 0);
+		bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
+			  ex.a_data, &pos);
 		goto beyond_if;
 	}
 #endif
 
 	if (N_MAGIC(ex) == OMAGIC) {
+		loff_t pos;
 #if defined(__alpha__) || defined(__sparc__)
+		pos = fd_offset;
 		do_brk(N_TXTADDR(ex) & PAGE_MASK,
 			ex.a_text+ex.a_data + PAGE_SIZE - 1);
-		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
-			  ex.a_text+ex.a_data, 0);
+		bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
+			  ex.a_text+ex.a_data, &pos);
 #else
+		pos = 32;
 		do_brk(0, ex.a_text+ex.a_data);
-		read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0);
+		bprm->file->f_op->read(bprm->file, (char *) 0,
+			ex.a_text+ex.a_data, &pos);
 #endif
 		flush_icache_range((unsigned long) 0,
 				   (unsigned long) ex.a_text+ex.a_data);
@@ -336,49 +341,48 @@
 			error_time2 = jiffies;
 		}
 
-		fd = open_dentry(bprm->dentry, O_RDONLY);
+		fd = get_unused_fd();
 		if (fd < 0)
 			return fd;
-		file = fget(fd);
+		get_file(bprm->file);
+		fd_install(fd, bprm->file);
 
 		if ((fd_offset & ~PAGE_MASK) != 0 &&
 		    (jiffies-error_time) > 5*HZ)
 		{
 			printk(KERN_WARNING 
 			       "fd_offset is not page aligned. Please convert program: %s\n",
-			       file->f_dentry->d_name.name);
+			       bprm->file->f_dentry->d_name.name);
 			error_time = jiffies;
 		}
 
-		if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) {
-			fput(file);
+		if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
+			loff_t pos = fd_offset;
 			sys_close(fd);
 			do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
-			read_exec(bprm->dentry, fd_offset,
-				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
+			bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
+					ex.a_text+ex.a_data, &pos);
 			flush_icache_range((unsigned long) N_TXTADDR(ex),
 					   (unsigned long) N_TXTADDR(ex) +
 					   ex.a_text+ex.a_data);
 			goto beyond_if;
 		}
 
-		error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
+		error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
 			PROT_READ | PROT_EXEC,
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 			fd_offset);
 
 		if (error != N_TXTADDR(ex)) {
-			fput(file);
 			sys_close(fd);
 			send_sig(SIGKILL, current, 0);
 			return error;
 		}
 
- 		error = do_mmap(file, N_DATADDR(ex), ex.a_data,
+ 		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
 				PROT_READ | PROT_WRITE | PROT_EXEC,
 				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 				fd_offset + ex.a_text);
-		fput(file);
 		sys_close(fd);
 		if (error != N_DATADDR(ex)) {
 			send_sig(SIGKILL, current, 0);
@@ -420,16 +424,12 @@
 	unsigned long bss, start_addr, len;
 	unsigned long error;
 	int retval;
-	loff_t offset = 0;
 	struct exec ex;
 
 	inode = file->f_dentry->d_inode;
 
 	retval = -ENOEXEC;
-	/* N.B. Save current fs? */
-	set_fs(KERNEL_DS);
-	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset);
-	set_fs(USER_DS);
+	error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
 	if (error != sizeof(ex))
 		goto out;
 
@@ -450,6 +450,7 @@
 
 	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
 		static unsigned long error_time;
+		loff_t pos = N_TXTOFF(ex);
 
 		if ((jiffies-error_time) > 5*HZ)
 		{
@@ -461,8 +462,8 @@
 
 		do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
 		
-		read_exec(file->f_dentry, N_TXTOFF(ex),
-			  (char *)start_addr, ex.a_text + ex.a_data, 0);
+		file->f_op->read(file, (char *)start_addr,
+			ex.a_text + ex.a_data, &pos);
 		flush_icache_range((unsigned long) start_addr,
 				   (unsigned long) start_addr + ex.a_text + ex.a_data);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.3.99-pre2/linux/fs/binfmt_elf.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/binfmt_elf.c	Thu Mar 23 12:46:26 2000
@@ -205,17 +205,15 @@
    an ELF header */
 
 static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
-				     struct dentry * interpreter_dentry,
+				     struct file * interpreter,
 				     unsigned long *interp_load_addr)
 {
-	struct file * file;
 	struct elf_phdr *elf_phdata;
 	struct elf_phdr *eppnt;
 	unsigned long load_addr = 0;
 	int load_addr_set = 0;
 	unsigned long last_bss = 0, elf_bss = 0;
 	unsigned long error = ~0UL;
-	int elf_exec_fileno;
 	int retval, i, size;
 
 	/* First of all, some simple consistency checks */
@@ -224,8 +222,7 @@
 		goto out;
 	if (!elf_check_arch(interp_elf_ex->e_machine))
 		goto out;
-	if (!interpreter_dentry->d_inode->i_fop ||
-	    !interpreter_dentry->d_inode->i_fop->mmap)
+	if (!interpreter->f_op->mmap)
 		goto out;
 
 	/*
@@ -244,17 +241,10 @@
 	if (!elf_phdata)
 		goto out;
 
-	retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff,
-			   (char *) elf_phdata, size, 1);
+	retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);
 	error = retval;
 	if (retval < 0)
-		goto out_free;
-
-	error = ~0UL;
-	elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY);
-	if (elf_exec_fileno < 0)
-		goto out_free;
-	file = fget(elf_exec_fileno);
+		goto out_close;
 
 	eppnt = elf_phdata;
 	for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
@@ -271,7 +261,7 @@
 	    if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
 	    	elf_type |= MAP_FIXED;
 
-	    map_addr = do_mmap(file,
+	    map_addr = do_mmap(interpreter,
 			    load_addr + ELF_PAGESTART(vaddr),
 			    eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr),
 			    elf_prot,
@@ -322,19 +312,17 @@
 	error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
 
 out_close:
-	fput(file);
-	sys_close(elf_exec_fileno);
-out_free:
 	kfree(elf_phdata);
 out:
 	return error;
 }
 
 static unsigned long load_aout_interp(struct exec * interp_ex,
-			     struct dentry * interpreter_dentry)
+			     struct file * interpreter)
 {
-	unsigned long text_data, offset, elf_entry = ~0UL;
+	unsigned long text_data, elf_entry = ~0UL;
 	char * addr;
+	loff_t offset;
 	int retval;
 
 	current->mm->end_code = interp_ex->a_text;
@@ -357,7 +345,10 @@
 	}
 
 	do_brk(0, text_data);
-	retval = read_exec(interpreter_dentry, offset, addr, text_data, 0);
+	retval = -ENOEXEC;
+	if (!interpreter->f_op->read)
+		goto out;
+	retval = interpreter->f_op->read(interpreter, addr, text_data, &offset);
 	if (retval < 0)
 		goto out;
 	flush_icache_range((unsigned long)addr,
@@ -383,8 +374,7 @@
 
 static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
-	struct file * file;
-	struct dentry *interpreter_dentry = NULL; /* to shut gcc up */
+	struct file *interpreter = NULL; /* to shut gcc up */
  	unsigned long load_addr = 0, load_bias;
 	int load_addr_set = 0;
 	char * elf_interpreter = NULL;
@@ -422,7 +412,7 @@
 		goto out;
 	}
 #endif
-	if (!bprm->dentry->d_inode->i_fop||!bprm->dentry->d_inode->i_fop->mmap)
+	if (!bprm->file->f_op||!bprm->file->f_op->mmap)
 		goto out;
 
 	/* Now read in all of the header information */
@@ -435,16 +425,15 @@
 	if (!elf_phdata)
 		goto out;
 
-	retval = read_exec(bprm->dentry, elf_ex.e_phoff,
-				(char *) elf_phdata, size, 1);
+	retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
 	if (retval < 0)
 		goto out_free_ph;
 
-	retval = open_dentry(bprm->dentry, O_RDONLY);
+	retval = get_unused_fd();
 	if (retval < 0)
 		goto out_free_ph;
-	elf_exec_fileno = retval;
-	file = fget(elf_exec_fileno);
+	get_file(bprm->file);
+	fd_install(elf_exec_fileno = retval, bprm->file);
 
 	elf_ppnt = elf_phdata;
 	elf_bss = 0;
@@ -472,9 +461,9 @@
 			if (!elf_interpreter)
 				goto out_free_file;
 
-			retval = read_exec(bprm->dentry, elf_ppnt->p_offset,
+			retval = kernel_read(bprm->file, elf_ppnt->p_offset,
 					   elf_interpreter,
-					   elf_ppnt->p_filesz, 1);
+					   elf_ppnt->p_filesz);
 			if (retval < 0)
 				goto out_free_interp;
 			/* If the program interpreter is one of these two,
@@ -487,32 +476,22 @@
 #if 0
 			printk("Using ELF interpreter %s\n", elf_interpreter);
 #endif
-			old_fs = get_fs(); /* This could probably be optimized */
-			set_fs(get_ds());
 #ifdef __sparc__
 			if (ibcs2_interpreter) {
 				unsigned long old_pers = current->personality;
 					
 				current->personality = PER_SVR4;
-				lock_kernel();
-				interpreter_dentry = open_namei(elf_interpreter);
-				unlock_kernel();
+				interpreter = open_exec(elf_interpreter);
 				current->personality = old_pers;
 			} else
 #endif
 			{
-				lock_kernel();
-				interpreter_dentry = open_namei(elf_interpreter);
-				unlock_kernel();
+				interpreter = open_exec(elf_interpreter);
 			}
-			set_fs(old_fs);
-			retval = PTR_ERR(interpreter_dentry);
-			if (IS_ERR(interpreter_dentry))
+			retval = PTR_ERR(interpreter);
+			if (IS_ERR(interpreter))
 				goto out_free_interp;
-			retval = permission(interpreter_dentry->d_inode, MAY_EXEC);
-			if (retval < 0)
-				goto out_free_dentry;
-			retval = read_exec(interpreter_dentry, 0, bprm->buf, 128, 1);
+			retval = kernel_read(interpreter, 0, bprm->buf, 128);
 			if (retval < 0)
 				goto out_free_dentry;
 
@@ -621,7 +600,7 @@
 			elf_flags |= MAP_FIXED;
 		}
 
-		error = do_mmap(file, ELF_PAGESTART(load_bias + vaddr),
+		error = do_mmap(bprm->file, ELF_PAGESTART(load_bias + vaddr),
 		                (elf_ppnt->p_filesz +
 		                ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
 		                elf_prot, elf_flags, (elf_ppnt->p_offset -
@@ -653,7 +632,6 @@
 			elf_brk = k;
 	}
 	set_fs(old_fs);
-	fput(file); /* all done with the file */
 
 	elf_entry += load_bias;
 	elf_bss += load_bias;
@@ -666,14 +644,14 @@
 	if (elf_interpreter) {
 		if (interpreter_type == INTERPRETER_AOUT)
 			elf_entry = load_aout_interp(&interp_ex,
-						     interpreter_dentry);
+						     interpreter);
 		else
 			elf_entry = load_elf_interp(&interp_elf_ex,
-						    interpreter_dentry,
+						    interpreter,
 						    &interp_load_addr);
 
 		lock_kernel();
-		dput(interpreter_dentry);
+		fput(interpreter);
 		unlock_kernel();
 		kfree(elf_interpreter);
 
@@ -700,7 +678,7 @@
 
 #ifndef VM_STACK_FLAGS
 	lock_kernel();
-	current->executable = dget(bprm->dentry);
+	current->executable = dget(bprm->file->f_dentry);
 	unlock_kernel();
 #endif
 	compute_creds(bprm);
@@ -771,13 +749,12 @@
 	/* error cleanup */
 out_free_dentry:
 	lock_kernel();
-	dput(interpreter_dentry);
+	fput(interpreter);
 	unlock_kernel();
 out_free_interp:
 	if (elf_interpreter)
 		kfree(elf_interpreter);
 out_free_file:
-	fput(file);
 	sys_close(elf_exec_fileno);
 out_free_ph:
 	kfree(elf_phdata);
@@ -789,25 +766,13 @@
 
 static int load_elf_library(struct file *file)
 {
-	struct dentry * dentry;
-	struct inode * inode;
 	struct elf_phdr *elf_phdata;
 	unsigned long elf_bss = 0, bss, len, k;
 	int retval, error, i, j;
 	struct elfhdr elf_ex;
-	loff_t offset = 0;
 
-	error = -EACCES;
-	dentry = file->f_dentry;
-	inode = dentry->d_inode;
-
-	/* seek to the beginning of the file */
 	error = -ENOEXEC;
-
-	/* N.B. save current DS?? */
-	set_fs(KERNEL_DS);
-	retval = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex), &offset);
-	set_fs(USER_DS);
+	retval = kernel_read(file, 0, (char *) &elf_ex, sizeof(elf_ex));
 	if (retval != sizeof(elf_ex))
 		goto out;
 
@@ -816,8 +781,7 @@
 
 	/* First of all, some simple consistency checks */
 	if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
-	   !elf_check_arch(elf_ex.e_machine) ||
-	   (!inode->i_fop || !inode->i_fop->mmap))
+	   !elf_check_arch(elf_ex.e_machine) || !file->f_op->mmap)
 		goto out;
 
 	/* Now read in all of the header information */
@@ -832,8 +796,8 @@
 		goto out;
 
 	/* N.B. check for error return?? */
-	retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
-			   sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
+	retval = kernel_read(file, elf_ex.e_phoff, (char *) elf_phdata,
+			   sizeof(struct elf_phdr) * elf_ex.e_phnum);
 
 	error = -ENOEXEC;
 	for (j = 0, i = 0; i<elf_ex.e_phnum; i++)
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/binfmt_em86.c linux/fs/binfmt_em86.c
--- v2.3.99-pre2/linux/fs/binfmt_em86.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/binfmt_em86.c	Thu Mar 23 12:47:05 2000
@@ -25,7 +25,7 @@
 static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
 {
 	char *interp, *i_name, *i_arg;
-	struct dentry * dentry;
+	struct file * file;
 	int retval;
 	struct elfhdr	elf_ex;
 
@@ -38,16 +38,13 @@
 	/* First of all, some simple consistency checks */
 	if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
 		(!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) ||
-		(!bprm->dentry->d_inode->i_fop || 
-		!bprm->dentry->d_inode->i_fop->mmap)) {
+		(!bprm->file->f_op || !bprm->file->f_op->mmap)) {
 			return -ENOEXEC;
 	}
 
 	bprm->sh_bang++;	/* Well, the bang-shell is implicit... */
-	lock_kernel();
-	dput(bprm->dentry);
-	unlock_kernel();
-	bprm->dentry = NULL;
+	fput(bprm->file);
+	bprm->file = NULL;
 
 	/* Unlike in the script case, we don't have to do any hairy
 	 * parsing to find our interpreter... it's hardcoded!
@@ -79,16 +76,14 @@
 
 	/*
 	 * OK, now restart the process with the interpreter's inode.
-	 * Note that we use open_namei() as the name is now in kernel
+	 * Note that we use open_exec() as the name is now in kernel
 	 * space, and we don't need to copy it.
 	 */
-	lock_kernel();
-	dentry = open_namei(interp);
-	unlock_kernel();
-	if (IS_ERR(dentry))
-		return PTR_ERR(dentry);
+	file = open_exec(interp);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
 
-	bprm->dentry = dentry;
+	bprm->file = file;
 
 	retval = prepare_binprm(bprm);
 	if (retval < 0)
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/binfmt_misc.c linux/fs/binfmt_misc.c
--- v2.3.99-pre2/linux/fs/binfmt_misc.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/binfmt_misc.c	Wed Mar 22 22:15:57 2000
@@ -27,6 +27,7 @@
 #include <linux/proc_fs.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
+#include <linux/file.h>
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 
@@ -180,7 +181,7 @@
 static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 {
 	struct binfmt_entry *fmt;
-	struct dentry * dentry;
+	struct file * file;
 	char iname[128];
 	char *iname_addr = iname;
 	int retval;
@@ -200,8 +201,8 @@
 	if (!fmt)
 		goto _ret;
 
-	dput(bprm->dentry);
-	bprm->dentry = NULL;
+	fput(bprm->file);
+	bprm->file = NULL;
 
 	/* Build args for interpreter */
 	remove_arg_zero(bprm);
@@ -213,11 +214,11 @@
 	bprm->argc++;
 	bprm->filename = iname;	/* for binfmt_script */
 
-	dentry = open_namei(iname);
-	retval = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
+	file = open_exec(iname);
+	retval = PTR_ERR(file);
+	if (IS_ERR(file))
 		goto _ret;
-	bprm->dentry = dentry;
+	bprm->file = file;
 
 	retval = prepare_binprm(bprm);
 	if (retval >= 0)
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/binfmt_script.c linux/fs/binfmt_script.c
--- v2.3.99-pre2/linux/fs/binfmt_script.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/binfmt_script.c	Thu Mar 23 12:46:56 2000
@@ -11,12 +11,13 @@
 #include <linux/malloc.h>
 #include <linux/binfmts.h>
 #include <linux/init.h>
+#include <linux/file.h>
 #include <linux/smp_lock.h>
 
 static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
 {
 	char *cp, *i_name, *i_arg;
-	struct dentry * dentry;
+	struct file *file;
 	char interp[128];
 	int retval;
 
@@ -28,10 +29,8 @@
 	 */
 
 	bprm->sh_bang++;
-	lock_kernel();
-	dput(bprm->dentry);
-	unlock_kernel();
-	bprm->dentry = NULL;
+	fput(bprm->file);
+	bprm->file = NULL;
 
 	bprm->buf[127] = '\0';
 	if ((cp = strchr(bprm->buf, '\n')) == NULL)
@@ -81,13 +80,11 @@
 	/*
 	 * OK, now restart the process with the interpreter's dentry.
 	 */
-	lock_kernel();
-	dentry = open_namei(interp);
-	unlock_kernel();
-	if (IS_ERR(dentry))
-		return PTR_ERR(dentry);
+	file = open_exec(interp);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
 
-	bprm->dentry = dentry;
+	bprm->file = file;
 	retval = prepare_binprm(bprm);
 	if (retval < 0)
 		return retval;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/buffer.c linux/fs/buffer.c
--- v2.3.99-pre2/linux/fs/buffer.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/buffer.c	Tue Mar 21 11:30:08 2000
@@ -2029,7 +2029,7 @@
 
 	if (!page)
 		goto fail;
-	err = mapping->a_ops->prepare_write(page, 0, len-1);
+	err = mapping->a_ops->prepare_write(NULL, page, 0, len-1);
 	if (err)
 		goto fail_map;
 	kaddr = (char*)page_address(page);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.3.99-pre2/linux/fs/coda/dir.c	Fri Mar 10 16:40:46 2000
+++ linux/fs/coda/dir.c	Wed Mar 22 22:15:57 2000
@@ -685,7 +685,7 @@
         }
 
         /* we use this routine to read the file into our buffer */
-        bufsize = read_exec(filp->f_dentry, filp->f_pos, buff, DIR_BUFSIZE, 1);
+        bufsize = kernel_read(filp, filp->f_pos, buff, DIR_BUFSIZE);
         if ( bufsize < 0) {
                 printk("coda_venus_readdir: cannot read directory %d.\n",
 		       bufsize);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/cramfs/inode.c linux/fs/cramfs/inode.c
--- v2.3.99-pre2/linux/fs/cramfs/inode.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/cramfs/inode.c	Thu Mar 23 08:13:35 2000
@@ -200,12 +200,6 @@
 	return retval;
 }
 
-/* Nothing to do.. */
-static void cramfs_put_super(struct super_block *sb)
-{
-	return;
-}
-
 static int cramfs_statfs(struct super_block *sb, struct statfs *buf)
 {
 	buf->f_type = CRAMFS_MAGIC;
@@ -361,7 +355,6 @@
 };
 
 static struct super_operations cramfs_ops = {
-	put_super:	cramfs_put_super,
 	statfs:		cramfs_statfs,
 };
 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/dcache.c linux/fs/dcache.c
--- v2.3.99-pre2/linux/fs/dcache.c	Fri Jan 28 15:09:08 2000
+++ linux/fs/dcache.c	Thu Mar 23 13:29:50 2000
@@ -27,6 +27,9 @@
 #define DCACHE_PARANOIA 1
 /* #define DCACHE_DEBUG 1 */
 
+/* Right now the dcache depends on the kernel lock */
+#define check_lock()	if (!kernel_locked()) BUG()
+
 /* For managing the dcache */
 extern unsigned long num_physpages, page_cache_size;
 extern int inodes_stat[];
@@ -104,6 +107,8 @@
 {
 	int count;
 
+	check_lock();
+
 	if (!dentry)
 		return;
 
@@ -158,7 +163,7 @@
 		count,
 		dentry->d_parent->d_name.name,
 		dentry->d_name.name);
-	*(int *)0 = 0;	
+	BUG();
 }
 
 /*
@@ -168,6 +173,8 @@
  */
 int d_invalidate(struct dentry * dentry)
 {
+	check_lock();
+
 	/*
 	 * If it's already been dropped, return OK.
 	 */
@@ -226,6 +233,7 @@
  */
 void prune_dcache(int count)
 {
+	check_lock();
 	for (;;) {
 		struct dentry *dentry;
 		struct list_head *tmp = dentry_unused.prev;
@@ -261,6 +269,8 @@
 	struct list_head *tmp, *next;
 	struct dentry *dentry;
 
+	check_lock();
+
 	/*
 	 * Pass one ... move the dentries for the specified
 	 * superblock to the most recent end of the unused list.
@@ -308,6 +318,8 @@
 	struct list_head *next;
 	int count = root->d_count;
 
+	check_lock();
+
 repeat:
 	next = this_parent->d_subdirs.next;
 resume:
@@ -337,6 +349,44 @@
 }
 
 /*
+ * Search for at least 1 mount point in the dentry's subdirs.
+ * We descend to the next level whenever the d_subdirs
+ * list is non-empty and continue searching.
+ */
+int have_submounts(struct dentry *parent)
+{
+	struct dentry *this_parent = parent;
+	struct list_head *next;
+
+	if (parent->d_mounts != parent)
+		return 1;
+repeat:
+	next = this_parent->d_subdirs.next;
+resume:
+	while (next != &this_parent->d_subdirs) {
+		struct list_head *tmp = next;
+		struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+		next = tmp->next;
+		/* Have we found a mount point ? */
+		if (dentry->d_mounts != dentry)
+			return 1;
+		if (!list_empty(&dentry->d_subdirs)) {
+			this_parent = dentry;
+			goto repeat;
+		}
+	}
+	/*
+	 * All done at this level ... ascend and resume the search.
+	 */
+	if (this_parent != parent) {
+		next = this_parent->d_child.next; 
+		this_parent = this_parent->d_parent;
+		goto resume;
+	}
+	return 0; /* No mount points found in tree */
+}
+
+/*
  * Search the dentry child list for the specified parent,
  * and move any unused dentries to the end of the unused
  * list for prune_dcache(). We descend to the next level
@@ -349,6 +399,8 @@
 	struct list_head *next;
 	int found = 0;
 
+	check_lock();
+
 repeat:
 	next = this_parent->d_subdirs.next;
 resume:
@@ -525,6 +577,8 @@
 	struct list_head *head = d_hash(parent,hash);
 	struct list_head *tmp = head->next;
 
+	check_lock();
+
 	for (;;) {
 		struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
 		if (tmp == head)
@@ -564,6 +618,8 @@
 	struct list_head *base, *lhp;
 	int valid = 1;
 
+	check_lock();
+
 	if (dentry != dparent) {
 		base = d_hash(dparent, hash);
 		lhp = base;
@@ -605,6 +661,10 @@
  */
 void d_delete(struct dentry * dentry)
 {
+	check_lock();
+
+	check_lock();
+
 	/*
 	 * Are we the only user?
 	 */
@@ -646,6 +706,7 @@
 {
 	const unsigned char *old_name, *new_name;
 
+	check_lock();
 	memcpy(dentry->d_iname, target->d_iname, DNAME_INLINE_LEN); 
 	old_name = target->d_name.name;
 	new_name = dentry->d_name.name;
@@ -674,6 +735,8 @@
  */
 void d_move(struct dentry * dentry, struct dentry * target)
 {
+	check_lock();
+
 	if (!dentry->d_inode)
 		printk(KERN_WARNING "VFS: moving negative dcache entry\n");
 
@@ -773,7 +836,11 @@
 		error = -ENOMEM;
 		if (page) {
 			unsigned long len;
-			char * cwd = d_path(pwd, page, PAGE_SIZE);
+			char * cwd;
+
+			lock_kernel();
+			cwd = d_path(pwd, page, PAGE_SIZE);
+			unlock_kernel();
 
 			error = -ERANGE;
 			len = PAGE_SIZE + page - cwd;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/devfs/Makefile linux/fs/devfs/Makefile
--- v2.3.99-pre2/linux/fs/devfs/Makefile	Wed Feb 16 17:03:52 2000
+++ linux/fs/devfs/Makefile	Wed Mar 22 22:23:55 2000
@@ -36,4 +36,4 @@
 test:
 	gcc -o /tmp/base.o -D__KERNEL__ -I../../include -Wall \
 	-Wstrict-prototypes -O2 -fomit-frame-pointer -pipe \
-	-fno-strength-reduce -DCPU=686   -DEXPORT_SYMTAB -c base.c
+	-fno-strength-reduce   -DEXPORT_SYMTAB -c base.c
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/exec.c linux/fs/exec.c
--- v2.3.99-pre2/linux/fs/exec.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/exec.c	Thu Mar 23 22:38:50 2000
@@ -93,56 +93,6 @@
 		__MOD_DEC_USE_COUNT(fmt->module);
 }
 
-/* N.B. Error returns must be < 0 */
-int open_dentry(struct dentry * dentry, int mode)
-{
-	struct inode * inode = dentry->d_inode;
-	struct file * f;
-	struct list_head * l = NULL;
-	int fd, error;
-
-	lock_kernel();
-	if (inode->i_sb)
-		l = &inode->i_sb->s_files;
-
-	error = -EINVAL;
-	if (!inode->i_fop)
-		goto out;
-	fd = get_unused_fd();
-	if (fd >= 0) {
-		error = -ENFILE;
-		f = get_empty_filp();
-		if (!f)
-			goto out_fd;
-		f->f_flags = mode;
-		f->f_mode = (mode+1) & O_ACCMODE;
-		f->f_dentry = dentry;
-		f->f_pos = 0;
-		f->f_reada = 0;
-		f->f_op = inode->i_fop;
-		if (f->f_op->open) {
-			error = f->f_op->open(inode,f);
-			if (error)
-				goto out_filp;
-		}
-		file_move(f, l);
-		fd_install(fd, f);
-		dget(dentry);
-	}
-	unlock_kernel();
-	return fd;
-
-out_filp:
-	if (error > 0)
-		error = -EIO;
-	put_filp(f);
-out_fd:
-	put_unused_fd(fd);
-out:
-	unlock_kernel();
-	return error;
-}
-
 /*
  * Note that a shared library must be both readable and executable due to
  * security reasons.
@@ -365,44 +315,45 @@
 	return 0;
 }
 
-/*
- * Read in the complete executable. This is used for "-N" files
- * that aren't on a block boundary, and for files on filesystems
- * without get_block support.
- */
-int read_exec(struct dentry *dentry, unsigned long offset,
-	char * addr, unsigned long count, int to_kmem)
-{
-	struct file file;
-	struct inode * inode = dentry->d_inode;
-	int result = -ENOEXEC;
-
-	if (!inode->i_fop)
-		goto end_readexec;
-	if (init_private_file(&file, dentry, 1))
-		goto end_readexec;
-	if (!file.f_op->read)
-		goto close_readexec;
-	if (file.f_op->llseek) {
-		if (file.f_op->llseek(&file,offset,0) != offset)
- 			goto close_readexec;
-	} else
-		file.f_pos = offset;
-	if (to_kmem) {
-		mm_segment_t old_fs = get_fs();
-		set_fs(get_ds());
-		result = file.f_op->read(&file, addr, count, &file.f_pos);
-		set_fs(old_fs);
-	} else {
-		result = verify_area(VERIFY_WRITE, addr, count);
-		if (result)
-			goto close_readexec;
-		result = file.f_op->read(&file, addr, count, &file.f_pos);
-	}
-close_readexec:
-	if (file.f_op->release)
-		file.f_op->release(inode,&file);
-end_readexec:
+struct file *open_exec(const char *name)
+{
+	struct dentry *dentry;
+	struct file *file;
+
+	lock_kernel();
+	dentry = lookup_dentry(name, NULL, LOOKUP_FOLLOW);
+	file = (struct file*) dentry;
+	if (!IS_ERR(dentry)) {
+		file = ERR_PTR(-EACCES);
+		if (dentry->d_inode && S_ISREG(dentry->d_inode->i_mode)) {
+			int err = permission(dentry->d_inode, MAY_EXEC);
+			file = ERR_PTR(err);
+			if (!err) {
+				file = dentry_open(dentry, O_RDONLY);
+out:
+				unlock_kernel();
+				return file;
+			}
+		}
+		dput(dentry);
+	}
+	goto out;
+}
+
+int kernel_read(struct file *file, unsigned long offset,
+	char * addr, unsigned long count)
+{
+	mm_segment_t old_fs;
+	loff_t pos = offset;
+	int result = -ENOSYS;
+
+	if (!file->f_op->read)
+		goto fail;
+	old_fs = get_fs();
+	set_fs(get_ds());
+	result = file->f_op->read(file, addr, count, &pos);
+	set_fs(old_fs);
+fail:
 	return result;
 }
 
@@ -540,7 +491,7 @@
 	flush_thread();
 
 	if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || 
-	    permission(bprm->dentry->d_inode,MAY_READ))
+	    permission(bprm->file->f_dentry->d_inode,MAY_READ))
 		current->dumpable = 0;
 
 	/* An exec changes our domain. We are no longer part of the thread
@@ -580,7 +531,7 @@
 {
 	int mode;
 	int retval,id_change,cap_raised;
-	struct inode * inode = bprm->dentry->d_inode;
+	struct inode * inode = bprm->file->f_dentry->d_inode;
 
 	mode = inode->i_mode;
 	if (!S_ISREG(mode))			/* must be regular file */
@@ -677,7 +628,7 @@
 	}
 
 	memset(bprm->buf,0,sizeof(bprm->buf));
-	return read_exec(bprm->dentry,0,bprm->buf,128,1);
+	return kernel_read(bprm->file,0,bprm->buf,128);
 }
 
 /*
@@ -763,24 +714,20 @@
 	    {
 		int i;
 		char * dynloader[] = { "/sbin/loader" };
-		struct dentry * dentry;
+		struct file * file;
 
-		lock_kernel();
-		dput(bprm->dentry);
-		unlock_kernel();
-		bprm->dentry = NULL;
+		fput(bprm->file);
+		bprm->file = NULL;
 
 	        bprm_loader.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
 	        for (i = 0 ; i < MAX_ARG_PAGES ; i++)	/* clear page-table */
                     bprm_loader.page[i] = NULL;
 
-		lock_kernel();
-		dentry = open_namei(dynloader[0]);
-		unlock_kernel();
-		retval = PTR_ERR(dentry);
-		if (IS_ERR(dentry))
+		file = open_exec(dynloader[0]);
+		retval = PTR_ERR(file);
+		if (IS_ERR(file))
 			return retval;
-		bprm->dentry = dentry;
+		bprm->file = file;
 		bprm->loader = bprm_loader.p;
 		retval = prepare_binprm(bprm);
 		if (retval<0)
@@ -802,12 +749,9 @@
 			retval = fn(bprm, regs);
 			if (retval >= 0) {
 				put_binfmt(fmt);
-				if (bprm->dentry) {
-					lock_kernel();
-					dput(bprm->dentry);
-					unlock_kernel();
-				}
-				bprm->dentry = NULL;
+				if (bprm->file)
+					fput(bprm->file);
+				bprm->file = NULL;
 				current->did_exec = 1;
 				return retval;
 			}
@@ -815,7 +759,7 @@
 			put_binfmt(fmt);
 			if (retval != -ENOEXEC)
 				break;
-			if (!bprm->dentry) {
+			if (!bprm->file) {
 				spin_unlock(&binfmt_lock);
 				return retval;
 			}
@@ -847,37 +791,31 @@
 int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
 {
 	struct linux_binprm bprm;
-	struct dentry * dentry;
+	struct file *file;
 	int retval;
 	int i;
 
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
 	memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); 
 
-	lock_kernel();
-	dentry = open_namei(filename);
-	unlock_kernel();
+	file = open_exec(filename);
 
-	retval = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
+	retval = PTR_ERR(file);
+	if (IS_ERR(file))
 		return retval;
 
-	bprm.dentry = dentry;
+	bprm.file = file;
 	bprm.filename = filename;
 	bprm.sh_bang = 0;
 	bprm.loader = 0;
 	bprm.exec = 0;
 	if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
-		lock_kernel();
-		dput(dentry);
-		unlock_kernel();
+		fput(file);
 		return bprm.argc;
 	}
 
 	if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
-		lock_kernel();
-		dput(dentry);
-		unlock_kernel();
+		fput(file);
 		return bprm.envc;
 	}
 
@@ -905,11 +843,8 @@
 
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
-	if (bprm.dentry) {
-		lock_kernel();
-		dput(bprm.dentry);
-		unlock_kernel();
-	}
+	if (bprm.file)
+		fput(bprm.file);
 
 	/* Assumes that free_page() can take a NULL argument. */ 
 	/* I hope this is ok for all architectures */ 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v2.3.99-pre2/linux/fs/ext2/balloc.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/ext2/balloc.c	Tue Mar 21 13:06:20 2000
@@ -11,6 +11,7 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/locks.h>
 #include <linux/quotaops.h>
@@ -300,21 +301,20 @@
 	if (!gdp)
 		goto error_return;
 
-	if (test_opt (sb, CHECK_STRICT) &&
-	    (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) ||
-	     in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) ||
-	     in_range (block, le32_to_cpu(gdp->bg_inode_table),
-		       sb->u.ext2_sb.s_itb_per_group) ||
-	     in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table),
-		       sb->u.ext2_sb.s_itb_per_group)))
-		ext2_panic (sb, "ext2_free_blocks",
+	if (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) ||
+	    in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) ||
+	    in_range (block, le32_to_cpu(gdp->bg_inode_table),
+		      sb->u.ext2_sb.s_itb_per_group) ||
+	    in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table),
+		      sb->u.ext2_sb.s_itb_per_group))
+		ext2_error (sb, "ext2_free_blocks",
 			    "Freeing blocks in system zones - "
 			    "Block = %lu, count = %lu",
 			    block, count);
 
 	for (i = 0; i < count; i++) {
 		if (!ext2_clear_bit (bit + i, bh->b_data))
-			ext2_warning (sb, "ext2_free_blocks",
+			ext2_error (sb, "ext2_free_blocks",
 				      "bit already cleared for block %lu", 
 				      block);
 		else {
@@ -527,11 +527,11 @@
 
 	tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block);
 
-	if (test_opt (sb, CHECK_STRICT) &&
-	    (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
-	     tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
-	     in_range (tmp, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group)))
-		ext2_panic (sb, "ext2_new_block",
+	if (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
+	    tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
+	    in_range (tmp, le32_to_cpu(gdp->bg_inode_table),
+		      sb->u.ext2_sb.s_itb_per_group))
+		ext2_error (sb, "ext2_new_block",
 			    "Allocating block in system zone - "
 			    "block = %u", tmp);
 
@@ -679,6 +679,7 @@
 		test_root(group, 7));
 }
 
+#ifdef CONFIG_EXT2_CHECK
 /* Called at mount-time, super-block is locked */
 void ext2_check_blocks_bitmap (struct super_block * sb)
 {
@@ -753,3 +754,4 @@
 			    "stored = %lu, counted = %lu",
 			    (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count);
 }
+#endif
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v2.3.99-pre2/linux/fs/ext2/ialloc.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/ext2/ialloc.c	Tue Mar 21 13:06:20 2000
@@ -12,6 +12,7 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/locks.h>
 #include <linux/quotaops.h>
@@ -236,7 +237,7 @@
 
 	/* Ok, now we can actually update the inode bitmaps.. */
 	if (!ext2_clear_bit (bit, bh->b_data))
-		ext2_warning (sb, "ext2_free_inode",
+		ext2_error (sb, "ext2_free_inode",
 			      "bit already cleared for inode %lu", ino);
 	else {
 		gdp = ext2_get_group_desc (sb, block_group, &bh2);
@@ -401,7 +402,7 @@
 				      EXT2_INODES_PER_GROUP(sb))) <
 	    EXT2_INODES_PER_GROUP(sb)) {
 		if (ext2_set_bit (j, bh->b_data)) {
-			ext2_warning (sb, "ext2_new_inode",
+			ext2_error (sb, "ext2_new_inode",
 				      "bit already set for inode %d", j);
 			goto repeat;
 		}
@@ -527,6 +528,7 @@
 #endif
 }
 
+#ifdef CONFIG_EXT2_CHECK
 /* Called at mount-time, super-block is locked */
 void ext2_check_inodes_bitmap (struct super_block * sb)
 {
@@ -565,3 +567,4 @@
 			    (unsigned long) le32_to_cpu(es->s_free_inodes_count),
 			    bitmap_count);
 }
+#endif
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.3.99-pre2/linux/fs/ext2/inode.c	Sat Feb 26 22:31:52 2000
+++ linux/fs/ext2/inode.c	Tue Mar 21 11:30:08 2000
@@ -628,7 +628,7 @@
 {
 	return block_read_full_page(page,ext2_get_block);
 }
-static int ext2_prepare_write(struct page *page, unsigned from, unsigned to)
+static int ext2_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return block_prepare_write(page,from,to,ext2_get_block);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v2.3.99-pre2/linux/fs/ext2/super.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/ext2/super.c	Tue Mar 21 13:06:20 2000
@@ -16,6 +16,7 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/fs.h>
@@ -153,23 +154,14 @@
 			set_opt (*mount_options, NO_UID32);
 		}
 		else if (!strcmp (this_char, "check")) {
-			if (!value || !*value)
-				set_opt (*mount_options, CHECK_NORMAL);
-			else if (!strcmp (value, "none")) {
-				clear_opt (*mount_options, CHECK_NORMAL);
-				clear_opt (*mount_options, CHECK_STRICT);
-			}
-			else if (!strcmp (value, "normal"))
-				set_opt (*mount_options, CHECK_NORMAL);
-			else if (!strcmp (value, "strict")) {
-				set_opt (*mount_options, CHECK_NORMAL);
-				set_opt (*mount_options, CHECK_STRICT);
-			}
-			else {
-				printk ("EXT2-fs: Invalid check option: %s\n",
-					value);
-				return 0;
-			}
+			if (!value || !*value || !strcmp (value, "none"))
+				clear_opt (*mount_options, CHECK);
+			else
+#ifdef CONFIG_EXT2_CHECK
+				set_opt (*mount_options, CHECK);
+#else
+				printk("EXT2 Check option not supported\n");
+#endif
 		}
 		else if (!strcmp (this_char, "debug"))
 			set_opt (*mount_options, DEBUG);
@@ -205,10 +197,6 @@
 			set_opt (*mount_options, GRPID);
 		else if (!strcmp (this_char, "minixdf"))
 			set_opt (*mount_options, MINIX_DF);
-		else if (!strcmp (this_char, "nocheck")) {
-			clear_opt (*mount_options, CHECK_NORMAL);
-			clear_opt (*mount_options, CHECK_STRICT);
-		}
 		else if (!strcmp (this_char, "nogrpid") ||
 			 !strcmp (this_char, "sysvgroups"))
 			clear_opt (*mount_options, GRPID);
@@ -305,10 +293,12 @@
 				EXT2_BLOCKS_PER_GROUP(sb),
 				EXT2_INODES_PER_GROUP(sb),
 				sb->u.ext2_sb.s_mount_opt);
+#ifdef CONFIG_EXT2_CHECK
 		if (test_opt (sb, CHECK)) {
 			ext2_check_blocks_bitmap (sb);
 			ext2_check_inodes_bitmap (sb);
 		}
+#endif
 	}
 #if 0 /* ibasket's still have unresolved bugs... -DaveM */
 
@@ -398,7 +388,6 @@
 	  }
 
 	sb->u.ext2_sb.s_mount_opt = 0;
-	set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
 	if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
 	    &sb->u.ext2_sb.s_mount_opt)) {
 		return NULL;
@@ -674,7 +663,6 @@
 	/*
 	 * Allow the "check" option to be passed as a remount option.
 	 */
-	new_mount_opt = EXT2_MOUNT_CHECK_NORMAL;
 	if (!parse_options (data, &tmp, &resuid, &resgid,
 			    &new_mount_opt))
 		return -EINVAL;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.3.99-pre2/linux/fs/fat/inode.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/fat/inode.c	Tue Mar 21 11:30:08 2000
@@ -737,7 +737,7 @@
 {
 	return block_read_full_page(page,fat_get_block);
 }
-static int fat_prepare_write(struct page *page, unsigned from, unsigned to)
+static int fat_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,fat_get_block,
 		&MSDOS_I((struct inode*)page->mapping->host)->mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/fifo.c linux/fs/fifo.c
--- v2.3.99-pre2/linux/fs/fifo.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/fifo.c	Tue Mar 21 12:47:12 2000
@@ -12,6 +12,21 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 
+static void wait_for_partner(struct inode* inode, unsigned int* cnt)
+{
+	int cur = *cnt;	
+	while(cur == *cnt) {
+		pipe_wait(inode);
+		if(signal_pending(current))
+			break;
+	}
+}
+
+static void wake_up_partner(struct inode* inode)
+{
+	wake_up_interruptible(PIPE_WAIT(*inode));
+}
+
 static int fifo_open(struct inode *inode, struct file *filp)
 {
 	int ret;
@@ -20,29 +35,12 @@
 	if (down_interruptible(PIPE_SEM(*inode)))
 		goto err_nolock_nocleanup;
 
-	if (! inode->i_pipe) {
-		unsigned long page;
-		struct pipe_inode_info *info;
-
-		info = kmalloc(sizeof(struct pipe_inode_info),GFP_KERNEL);
-
+	if (!inode->i_pipe) {
 		ret = -ENOMEM;
-		if (!info)
+		if(!pipe_new(inode))
 			goto err_nocleanup;
-		page = __get_free_page(GFP_KERNEL);
-		if (!page) {
-			kfree(info);
-			goto err_nocleanup;
-		}
-
-		inode->i_pipe = info;
-
-		init_waitqueue_head(PIPE_WAIT(*inode));
-		PIPE_BASE(*inode) = (char *) page;
-		PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
-		PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
-		PIPE_WAITING_WRITERS(*inode) = PIPE_WAITING_READERS(*inode) = 0;
 	}
+	filp->f_version = 0;
 
 	switch (filp->f_mode) {
 	case 1:
@@ -51,27 +49,23 @@
 	 *  POSIX.1 says that O_NONBLOCK means return with the FIFO
 	 *  opened, even when there is no process writing the FIFO.
 	 */
-		filp->f_op = &connecting_fifo_fops;
+		filp->f_op = &read_fifo_fops;
+		PIPE_RCOUNTER(*inode)++;
 		if (PIPE_READERS(*inode)++ == 0)
-			wake_up_interruptible(PIPE_WAIT(*inode));
+			wake_up_partner(inode);
 
-		if (!(filp->f_flags & O_NONBLOCK)) {
-			while (!PIPE_WRITERS(*inode)) {
-				if (signal_pending(current))
+		if (!PIPE_WRITERS(*inode)) {
+			if ((filp->f_flags & O_NONBLOCK)) {
+				/* suppress POLLHUP until we have
+				 * seen a writer */
+				filp->f_version = PIPE_WCOUNTER(*inode);
+			} else 
+			{
+				wait_for_partner(inode, &PIPE_WCOUNTER(*inode));
+				if(signal_pending(current))
 					goto err_rd;
-				up(PIPE_SEM(*inode));
-				interruptible_sleep_on(PIPE_WAIT(*inode));
-
-				/* Note that using down_interruptible here
-				   and similar places below is pointless,
-				   since we have to acquire the lock to clean
-				   up properly.  */
-				down(PIPE_SEM(*inode));
 			}
 		}
-
-		if (PIPE_WRITERS(*inode))
-			filp->f_op = &read_fifo_fops;
 		break;
 	
 	case 2:
@@ -85,15 +79,14 @@
 			goto err;
 
 		filp->f_op = &write_fifo_fops;
+		PIPE_WCOUNTER(*inode)++;
 		if (!PIPE_WRITERS(*inode)++)
-			wake_up_interruptible(PIPE_WAIT(*inode));
+			wake_up_partner(inode);
 
-		while (!PIPE_READERS(*inode)) {
+		if (!PIPE_READERS(*inode)) {
+			wait_for_partner(inode, &PIPE_RCOUNTER(*inode));
 			if (signal_pending(current))
 				goto err_wr;
-			up(PIPE_SEM(*inode));
-			interruptible_sleep_on(PIPE_WAIT(*inode));
-			down(PIPE_SEM(*inode));
 		}
 		break;
 	
@@ -108,8 +101,10 @@
 
 		PIPE_READERS(*inode)++;
 		PIPE_WRITERS(*inode)++;
+		PIPE_RCOUNTER(*inode)++;
+		PIPE_WCOUNTER(*inode)++;
 		if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1)
-			wake_up_interruptible(PIPE_WAIT(*inode));
+			wake_up_partner(inode);
 		break;
 
 	default:
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/hfs/inode.c linux/fs/hfs/inode.c
--- v2.3.99-pre2/linux/fs/hfs/inode.c	Sat Feb 26 22:31:52 2000
+++ linux/fs/hfs/inode.c	Tue Mar 21 11:30:08 2000
@@ -225,7 +225,7 @@
 {
 	return block_read_full_page(page,hfs_get_block);
 }
-static int hfs_prepare_write(struct page *page, unsigned from, unsigned to)
+static int hfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,hfs_get_block,
 		&((struct inode*)page->mapping->host)->u.hfs_i.mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/hpfs/file.c linux/fs/hpfs/file.c
--- v2.3.99-pre2/linux/fs/hpfs/file.c	Thu Feb 10 17:11:17 2000
+++ linux/fs/hpfs/file.c	Tue Mar 21 11:30:08 2000
@@ -94,7 +94,7 @@
 {
 	return block_read_full_page(page,hpfs_get_block);
 }
-static int hpfs_prepare_write(struct page *page, unsigned from, unsigned to)
+static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,hpfs_get_block,
 		&((struct inode*)page->mapping->host)->u.hpfs_i.mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c
--- v2.3.99-pre2/linux/fs/lockd/clntproc.c	Sun Feb 20 21:12:39 2000
+++ linux/fs/lockd/clntproc.c	Mon Mar 20 08:14:04 2000
@@ -296,6 +296,7 @@
 	struct rpc_clnt	*clnt;
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_res	*resp = &req->a_res;
+	struct rpc_message msg;
 	int		status;
 
 	dprintk("lockd: call procedure %s on %s (async)\n",
@@ -306,8 +307,11 @@
 		return -ENOLCK;
 
         /* bootstrap and kick off the async RPC call */
-        status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC,
-					callback, req);
+	msg.rpc_proc = proc;
+	msg.rpc_argp = argp;
+	msg.rpc_resp =resp;
+	msg.rpc_cred = NULL;	
+        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
 
 	/* If the async call is proceeding, increment host refcount */
         if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC))
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/lockd/mon.c linux/fs/lockd/mon.c
--- v2.3.99-pre2/linux/fs/lockd/mon.c	Fri Jan 21 18:19:17 2000
+++ linux/fs/lockd/mon.c	Mon Mar 20 08:14:04 2000
@@ -163,7 +163,7 @@
 	*p++ = htonl(argp->proc);
 
 	/* This is the private part. Needed only for SM_MON call */
-	if (rqstp->rq_task->tk_proc == SM_MON) {
+	if (rqstp->rq_task->tk_msg.rpc_proc == SM_MON) {
 		*p++ = argp->addr;
 		*p++ = 0;
 		*p++ = 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/minix/inode.c linux/fs/minix/inode.c
--- v2.3.99-pre2/linux/fs/minix/inode.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/minix/inode.c	Tue Mar 21 11:30:08 2000
@@ -1014,7 +1014,7 @@
 {
 	return block_read_full_page(page,minix_get_block);
 }
-static int minix_prepare_write(struct page *page, unsigned from, unsigned to)
+static int minix_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return block_prepare_write(page,from,to,minix_get_block);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/Makefile linux/fs/nfs/Makefile
--- v2.3.99-pre2/linux/fs/nfs/Makefile	Wed Jun 24 14:30:10 1998
+++ linux/fs/nfs/Makefile	Tue Mar 21 11:30:08 2000
@@ -9,7 +9,7 @@
 
 O_TARGET := nfs.o
 O_OBJS   := inode.o file.o read.o write.o dir.o symlink.o proc.o \
-	    nfs2xdr.o
+	    nfs2xdr.o flushd.o
 
 ifdef CONFIG_ROOT_NFS
   O_OBJS += nfsroot.o mount_clnt.o
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.3.99-pre2/linux/fs/nfs/dir.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/nfs/dir.c	Thu Mar 23 21:52:32 2000
@@ -596,9 +596,12 @@
 out_valid:
 	return 1;
 out_bad:
-	d_drop(dentry);
 	if (!list_empty(&dentry->d_subdirs))
 		shrink_dcache_parent(dentry);
+	/* If we have submounts, don't unhash ! */
+	if (have_submounts(dentry))
+		goto out_valid;
+	d_drop(dentry);
 	/* Purge readdir caches. */
 	if (dentry->d_parent->d_inode) {
 		nfs_zap_caches(dentry->d_parent->d_inode);
@@ -862,61 +865,6 @@
 	return error;
 }
 
-
-/*  Note: we copy the code from lookup_dentry() here, only: we have to
- *  omit the directory lock. We are already the owner of the lock when
- *  we reach here. And "down(&dir->i_sem)" would make us sleep forever
- *  ('cause WE have the lock)
- * 
- *  VERY IMPORTANT: calculate the hash for this dentry!!!!!!!!
- *  Otherwise the cached lookup DEFINITELY WILL fail. And a new dentry
- *  is created. Without the DCACHE_NFSFS_RENAMED flag. And with d_count
- *  == 1. And trouble.
- *
- *  Concerning my choice of the temp name: it is just nice to have
- *  i_ino part of the temp name, as this offers another check whether
- *  somebody attempts to remove the "silly renamed" dentry itself.
- *  Which is something that I consider evil. Your opinion may vary.
- *  BUT:
- *  Now that I compute the hash value right, it should be possible to simply
- *  check for the DCACHE_NFSFS_RENAMED flag in dentry->d_flag instead of
- *  doing the string compare.
- *  WHICH MEANS:
- *  This offers the opportunity to shorten the temp name. Currently, I use
- *  the hex representation of i_ino + an event counter. This sums up to
- *  as much as 36 characters for a 64 bit machine, and needs 20 chars on 
- *  a 32 bit machine.
- *  QUINTESSENCE
- *  The use of i_ino is simply cosmetic. All we need is a unique temp
- *  file name for the .nfs files. The event counter seemed to be adequate.
- *  And as we retry in case such a file already exists, we are guaranteed
- *  to succeed.
- */
-
-static
-struct dentry *nfs_silly_lookup(struct dentry *parent, char *silly, int slen)
-{
-	struct qstr    sqstr;
-	struct dentry *sdentry;
-	struct dentry *res;
-
-	sqstr.name = silly;
-	sqstr.len  = slen;
-	sqstr.hash = full_name_hash(silly, slen);
-	sdentry = d_lookup(parent, &sqstr);
-	if (!sdentry) {
-		sdentry = d_alloc(parent, &sqstr);
-		if (sdentry == NULL)
-			return ERR_PTR(-ENOMEM);
-		res = nfs_lookup(parent->d_inode, sdentry);
-		if (res) {
-			dput(sdentry);
-			return res;
-		}
-	}
-	return sdentry;
-}
-
 static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
 {
 	static unsigned int sillycounter = 0;
@@ -966,7 +914,7 @@
 		dfprintk(VFS, "trying to rename %s to %s\n",
 			 dentry->d_name.name, silly);
 		
-		sdentry = nfs_silly_lookup(dentry->d_parent, silly, slen);
+		sdentry = lookup_one(silly, dget(dentry->d_parent));
 		/*
 		 * N.B. Better to return EBUSY here ... it could be
 		 * dangerous to delete the file while it's in use.
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v2.3.99-pre2/linux/fs/nfs/file.c	Sat Feb 26 22:31:53 2000
+++ linux/fs/nfs/file.c	Tue Mar 21 11:30:08 2000
@@ -144,10 +144,10 @@
  * If the writer ends up delaying the write, the writer needs to
  * increment the page use counts until he is done with the page.
  */
-static int nfs_prepare_write(struct page *page, unsigned offset, unsigned to)
+static int nfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
 	kmap(page);
-	return 0;
+	return nfs_flush_incompatible(file, page);
 }
 static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/flushd.c linux/fs/nfs/flushd.c
--- v2.3.99-pre2/linux/fs/nfs/flushd.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/flushd.c	Wed Mar 22 00:05:02 2000
@@ -0,0 +1,304 @@
+/*
+ * linux/fs/nfs/flushd.c
+ *
+ * For each NFS mount, there is a separate cache object that contains
+ * a hash table of all clusters. With this cache, an async RPC task
+ * (`flushd') is associated, which wakes up occasionally to inspect
+ * its list of dirty buffers.
+ * (Note that RPC tasks aren't kernel threads. Take a look at the
+ * rpciod code to understand what they are).
+ *
+ * Inside the cache object, we also maintain a count of the current number
+ * of dirty pages, which may not exceed a certain threshold.
+ * (FIXME: This threshold should be configurable).
+ *
+ * The code is streamlined for what I think is the prevalent case for
+ * NFS traffic, which is sequential write access without concurrent
+ * access by different processes.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Rewritten 6/3/2000 by Trond Myklebust
+ * Copyright (C) 1999, 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+
+#include <linux/sched.h>
+
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/sched.h>
+
+#include <linux/spinlock.h>
+
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_mount.h>
+
+/*
+ * Various constants
+ */
+#define NFSDBG_FACILITY         NFSDBG_PAGECACHE
+
+/*
+ * This is the wait queue all cluster daemons sleep on
+ */
+static struct rpc_wait_queue    flushd_queue = RPC_INIT_WAITQ("nfs_flushd");
+
+/*
+ * Spinlock
+ */
+spinlock_t nfs_flushd_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Local function declarations.
+ */
+static void	nfs_flushd(struct rpc_task *);
+static void	nfs_flushd_exit(struct rpc_task *);
+
+
+int nfs_reqlist_init(struct nfs_server *server)
+{
+	struct nfs_reqlist	*cache;
+	struct rpc_task		*task;
+	int			status = 0;
+
+	dprintk("NFS: writecache_init\n");
+	spin_lock(&nfs_flushd_lock);
+	cache = server->rw_requests;
+
+	if (cache->task)
+		goto out_unlock;
+
+	/* Create the RPC task */
+	status = -ENOMEM;
+	task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC);
+	if (!task)
+		goto out_unlock;
+
+	task->tk_calldata = server;
+
+	cache->task = task;
+
+	/* Run the task */
+	cache->runat = jiffies;
+
+	cache->auth = server->client->cl_auth;
+	task->tk_action   = nfs_flushd;
+	task->tk_exit   = nfs_flushd_exit;
+
+	spin_unlock(&nfs_flushd_lock);
+	rpc_execute(task);
+	return 0;
+ out_unlock:
+	spin_unlock(&nfs_flushd_lock);
+	return status;
+}
+
+void nfs_reqlist_exit(struct nfs_server *server)
+{
+	struct nfs_reqlist      *cache;
+
+	cache = server->rw_requests;
+	if (!cache)
+		return;
+
+	dprintk("NFS: reqlist_exit (ptr %p rpc %p)\n", cache, cache->task);
+	while (cache->task || cache->inodes) {
+		spin_lock(&nfs_flushd_lock);
+		if (!cache->task) {
+			spin_unlock(&nfs_flushd_lock);
+			nfs_reqlist_init(server);
+		} else {
+			cache->task->tk_status = -ENOMEM;
+			rpc_wake_up_task(cache->task);
+			spin_unlock(&nfs_flushd_lock);
+		}
+		interruptible_sleep_on_timeout(&cache->request_wait, 1 * HZ);
+	}
+}
+
+int nfs_reqlist_alloc(struct nfs_server *server)
+{
+	struct nfs_reqlist	*cache;
+	if (server->rw_requests)
+		return 0;
+
+	cache = (struct nfs_reqlist *)kmalloc(sizeof(*cache), GFP_KERNEL);
+	if (!cache)
+		return -ENOMEM;
+
+	memset(cache, 0, sizeof(*cache));
+	init_waitqueue_head(&cache->request_wait);
+	server->rw_requests = cache;
+
+	return 0;
+}
+
+void nfs_reqlist_free(struct nfs_server *server)
+{
+	if (server->rw_requests) {
+		kfree(server->rw_requests);
+		server->rw_requests = NULL;
+	}
+}
+
+void nfs_wake_flushd()
+{
+	rpc_wake_up_status(&flushd_queue, -ENOMEM);
+}
+
+static void inode_append_flushd(struct inode *inode)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct inode		**q;
+
+	spin_lock(&nfs_flushd_lock);
+	if (NFS_FLAGS(inode) & NFS_INO_FLUSH)
+		goto out;
+	inode->u.nfs_i.hash_next = NULL;
+
+	q = &cache->inodes;
+	while (*q)
+		q = &(*q)->u.nfs_i.hash_next;
+	*q = inode;
+
+	/* Note: we increase the inode i_count in order to prevent
+	 *	 it from disappearing when on the flush list
+	 */
+	NFS_FLAGS(inode) |= NFS_INO_FLUSH;
+	inode->i_count++;
+ out:
+	spin_unlock(&nfs_flushd_lock);
+}
+
+void inode_remove_flushd(struct inode *inode)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct inode		**q;
+
+	spin_lock(&nfs_flushd_lock);
+	if (!(NFS_FLAGS(inode) & NFS_INO_FLUSH))
+		goto out;
+
+	q = &cache->inodes;
+	while (*q && *q != inode)
+		q = &(*q)->u.nfs_i.hash_next;
+	if (*q) {
+		*q = inode->u.nfs_i.hash_next;
+		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
+		iput(inode);
+	}
+ out:
+	spin_unlock(&nfs_flushd_lock);
+}
+
+void inode_schedule_scan(struct inode *inode, unsigned long time)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct rpc_task		*task;
+	unsigned long		mintimeout;
+
+	if (time_after(NFS_NEXTSCAN(inode), time))
+		NFS_NEXTSCAN(inode) = time;
+	mintimeout = jiffies + 1 * HZ;
+	if (time_before(mintimeout, NFS_NEXTSCAN(inode)))
+		mintimeout = NFS_NEXTSCAN(inode);
+	inode_append_flushd(inode);
+
+	spin_lock(&nfs_flushd_lock);
+	task = cache->task;
+	if (!task) {
+		spin_unlock(&nfs_flushd_lock);
+		nfs_reqlist_init(NFS_SERVER(inode));
+	} else {
+		if (time_after(cache->runat, mintimeout))
+			rpc_wake_up_task(task);
+		spin_unlock(&nfs_flushd_lock);
+	}
+}
+
+
+static void
+nfs_flushd(struct rpc_task *task)
+{
+	struct nfs_server	*server;
+	struct nfs_reqlist	*cache;
+	struct inode		*inode, *next;
+	unsigned long		delay = jiffies + NFS_WRITEBACK_LOCKDELAY;
+	int			flush = (task->tk_status == -ENOMEM);
+
+        dprintk("NFS: %4d flushd starting\n", task->tk_pid);
+	server = (struct nfs_server *) task->tk_calldata;
+        cache = server->rw_requests;
+
+	spin_lock(&nfs_flushd_lock);
+	next = cache->inodes;
+	cache->inodes = NULL;
+	spin_unlock(&nfs_flushd_lock);
+
+	while ((inode = next) != NULL) {
+		next = next->u.nfs_i.hash_next;
+		inode->u.nfs_i.hash_next = NULL;
+		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
+
+		if (flush) {
+			nfs_sync_file(inode, NULL, 0, 0, FLUSH_AGING);
+		} else if (time_after(jiffies, NFS_NEXTSCAN(inode))) {
+			NFS_NEXTSCAN(inode) = jiffies + NFS_WRITEBACK_LOCKDELAY;
+			nfs_flush_timeout(inode, FLUSH_AGING);
+#ifdef CONFIG_NFS_V3
+			nfs_commit_timeout(inode, FLUSH_AGING);
+#endif
+		}
+
+		if (nfs_have_writebacks(inode)) {
+			inode_append_flushd(inode);
+			if (time_after(delay, NFS_NEXTSCAN(inode)))
+				delay = NFS_NEXTSCAN(inode);
+		}
+		iput(inode);
+	}
+
+	dprintk("NFS: %4d flushd back to sleep\n", task->tk_pid);
+	if (time_after(jiffies + 1 * HZ, delay))
+		delay = 1 * HZ;
+	else
+		delay = delay - jiffies;
+	task->tk_status = 0;
+	task->tk_action = nfs_flushd;
+	task->tk_timeout = delay;
+	cache->runat = jiffies + task->tk_timeout;
+
+	spin_lock(&nfs_flushd_lock);
+	if (!cache->nr_requests && !cache->inodes) {
+		cache->task = NULL;
+		task->tk_action = NULL;
+	} else
+		rpc_sleep_on(&flushd_queue, task, NULL, NULL);
+	spin_unlock(&nfs_flushd_lock);
+}
+
+static void
+nfs_flushd_exit(struct rpc_task *task)
+{
+	struct nfs_server	*server;
+	struct nfs_reqlist	*cache;
+	server = (struct nfs_server *) task->tk_calldata;
+	cache = server->rw_requests;
+
+	spin_lock(&nfs_flushd_lock);
+	if (cache->task == task)
+		cache->task = NULL;
+	spin_unlock(&nfs_flushd_lock);
+	wake_up(&cache->request_wait);
+	rpc_release_task(task);
+}
+
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.3.99-pre2/linux/fs/nfs/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/nfs/inode.c	Tue Mar 21 11:30:08 2000
@@ -27,6 +27,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/stats.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_flushd.h>
 #include <linux/lockd/bind.h>
 #include <linux/smp_lock.h>
 
@@ -74,6 +75,12 @@
 	inode->i_rdev = 0;
 	NFS_FILEID(inode) = 0;
 	NFS_FSID(inode) = 0;
+	INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
+	INIT_LIST_HEAD(&inode->u.nfs_i.commit);
+	INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
+	inode->u.nfs_i.ndirty = 0;
+	inode->u.nfs_i.ncommit = 0;
+	inode->u.nfs_i.npages = 0;
 	NFS_CACHEINV(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 }
@@ -92,8 +99,6 @@
 static void
 nfs_delete_inode(struct inode * inode)
 {
-	int failed;
-
 	dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
 	lock_kernel();
@@ -101,29 +106,12 @@
 		nfs_free_dircache(inode);
 	} else {
 		/*
-		 * Flush out any pending write requests ...
+		 * The following can never actually happen...
 		 */
-		if (NFS_WRITEBACK(inode) != NULL) {
-			unsigned long timeout = jiffies + 5*HZ;
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
-#endif
-			nfs_inval(inode);
-			while (NFS_WRITEBACK(inode) != NULL &&
-			       time_before(jiffies, timeout)) {
-				current->state = TASK_INTERRUPTIBLE;
-				schedule_timeout(HZ/10);
-			}
-			current->state = TASK_RUNNING;
-			if (NFS_WRITEBACK(inode) != NULL)
-				printk("NFS: Arghhh, stuck RPC requests!\n");
+		if (nfs_have_writebacks(inode)) {
+			printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
 		}
 	}
-
-	failed = nfs_check_failed_request(inode);
-	if (failed)
-		printk("NFS: inode %ld had %d failed requests\n",
-			inode->i_ino, failed);
 	unlock_kernel();
 
 	clear_inode(inode);
@@ -135,9 +123,18 @@
 	struct nfs_server *server = &sb->u.nfs_sb.s_server;
 	struct rpc_clnt	*rpc;
 
+	/*
+	 * First get rid of the request flushing daemon.
+	 * Relies on rpc_shutdown_client() waiting on all
+	 * client tasks to finish.
+	 */
+	nfs_reqlist_exit(server);
+
 	if ((rpc = server->client) != NULL)
 		rpc_shutdown_client(rpc);
 
+	nfs_reqlist_free(server);
+
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
 	rpciod_down();		/* release rpciod */
@@ -306,6 +303,12 @@
 	sb->s_root->d_op = &nfs_dentry_operations;
 	sb->s_root->d_fsdata = root_fh;
 
+	/* Fire up the writeback cache */
+	if (nfs_reqlist_alloc(server) < 0) {
+		printk(KERN_NOTICE "NFS: cannot initialize writeback cache.\n");
+		goto failure_kill_reqlist;
+	}
+
 	/* We're airborne */
 
 	/* Check whether to start the lockd process */
@@ -314,6 +317,8 @@
 	return sb;
 
 	/* Yargs. It didn't work out. */
+ failure_kill_reqlist:
+	nfs_reqlist_exit(server);
 out_no_root:
 	printk("nfs_read_super: get root inode failed\n");
 	iput(root_inode);
@@ -342,6 +347,7 @@
 	printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
 
 out_free_host:
+	nfs_reqlist_free(server);
 	kfree(server->hostname);
 out_unlock:
 	goto out_fail;
@@ -440,7 +446,6 @@
 
 	make_bad_inode(inode);
 	inode->i_mode = save_mode;
-	nfs_inval(inode);
 	nfs_zap_caches(inode);
 }
 
@@ -864,7 +869,7 @@
 	 * to look at the size or the mtime the server sends us
 	 * too closely, as we're in the middle of modifying them.
 	 */
-	if (NFS_WRITEBACK(inode))
+	if (nfs_have_writebacks(inode))
 		goto out;
 
 	if (inode->i_size != fattr->size) {
@@ -925,7 +930,7 @@
 static DECLARE_FSTYPE(nfs_fs_type, "nfs", nfs_read_super, 0);
 
 extern int nfs_init_fhcache(void);
-extern int nfs_init_wreqcache(void);
+extern int nfs_init_nfspagecache(void);
 
 /*
  * Initialize NFS
@@ -939,7 +944,7 @@
 	if (err)
 		return err;
 
-	err = nfs_init_wreqcache();
+	err = nfs_init_nfspagecache();
 	if (err)
 		return err;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- v2.3.99-pre2/linux/fs/nfs/nfs2xdr.c	Fri Oct 22 13:21:52 1999
+++ linux/fs/nfs/nfs2xdr.c	Thu Mar 23 23:18:20 2000
@@ -63,6 +63,7 @@
 #define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
 #define NFS_readlinkres_sz	1
 #define NFS_readres_sz		1+NFS_fattr_sz+1
+#define NFS_writeres_sz		NFS_attrstat_sz
 #define NFS_stat_sz		1
 #define NFS_readdirres_sz	1
 #define NFS_statfsres_sz	1+NFS_info_sz
@@ -273,6 +274,7 @@
 static int
 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
+	unsigned int nr;
 	u32 count = args->count;
 
 	p = xdr_encode_fhandle(p, args->fh);
@@ -282,28 +284,35 @@
 	*p++ = htonl(count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = count;
-	req->rq_slen += count;
-	req->rq_snr = 2;
+	/* Get the number of buffers in the send iovec */
+	nr = args->nriov;
+
+	if (nr+2 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs "
+			"(nr %d max %d)\n", nr, MAX_IOVEC);
+		return -EINVAL;
+	}
+
+	/* Copy the iovec */
+	memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
 
 #ifdef NFS_PAD_WRITES
 	/*
 	 * Some old servers require that the message length
 	 * be a multiple of 4, so we pad it here if needed.
 	 */
-	count = ((count + 3) & ~3) - count;
-	if (count) {
-#if 0
-printk("nfs_writeargs: padding write, len=%d, slen=%d, pad=%d\n",
-req->rq_svec[1].iov_len, req->rq_slen, count);
-#endif
-		req->rq_svec[2].iov_base = (void *) "\0\0\0";
-		req->rq_svec[2].iov_len  = count;
-		req->rq_slen += count;
-		req->rq_snr = 3;
+	if (count & 3) {
+		struct iovec	*iov = req->rq_svec + nr + 1;
+		int		pad = 4 - (count & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		count += pad;
+		nr++;
 	}
 #endif
+	req->rq_slen += count;
+	req->rq_snr += nr;
 
 	return 0;
 }
@@ -593,6 +602,16 @@
 }
 
 /*
+ * Decode WRITE reply
+ */
+static int
+nfs_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	res->verf->committed = NFS_FILE_SYNC;
+	return nfs_xdr_attrstat(req, p, res->fattr);
+}
+
+/*
  * Decode STATFS reply
  */
 static int
@@ -678,7 +697,7 @@
     PROC(readlink,	readlinkargs,	readlinkres),
     PROC(read,		readargs,	readres),
     PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	attrstat),
+    PROC(write,		writeargs,	writeres),
     PROC(create,	createargs,	diropres),
     PROC(remove,	diropargs,	stat),
     PROC(rename,	renameargs,	stat),
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.3.99-pre2/linux/fs/nfs/nfsroot.c	Mon Oct  4 15:49:30 1999
+++ linux/fs/nfs/nfsroot.c	Wed Mar 22 09:55:33 2000
@@ -145,6 +145,8 @@
 	{ "nocto",	~NFS_MOUNT_NOCTO,	NFS_MOUNT_NOCTO },
 	{ "ac",		~NFS_MOUNT_NOAC,	0 },
 	{ "noac",	~NFS_MOUNT_NOAC,	NFS_MOUNT_NOAC },
+	{ "lock",	~NFS_MOUNT_LOCK,	0 },
+	{ "nolock",	~NFS_MOUNT_NONLM,	NFS_MOUNT_NONLM },
 	{ NULL,		0,			0 }
 };
 
@@ -320,7 +322,7 @@
  *  Parse NFS server and directory information passed on the kernel
  *  command line.
  */
-void __init nfs_root_setup(char *line)
+int __init nfs_root_setup(char *line)
 {
 	ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
 	if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
@@ -333,6 +335,7 @@
 		sprintf(nfs_root_name, NFS_ROOT, line);
 	}
 	root_nfs_parse_addr(nfs_root_name);
+	return 1;
 }
 
 __setup("nfsroot=", nfs_root_setup);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v2.3.99-pre2/linux/fs/nfs/proc.c	Fri Oct 22 13:21:52 1999
+++ linux/fs/nfs/proc.c	Thu Mar 23 08:24:27 2000
@@ -111,11 +111,15 @@
 			unsigned long offset, unsigned int count,
 			const void *buffer, struct nfs_fattr *fattr)
 {
-	struct nfs_writeargs	arg = { fhandle, offset, count, buffer };
+	struct nfs_writeargs	arg = { fhandle, offset, count, 1, 1,
+					{{(void *) buffer, count}, {0,0}, {0,0}, {0,0},
+					{0,0}, {0,0}, {0,0}, {0,0}}};
+	struct nfs_writeverf	verf;
+	struct nfs_writeres	res = {fattr, &verf, count};
 	int			status;
 
 	dprintk("NFS call  write %d @ %ld\n", count, offset);
-	status = rpc_call(server->client, NFSPROC_WRITE, &arg, fattr,
+	status = rpc_call(server->client, NFSPROC_WRITE, &arg, &res,
 			swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0);
 	dprintk("NFS reply read: %d\n", status);
 	return status < 0? status : count;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.3.99-pre2/linux/fs/nfs/read.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/nfs/read.c	Mon Mar 20 08:14:04 2000
@@ -171,6 +171,7 @@
 nfs_readpage_async(struct dentry *dentry, struct inode *inode,
 			struct page *page)
 {
+	struct rpc_message msg;
 	unsigned long address;
 	struct nfs_rreq	*req;
 	int		result = -1, flags;
@@ -195,8 +196,13 @@
 
 	/* Start the async call */
 	dprintk("NFS: executing async READ request.\n");
-	result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ,
-				&req->ra_args, &req->ra_res, flags,
+
+	msg.rpc_proc = NFSPROC_READ;
+	msg.rpc_argp = &req->ra_args;
+	msg.rpc_resp = &req->ra_res;
+	msg.rpc_cred = NULL;
+
+	result = rpc_call_async(NFS_CLIENT(inode), &msg, flags,
 				nfs_readpage_result, req);
 	if (result < 0)
 		goto out_free;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.3.99-pre2/linux/fs/nfs/write.c	Sat Feb 26 22:31:53 2000
+++ linux/fs/nfs/write.c	Wed Mar 22 15:03:26 2000
@@ -46,6 +46,7 @@
  * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/malloc.h>
 #include <linux/swap.h>
@@ -54,33 +55,126 @@
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_flushd.h>
 #include <asm/uaccess.h>
 #include <linux/smp_lock.h>
 
 #define NFS_PARANOIA 1
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
-static void			nfs_wback_begin(struct rpc_task *task);
-static void			nfs_wback_result(struct rpc_task *task);
-static void			nfs_cancel_request(struct nfs_wreq *req);
+/*
+ * Spinlock
+ */
+spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int	nfs_nr_requests = 0;
 
 /*
- * Cache parameters
+ * Local structures
+ *
+ * Valid flags for a dirty buffer
  */
-#define NFS_WRITEBACK_DELAY	(10 * HZ)
-#define NFS_WRITEBACK_MAX	64
+#define PG_BUSY			0x0001
 
 /*
- * Limit number of delayed writes
+ * This is the struct where the WRITE/COMMIT arguments go.
  */
-static int			nr_write_requests = 0;
-static struct rpc_wait_queue	write_queue = RPC_INIT_WAITQ("write_chain");
+struct nfs_write_data {
+	struct rpc_task		task;
+	struct file		*file;
+	struct rpc_cred		*cred;
+	struct nfs_writeargs	args;		/* argument struct */
+	struct nfs_writeres	res;		/* result struct */
+	struct nfs_fattr	fattr;
+	struct nfs_writeverf	verf;
+	struct list_head	pages;		/* Coalesced requests we wish to flush */
+};
+
+struct nfs_page {
+	struct list_head	wb_hash,	/* Inode */
+				wb_list,
+				*wb_list_head;
+	struct file		*wb_file;
+	struct rpc_cred		*wb_cred;
+	struct page		*wb_page;	/* page to write out */
+	wait_queue_head_t	wb_wait;	/* wait queue */
+	unsigned long		wb_timeout;	/* when to write/commit */
+	unsigned int		wb_offset,	/* Offset of write */
+				wb_bytes,	/* Length of request */
+				wb_count,	/* reference count */
+				wb_flags;
+	struct nfs_writeverf	wb_verf;	/* Commit cookie */
+};
+
+#define NFS_WBACK_BUSY(req)	((req)->wb_flags & PG_BUSY)
+
+/*
+ * Local function declarations
+ */
+static void	nfs_writeback_done(struct rpc_task *);
+#ifdef CONFIG_NFS_V3
+static void	nfs_commit_done(struct rpc_task *);
+#endif
 
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)	(0)
 #endif
 
+static kmem_cache_t *nfs_page_cachep = NULL;
+static kmem_cache_t *nfs_wdata_cachep = NULL;
+
+static __inline__ struct nfs_page *nfs_page_alloc(void)
+{
+	struct nfs_page	*p;
+	p = kmem_cache_alloc(nfs_page_cachep, SLAB_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->wb_hash);
+		INIT_LIST_HEAD(&p->wb_list);
+		init_waitqueue_head(&p->wb_wait);
+	}
+	return p;
+}
+
+static __inline__ void nfs_page_free(struct nfs_page *p)
+{
+	kmem_cache_free(nfs_page_cachep, p);
+}
+
+static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
+{
+	struct nfs_write_data	*p;
+	p = kmem_cache_alloc(nfs_wdata_cachep, SLAB_NFS);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->pages);
+	}
+	return p;
+}
+
+static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
+{
+	kmem_cache_free(nfs_wdata_cachep, p);
+}
+
+static void nfs_writedata_release(struct rpc_task *task)
+{
+	struct nfs_write_data	*wdata = (struct nfs_write_data *)task->tk_calldata;
+	rpc_release_task(task);
+	nfs_writedata_free(wdata);
+}
+
+/*
+ * This function will be used to simulate weak cache consistency
+ * under NFSv2 when the NFSv3 attribute patch is included.
+ * For the moment, we just call nfs_refresh_inode().
+ */
+static __inline__ int
+nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
+{
+	return nfs_refresh_inode(inode, fattr);
+}
+
 /*
  * Write a page synchronously.
  * Offset is the data offset within the page.
@@ -161,278 +255,770 @@
 }
 
 /*
- * Append a writeback request to a list
+ * Write a page to the server. This was supposed to be used for
+ * NFS swapping only.
+ * FIXME: Using this for mmap is pointless, breaks asynchronous
+ *        writebacks, and is extremely slow.
+ */
+int
+nfs_writepage(struct dentry * dentry, struct page *page)
+{
+	struct inode *inode = dentry->d_inode;
+	unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+	unsigned offset = PAGE_CACHE_SIZE;
+	int err;
+
+	/* easy case */
+	if (page->index < end_index)
+		goto do_it;
+	/* things got complicated... */
+	offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+	/* OK, are we completely out? */
+	if (page->index >= end_index+1 || !offset)
+		return -EIO;
+do_it:
+	err = nfs_writepage_sync(dentry, inode, page, 0, offset); 
+	if ( err == offset) return 0; 
+	return err; 
+}
+
+/*
+ * Check whether the file range we want to write to is locked by
+ * us.
+ */
+static int
+region_locked(struct inode *inode, struct nfs_page *req)
+{
+	struct file_lock	*fl;
+	unsigned long		rqstart, rqend;
+
+	/* Don't optimize writes if we don't use NLM */
+	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
+		return 0;
+
+	rqstart = page_offset(req->wb_page) + req->wb_offset;
+	rqend = rqstart + req->wb_bytes;
+	for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+		if (fl->fl_owner == current->files && (fl->fl_flags & FL_POSIX)
+		    && fl->fl_type == F_WRLCK
+		    && fl->fl_start <= rqstart && rqend <= fl->fl_end) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static inline struct nfs_page *
+nfs_inode_wb_entry(struct list_head *head)
+{
+	return list_entry(head, struct nfs_page, wb_hash);
+}
+
+/*
+ * Insert a write request into an inode
  */
 static inline void
-append_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 {
-	dprintk("NFS:      append_write_request(%p, %p)\n", q, wreq);
-	rpc_append_list(q, wreq);
+	if (!list_empty(&req->wb_hash))
+		return;
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted hashed!\n");
+	inode->u.nfs_i.npages++;
+	list_add(&req->wb_hash, &inode->u.nfs_i.writeback);
+	req->wb_count++;
 }
 
 /*
- * Remove a writeback request from a list
+ * Insert a write request into an inode
  */
 static inline void
-remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+nfs_inode_remove_request(struct nfs_page *req)
 {
-	dprintk("NFS:      remove_write_request(%p, %p)\n", q, wreq);
-	rpc_remove_list(q, wreq);
+	struct inode *inode;
+	spin_lock(&nfs_wreq_lock);
+	if (list_empty(&req->wb_hash)) {
+		spin_unlock(&nfs_wreq_lock);
+		return;
+	}
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted unhashed!\n");
+	inode = req->wb_file->f_dentry->d_inode;
+	list_del(&req->wb_hash);
+	INIT_LIST_HEAD(&req->wb_hash);
+	inode->u.nfs_i.npages--;
+	if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n");
+	if (!nfs_have_writebacks(inode))
+		inode_remove_flushd(inode);
+	spin_unlock(&nfs_wreq_lock);
+	nfs_release_request(req);
 }
 
 /*
- * Find a non-busy write request for a given page to
- * try to combine with.
+ * Find a request
  */
-static inline struct nfs_wreq *
-find_write_request(struct inode *inode, struct page *page)
+static inline struct nfs_page *
+_nfs_find_request(struct inode *inode, struct page *page)
 {
-	pid_t pid = current->pid;
-	struct nfs_wreq	*head, *req;
+	struct list_head	*head, *next;
 
-	dprintk("NFS:      find_write_request(%x/%ld, %p)\n",
-				inode->i_dev, inode->i_ino, page);
-	if (!(req = head = NFS_WRITEBACK(inode)))
-		return NULL;
-	do {
-		/*
-		 * We can't combine with canceled requests or
-		 * requests that have already been started..
-		 */
-		if (req->wb_flags & (NFS_WRITE_CANCELLED | NFS_WRITE_INPROGRESS))
+	head = &inode->u.nfs_i.writeback;
+	next = head->next;
+	while (next != head) {
+		struct nfs_page *req = nfs_inode_wb_entry(next);
+		next = next->next;
+		if (page_index(req->wb_page) != page_index(page))
 			continue;
+		req->wb_count++;
+		return req;
+	}
+	return NULL;
+}
 
-		if (req->wb_page == page && req->wb_pid == pid)
-			return req;
+struct nfs_page *
+nfs_find_request(struct inode *inode, struct page *page)
+{
+	struct nfs_page		*req;
 
-		/*
-		 * Ehh, don't keep too many tasks queued..
-		 */
-		rpc_wake_up_task(&req->wb_task);
+	spin_lock(&nfs_wreq_lock);
+	req = _nfs_find_request(inode, page);
+	spin_unlock(&nfs_wreq_lock);
+	return req;
+}
 
-	} while ((req = WB_NEXT(req)) != head);
-	return NULL;
+static inline struct nfs_page *
+nfs_list_entry(struct list_head *head)
+{
+	return list_entry(head, struct nfs_page, wb_list);
 }
 
 /*
- * Find and release all failed requests for this inode.
+ * Insert a write request into a sorted list
  */
-int
-nfs_check_failed_request(struct inode * inode)
+static inline void
+nfs_list_add_request(struct nfs_page *req, struct list_head *head)
 {
-	/* FIXME! */
-	return 0;
+	struct list_head *prev;
+
+	if (!list_empty(&req->wb_list)) {
+		printk(KERN_ERR "NFS: Add to list failed!\n");
+		return;
+	}
+	if (list_empty(&req->wb_hash)) {
+		printk(KERN_ERR "NFS: Unhashed request attempted added to a list!\n");
+		return;
+	}
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted added to list!\n");
+	prev = head->prev;
+	while (prev != head) {
+		struct nfs_page	*p = nfs_list_entry(prev);
+		if (page_index(p->wb_page) < page_index(req->wb_page))
+			break;
+		prev = prev->prev;
+	}
+	list_add(&req->wb_list, prev);
+	req->wb_list_head = head;
 }
 
 /*
- * Try to merge adjacent write requests. This works only for requests
- * issued by the same user.
+ * Insert a write request into an inode
  */
-static inline int
-update_write_request(struct nfs_wreq *req, unsigned int first,
-			unsigned int bytes)
+static inline void
+nfs_list_remove_request(struct nfs_page *req)
 {
-	unsigned int	rqfirst = req->wb_offset,
-			rqlast = rqfirst + req->wb_bytes,
-			last = first + bytes;
+	if (list_empty(&req->wb_list))
+		return;
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted removed from list!\n");
+	list_del(&req->wb_list);
+	INIT_LIST_HEAD(&req->wb_list);
+	req->wb_list_head = NULL;
+}
 
-	dprintk("nfs:      trying to update write request %p\n", req);
+/*
+ * Add a request to the inode's dirty list.
+ */
+static inline void
+nfs_mark_request_dirty(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_file->f_dentry->d_inode;
 
-	/* not contiguous? */
-	if (rqlast < first || last < rqfirst)
-		return 0;
+	spin_lock(&nfs_wreq_lock);
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.dirty);
+		inode->u.nfs_i.ndirty++;
+	}
+	spin_unlock(&nfs_wreq_lock);
+	/*
+	 * NB: the call to inode_schedule_scan() must lie outside the
+	 *     spinlock since it can run flushd().
+	 */
+	inode_schedule_scan(inode, req->wb_timeout);
+}
 
-	if (first < rqfirst)
-		rqfirst = first;
-	if (rqlast < last)
-		rqlast = last;
+/*
+ * Check if a request is dirty
+ */
+static inline int
+nfs_dirty_request(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_file->f_dentry->d_inode;
+	return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty;
+}
 
-	req->wb_offset = rqfirst;
-	req->wb_bytes  = rqlast - rqfirst;
-	req->wb_count++;
+#ifdef CONFIG_NFS_V3
+/*
+ * Add a request to the inode's commit list.
+ */
+static inline void
+nfs_mark_request_commit(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_file->f_dentry->d_inode;
 
-	return 1;
+	spin_lock(&nfs_wreq_lock);
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.commit);
+		inode->u.nfs_i.ncommit++;
+	}
+	spin_unlock(&nfs_wreq_lock);
+	/*
+	 * NB: the call to inode_schedule_scan() must lie outside the
+	 *     spinlock since it can run flushd().
+	 */
+	inode_schedule_scan(inode, req->wb_timeout);
 }
+#endif
 
-static kmem_cache_t *nfs_wreq_cachep;
-
-int nfs_init_wreqcache(void)
+/*
+ * Lock the page of an asynchronous request
+ */
+static inline int
+nfs_lock_request(struct nfs_page *req)
 {
-	nfs_wreq_cachep = kmem_cache_create("nfs_wreq",
-					    sizeof(struct nfs_wreq),
-					    0, SLAB_HWCACHE_ALIGN,
-					    NULL, NULL);
-	if (nfs_wreq_cachep == NULL)
-		return -ENOMEM;
-	return 0;
+	if (NFS_WBACK_BUSY(req))
+		return 0;
+	req->wb_count++;
+	req->wb_flags |= PG_BUSY;
+	return 1;
 }
 
 static inline void
-free_write_request(struct nfs_wreq * req)
+nfs_unlock_request(struct nfs_page *req)
 {
-	if (!--req->wb_count)
-		kmem_cache_free(nfs_wreq_cachep, req);
+	if (!NFS_WBACK_BUSY(req)) {
+		printk(KERN_ERR "NFS: Invalid unlock attempted\n");
+		return;
+	}
+	req->wb_flags &= ~PG_BUSY;
+	wake_up(&req->wb_wait);
+	nfs_release_request(req);
 }
 
 /*
- * Create and initialize a writeback request
+ * Create a write request.
+ * Page must be locked by the caller. This makes sure we never create
+ * two different requests for the same page, and avoids possible deadlock
+ * when we reach the hard limit on the number of dirty pages.
  */
-static inline struct nfs_wreq *
-create_write_request(struct file * file, struct page *page, unsigned int offset, unsigned int bytes)
-{
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
-	struct rpc_clnt	*clnt = NFS_CLIENT(inode);
-	struct nfs_wreq *wreq;
-	struct rpc_task	*task;
+static struct nfs_page *
+nfs_create_request(struct inode *inode, struct file *file, struct page *page,
+		   unsigned int offset, unsigned int count)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct nfs_page		*req = NULL;
+	long			timeout;
 
-	dprintk("NFS:      create_write_request(%s/%s, %ld+%d)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		(page->index << PAGE_CACHE_SHIFT) + offset, bytes);
+	/* Deal with hard/soft limits.
+	 */
+	do {
+		/* If we're over the soft limit, flush out old requests */
+		if (nfs_nr_requests >= MAX_REQUEST_SOFT)
+			nfs_wb_file(inode, file);
+
+		/* If we're still over the soft limit, wake up some requests */
+		if (nfs_nr_requests >= MAX_REQUEST_SOFT) {
+			dprintk("NFS:      hit soft limit (%d requests)\n",
+				nfs_nr_requests);
+			if (!cache->task)
+				nfs_reqlist_init(NFS_SERVER(inode));
+			nfs_wake_flushd();
+		}
+
+		/* If we haven't reached the hard limit yet,
+		 * try to allocate the request struct */
+		if (nfs_nr_requests < MAX_REQUEST_HARD) {
+			req = nfs_page_alloc();
+			if (req != NULL)
+				break;
+		}
 
-	/* FIXME: Enforce hard limit on number of concurrent writes? */
-	wreq = kmem_cache_alloc(nfs_wreq_cachep, SLAB_KERNEL);
-	if (!wreq)
-		goto out_fail;
-	memset(wreq, 0, sizeof(*wreq));
-
-	task = &wreq->wb_task;
-	rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
-	task->tk_calldata = wreq;
-	task->tk_action = nfs_wback_begin;
-
-	rpcauth_lookupcred(task);	/* Obtain user creds */
-	if (task->tk_status < 0)
-		goto out_req;
+		/* We're over the hard limit. Wait for better times */
+		dprintk("NFS:      create_request sleeping (total %d pid %d)\n",
+			nfs_nr_requests, current->pid);
+
+		timeout = 1 * HZ;
+		if (NFS_SERVER(inode)->flags & NFS_MOUNT_INTR) {
+			interruptible_sleep_on_timeout(&cache->request_wait,
+						       timeout);
+			if (signalled())
+				break;
+		} else
+			sleep_on_timeout(&cache->request_wait, timeout);
+
+		dprintk("NFS:      create_request waking up (tot %d pid %d)\n",
+			nfs_nr_requests, current->pid);
+	} while (!req);
+	if (!req)
+		return NULL;
 
-	/* Put the task on inode's writeback request list. */
+	/* Initialize the request struct. Initially, we assume a
+	 * long write-back delay. This will be adjusted in
+	 * update_nfs_request below if the region is not locked. */
+	req->wb_page    = page;
+	atomic_inc(&page->count);
+	req->wb_offset  = offset;
+	req->wb_bytes   = count;
+	/* If the region is locked, adjust the timeout */
+	if (region_locked(inode, req))
+		req->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY;
+	else
+		req->wb_timeout = jiffies + NFS_WRITEBACK_DELAY;
+	req->wb_file    = file;
+	req->wb_cred	= rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
 	get_file(file);
-	wreq->wb_file = file;
-	wreq->wb_pid    = current->pid;
-	wreq->wb_page   = page;
-	init_waitqueue_head(&wreq->wb_wait);
-	wreq->wb_offset = offset;
-	wreq->wb_bytes  = bytes;
-	wreq->wb_count	= 2;		/* One for the IO, one for us */
-
-	kmap(page);
-	append_write_request(&NFS_WRITEBACK(inode), wreq);
+	req->wb_count   = 1;
+
+	/* register request's existence */
+	cache->nr_requests++;
+	nfs_nr_requests++;
+	return req;
+}
 
-	if (nr_write_requests++ > NFS_WRITEBACK_MAX*3/4)
-		rpc_wake_up_next(&write_queue);
 
-	return wreq;
+/*
+ * Release all resources associated with a write request after it
+ * has been committed to stable storage
+ *
+ * Note: Should always be called with the spinlock held!
+ */
+void
+nfs_release_request(struct nfs_page *req)
+{
+	struct inode		*inode = req->wb_file->f_dentry->d_inode;
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct page		*page = req->wb_page;
+
+	spin_lock(&nfs_wreq_lock);
+	if (--req->wb_count) {
+		spin_unlock(&nfs_wreq_lock);
+		return;
+	}
+	spin_unlock(&nfs_wreq_lock);
 
-out_req:
-	rpc_release_task(task);
-	kmem_cache_free(nfs_wreq_cachep, wreq);
-out_fail:
-	return NULL;
+	if (!list_empty(&req->wb_list)) {
+		printk(KERN_ERR "NFS: Request released while still on a list!\n");
+		nfs_list_remove_request(req);
+	}
+	if (!list_empty(&req->wb_hash)) {
+		printk(KERN_ERR "NFS: Request released while still hashed!\n");
+		nfs_inode_remove_request(req);
+	}
+	if (NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: Request released while still locked!\n");
+
+	rpcauth_releasecred(NFS_CLIENT(inode)->cl_auth, req->wb_cred);
+	fput(req->wb_file);
+	page_cache_release(page);
+	nfs_page_free(req);
+	/* wake up anyone waiting to allocate a request */
+	cache->nr_requests--;
+	nfs_nr_requests--;
+	wake_up(&cache->request_wait);
 }
 
 /*
- * Schedule a writeback RPC call.
- * If the server is congested, don't add to our backlog of queued
- * requests but call it synchronously.
- * The function returns whether we should wait for the thing or not.
- *
- * FIXME: Here we could walk the inode's lock list to see whether the
- * page we're currently writing to has been write-locked by the caller.
- * If it is, we could schedule an async write request with a long
- * delay in order to avoid writing back the page until the lock is
- * released.
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
  */
-static inline int
-schedule_write_request(struct nfs_wreq *req, int sync)
+static int
+nfs_wait_on_request(struct nfs_page *req)
 {
-	struct rpc_task	*task = &req->wb_task;
-	struct file	*file = req->wb_file;
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
+	struct inode	*inode = req->wb_file->f_dentry->d_inode;
+        struct rpc_clnt	*clnt = NFS_CLIENT(inode);
+        int retval;
 
-	if (NFS_CONGESTED(inode) || nr_write_requests >= NFS_WRITEBACK_MAX)
-		sync = 1;
+	if (!NFS_WBACK_BUSY(req))
+		return 0;
+	req->wb_count++;
+	retval = nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req));
+	nfs_release_request(req);
+        return retval;
+}
 
-	if (sync) {
-		sigset_t	oldmask;
-		struct rpc_clnt *clnt = NFS_CLIENT(inode);
-		dprintk("NFS: %4d schedule_write_request (sync)\n",
-					task->tk_pid);
-		/* Page is already locked */
-		rpc_clnt_sigmask(clnt, &oldmask);
-		rpc_execute(task);
-		rpc_clnt_sigunmask(clnt, &oldmask);
-	} else {
-		dprintk("NFS: %4d schedule_write_request (async)\n",
-					task->tk_pid);
-		task->tk_flags |= RPC_TASK_ASYNC;
-		task->tk_timeout = NFS_WRITEBACK_DELAY;
-		rpc_sleep_on(&write_queue, task, NULL, NULL);
+/*
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
+ */
+static int
+nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long start, unsigned int count)
+{
+	struct list_head	*p, *head;
+	unsigned long		idx_start, idx_end;
+	unsigned int		pages = 0;
+	int			error;
+
+	idx_start = start >> PAGE_CACHE_SHIFT;
+	if (count == 0)
+		idx_end = ~0;
+	else {
+		unsigned long idx_count = (count-1) >> PAGE_CACHE_SHIFT;
+		idx_end = idx_start + idx_count;
 	}
+	spin_lock(&nfs_wreq_lock);
+	head = &inode->u.nfs_i.writeback;
+	p = head->next;
+	while (p != head) {
+		unsigned long pg_idx;
+		struct nfs_page *req = nfs_inode_wb_entry(p);
+
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = page_index(req->wb_page);
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
 
-	return sync;
+		if (!NFS_WBACK_BUSY(req))
+			continue;
+		req->wb_count++;
+		spin_unlock(&nfs_wreq_lock);
+		error = nfs_wait_on_request(req);
+		nfs_release_request(req);
+		if (error < 0)
+			return error;
+		spin_lock(&nfs_wreq_lock);
+		p = head->next;
+		pages++;
+	}
+	spin_unlock(&nfs_wreq_lock);
+	return pages;
 }
 
 /*
- * Wait for request to complete.
+ * Scan cluster for dirty pages and send as many of them to the
+ * server as possible.
  */
 static int
-wait_on_write_request(struct nfs_wreq *req)
+nfs_scan_list_timeout(struct list_head *head, struct list_head *dst, struct inode *inode)
 {
-	struct file		*file = req->wb_file;
-	struct dentry		*dentry = file->f_dentry;
-	struct inode		*inode = dentry->d_inode;
-	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
-	DECLARE_WAITQUEUE(wait, current);
-	sigset_t		oldmask;
-	int retval;
-
-	/* Make sure it's started.. */
-	if (!WB_INPROGRESS(req))
-		rpc_wake_up_task(&req->wb_task);
+	struct list_head	*p;
+        struct nfs_page		*req;
+        int			pages = 0;
+
+	p = head->next;
+        while (p != head) {
+		req = nfs_list_entry(p);
+		p = p->next;
+		if (time_after(req->wb_timeout, jiffies)) {
+			if (time_after(NFS_NEXTSCAN(inode), req->wb_timeout))
+				NFS_NEXTSCAN(inode) = req->wb_timeout;
+			continue;
+		}
+		if (!nfs_lock_request(req))
+			continue;
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+	}
+	return pages;
+}
+
+static int
+nfs_scan_dirty_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	spin_lock(&nfs_wreq_lock);
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.dirty, dst, inode);
+	inode->u.nfs_i.ndirty -= pages;
+	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+	spin_unlock(&nfs_wreq_lock);
+	return pages;
+}
+
+#ifdef CONFIG_NFS_V3
+static int
+nfs_scan_commit_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	spin_lock(&nfs_wreq_lock);
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.commit, dst, inode);
+	inode->u.nfs_i.ncommit -= pages;
+	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+	spin_unlock(&nfs_wreq_lock);
+	return pages;
+}
+#endif
+
+static int
+nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+{
+	struct list_head	*p;
+	struct nfs_page		*req;
+	unsigned long		idx_start, idx_end;
+	int			pages;
+
+	pages = 0;
+	idx_start = start >> PAGE_CACHE_SHIFT;
+	if (count == 0)
+		idx_end = ~0;
+	else
+		idx_end = idx_start + ((count-1) >> PAGE_CACHE_SHIFT);
+	p = src->next;
+	while (p != src) {
+		unsigned long pg_idx;
+
+		req = nfs_list_entry(p);
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = page_index(req->wb_page);
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
+
+		if (!nfs_lock_request(req))
+			continue;
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+	}
+	return pages;
+}
+
+static int
+nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+{
+	int	pages;
+	spin_lock(&nfs_wreq_lock);
+	pages = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, start, count);
+	inode->u.nfs_i.ndirty -= pages;
+	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+	spin_unlock(&nfs_wreq_lock);
+	return pages;
+}
+
+#ifdef CONFIG_NFS_V3
+static int
+nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+{
+	int	pages;
+	spin_lock(&nfs_wreq_lock);
+	pages = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, start, count);
+	inode->u.nfs_i.ncommit -= pages;
+	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+	spin_unlock(&nfs_wreq_lock);
+	return pages;
+}
+#endif
+
+
+static int
+coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int maxpages)
+{
+	struct nfs_page		*req = NULL;
+	unsigned int		pages = 0;
+
+	while (!list_empty(src)) {
+		struct nfs_page	*prev = req;
+
+		req = nfs_list_entry(src->next);
+		if (prev) {
+			if (req->wb_file != prev->wb_file)
+				break;
+
+			if (page_index(req->wb_page) != page_index(prev->wb_page)+1)
+				break;
+
+			if (req->wb_offset != 0)
+				break;
+		}
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+		if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
+			break;
+		if (pages >= maxpages)
+			break;
+	}
+	return pages;
+}
+
+/*
+ * Try to update any existing write request, or create one if there is none.
+ * In order to match, the request's credentials must match those of
+ * the calling process.
+ *
+ * Note: Should always be called with the Page Lock held!
+ */
+static struct nfs_page *
+nfs_update_request(struct file* file, struct page *page,
+		   unsigned long offset, unsigned int bytes)
+{
+	struct inode		*inode = file->f_dentry->d_inode;
+	struct nfs_page		*req, *new = NULL;
+	unsigned long		rqend, end;
+
+	end = offset + bytes;
 
-	rpc_clnt_sigmask(clnt, &oldmask);
-	add_wait_queue(&req->wb_wait, &wait);
 	for (;;) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		retval = 0;
-		if (req->wb_flags & NFS_WRITE_COMPLETE)
+		/* Loop over all inode entries and see if we find
+		 * A request for the page we wish to update
+		 */
+		spin_lock(&nfs_wreq_lock);
+		req = _nfs_find_request(inode, page);
+		if (req) {
+			if (!nfs_lock_request(req)) {
+				spin_unlock(&nfs_wreq_lock);
+				nfs_wait_on_request(req);
+				nfs_release_request(req);
+				continue;
+			}
+			spin_unlock(&nfs_wreq_lock);
+			if (new)
+				nfs_release_request(new);
 			break;
-		retval = -ERESTARTSYS;
-		if (signalled())
+		}
+
+		req = new;
+		if (req) {
+			nfs_lock_request(req);
+			nfs_inode_add_request(inode, req);
+			spin_unlock(&nfs_wreq_lock);
+			nfs_mark_request_dirty(req);
 			break;
-		schedule();
+		}
+		spin_unlock(&nfs_wreq_lock);
+
+		/* Create the request. It's safe to sleep in this call because
+		 * we only get here if the page is locked.
+		 */
+		new = nfs_create_request(inode, file, page, offset, bytes);
+		if (!new)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	/* We have a request for our page.
+	 * If the creds don't match, or the
+	 * page addresses don't match,
+	 * tell the caller to wait on the conflicting
+	 * request.
+	 */
+	rqend = req->wb_offset + req->wb_bytes;
+	if (req->wb_file != file
+	    || req->wb_page != page
+	    || !nfs_dirty_request(req)
+	    || offset > rqend || end < req->wb_offset) {
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+		return ERR_PTR(-EBUSY);
 	}
-	remove_wait_queue(&req->wb_wait, &wait);
-	current->state = TASK_RUNNING;
-	rpc_clnt_sigunmask(clnt, &oldmask);
-	return retval;
+
+	/* Okay, the request matches. Update the region */
+	if (offset < req->wb_offset) {
+		req->wb_offset = offset;
+		req->wb_bytes = rqend - req->wb_offset;
+	}
+
+	if (end > rqend)
+		req->wb_bytes = end - req->wb_offset;
+
+	nfs_unlock_request(req);
+
+	return req;
 }
 
 /*
- * Write a page to the server. This will be used for NFS swapping only
- * (for now), and we currently do this synchronously only.
+ * This is the strategy routine for NFS.
+ * It is called by nfs_updatepage whenever the user wrote up to the end
+ * of a page.
+ *
+ * We always try to submit a set of requests in parallel so that the
+ * server's write code can gather writes. This is mainly for the benefit
+ * of NFSv2.
+ *
+ * We never submit more requests than we think the remote can handle.
+ * For UDP sockets, we make sure we don't exceed the congestion window;
+ * for TCP, we limit the number of requests to 8.
+ *
+ * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that
+ * should be sent out in one go. This is for the benefit of NFSv2 servers
+ * that perform write gathering.
+ *
+ * FIXME: Different servers may have different sweet spots.
+ * Record the average congestion window in server struct?
  */
-int
-nfs_writepage(struct dentry * dentry, struct page *page)
+#define NFS_STRATEGY_PAGES      8
+static void
+nfs_strategy(struct file *file)
 {
-	struct inode *inode = dentry->d_inode;
-	unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT;
-	unsigned offset = PAGE_CACHE_SIZE;
-	int err;
+	struct inode	*inode = file->f_dentry->d_inode;
+	unsigned int	dirty, wpages;
 
-	/* easy case */
-	if (page->index < end_index)
-		goto do_it;
-	/* things got complicated... */
-	offset = inode->i_size & (PAGE_CACHE_SIZE-1);
-	/* OK, are we completely out? */
-	if (page->index >= end_index+1 || !offset)
-		return -EIO;
-do_it:
-	err = nfs_writepage_sync(dentry, inode, page, 0, offset); 
-	if ( err == offset) return 0; 
-	return err; 
+	dirty  = inode->u.nfs_i.ndirty;
+	wpages = NFS_SERVER(inode)->wsize >> PAGE_CACHE_SHIFT;
+#ifdef CONFIG_NFS_V3
+	if (NFS_PROTO(inode)->version == 2) {
+		if (dirty >= NFS_STRATEGY_PAGES * wpages)
+			nfs_flush_file(inode, file, 0, 0, 0);
+	} else {
+		if (dirty >= wpages)
+			nfs_flush_file(inode, file, 0, 0, 0);
+	}
+#else
+	if (dirty >= NFS_STRATEGY_PAGES * wpages)
+		nfs_flush_file(inode, file, 0, 0, 0);
+#endif
+	/*
+	 * If we're running out of requests, flush out everything
+	 * in order to reduce memory useage...
+	 */
+	if (nfs_nr_requests > MAX_REQUEST_SOFT)
+		nfs_wb_file(inode, file);
+}
+
+int
+nfs_flush_incompatible(struct file *file, struct page *page)
+{
+	struct inode	*inode = file->f_dentry->d_inode;
+	struct nfs_page	*req;
+	int		status = 0;
+	/*
+	 * Look for a request corresponding to this page. If there
+	 * is one, and it belongs to another file, we flush it out
+	 * before we try to copy anything into the page. Do this
+	 * due to the lack of an ACCESS-type call in NFSv2.
+	 * Also do the same if we find a request from an existing
+	 * dropped page.
+	 */
+	req = nfs_find_request(inode,page);
+	if (req) {
+		if (req->wb_file != file || req->wb_page != page)
+			status = nfs_wb_page(inode, page);
+		nfs_release_request(req);
+	}
+	return (status < 0) ? status : 0;
 }
 
 /*
@@ -446,27 +1032,13 @@
 {
 	struct dentry	*dentry = file->f_dentry;
 	struct inode	*inode = dentry->d_inode;
-	struct nfs_wreq	*req;
+	struct nfs_page	*req;
 	int		synchronous = file->f_flags & O_SYNC;
-	int		retval;
+	int		status = 0;
 
-	dprintk("NFS:      nfs_updatepage(%s/%s %d@%ld)\n",
+	dprintk("NFS:      nfs_updatepage(%s/%s %d@%Ld)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		count, (page->index << PAGE_CACHE_SHIFT) +offset);
-
-	/*
-	 * Try to find a corresponding request on the writeback queue.
-	 * If there is one, we can be sure that this request is not
-	 * yet being processed, because we hold a lock on the page.
-	 *
-	 * If the request was created by us, update it. Otherwise,
-	 * transfer the page lock and flush out the dirty page now.
-	 * After returning, generic_file_write will wait on the
-	 * page and retry the update.
-	 */
-	req = find_write_request(inode, page);
-	if (req && req->wb_file == file && update_write_request(req, offset, count))
-		goto updated;
+		count, page_offset(page) +offset);
 
 	/*
 	 * If wsize is smaller than page size, update and write
@@ -475,241 +1047,542 @@
 	if (NFS_SERVER(inode)->wsize < PAGE_SIZE)
 		return nfs_writepage_sync(dentry, inode, page, offset, count);
 
-	/* Create the write request. */
-	req = create_write_request(file, page, offset, count);
-	if (!req)
-		return -ENOBUFS;
-
 	/*
-	 * Ok, there's another user of this page with the new request..
-	 * The IO completion will then free the page and the dentry.
+	 * Try to find an NFS request corresponding to this page
+	 * and update it.
+	 * If the existing request cannot be updated, we must flush
+	 * it out now.
 	 */
-	get_page(page);
-
-	/* Schedule request */
-	synchronous = schedule_write_request(req, synchronous);
+	do {
+		req = nfs_update_request(file, page, offset, count);
+		status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+		if (status != -EBUSY)
+			break;
+		/* Request could not be updated. Flush it out and try again */
+		status = nfs_wb_page(inode, page);
+	} while (status >= 0);
+	if (status < 0)
+		goto done;
 
-updated:
-	if (req->wb_bytes == PAGE_SIZE)
+	if (req->wb_bytes == PAGE_CACHE_SIZE)
 		SetPageUptodate(page);
 
-	retval = 0;
+	status = 0;
 	if (synchronous) {
-		int status = wait_on_write_request(req);
-		if (status) {
-			nfs_cancel_request(req);
-			retval = status;
-		} else {
-			status = req->wb_status;
-			if (status < 0)
-				retval = status;
-		}
+		int error;
 
-		if (retval < 0)
-			ClearPageUptodate(page);
+		error = nfs_sync_file(inode, file, page_offset(page) + offset, count, FLUSH_SYNC|FLUSH_STABLE);
+		if (error < 0 || (error = file->f_error) < 0)
+			status = error;
+		file->f_error = 0;
+	} else {
+		/* If we wrote past the end of the page.
+		 * Call the strategy routine so it can send out a bunch
+		 * of requests.
+		 */
+		if (req->wb_offset == 0 && req->wb_bytes == PAGE_CACHE_SIZE)
+			nfs_strategy(file);
 	}
-
-	free_write_request(req);
-	return retval;
+	nfs_release_request(req);
+done:
+        dprintk("NFS:      nfs_updatepage returns %d (isize %Ld)\n",
+                                                status, inode->i_size);
+	if (status < 0)
+		clear_bit(PG_uptodate, &page->flags);
+	return status;
 }
 
 /*
- * Cancel a write request. We always mark it cancelled,
- * but if it's already in progress there's no point in
- * calling rpc_exit, and we don't want to overwrite the
- * tk_status field.
- */ 
+ * Set up the argument/result storage required for the RPC call.
+ */
 static void
-nfs_cancel_request(struct nfs_wreq *req)
+nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 {
-	req->wb_flags |= NFS_WRITE_CANCELLED;
-	if (!WB_INPROGRESS(req)) {
-		rpc_exit(&req->wb_task, 0);
-		rpc_wake_up_task(&req->wb_task);
+	struct nfs_page		*req;
+	struct iovec		*iov;
+	unsigned int		count;
+
+	/* Set up the RPC argument and reply structs
+	 * NB: take care not to mess about with data->commit et al. */
+
+	iov = data->args.iov;
+	count = 0;
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		iov->iov_base = (void *)(kmap(req->wb_page) + req->wb_offset);
+		iov->iov_len = req->wb_bytes;
+		count += req->wb_bytes;
+		iov++;
+		data->args.nriov++;
 	}
+	req = nfs_list_entry(data->pages.next);
+	data->file = req->wb_file;
+	data->cred = req->wb_cred;
+	data->args.fh     = NFS_FH(req->wb_file->f_dentry);
+	data->args.offset = page_offset(req->wb_page) + req->wb_offset;
+	data->args.count  = count;
+	data->res.fattr   = &data->fattr;
+	data->res.count   = count;
+	data->res.verf    = &data->verf;
 }
 
+
 /*
- * Cancel all writeback requests, both pending and in progress.
+ * Create an RPC task for the given write request and kick it.
+ * The page must have been locked by the caller.
+ *
+ * It may happen that the page we're passed is not marked dirty.
+ * This is the case if nfs_updatepage detects a conflicting request
+ * that has been written but not committed.
  */
-static void
-nfs_cancel_dirty(struct inode *inode, pid_t pid)
+static int
+nfs_flush_one(struct list_head *head, struct file *file, int how)
 {
-	struct nfs_wreq *head, *req;
+	struct dentry           *dentry = file->f_dentry;
+	struct inode            *inode = dentry->d_inode;
+	struct rpc_clnt 	*clnt = NFS_CLIENT(inode);
+	struct nfs_write_data	*data;
+	struct rpc_task		*task;
+	struct rpc_message	msg;
+	int                     flags,
+				async = !(how & FLUSH_SYNC),
+				stable = (how & FLUSH_STABLE);
+	sigset_t		oldset;
+
+
+	data = nfs_writedata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
+
+	/* Set the initial flags for the task.  */
+	flags = (async) ? RPC_TASK_ASYNC : 0;
+
+	/* Set up the argument struct */
+	nfs_write_rpcsetup(head, data);
+	if (stable) {
+		if (!inode->u.nfs_i.ncommit)
+			data->args.stable = NFS_FILE_SYNC;
+		else
+			data->args.stable = NFS_DATA_SYNC;
+	} else
+		data->args.stable = NFS_UNSTABLE;
+
+	/* Finalize the task. */
+	rpc_init_task(task, clnt, nfs_writeback_done, flags);
+	task->tk_calldata = data;
+
+#ifdef CONFIG_NFS_V3
+	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_WRITE : NFSPROC_WRITE;
+#else
+	msg.rpc_proc = NFSPROC_WRITE;
+#endif
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
+
+	dprintk("NFS: %4d initiated write call (req %s/%s count %d nriov %d)\n",
+		task->tk_pid, 
+		dentry->d_parent->d_name.name,
+		dentry->d_name.name,
+		data->args.count, data->args.nriov);
+
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+ out_bad:
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_dirty(req);
+		nfs_unlock_request(req);
+	}
+	return -ENOMEM;
+}
 
-	req = head = NFS_WRITEBACK(inode);
-	while (req != NULL) {
-		if (pid == 0 || req->wb_pid == pid)
-			nfs_cancel_request(req);
-		if ((req = WB_NEXT(req)) == head)
+static int
+nfs_flush_list(struct inode *inode, struct list_head *head, int how)
+{
+	LIST_HEAD(one_request);
+	struct nfs_page		*req;
+	int			error = 0;
+	unsigned int		pages = 0,
+				wpages = NFS_SERVER(inode)->wsize >> PAGE_CACHE_SHIFT;
+
+	while (!list_empty(head)) {
+		pages += coalesce_requests(head, &one_request, wpages);
+		req = nfs_list_entry(one_request.next);
+		error = nfs_flush_one(&one_request, req->wb_file, how);
+		if (error < 0)
 			break;
 	}
+	if (error >= 0)
+		return pages;
+
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_dirty(req);
+		nfs_unlock_request(req);
+	}
+	return error;
 }
 
+
 /*
- * If we're waiting on somebody else's request
- * we need to increment the counter during the
- * wait so that the request doesn't disappear
- * from under us during the wait..
+ * This function is called when the WRITE call is complete.
  */
-static int FASTCALL(wait_on_other_req(struct nfs_wreq *));
-static int wait_on_other_req(struct nfs_wreq *req)
+static void
+nfs_writeback_done(struct rpc_task *task)
 {
-	int retval;
-	req->wb_count++;
-	retval = wait_on_write_request(req);
-	free_write_request(req);
-	return retval;
-}
+	struct nfs_write_data	*data = (struct nfs_write_data *) task->tk_calldata;
+	struct nfs_writeargs	*argp = &data->args;
+	struct nfs_writeres	*resp = &data->res;
+	struct dentry		*dentry = data->file->f_dentry;
+	struct inode		*inode = dentry->d_inode;
+	struct nfs_page		*req;
 
-/*
- * This writes back a set of requests according to the condition.
- *
- * If this ever gets much more convoluted, use a fn pointer for
- * the condition..
- */
-#define NFS_WB(inode, cond) { int retval = 0 ; \
-	do { \
-		struct nfs_wreq *req = NFS_WRITEBACK(inode); \
-		struct nfs_wreq *head = req; \
-		if (!req) break; \
-		for (;;) { \
-			if (!(req->wb_flags & NFS_WRITE_COMPLETE)) \
-				if (cond) break; \
-			req = WB_NEXT(req); \
-			if (req == head) goto out; \
-		} \
-		retval = wait_on_other_req(req); \
-	} while (!retval); \
-out:	return retval; \
+	dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
+		task->tk_pid, task->tk_status);
+
+	/* We can't handle that yet but we check for it nevertheless */
+	if (resp->count < argp->count && task->tk_status >= 0) {
+		static unsigned long    complain = 0;
+		if (time_before(complain, jiffies)) {
+			printk(KERN_WARNING
+			       "NFS: Server wrote less than requested.\n");
+			complain = jiffies + 300 * HZ;
+		}
+		/* Can't do anything about it right now except throw
+		 * an error. */
+		task->tk_status = -EIO;
+	}
+#ifdef CONFIG_NFS_V3
+	if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
+		/* We tried a write call, but the server did not
+		 * commit data to stable storage even though we
+		 * requested it.
+		 */
+		static unsigned long    complain = 0;
+
+		if (time_before(complain, jiffies)) {
+			printk(KERN_NOTICE "NFS: faulty NFSv3 server %s:"
+			       " (committed = %d) != (stable = %d)\n",
+			       NFS_SERVER(inode)->hostname,
+			       resp->verf->committed, argp->stable);
+			complain = jiffies + 300 * HZ;
+		}
+	}
+#endif
+
+	/* Update attributes as result of writeback. */
+	if (task->tk_status >= 0)
+		nfs_write_attributes(inode, resp->fattr);
+
+	while (!list_empty(&data->pages)) {
+		req = nfs_list_entry(data->pages.next);
+		nfs_list_remove_request(req);
+
+		kunmap(req->wb_page);
+
+		dprintk("NFS: write (%s/%s %d@%Ld)",
+			req->wb_file->f_dentry->d_parent->d_name.name,
+			req->wb_file->f_dentry->d_name.name,
+			req->wb_bytes,
+			page_offset(req->wb_page) + req->wb_offset);
+
+		if (task->tk_status < 0) {
+			req->wb_file->f_error = task->tk_status;
+			nfs_inode_remove_request(req);
+			dprintk(", error = %d\n", task->tk_status);
+			goto next;
+		}
+
+#ifdef CONFIG_NFS_V3
+		if (resp->verf->committed != NFS_UNSTABLE) {
+			nfs_inode_remove_request(req);
+			dprintk(" OK\n");
+			goto next;
+		}
+		memcpy(&req->wb_verf, resp->verf, sizeof(req->wb_verf));
+		req->wb_timeout = jiffies + NFS_COMMIT_DELAY;
+		nfs_mark_request_commit(req);
+		dprintk(" marked for commit\n");
+#else
+		nfs_inode_remove_request(req);
+#endif
+	next:
+		nfs_unlock_request(req);
+	}
+	nfs_writedata_release(task);
 }
 
-int
-nfs_wb_all(struct inode *inode)
+
+#ifdef CONFIG_NFS_V3
+/*
+ * Set up the argument/result storage required for the RPC call.
+ */
+static void
+nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 {
-	NFS_WB(inode, 1);
+	struct nfs_page		*req;
+	struct dentry		*dentry;
+	struct inode		*inode;
+	unsigned long		start, end, len;
+
+	/* Set up the RPC argument and reply structs
+	 * NB: take care not to mess about with data->commit et al. */
+
+	end = 0;
+	start = ~0;
+	req = nfs_list_entry(head->next);
+	data->file = req->wb_file;
+	data->cred = req->wb_cred;
+	dentry = data->file->f_dentry;
+	inode = dentry->d_inode;
+	while (!list_empty(head)) {
+		struct nfs_page	*req;
+		unsigned long	rqstart, rqend;
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		rqstart = page_offset(req->wb_page) + req->wb_offset;
+		rqend = rqstart + req->wb_bytes;
+		if (rqstart < start)
+			start = rqstart;
+		if (rqend > end)
+			end = rqend;
+	}
+	data->args.fh     = NFS_FH(dentry);
+	data->args.offset = start;
+	len = end - start;
+	if (end >= inode->i_size || len > (~((u32)0) >> 1))
+		len = 0;
+	data->res.count   = data->args.count = (u32)len;
+	data->res.fattr   = &data->fattr;
+	data->res.verf    = &data->verf;
 }
 
 /*
- * Write back all requests on one page - we do this before reading it.
+ * Commit dirty pages
  */
-int
-nfs_wb_page(struct inode *inode, struct page *page)
+static int
+nfs_commit_list(struct list_head *head, int how)
 {
-	NFS_WB(inode, req->wb_page == page);
+	struct rpc_message	msg;
+	struct file		*file;
+	struct rpc_clnt		*clnt;
+	struct nfs_write_data	*data;
+	struct rpc_task         *task;
+	struct nfs_page         *req;
+	int                     flags,
+				async = !(how & FLUSH_SYNC);
+	sigset_t		oldset;
+
+	data = nfs_writedata_alloc();
+
+	if (!data)
+		goto out_bad;
+	task = &data->task;
+
+	flags = (async) ? RPC_TASK_ASYNC : 0;
+
+	/* Set up the argument struct */
+	nfs_commit_rpcsetup(head, data);
+	req = nfs_list_entry(data->pages.next);
+	file = req->wb_file;
+	clnt = NFS_CLIENT(file->f_dentry->d_inode);
+
+	rpc_init_task(task, clnt, nfs_commit_done, flags);
+	task->tk_calldata = data;
+
+	msg.rpc_proc = NFS3PROC_COMMIT;
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
+
+	dprintk("NFS: %4d initiated commit call\n", task->tk_pid);
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+ out_bad:
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_commit(req);
+		nfs_unlock_request(req);
+	}
+	return -ENOMEM;
 }
 
 /*
- * Write back all pending writes from one file descriptor..
+ * COMMIT call returned
  */
-int
-nfs_wb_file(struct inode *inode, struct file *file)
+static void
+nfs_commit_done(struct rpc_task *task)
 {
-	NFS_WB(inode, req->wb_file == file);
+	struct nfs_write_data	*data = (struct nfs_write_data *)task->tk_calldata;
+	struct nfs_writeres	*resp = &data->res;
+	struct nfs_page		*req;
+	struct dentry		*dentry = data->file->f_dentry;
+	struct inode		*inode = dentry->d_inode;
+
+        dprintk("NFS: %4d nfs_commit_done (status %d)\n",
+                                task->tk_pid, task->tk_status);
+
+	nfs_refresh_inode(inode, resp->fattr);
+	while (!list_empty(&data->pages)) {
+		req = nfs_list_entry(data->pages.next);
+		nfs_list_remove_request(req);
+
+		dprintk("NFS: commit (%s/%s %d@%ld)",
+			req->wb_file->f_dentry->d_parent->d_name.name,
+			req->wb_file->f_dentry->d_name.name,
+			req->wb_bytes,
+			page_offset(req->wb_page) + req->wb_offset);
+		if (task->tk_status < 0) {
+			req->wb_file->f_error = task->tk_status;
+			nfs_inode_remove_request(req);
+			dprintk(", error = %d\n", task->tk_status);
+			goto next;
+		}
+
+		/* Okay, COMMIT succeeded, apparently. Check the verifier
+		 * returned by the server against all stored verfs. */
+		if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
+			/* We have a match */
+			nfs_inode_remove_request(req);
+			dprintk(" OK\n");
+			goto next;
+		}
+		/* We have a mismatch. Write the page again */
+		dprintk(" mismatch\n");
+		nfs_mark_request_dirty(req);
+	next:
+		nfs_unlock_request(req);
+	}
+	nfs_writedata_release(task);
 }
+#endif
 
-void
-nfs_inval(struct inode *inode)
+int nfs_flush_file(struct inode *inode, struct file *file, unsigned long start,
+		   unsigned int count, int how)
 {
-	nfs_cancel_dirty(inode,0);
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
+
+	pages = nfs_scan_dirty(inode, &head, file, start, count);
+	if (pages)
+		error = nfs_flush_list(inode, &head, how);
+	if (error < 0)
+		return error;
+	return pages;
 }
 
-/*
- * The following procedures make up the writeback finite state machinery:
- *
- * 1.	Try to lock the page if not yet locked by us,
- *	set up the RPC call info, and pass to the call FSM.
- */
-static void
-nfs_wback_begin(struct rpc_task *task)
+int nfs_flush_timeout(struct inode *inode, int how)
 {
-	struct nfs_wreq	*req = (struct nfs_wreq *) task->tk_calldata;
-	struct page	*page = req->wb_page;
-	struct file	*file = req->wb_file;
-	struct dentry	*dentry = file->f_dentry;
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
 
-	dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n",
-		task->tk_pid, dentry->d_parent->d_name.name,
-		dentry->d_name.name, task->tk_status, req->wb_flags);
+	pages = nfs_scan_dirty_timeout(inode, &head);
+	if (pages)
+		error = nfs_flush_list(inode, &head, how);
+	if (error < 0)
+		return error;
+	return pages;
+}
 
-	task->tk_status = 0;
+#ifdef CONFIG_NFS_V3
+int nfs_commit_file(struct inode *inode, struct file *file, unsigned long start,
+		    unsigned int count, int how)
+{
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
 
-	/* Setup the task struct for a writeback call */
-	req->wb_flags |= NFS_WRITE_INPROGRESS;
-	req->wb_args.fh     = NFS_FH(dentry);
-	req->wb_args.offset = (page->index << PAGE_CACHE_SHIFT) + req->wb_offset;
-	req->wb_args.count  = req->wb_bytes;
-	req->wb_args.buffer = (void *) (page_address(page) + req->wb_offset);
+	pages = nfs_scan_commit(inode, &head, file, start, count);
+	if (pages)
+		error = nfs_commit_list(&head, how);
+	if (error < 0)
+		return error;
+	return pages;
+}
 
-	rpc_call_setup(task, NFSPROC_WRITE, &req->wb_args, &req->wb_fattr, 0);
+int nfs_commit_timeout(struct inode *inode, int how)
+{
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
 
-	return;
+	pages = nfs_scan_commit_timeout(inode, &head);
+	if (pages) {
+		pages += nfs_scan_commit(inode, &head, NULL, 0, 0);
+		error = nfs_commit_list(&head, how);
+	}
+	if (error < 0)
+		return error;
+	return pages;
 }
+#endif
 
-/*
- * 2.	Collect the result
- */
-static void
-nfs_wback_result(struct rpc_task *task)
+int nfs_sync_file(struct inode *inode, struct file *file, unsigned long start,
+		  unsigned int count, int how)
 {
-	struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
-	struct file	*file = req->wb_file;
-	struct page	*page = req->wb_page;
-	int		status = task->tk_status;
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
+	int	error,
+		wait;
 
-	dprintk("NFS: %4d nfs_wback_result (%s/%s, status=%d, flags=%x)\n",
-		task->tk_pid, dentry->d_parent->d_name.name,
-		dentry->d_name.name, status, req->wb_flags);
-
-	/* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */
-	req->wb_flags |= NFS_WRITE_COMPLETE;
-	req->wb_status = status;
-
-	if (status < 0) {
-		req->wb_flags |= NFS_WRITE_INVALIDATE;
-		file->f_error = status;
-	} else if (!WB_CANCELLED(req)) {
-		struct nfs_fattr *fattr = &req->wb_fattr;
-		/* Update attributes as result of writeback. 
-		 * Beware: when UDP replies arrive out of order, we
-		 * may end up overwriting a previous, bigger file size.
-		 *
-		 * When the file size shrinks we cancel all pending
-		 * writebacks. 
-		 */
-		if (fattr->mtime.seconds >= inode->i_mtime) {
-			if (fattr->size < inode->i_size)
-				fattr->size = inode->i_size;
-
-			/* possible Solaris 2.5 server bug workaround */
-			if (inode->i_ino == fattr->fileid) {
-				/*
-				 * We expect these values to change, and
-				 * don't want to invalidate the caches.
-				 */
-				inode->i_size  = fattr->size;
-				inode->i_mtime = fattr->mtime.seconds;
-				nfs_refresh_inode(inode, fattr);
-			}
-			else
-				printk("nfs_wback_result: inode %ld, got %u?\n",
-					inode->i_ino, fattr->fileid);
-		}
-	}
+	wait = how & FLUSH_WAIT;
+	how &= ~FLUSH_WAIT;
 
-	rpc_release_task(task);
+	if (!inode && file)
+		inode = file->f_dentry->d_inode;
 
-	if (WB_INVALIDATE(req))
-		ClearPageUptodate(page);
+	do {
+		error = 0;
+		if (wait)
+			error = nfs_wait_on_requests(inode, file, start, count);
+		if (error == 0)
+			error = nfs_flush_file(inode, file, start, count, how);
+#ifdef CONFIG_NFS_V3
+		if (error == 0)
+			error = nfs_commit_file(inode, file, start, count, how);
+#endif
+	} while (error > 0);
+	return error;
+}
 
-	kunmap(page);
-	__free_page(page);
-	remove_write_request(&NFS_WRITEBACK(inode), req);
-	nr_write_requests--;
-	fput(req->wb_file);
+int nfs_init_nfspagecache(void)
+{
+	nfs_page_cachep = kmem_cache_create("nfs_page",
+					    sizeof(struct nfs_page),
+					    0, SLAB_HWCACHE_ALIGN,
+					    NULL, NULL);
+	if (nfs_page_cachep == NULL)
+		return -ENOMEM;
 
-	wake_up(&req->wb_wait);
-	free_write_request(req);
+	nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
+					     sizeof(struct nfs_write_data),
+					     0, SLAB_HWCACHE_ALIGN,
+					     NULL, NULL);
+	if (nfs_wdata_cachep == NULL)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void nfs_destroy_nfspagecache(void)
+{
+	if (kmem_cache_destroy(nfs_page_cachep))
+		printk(KERN_INFO "nfs_page: not all structures were freed\n");
+	if (kmem_cache_destroy(nfs_wdata_cachep))
+		printk(KERN_INFO "nfs_write_data: not all structures were freed\n");
 }
+
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.3.99-pre2/linux/fs/nfsd/export.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/nfsd/export.c	Tue Mar 21 11:10:48 2000
@@ -483,7 +483,10 @@
 	 * fh must be initialized before calling fh_compose
 	 */
 	fh_init(&fh, maxsize);
-	err = fh_compose(&fh, exp, dentry);
+	if (fh_compose(&fh, exp, dentry))
+		err = -EINVAL;
+	else
+		err = 0;
 	memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh));
 	fh_put(&fh);
 	return err;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/nfs3xdr.c linux/fs/nfsd/nfs3xdr.c
--- v2.3.99-pre2/linux/fs/nfsd/nfs3xdr.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/nfsd/nfs3xdr.c	Tue Mar 21 11:10:48 2000
@@ -748,6 +748,7 @@
 		if (fh_compose(&fh, exp, dchild) != 0 || !dchild->d_inode)
 			goto noexec;
 		p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
+		*p++ = xdr_one; /* yes, a file handle follows */
 		p = encode_fh(p, &fh);
 		fh_put(&fh);
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- v2.3.99-pre2/linux/fs/nfsd/nfsctl.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/nfsd/nfsctl.c	Tue Mar 21 10:49:16 2000
@@ -42,8 +42,8 @@
 static int	nfsctl_unexport(struct nfsctl_export *data);
 static int	nfsctl_getfh(struct nfsctl_fhparm *, __u8 *);
 static int	nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
-#ifdef notyet
 static int	nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
+#ifdef notyet
 static int	nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
 #endif
 
@@ -112,7 +112,6 @@
 }
 #endif
 
-#ifdef notyet
 static inline int
 nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
 {
@@ -131,10 +130,9 @@
 	else
 		err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen);
 	exp_unlock();
-
+	/*HACK*/ res->fh_size = NFS_FHSIZE; /* HACK until lockd handles var-length handles */
 	return err;
 }
-#endif
 
 static inline int
 nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
@@ -206,6 +204,21 @@
 #define handle_sys_nfsservctl sys_nfsservctl
 #endif
 
+static struct {
+	int argsize, respsize;
+}  sizes[] = {
+	/* NFSCTL_SVC        */ { sizeof(struct nfsctl_svc), 0 },
+	/* NFSCTL_ADDCLIENT  */ { sizeof(struct nfsctl_client), 0},
+	/* NFSCTL_DELCLIENT  */ { sizeof(struct nfsctl_client), 0},
+	/* NFSCTL_EXPORT     */ { sizeof(struct nfsctl_export), 0},
+	/* NFSCTL_UNEXPORT   */ { sizeof(struct nfsctl_export), 0},
+	/* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
+	/* NFSCTL_GETFH      */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
+	/* NFSCTL_GETFD      */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
+	/* NFSCTL_GETFS      */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
+};
+#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
+
 int
 asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
 {
@@ -214,6 +227,7 @@
 	struct nfsctl_arg *	arg = NULL;
 	union nfsctl_res *	res = NULL;
 	int			err;
+	int			argsize, respsize;
 
 	MOD_INC_USE_COUNT;
 	lock_kernel ();
@@ -223,12 +237,16 @@
 	if (!capable(CAP_SYS_ADMIN)) {
 		goto done;
 	}
+	err = -EINVAL;
+	if (cmd<0 || cmd > CMD_MAX)
+		goto done;
 	err = -EFAULT;
-	if (!access_ok(VERIFY_READ, argp, sizeof(*argp))
-	 || (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp)))) {
+	argsize = sizes[cmd].argsize + sizeof(int); /* int for ca_version */
+	respsize = sizes[cmd].respsize;	/* maximum */
+	if (!access_ok(VERIFY_READ, argp, argsize)
+	 || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
 		goto done;
 	}
-
 	err = -ENOMEM;	/* ??? */
 	if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
 	    (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
@@ -236,7 +254,7 @@
 	}
 
 	err = -EINVAL;
-	copy_from_user(arg, argp, sizeof(*argp));
+	copy_from_user(arg, argp, argsize);
 	if (arg->ca_version != NFSCTL_VERSION) {
 		printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
 		goto done;
@@ -269,16 +287,16 @@
 	case NFSCTL_GETFD:
 		err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
 		break;
-#ifdef notyet
 	case NFSCTL_GETFS:
 		err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
-#endif
+		respsize = res->cr_getfs.fh_size+sizeof(int);
+		break;
 	default:
 		err = -EINVAL;
 	}
 
-	if (!err && resp)
-		copy_to_user(resp, res, sizeof(*resp));
+	if (!err && resp && respsize)
+		copy_to_user(resp, res, respsize);
 
 done:
 	if (arg)
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- v2.3.99-pre2/linux/fs/nfsd/nfsproc.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/nfsd/nfsproc.c	Tue Mar 21 11:10:48 2000
@@ -223,9 +223,10 @@
 		goto done;
 	fh_lock(dirfhp);
 	dchild = lookup_one(argp->name, dget(dirfhp->fh_dentry));
-	nfserr = nfserrno(PTR_ERR(dchild));
-	if (IS_ERR(dchild))
+	if (IS_ERR(dchild)) {
+		nfserr = nfserrno(PTR_ERR(dchild));
 		goto out_unlock;
+	}
 	fh_init(newfhp, NFS_FHSIZE);
 	nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild);
 	if (!nfserr && !dchild->d_inode)
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.3.99-pre2/linux/fs/nfsd/nfssvc.c	Wed Feb 16 17:03:52 2000
+++ linux/fs/nfsd/nfssvc.c	Tue Mar 21 11:10:48 2000
@@ -43,6 +43,8 @@
 static void			nfsd(struct svc_rqst *rqstp);
 struct timeval			nfssvc_boot = { 0, 0 };
 static struct svc_serv 		*nfsd_serv = NULL;
+static int			nfsd_busy = 0;
+static unsigned long		nfsd_last_call;
 
 struct nfsd_list {
 	struct list_head 	list;
@@ -115,6 +117,24 @@
 	return error;
 }
 
+static void inline
+update_thread_usage(int busy_threads)
+{
+	unsigned long prev_call;
+	unsigned long diff;
+	int decile;
+
+	prev_call = nfsd_last_call;
+	nfsd_last_call = jiffies;
+	decile = busy_threads*10/nfsdstats.th_cnt;
+	if (decile>0 && decile <= 10) {
+		diff = nfsd_last_call - prev_call;
+		nfsdstats.th_usage[decile-1] += diff;
+		if (decile == 10)
+			nfsdstats.th_fullcnt++;
+	}
+}
+
 /*
  * This is the NFS server kernel thread
  */
@@ -134,6 +154,7 @@
 	sprintf(current->comm, "nfsd");
 	current->fs->umask = 0;
 
+	nfsdstats.th_cnt++;
 	/* Let svc_process check client's authentication. */
 	rqstp->rq_auth = 1;
 
@@ -161,6 +182,8 @@
 		    ;
 		if (err < 0)
 			break;
+		update_thread_usage(nfsd_busy);
+		nfsd_busy++;
 
 		/* Lock the export hash tables for reading. */
 		exp_readlock();
@@ -179,6 +202,8 @@
 
 		/* Unlock export hash tables */
 		exp_unlock();
+		update_thread_usage(nfsd_busy);
+		nfsd_busy--;
 	}
 
 	if (err != -EINTR) {
@@ -202,6 +227,7 @@
 	        nfsd_racache_shutdown();	/* release read-ahead cache */
 	}
 	list_del(&me.list);
+	nfsdstats.th_cnt --;
 
 	/* Release the thread */
 	svc_exit_thread(rqstp);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- v2.3.99-pre2/linux/fs/nfsd/stats.c	Tue Nov 23 22:42:21 1999
+++ linux/fs/nfsd/stats.c	Tue Mar 21 11:10:48 2000
@@ -8,6 +8,16 @@
  * Format:
  *	rc <hits> <misses> <nocache>
  *			Statistsics for the reply cache
+ *	fh <stale> <total-lookups> <anonlookups> <dir-not-in-dcache> <nondir-not-in-dcache>
+ *			statistics for filehandle lookup
+ *	io <bytes-read> <bytes-writtten>
+ *			statistics for IO throughput
+ *	th <threads> <fullcnt> <10%-20%> <20%-30%> ... <90%-100%> <100%> 
+ *			time (milliseconds) when nfsd thread usage above thresholds
+ *			and number of times that all threads were in use
+ *	ra cache-size  <10%  <20%  <30% ... <100% not-found
+ *			number of times that read-ahead entry was found that deep in
+ *			the cache.
  *	plus generic RPC stats (see net/sunrpc/stats.c)
  *
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
@@ -33,17 +43,30 @@
 				int *eof, void *data)
 {
 	int	len;
+	int	i;
 
-	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d\n",
-			nfsdstats.rchits,
-			nfsdstats.rcmisses,
-			nfsdstats.rcnocache,
-			nfsdstats.fh_stale,
-			nfsdstats.fh_lookup,
-		        nfsdstats.fh_anon,
-			nfsdstats.fh_nocache_dir,
-			nfsdstats.fh_nocache_nondir);
-
+	len = sprintf(buffer, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
+		      nfsdstats.rchits,
+		      nfsdstats.rcmisses,
+		      nfsdstats.rcnocache,
+		      nfsdstats.fh_stale,
+		      nfsdstats.fh_lookup,
+		      nfsdstats.fh_anon,
+		      nfsdstats.fh_nocache_dir,
+		      nfsdstats.fh_nocache_nondir,
+		      nfsdstats.io_read,
+		      nfsdstats.io_write);
+	/* thread usage: */
+	len += sprintf(buffer+len, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
+	for (i=0; i<10; i++)
+		len += sprintf(buffer+len, " %u", nfsdstats.th_usage[i]);
+
+	/* newline and ra-cache */
+	len += sprintf(buffer+len, "\nra %u", nfsdstats.ra_size);
+	for (i=0; i<11; i++)
+		len += sprintf(buffer+len, " %u", nfsdstats.ra_depth[i]);
+	len += sprintf(buffer+len, "\n");
+	
 
 	/* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
 	*eof = 0;
@@ -53,13 +76,13 @@
 	 */
 	if (len <= offset) {
 		len = svc_proc_read(buffer, start, offset - len, count,
-				eof, data);
+				    eof, data);
 		return len;
 	}
 
 	if (len < count) {
 		len += svc_proc_read(buffer + len, start, 0, count - len,
-				eof, data);
+				     eof, data);
 	}
 
 	if (offset >= len) {
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.3.99-pre2/linux/fs/nfsd/vfs.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/nfsd/vfs.c	Tue Mar 21 11:10:48 2000
@@ -539,13 +539,16 @@
 nfsd_get_raparms(dev_t dev, ino_t ino)
 {
 	struct raparms	*ra, **rap, **frap = NULL;
-
+	int depth = 0;
+	
 	for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
 		if (ra->p_ino == ino && ra->p_dev == dev)
 			goto found;
+		depth++;
 		if (ra->p_count == 0)
 			frap = rap;
 	}
+	depth = nfsdstats.ra_size*11/10;
 	if (!frap)
 		return NULL;
 	rap = frap;
@@ -560,6 +563,7 @@
 		raparm_cache = ra;
 	}
 	ra->p_count++;
+	nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++;
 	return ra;
 }
 
@@ -598,6 +602,7 @@
 	oldfs = get_fs(); set_fs(KERNEL_DS);
 	err = file.f_op->read(&file, buf, *count, &file.f_pos);
 	set_fs(oldfs);
+	nfsdstats.io_read += *count;
 
 	/* Write back readahead params */
 	if (ra != NULL) {
@@ -691,6 +696,7 @@
 #else
 	err = file.f_op->write(&file, buf, cnt, &file.f_pos);
 #endif
+	nfsdstats.io_write += cnt;
 	set_fs(oldfs);
 
 	/* clear setuid/setgid flag after write */
@@ -1559,5 +1565,6 @@
 		       "nfsd: Could not allocate memory read-ahead cache.\n");
 		return -ENOMEM;
 	}
+	nfsdstats.ra_size = cache_size;
 	return 0;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.3.99-pre2/linux/fs/ntfs/fs.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/ntfs/fs.c	Tue Mar 21 11:30:08 2000
@@ -595,7 +595,7 @@
 {
 	return block_read_full_page(page,ntfs_get_block);
 }
-static int ntfs_prepare_write(struct page *page, unsigned from, unsigned to)
+static int ntfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,ntfs_get_block,
 		&((struct inode*)page->mapping->host)->u.ntfs_i.mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ntfs/inode.c linux/fs/ntfs/inode.c
--- v2.3.99-pre2/linux/fs/ntfs/inode.c	Fri Jan 21 18:19:17 2000
+++ linux/fs/ntfs/inode.c	Sun Mar 19 19:10:51 2000
@@ -258,7 +258,7 @@
 	if( !buf )
 		return;
 	delta=0;
-	for(offset=0;datasize;datasize-=len)
+	for(offset=0;datasize;datasize-=len,offset+=len)
 	{
 		ntfs_io io;
 		io.fn_put=ntfs_put;
@@ -268,7 +268,7 @@
 		if(ntfs_read_attr(ino,vol->at_attribute_list,0,offset,&io)){
 			ntfs_error("error in load_attributes\n");
 		}
-		delta=len;
+		delta+=len;
 		parse_attributes(ino,buf,&delta);
 		if(delta)
 			/* move remaining bytes to buffer start */
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/open.c linux/fs/open.c
--- v2.3.99-pre2/linux/fs/open.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/open.c	Thu Mar 23 13:11:49 2000
@@ -298,7 +298,6 @@
 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
 		return -EINVAL;
 
-	lock_kernel();
 	old_fsuid = current->fsuid;
 	old_fsgid = current->fsgid;
 	old_cap = current->cap_effective;
@@ -311,7 +310,8 @@
 		cap_clear(current->cap_effective);
 	else
 		current->cap_effective = current->cap_permitted;
-		
+
+	lock_kernel();		
 	dentry = namei(filename);
 	res = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
@@ -321,12 +321,12 @@
 			res = -EROFS;
 		dput(dentry);
 	}
+	unlock_kernel();
 
 	current->fsuid = old_fsuid;
 	current->fsgid = old_fsgid;
 	current->cap_effective = old_cap;
 
-	unlock_kernel();
 	return res;
 }
 
@@ -646,25 +646,35 @@
  */
 struct file *filp_open(const char * filename, int flags, int mode, struct dentry * base)
 {
-	struct inode * inode;
 	struct dentry * dentry;
-	struct file * f;
 	int flag,error;
 
-	error = -ENFILE;
-	f = get_empty_filp();
-	if (!f)
-		goto out;
-	f->f_flags = flag = flags;
-	f->f_mode = (flag+1) & O_ACCMODE;
-	if (f->f_mode)
+	flag = flags;
+	if ((flag+1) & O_ACCMODE)
 		flag++;
 	if (flag & O_TRUNC)
 		flag |= 2;
+
 	dentry = __open_namei(filename, flag, mode, base);
 	error = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
-		goto cleanup_file;
+	if (!IS_ERR(dentry))
+		return dentry_open(dentry, flags);
+
+	return ERR_PTR(error);
+}
+
+struct file *dentry_open(struct dentry *dentry, int flags)
+{
+	struct file * f;
+	struct inode *inode;
+	int error;
+
+	error = -ENFILE;
+	f = get_empty_filp();
+	if (!f)
+		goto cleanup_dentry;
+	f->f_flags = flags;
+	f->f_mode = (flags+1) & O_ACCMODE;
 	inode = dentry->d_inode;
 	if (f->f_mode & FMODE_WRITE) {
 		error = get_write_access(inode);
@@ -692,12 +702,10 @@
 cleanup_all:
 	if (f->f_mode & FMODE_WRITE)
 		put_write_access(inode);
-cleanup_dentry:
 	f->f_dentry = NULL;
+cleanup_dentry:
 	dput(dentry);
-cleanup_file:
 	put_filp(f);
-out:
 	return ERR_PTR(error);
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/openpromfs/inode.c linux/fs/openpromfs/inode.c
--- v2.3.99-pre2/linux/fs/openpromfs/inode.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/openpromfs/inode.c	Thu Mar 23 23:36:10 2000
@@ -1,4 +1,4 @@
-/* $Id: inode.c,v 1.9 2000/03/13 21:59:43 davem Exp $
+/* $Id: inode.c,v 1.10 2000/03/24 01:32:51 davem Exp $
  * openpromfs.c: /proc/openprom handling routines
  *
  * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
@@ -980,10 +980,6 @@
 	}
 }
 
-static void openprom_put_super(struct super_block *sb)
-{
-}
-
 static int openprom_statfs(struct super_block *sb, struct statfs *buf)
 {
 	buf->f_type = OPENPROM_SUPER_MAGIC;
@@ -997,7 +993,6 @@
 
 static struct super_operations openprom_sops = { 
 	read_inode:	openprom_read_inode,
-	put_super:	openprom_put_super,
 	statfs:		openprom_statfs,
 };
 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/partitions/msdos.c linux/fs/partitions/msdos.c
--- v2.3.99-pre2/linux/fs/partitions/msdos.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/partitions/msdos.c	Wed Mar 22 17:18:45 2000
@@ -26,7 +26,10 @@
 #include <linux/major.h>
 #include <linux/string.h>
 #include <linux/blk.h>
+
+#ifdef CONFIG_IDE
 #include <linux/ide.h>	/* IDE xlate */
+#endif /* CONFIG_IDE */
 
 #include <asm/system.h>
 
@@ -347,19 +350,19 @@
 	unsigned char *data;
 	int mask = (1 << hd->minor_shift) - 1;
 	int sector_size = get_hardsect_size(dev) / 512;
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+#ifdef CONFIG_IDE
 	int tested_for_xlate = 0;
 
 read_mbr:
-#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
+#endif /* CONFIG_IDE */
 	if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
 		if (warn_no_part) printk(" unable to read partition table\n");
 		return -1;
 	}
 	data = bh->b_data;
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+#ifdef CONFIG_IDE
 check_table:
-#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
+#endif /* CONFIG_IDE */
 	/* Use bforget(), because we may have changed the disk geometry */
 	if (*(unsigned short *)  (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) {
 		bforget(bh);
@@ -367,7 +370,7 @@
 	}
 	p = (struct partition *) (0x1be + data);
 
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+#ifdef CONFIG_IDE
 	if (!tested_for_xlate++) {	/* Do this only once per disk */
 		/*
 		 * Look for various forms of IDE disk geometry translation
@@ -423,7 +426,7 @@
 			(void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
 		}
 	}
-#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
+#endif /* CONFIG_IDE */
 
 	/* Look for partitions in two passes:
 	   First find the primary partitions, and the DOS-type extended partitions.
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/pipe.c linux/fs/pipe.c
--- v2.3.99-pre2/linux/fs/pipe.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/pipe.c	Tue Mar 21 18:28:45 2000
@@ -13,16 +13,6 @@
 #include <asm/uaccess.h>
 
 /*
- * Define this if you want SunOS compatibility wrt braindead
- * select behaviour on FIFO's.
- */
-#ifdef __sparc__
-#define FIFO_SUNOS_BRAINDAMAGE
-#else
-#undef FIFO_SUNOS_BRAINDAMAGE
-#endif
-
-/*
  * We use a start+len construction, which provides full use of the 
  * allocated memory.
  * -- Florian Coosmann (FGC)
@@ -32,7 +22,7 @@
  */
 
 /* Drop the inode semaphore and wait for a pipe event, atomically */
-static void pipe_wait(struct inode * inode)
+void pipe_wait(struct inode * inode)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	current->state = TASK_INTERRUPTIBLE;
@@ -296,7 +286,7 @@
 	mask = POLLIN | POLLRDNORM;
 	if (PIPE_EMPTY(*inode))
 		mask = POLLOUT | POLLWRNORM;
-	if (!PIPE_WRITERS(*inode))
+	if (!PIPE_WRITERS(*inode) && filp->f_version != PIPE_WCOUNTER(*inode))
 		mask |= POLLHUP;
 	if (!PIPE_READERS(*inode))
 		mask |= POLLERR;
@@ -304,72 +294,9 @@
 	return mask;
 }
 
-#ifdef FIFO_SUNOS_BRAINDAMAGE
-/*
- * Argh!  Why does SunOS have to have different select() behaviour
- * for pipes and FIFOs?  Hate, hate, hate!  SunOS lacks POLLHUP.
- */
-static unsigned int
-fifo_poll(struct file *filp, poll_table *wait)
-{
-	unsigned int mask;
-	struct inode *inode = filp->f_dentry->d_inode;
-
-	poll_wait(filp, PIPE_WAIT(*inode), wait);
-
-	/* Reading only -- no need for aquiring the semaphore.  */
-	mask = POLLIN | POLLRDNORM;
-	if (PIPE_EMPTY(*inode))
-		mask = POLLOUT | POLLWRNORM;
-	if (!PIPE_READERS(*inode))
-		mask |= POLLERR;
-
-	return mask;
-}
-#else
-
+/* FIXME: most Unices do not set POLLERR for fifos */
 #define fifo_poll pipe_poll
 
-#endif /* FIFO_SUNOS_BRAINDAMAGE */
-
-/*
- * The 'connect_xxx()' functions are needed for named pipes when
- * the open() code hasn't guaranteed a connection (O_NONBLOCK),
- * and we need to act differently until we do get a writer..
- */
-static ssize_t
-connect_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
-{
-	struct inode *inode = filp->f_dentry->d_inode;
-
-	/* Reading only -- no need for aquiring the semaphore.  */
-	if (PIPE_EMPTY(*inode) && !PIPE_WRITERS(*inode))
-		return 0;
-
-	filp->f_op = &read_fifo_fops;
-	return pipe_read(filp, buf, count, ppos);
-}
-
-static unsigned int
-connect_poll(struct file *filp, poll_table *wait)
-{
-	struct inode *inode = filp->f_dentry->d_inode;
-	unsigned int mask = 0;
-
-	poll_wait(filp, PIPE_WAIT(*inode), wait);
-
-	/* Reading only -- no need for aquiring the semaphore.  */
-	if (!PIPE_EMPTY(*inode)) {
-		filp->f_op = &read_fifo_fops;
-		mask = POLLIN | POLLRDNORM;
-	} else if (PIPE_WRITERS(*inode)) {
-		filp->f_op = &read_fifo_fops;
-		mask = POLLOUT | POLLWRNORM;
-	}
-
-	return mask;
-}
-
 static int
 pipe_release(struct inode *inode, int decr, int decw)
 {
@@ -450,16 +377,6 @@
  * The file_operations structs are not static because they
  * are also used in linux/fs/fifo.c to do operations on FIFOs.
  */
-struct file_operations connecting_fifo_fops = {
-	llseek:		pipe_lseek,
-	read:		connect_read,
-	write:		bad_pipe_w,
-	poll:		connect_poll,
-	ioctl:		pipe_ioctl,
-	open:		pipe_read_open,
-	release:	pipe_read_release,
-};
-
 struct file_operations read_fifo_fops = {
 	llseek:		pipe_lseek,
 	read:		pipe_read,
@@ -520,29 +437,42 @@
 	release:	pipe_rdwr_release,
 };
 
-static struct inode * get_pipe_inode(void)
+struct inode* pipe_new(struct inode* inode)
 {
-	struct inode *inode = get_empty_inode();
 	unsigned long page;
 
-	if (!inode)
-		goto fail_inode;
-
 	page = __get_free_page(GFP_USER);
 	if (!page)
-		goto fail_iput;
+		return NULL;
 
 	inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
 	if (!inode->i_pipe)
 		goto fail_page;
 
-	inode->i_fop = &rdwr_pipe_fops;
-
 	init_waitqueue_head(PIPE_WAIT(*inode));
-	PIPE_BASE(*inode) = (char *) page;
+	PIPE_BASE(*inode) = (char*) page;
 	PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
-	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
+	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
 	PIPE_WAITING_READERS(*inode) = PIPE_WAITING_WRITERS(*inode) = 0;
+	PIPE_RCOUNTER(*inode) = PIPE_WCOUNTER(*inode) = 1;
+
+	return inode;
+fail_page:
+	free_page(page);
+	return NULL;
+}
+
+static struct inode * get_pipe_inode(void)
+{
+	struct inode *inode = get_empty_inode();
+
+	if (!inode)
+		goto fail_inode;
+
+	if(!pipe_new(inode))
+		goto fail_iput;
+	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
+	inode->i_fop = &rdwr_pipe_fops;
 
 	/*
 	 * Mark the inode dirty from the very beginning,
@@ -558,8 +488,6 @@
 	inode->i_blksize = PAGE_SIZE;
 	return inode;
 
-fail_page:
-	free_page(page);
 fail_iput:
 	iput(inode);
 fail_inode:
@@ -606,11 +534,13 @@
 	f1->f_flags = O_RDONLY;
 	f1->f_op = &read_pipe_fops;
 	f1->f_mode = 1;
+	f1->f_version = 0;
 
 	/* write file */
 	f2->f_flags = O_WRONLY;
 	f2->f_op = &write_pipe_fops;
 	f2->f_mode = 2;
+	f2->f_version = 0;
 
 	fd_install(i, f1);
 	fd_install(j, f2);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.3.99-pre2/linux/fs/proc/array.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/proc/array.c	Tue Mar 21 11:01:25 2000
@@ -402,7 +402,8 @@
 		++*pages;
 		if (pte_dirty(page))
 			++*dirty;
-		if (pte_pagenr(page) >= max_mapnr)
+		if ((pte_pagenr(page) >= max_mapnr) || 
+					PageReserved(pte_pagenr(page) + mem_map))
 			continue;
 		if (page_count(pte_page(page)) > 1)
 			++*shared;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- v2.3.99-pre2/linux/fs/qnx4/inode.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/qnx4/inode.c	Tue Mar 21 11:30:08 2000
@@ -418,7 +418,7 @@
 {
 	return block_read_full_page(page,qnx4_get_block);
 }
-static int qnx4_prepare_write(struct page *page, unsigned from, unsigned to)
+static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,qnx4_get_block,
 		&((struct inode*)page->mapping->host)->u.qnx4_i.mmu_private);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.3.99-pre2/linux/fs/romfs/inode.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/romfs/inode.c	Wed Mar 22 21:49:58 2000
@@ -161,14 +161,6 @@
 	return s;
 }
 
-/* Nothing to do.. */
-
-static void
-romfs_put_super(struct super_block *sb)
-{
-	return;
-}
-
 /* That's simple too. */
 
 static int
@@ -526,7 +518,6 @@
 
 static struct super_operations romfs_ops = {
 	read_inode:	romfs_read_inode,
-	put_super:	romfs_put_super,
 	statfs:		romfs_statfs,
 };
 
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/smbfs/file.c linux/fs/smbfs/file.c
--- v2.3.99-pre2/linux/fs/smbfs/file.c	Sat Feb 26 22:31:54 2000
+++ linux/fs/smbfs/file.c	Tue Mar 21 11:30:08 2000
@@ -268,7 +268,7 @@
  * If the writer ends up delaying the write, the writer needs to
  * increment the page use counts until he is done with the page.
  */
-static int smb_prepare_write(struct page *page, unsigned offset, unsigned to)
+static int smb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
 	kmap(page);
 	return 0;
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/super.c linux/fs/super.c
--- v2.3.99-pre2/linux/fs/super.c	Sun Mar 19 18:35:31 2000
+++ linux/fs/super.c	Tue Mar 21 16:06:44 2000
@@ -147,7 +147,6 @@
 			err = index;
 			break;
 		}
-		index++;
 	}
 	spin_unlock(&file_systems_lock);
 	putname(name);
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.3.99-pre2/linux/fs/sysv/inode.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/sysv/inode.c	Tue Mar 21 11:30:08 2000
@@ -949,7 +949,7 @@
 {
 	return block_read_full_page(page,sysv_get_block);
 }
-static int sysv_prepare_write(struct page *page, unsigned from, unsigned to)
+static int sysv_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return block_prepare_write(page,from,to,sysv_get_block);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/balloc.c linux/fs/udf/balloc.c
--- v2.3.99-pre2/linux/fs/udf/balloc.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/balloc.c	Mon Mar 20 08:17:43 2000
@@ -85,14 +85,14 @@
 #define find_first_one_bit(addr, size)\
 	find_next_one_bit((addr), (size), 0)
 
-static int read_block_bitmap(struct super_block * sb, unsigned int block,
-	unsigned long bitmap_nr)
+static int read_block_bitmap(struct super_block * sb, Uint32 bitmap,
+	unsigned int block, unsigned long bitmap_nr)
 {
 	struct buffer_head *bh = NULL;
 	int retval = 0;
 	lb_addr loc;
 
-	loc.logicalBlockNum = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap;
+	loc.logicalBlockNum = bitmap;
 	loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
 
 	bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block), sb->s_blocksize);
@@ -105,7 +105,8 @@
 	return retval;
 }
 
-static int __load_block_bitmap(struct super_block * sb, unsigned int block_group)
+static int __load_block_bitmap(struct super_block * sb, Uint32 bitmap,
+	unsigned int block_group)
 {
 	int i, j, retval = 0;
 	unsigned long block_bitmap_number;
@@ -125,7 +126,7 @@
 			if (UDF_SB_BLOCK_BITMAP_NUMBER(sb, block_group) == block_group)
 				return block_group;
 		}
-		retval = read_block_bitmap(sb, block_group, block_group);
+		retval = read_block_bitmap(sb, bitmap, block_group, block_group);
 		if (retval < 0)
 			return retval;
 		return block_group;
@@ -150,7 +151,7 @@
 		UDF_SB_BLOCK_BITMAP(sb, 0) = block_bitmap;
 
 		if (!block_bitmap)
-			retval = read_block_bitmap(sb, block_group, 0);
+			retval = read_block_bitmap(sb, bitmap, block_group, 0);
 	}
 	else
 	{
@@ -163,12 +164,12 @@
 			UDF_SB_BLOCK_BITMAP_NUMBER(sb, j) = UDF_SB_BLOCK_BITMAP_NUMBER(sb, j-1);
 			UDF_SB_BLOCK_BITMAP(sb, j) = UDF_SB_BLOCK_BITMAP(sb, j-1);
 		}
-		retval = read_block_bitmap(sb, block_group, 0);
+		retval = read_block_bitmap(sb, bitmap, block_group, 0);
 	}
 	return retval;
 }
 
-static inline int load_block_bitmap(struct super_block *sb,
+static inline int load_block_bitmap(struct super_block *sb, Uint32 bitmap,
 	unsigned int block_group)
 {
 	int slot;
@@ -189,7 +190,7 @@
 	}
 	else
 	{
-		slot = __load_block_bitmap(sb, block_group);
+		slot = __load_block_bitmap(sb, bitmap, block_group);
 	}
 
 	if (slot < 0)
@@ -201,8 +202,8 @@
 	return slot;
 }
 
-void udf_free_blocks(const struct inode * inode, lb_addr bloc, Uint32 offset,
-	Uint32 count)
+static void udf_bitmap_free_blocks(const struct inode * inode, Uint32 bitmap,
+	lb_addr bloc, Uint32 offset, Uint32 count)
 {
 	struct buffer_head * bh = NULL;
 	unsigned long block;
@@ -220,9 +221,6 @@
 		return;
 	}
 
-	if (UDF_SB_PARTMAPS(sb)[bloc.partitionReferenceNum].s_uspace_bitmap == 0xFFFFFFFF)
-		return;
-
 	lock_super(sb);
 	if (bloc.logicalBlockNum < 0 ||
 		(bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum))
@@ -248,7 +246,7 @@
 		overflow = bit + count - (sb->s_blocksize << 3);
 		count -= overflow;
 	}
-	bitmap_nr = load_block_bitmap(sb, block_group);
+	bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
 	if (bitmap_nr < 0)
 		goto error_return;
 
@@ -285,8 +283,8 @@
 	return;
 }
 
-int udf_prealloc_blocks(const struct inode * inode, Uint16 partition,
-	Uint32 first_block, Uint32 block_count)
+static int udf_bitmap_prealloc_blocks(const struct inode * inode, Uint32 bitmap,
+	Uint16 partition, Uint32 first_block, Uint32 block_count)
 {
 	int alloc_count = 0;
 	int bit, block, block_group, group_start;
@@ -312,7 +310,7 @@
 	block_group = block >> (sb->s_blocksize_bits + 3);
 	group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc);
 
-	bitmap_nr = load_block_bitmap(sb, block_group);
+	bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
 	if (bitmap_nr < 0)
 		goto out;
 	bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr);
@@ -351,7 +349,8 @@
 	return alloc_count;
 }
 
-int udf_new_block(const struct inode * inode, Uint16 partition, Uint32 goal, int *err)
+static int udf_bitmap_new_block(const struct inode * inode, Uint32 bitmap,
+	Uint16 partition, Uint32 goal, int *err)
 {
 	int tmp, newbit, bit=0, block, block_group, group_start;
 	int end_goal, nr_groups, bitmap_nr, i;
@@ -379,7 +378,7 @@
 	block_group = block >> (sb->s_blocksize_bits + 3);
 	group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc);
 
-	bitmap_nr = load_block_bitmap(sb, block_group);
+	bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
 	if (bitmap_nr < 0)
 		goto error_return;
 	bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr);
@@ -419,7 +418,7 @@
 			block_group = 0;
 		group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc);
 
-		bitmap_nr = load_block_bitmap(sb, block_group);
+		bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
 		if (bitmap_nr < 0)
 			goto error_return;
 		bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr);
@@ -497,3 +496,64 @@
 	unlock_super(sb);
 	return 0;
 }
+
+inline void udf_free_blocks(const struct inode * inode, lb_addr bloc,
+    Uint32 offset, Uint32 count)
+{
+	if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		return udf_bitmap_free_blocks(inode,
+			UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_uspace.bitmap,
+			bloc, offset, count);
+	}
+	else if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		return udf_bitmap_free_blocks(inode,
+			UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_fspace.bitmap,
+			bloc, offset, count);
+	}
+	else
+		return;
+}
+
+inline int udf_prealloc_blocks(const struct inode * inode, Uint16 partition,
+	Uint32 first_block, Uint32 block_count)
+{
+	if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		return udf_bitmap_prealloc_blocks(inode,
+			UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.bitmap,
+			partition, first_block, block_count);
+	}
+	else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		return udf_bitmap_prealloc_blocks(inode,
+			UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.bitmap,
+			partition, first_block, block_count);
+	}
+	else
+		return 0;
+}
+
+inline int udf_new_block(const struct inode * inode, Uint16 partition,
+	Uint32 goal, int *err)
+{
+	if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		return udf_bitmap_new_block(inode,
+			UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.bitmap,
+			partition, goal, err);
+	}
+	else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		return udf_bitmap_new_block(inode,
+			UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.bitmap,
+			partition, goal, err);
+	}
+	else
+	{
+		*err = -EIO;
+		return 0;
+	}
+}
+
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/file.c linux/fs/udf/file.c
--- v2.3.99-pre2/linux/fs/udf/file.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/file.c	Tue Mar 21 11:30:08 2000
@@ -87,7 +87,7 @@
 	return 0;
 }
 
-static int udf_adinicb_prepare_write(struct page *page, unsigned offset, unsigned to)
+static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
 	kmap(page);
 	return 0;
@@ -246,7 +246,7 @@
 		struct FileEntry *fe;
 
 		fe = (struct FileEntry *)bh->b_data;
-		eaicb = fe->extendedAttrICB;
+		eaicb = lela_to_cpu(fe->extendedAttrICB);
 		if (UDF_I_LENEATTR(inode))
 			ea = fe->extendedAttr;
 	}
@@ -255,7 +255,7 @@
 		struct ExtendedFileEntry *efe;
 
 		efe = (struct ExtendedFileEntry *)bh->b_data;
-		eaicb = efe->extendedAttrICB;
+		eaicb = lela_to_cpu(efe->extendedAttrICB);
 		if (UDF_I_LENEATTR(inode))
 			ea = efe->extendedAttr;
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/inode.c linux/fs/udf/inode.c
--- v2.3.99-pre2/linux/fs/udf/inode.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/inode.c	Tue Mar 21 11:30:08 2000
@@ -74,10 +74,13 @@
  */
 void udf_put_inode(struct inode * inode)
 {
-	lock_kernel();
-	udf_discard_prealloc(inode);
-	write_inode_now(inode);
-	unlock_kernel();
+	if (!(inode->i_sb->s_flags & MS_RDONLY))
+	{
+		lock_kernel();
+		udf_discard_prealloc(inode);
+		write_inode_now(inode);
+		unlock_kernel();
+	}
 }
 
 /*
@@ -130,7 +133,7 @@
 	return block_read_full_page(page, udf_get_block);
 }
 
-static int udf_prepare_write(struct page *page, unsigned from, unsigned to)
+static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return block_prepare_write(page, from, to, udf_get_block);
 }
@@ -1554,16 +1557,18 @@
 			case ICB_FLAG_AD_SHORT:
 			{
 				sad = (short_ad *)sptr;
-				sad->extLength = EXTENT_NEXT_EXTENT_ALLOCDECS << 30 |
-					inode->i_sb->s_blocksize;
+				sad->extLength = cpu_to_le32(
+					EXTENT_NEXT_EXTENT_ALLOCDECS << 30 |
+					inode->i_sb->s_blocksize);
 				sad->extPosition = cpu_to_le32(bloc->logicalBlockNum);
 				break;
 			}
 			case ICB_FLAG_AD_LONG:
 			{
 				lad = (long_ad *)sptr;
-				lad->extLength = EXTENT_NEXT_EXTENT_ALLOCDECS << 30 |
-					inode->i_sb->s_blocksize;
+				lad->extLength = cpu_to_le32(
+					EXTENT_NEXT_EXTENT_ALLOCDECS << 30 |
+					inode->i_sb->s_blocksize);
 				lad->extLocation = cpu_to_lelb(*bloc);
 				break;
 			}
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/lowlevel.c linux/fs/udf/lowlevel.c
--- v2.3.99-pre2/linux/fs/udf/lowlevel.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/lowlevel.c	Mon Mar 20 08:17:43 2000
@@ -70,8 +70,6 @@
 unsigned int
 udf_get_last_block(struct super_block *sb)
 {
-	extern int *blksize_size[];
-	kdev_t dev = sb->s_dev;
 	struct block_device *bdev = sb->s_bdev;
 	int ret;
 	unsigned long lblock = 0;
@@ -80,28 +78,10 @@
 
 	if (ret) /* Hard Disk */
 	{
-		unsigned int hbsize = get_hardblocksize(dev);
-		unsigned int blocksize = sb->s_blocksize;
-		unsigned int mult = 0;
-		unsigned int div = 0;
-
-		if (!hbsize)
-			hbsize = blksize_size[MAJOR(dev)][MINOR(dev)];
-
-		if (hbsize > blocksize)
-			mult = hbsize / blocksize;
-		else if (blocksize > hbsize)
-			div = blocksize / hbsize;
-
 		ret = ioctl_by_bdev(bdev, BLKGETSIZE, (unsigned long) &lblock);
 
 		if (!ret && lblock != 0x7FFFFFFF)
-		{
-			if (mult)
-				lblock *= mult;
-			else if (div)
-				lblock /= div;
-		}
+			lblock = ((512 * lblock) / sb->s_blocksize);
 	}
 
 	if (!ret && lblock)
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/misc.c linux/fs/udf/misc.c
--- v2.3.99-pre2/linux/fs/udf/misc.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/misc.c	Mon Mar 20 08:17:43 2000
@@ -90,7 +90,7 @@
 		struct FileEntry *fe;
 
 		fe = (struct FileEntry *)(*bh)->b_data;
-		eaicb = fe->extendedAttrICB;
+		eaicb = lela_to_cpu(fe->extendedAttrICB);
 		offset = sizeof(struct FileEntry);
 	}
 	else
@@ -98,7 +98,7 @@
 		struct ExtendedFileEntry *efe;
 
 		efe = (struct ExtendedFileEntry *)(*bh)->b_data;
-		eaicb = efe->extendedAttrICB;
+		eaicb = lela_to_cpu(efe->extendedAttrICB);
 		offset = sizeof(struct ExtendedFileEntry);
 	}
 
@@ -206,7 +206,7 @@
 		struct FileEntry *fe;
 
 		fe = (struct FileEntry *)(*bh)->b_data;
-		eaicb = fe->extendedAttrICB;
+		eaicb = lela_to_cpu(fe->extendedAttrICB);
 		if (UDF_I_LENEATTR(inode))
 			ea = fe->extendedAttr;
 	}
@@ -215,7 +215,7 @@
 		struct ExtendedFileEntry *efe;
 
 		efe = (struct ExtendedFileEntry *)(*bh)->b_data;
-		eaicb = efe->extendedAttrICB;
+		eaicb = lela_to_cpu(efe->extendedAttrICB);
 		if (UDF_I_LENEATTR(inode))
 			ea = efe->extendedAttr;
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/namei.c linux/fs/udf/namei.c
--- v2.3.99-pre2/linux/fs/udf/namei.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/udf/namei.c	Mon Mar 20 08:17:43 2000
@@ -935,7 +935,7 @@
 	inode->i_data.a_ops = &udf_symlink_aops;
 	inode->i_op = &page_symlink_inode_operations;
 
-	if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB)
+	if (UDF_I_ALLOCTYPE(inode) != ICB_FLAG_AD_IN_ICB)
 	{
 		struct buffer_head *bh = NULL;
 		lb_addr bloc, eloc;
@@ -964,7 +964,7 @@
 	bh = udf_tread(inode->i_sb, block, inode->i_sb->s_blocksize);
 	ea = bh->b_data + udf_ext0_offset(inode);
 
-	eoffset = inode->i_sb->s_blocksize - (ea - bh->b_data);
+	eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode);
 	pc = (struct PathComponent *)ea;
 
 	if (*symname == '/')
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/super.c linux/fs/udf/super.c
--- v2.3.99-pre2/linux/fs/udf/super.c	Tue Mar 14 19:10:40 2000
+++ linux/fs/udf/super.c	Mon Mar 20 08:17:43 2000
@@ -100,14 +100,14 @@
 
 /* Superblock operations */
 static struct super_operations udf_sb_ops = {
-	read_inode:		udf_read_inode,
+	read_inode:			udf_read_inode,
 	write_inode:		udf_write_inode,
-	put_inode:		udf_put_inode,
+	put_inode:			udf_put_inode,
 	delete_inode:		udf_delete_inode,
-	put_super:		udf_put_super,
+	put_super:			udf_put_super,
 	write_super:		udf_write_super,
-	statfs:			udf_statfs,
-	remount_fs:		udf_remount_fs,
+	statfs:				udf_statfs,
+	remount_fs:			udf_remount_fs,
 };
 
 struct udf_options
@@ -127,7 +127,6 @@
 	uid_t uid;
 };
 
-
 static int __init init_udf_fs(void)
 {
 	printk(KERN_NOTICE "udf: registering filesystem\n");
@@ -745,8 +744,9 @@
 	{
 		if (udf_CS0toUTF8(&outstr, &instr))
 		{
-			udf_debug("volIdent[] = '%s'\n", outstr.u_name);
-			strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, outstr.u_len);
+			strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name,
+				outstr.u_len > 31 ? 31 : outstr.u_len);
+			udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb));
 		}
 	}
 
@@ -788,7 +788,6 @@
 		{
 			UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */
 			UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation) + UDF_SB_SESSION(sb);
-			UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF;
 
 			if (UDF_SB_PARTTYPE(sb,i) == UDF_SPARABLE_MAP15)
 				udf_fill_spartable(sb, &UDF_SB_TYPESPAR(sb,i), UDF_SB_PARTLEN(sb,i));
@@ -803,17 +802,24 @@
 					udf_debug("unallocatedSpaceTable (part %d)\n", i);
 				if (phd->unallocatedSpaceBitmap.extLength)
 				{
-					UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap =
+					UDF_SB_PARTMAPS(sb)[i].s_uspace.bitmap =
 						le32_to_cpu(phd->unallocatedSpaceBitmap.extPosition);
+					UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP;
 					udf_debug("unallocatedSpaceBitmap (part %d) @ %d\n",
-						i, UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap);
+						i, UDF_SB_PARTMAPS(sb)[i].s_uspace.bitmap);
 				}
 				if (phd->partitionIntegrityTable.extLength)
 					udf_debug("partitionIntegrityTable (part %d)\n", i);
 				if (phd->freedSpaceTable.extLength)
 					udf_debug("freedSpaceTable (part %d)\n", i);
 				if (phd->freedSpaceBitmap.extLength)
-					udf_debug("freedSpaceBitmap (part %d\n", i);
+				{
+					UDF_SB_PARTMAPS(sb)[i].s_fspace.bitmap =
+						le32_to_cpu(phd->freedSpaceBitmap.extPosition);
+					UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP;
+					udf_debug("freedSpaceBitmap (part %d) @ %d\n",
+						i, UDF_SB_PARTMAPS(sb)[i].s_fspace.bitmap);
+				}
 			}
 			break;
 		}
@@ -1184,7 +1190,6 @@
 				}
 				UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0);
 				UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum);
-				UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF;
 			}
 		}
 	}
@@ -1520,34 +1525,27 @@
 udf_count_free(struct super_block *sb)
 {
 	struct buffer_head *bh = NULL;
-	unsigned int accum=0;
-	int index;
-	int block=0, newblock;
+	unsigned int accum = 0;
 	lb_addr loc;
-	Uint32  bytes;
-	Uint8   value;
-	Uint8 * ptr;
-	Uint16 ident;
-
-	if (UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap == 0xFFFFFFFF)
-	{
-		if (UDF_SB_LVIDBH(sb))
-		{
-			if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb))
-				accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]);
-
-			if (accum == 0xFFFFFFFF)
-				accum = 0;
+	Uint32 bitmap;
 
-			return accum;
-		}
-		else
-			return 0;
-	}
+	if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+		bitmap = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.bitmap;
+	else if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+		bitmap = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.bitmap;
 	else
+		bitmap = 0xFFFFFFFF;
+
+	if (bitmap != 0xFFFFFFFF)
 	{
 		struct SpaceBitmapDesc *bm;
-		loc.logicalBlockNum = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap;
+		int block = 0, newblock, index;
+		Uint16 ident;
+		Uint32 bytes;
+		Uint8 value;
+		Uint8 * ptr;
+
+		loc.logicalBlockNum = bitmap;
 		loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
 		bh = udf_read_ptagged(sb, loc, 0, &ident);
 
@@ -1593,6 +1591,18 @@
 			}
 		}
 		udf_release_data(bh);
-		return accum;
 	}
+	else
+	{
+		if (UDF_SB_LVIDBH(sb))
+		{
+			if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb))
+				accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]);
+
+			if (accum == 0xFFFFFFFF)
+				accum = 0;
+		}
+	}
+
+	return accum;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/udf_sb.h linux/fs/udf/udf_sb.h
--- v2.3.99-pre2/linux/fs/udf/udf_sb.h	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/udf_sb.h	Mon Mar 20 08:17:43 2000
@@ -18,6 +18,11 @@
 #define UDF_FLAG_UNDELETE			6
 #define UDF_FLAG_UNHIDE				7
 #define UDF_FLAG_VARCONV			8
+
+#define UDF_PART_FLAG_UNALLOC_BITMAP		0x0001
+#define UDF_PART_FLAG_UNALLOC_TABLE			0x0002
+#define UDF_PART_FLAG_FREED_BITMAP			0x0004
+#define UDF_PART_FLAG_FREED_TABLE			0x0008
 	
 #define UDF_SB_FREE(X)\
 {\
@@ -52,6 +57,7 @@
 #define UDF_SB_TYPESPAR(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing )
 #define UDF_SB_TYPEVIRT(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual )
 #define UDF_SB_PARTFUNC(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func )
+#define UDF_SB_PARTFLAGS(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags )
 
 #define UDF_SB_VOLIDENT(X)				( UDF_SB(X)->s_volident )
 #define UDF_SB_NUMPARTS(X)				( UDF_SB(X)->s_partitions )
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/udfdecl.h linux/fs/udf/udfdecl.h
--- v2.3.99-pre2/linux/fs/udf/udfdecl.h	Tue Mar  7 14:32:26 2000
+++ linux/fs/udf/udfdecl.h	Mon Mar 20 08:17:43 2000
@@ -106,7 +106,7 @@
 struct ustr
 {
 	Uint8 u_cmpID;
-	Uint8 u_name[UDF_NAME_LEN-1];
+	Uint8 u_name[UDF_NAME_LEN];
 	Uint8 u_len;
 	Uint8 padding;
 	unsigned long u_hash;
@@ -182,6 +182,8 @@
 extern void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32);
 extern int udf_prealloc_blocks(const struct inode *, Uint16, Uint32, Uint32);
 extern int udf_new_block(const struct inode *, Uint16, Uint32, int *);
+
+/* fsync.c */
 extern int udf_sync_file(struct file *, struct dentry *);
 
 /* directory.c */
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/udf/unicode.c linux/fs/udf/unicode.c
--- v2.3.99-pre2/linux/fs/udf/unicode.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/udf/unicode.c	Mon Mar 20 08:17:43 2000
@@ -177,7 +177,8 @@
 		return 0;
 	}
 
-	for (i = 0; (i < ocu_len) && (utf_o->u_len < UDF_NAME_LEN) ;) {
+	for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;)
+	{
 
 		/* Expand OSTA compressed Unicode to Unicode */
 		c = ocu[i++];
diff -u --recursive --new-file v2.3.99-pre2/linux/fs/ufs/inode.c linux/fs/ufs/inode.c
--- v2.3.99-pre2/linux/fs/ufs/inode.c	Sat Feb 26 22:31:54 2000
+++ linux/fs/ufs/inode.c	Tue Mar 21 11:30:08 2000
@@ -548,7 +548,7 @@
 {
 	return block_read_full_page(page,ufs_getfrag_block);
 }
-static int ufs_prepare_write(struct page *page, unsigned from, unsigned to)
+static int ufs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return block_prepare_write(page,from,to,ufs_getfrag_block);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-alpha/fpu.h linux/include/asm-alpha/fpu.h
--- v2.3.99-pre2/linux/include/asm-alpha/fpu.h	Tue Dec  7 09:32:49 1999
+++ linux/include/asm-alpha/fpu.h	Tue Mar 21 10:47:06 2000
@@ -27,7 +27,7 @@
 #define FPCR_DYN_PLUS	 (0x3UL << FPCR_DYN_SHIFT)	/* towards +INF */
 #define FPCR_DYN_MASK	 (0x3UL << FPCR_DYN_SHIFT)
 
-#define FPCR_MASK	0xfffe000000000000
+#define FPCR_MASK	0xffff800000000000
 
 /*
  * IEEE trap enables are implemented in software.  These per-thread
@@ -67,7 +67,8 @@
 				 IEEE_STATUS_OVF | IEEE_STATUS_UNF |	\
 				 IEEE_STATUS_INE | IEEE_STATUS_DNO)
 
-#define IEEE_SW_MASK		(IEEE_TRAP_ENABLE_MASK | IEEE_STATUS_MASK | IEEE_MAP_MASK)
+#define IEEE_SW_MASK		(IEEE_TRAP_ENABLE_MASK |		\
+				 IEEE_STATUS_MASK | IEEE_MAP_MASK)
 
 #define IEEE_CURRENT_RM_SHIFT	32
 #define IEEE_CURRENT_RM_MASK	(3UL<<IEEE_CURRENT_RM_SHIFT)
@@ -78,7 +79,12 @@
 
 /*
  * Convert the software IEEE trap enable and status bits into the
- * hardware fpcr format.
+ * hardware fpcr format. 
+ *
+ * Digital Unix engineers receive my thanks for not defining the
+ * software bits identical to the hardware bits.  The chip designers
+ * receive my thanks for making all the not-implemented fpcr bits
+ * RAZ forcing us to use system calls to read/write this value.
  */
 
 static inline unsigned long
@@ -86,11 +92,13 @@
 {
 	unsigned long fp;
 	fp = (sw & IEEE_STATUS_MASK) << 35;
-	fp |= sw & IEEE_STATUS_MASK ? FPCR_SUM : 0;
+	fp |= (sw & IEEE_MAP_DMZ) << 36;
+	fp |= (sw & IEEE_STATUS_MASK ? FPCR_SUM : 0);
 	fp |= (~sw & (IEEE_TRAP_ENABLE_INV
 		      | IEEE_TRAP_ENABLE_DZE
 		      | IEEE_TRAP_ENABLE_OVF)) << 48;
 	fp |= (~sw & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE)) << 57;
+	fp |= (sw & IEEE_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0);
 	fp |= (~sw & IEEE_TRAP_ENABLE_DNO) << 41;
 	return fp;
 }
@@ -100,10 +108,12 @@
 {
 	unsigned long sw;
 	sw = (fp >> 35) & IEEE_STATUS_MASK;
+	sw |= (fp >> 36) & IEEE_MAP_DMZ;
 	sw |= (~fp >> 48) & (IEEE_TRAP_ENABLE_INV
 			     | IEEE_TRAP_ENABLE_DZE
 			     | IEEE_TRAP_ENABLE_OVF);
 	sw |= (~fp >> 57) & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE);
+	sw |= (fp >> 47) & IEEE_MAP_UMZ;
 	sw |= (~fp >> 41) & IEEE_TRAP_ENABLE_DNO;
 	return sw;
 }
@@ -115,26 +125,59 @@
    never generates arithmetic faults and (b) call_pal instructions
    are implied trap barriers.  */
 
-static inline unsigned long rdfpcr(void)
+static inline unsigned long
+rdfpcr(void)
 {
 	unsigned long tmp, ret;
+
+#if defined(__alpha_cix__) || defined(__alpha_fix__)
+	__asm__ ("ftoit $f0,%0\n\t"
+		 "mf_fpcr $f0\n\t"
+		 "ftoit $f0,%1\n\t"
+		 "itoft %0,$f0"
+		 : "=r"(tmp), "=r"(ret));
+#else
 	__asm__ ("stt $f0,%0\n\t"
 		 "mf_fpcr $f0\n\t"
 		 "stt $f0,%1\n\t"
 		 "ldt $f0,%0"
-		: "=m"(tmp), "=m"(ret));
+		 : "=m"(tmp), "=m"(ret));
+#endif
+
 	return ret;
 }
 
-static inline void wrfpcr(unsigned long val)
+static inline void
+wrfpcr(unsigned long val)
 {
 	unsigned long tmp;
+
+#if defined(__alpha_cix__) || defined(__alpha_fix__)
+	__asm__ ("ftoit $f0,%0\n\t"
+		 "itoft %1,$f0\n\t"
+		 "mt_fpcr $f0\n\t"
+		 "itoft %0,$f0"
+		 : "=&r"(tmp) : "r"(val));
+#else
 	__asm__ __volatile__ (
 		"stt $f0,%0\n\t"
 		"ldt $f0,%1\n\t"
 		"mt_fpcr $f0\n\t"
 		"ldt $f0,%0"
 		: "=m"(tmp) : "m"(val));
+#endif
+}
+
+static inline unsigned long
+swcr_update_status(unsigned long swcr, unsigned long fpcr)
+{
+	/* EV6 implements most of the bits in hardware.  Collect
+	   the acrued exception bits from the real fpcr.  */
+	if (implver() == IMPLVER_EV6) {
+		swcr &= ~IEEE_STATUS_MASK;
+		swcr |= (fpcr >> 35) & IEEE_STATUS_MASK;
+	}
+	return swcr;
 }
 
 extern unsigned long alpha_read_fp_reg (unsigned long reg);
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-alpha/pci.h linux/include/asm-alpha/pci.h
--- v2.3.99-pre2/linux/include/asm-alpha/pci.h	Sun Mar 19 18:35:31 2000
+++ linux/include/asm-alpha/pci.h	Tue Mar 21 10:46:21 2000
@@ -1,6 +1,8 @@
 #ifndef __ALPHA_PCI_H
 #define __ALPHA_PCI_H
 
+#ifdef __KERNEL__
+
 #include <linux/spinlock.h>
 #include <asm/scatterlist.h>
 #include <asm/machvec.h>
@@ -22,7 +24,17 @@
 	struct resource *io_space;
 	struct resource *mem_space;
 
-	unsigned long config_space;
+	/* The following are for reporting to userland.  The invariant is
+	   that if we report a BWX-capable dense memory, we do not report
+	   a sparse memory at all, even if it exists.  */
+	unsigned long sparse_mem_base;
+	unsigned long dense_mem_base;
+	unsigned long sparse_io_base;
+	unsigned long dense_io_base;
+
+	/* This one's for the kernel only.  It's in KSEG somewhere.  */
+	unsigned long config_space_base;
+
 	unsigned int index;
 	unsigned int first_busno;
 	unsigned int last_busno;
@@ -131,5 +143,14 @@
    you would pass 0x00ffffff as the mask to this function.  */
 
 extern int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask);
+
+#endif /* __KERNEL__ */
+
+/* Values for the `which' argument to sys_pciconfig_iobase.  */
+#define IOBASE_HOSE		0
+#define IOBASE_SPARSE_MEM	1
+#define IOBASE_DENSE_MEM	2
+#define IOBASE_SPARSE_IO	3
+#define IOBASE_DENSE_IO		4
 
 #endif /* __ALPHA_PCI_H */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v2.3.99-pre2/linux/include/asm-alpha/pgtable.h	Thu Mar  2 14:36:23 2000
+++ linux/include/asm-alpha/pgtable.h	Tue Mar 21 10:46:21 2000
@@ -159,6 +159,16 @@
  * On certain platforms whose physical address space can overlap KSEG,
  * namely EV6 and above, we must re-twiddle the physaddr to restore the
  * correct high-order bits.
+ *
+ * This is extremely confusing until you realize that this is actually
+ * just working around a userspace bug.  The X server was intending to
+ * provide the physical address but instead provided the KSEG address.
+ * Or tried to, except it's not representable.
+ * 
+ * On Tsunami there's nothing meaningful at 0x40000000000, so this is
+ * a safe thing to do.  Come the first core logic that does put something
+ * in this area -- memory or whathaveyou -- then this hack will have
+ * to go away.  So be prepared!
  */
 
 #if defined(CONFIG_ALPHA_GENERIC) && defined(USE_48_BIT_KSEG)
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-alpha/sfp-machine.h linux/include/asm-alpha/sfp-machine.h
--- v2.3.99-pre2/linux/include/asm-alpha/sfp-machine.h	Tue Dec  7 09:32:49 1999
+++ linux/include/asm-alpha/sfp-machine.h	Tue Mar 21 10:47:06 2000
@@ -74,9 +74,7 @@
 #define FP_EX_INEXACT		IEEE_TRAP_ENABLE_INE
 #define FP_EX_DENORM		IEEE_TRAP_ENABLE_DNO
 
-#define FP_DENORM_ZERO		(fpcw & IEEE_MAP_DMZ)
-
-#define FP_HANDLE_EXCEPTIONS return _fex
+#define FP_DENORM_ZERO		(swcr & IEEE_MAP_DMZ)
 
 /* We write the results always */
 #define FP_INHIBIT_RESULTS 0
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-alpha/smplock.h linux/include/asm-alpha/smplock.h
--- v2.3.99-pre2/linux/include/asm-alpha/smplock.h	Fri Sep 10 23:57:36 1999
+++ linux/include/asm-alpha/smplock.h	Thu Mar 23 12:50:09 2000
@@ -10,6 +10,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h
--- v2.3.99-pre2/linux/include/asm-alpha/unistd.h	Sun Mar 19 18:35:31 2000
+++ linux/include/asm-alpha/unistd.h	Tue Mar 21 10:46:21 2000
@@ -313,6 +313,7 @@
 #define __NR_dipc			373
 #define __NR_pivot_root			374
 #define __NR_mincore			375
+#define __NR_pciconfig_iobase		376
 
 #if defined(__LIBRARY__) && defined(__GNUC__)
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-arm/smplock.h linux/include/asm-arm/smplock.h
--- v2.3.99-pre2/linux/include/asm-arm/smplock.h	Fri Sep 10 23:57:36 1999
+++ linux/include/asm-arm/smplock.h	Thu Mar 23 12:50:09 2000
@@ -8,6 +8,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-generic/smplock.h linux/include/asm-generic/smplock.h
--- v2.3.99-pre2/linux/include/asm-generic/smplock.h	Fri Sep 10 23:57:36 1999
+++ linux/include/asm-generic/smplock.h	Thu Mar 23 12:50:09 2000
@@ -8,6 +8,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-i386/cache.h linux/include/asm-i386/cache.h
--- v2.3.99-pre2/linux/include/asm-i386/cache.h	Tue Dec  7 09:32:49 1999
+++ linux/include/asm-i386/cache.h	Thu Mar 23 15:36:06 2000
@@ -4,11 +4,9 @@
 #ifndef __ARCH_I386_CACHE_H
 #define __ARCH_I386_CACHE_H
 
+#include <linux/config.h>
+
 /* bytes per L1 cache line */
-#if    CPU==586 || CPU==686
-#define        L1_CACHE_BYTES  32
-#else
-#define        L1_CACHE_BYTES  16
-#endif
+#define        L1_CACHE_BYTES  CONFIG_X86_L1_CACHE_BYTES
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-i386/smplock.h linux/include/asm-i386/smplock.h
--- v2.3.99-pre2/linux/include/asm-i386/smplock.h	Fri Jan 21 18:19:17 2000
+++ linux/include/asm-i386/smplock.h	Thu Mar 23 15:36:06 2000
@@ -10,6 +10,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v2.3.99-pre2/linux/include/asm-i386/string.h	Tue Dec 14 01:27:24 1999
+++ linux/include/asm-i386/string.h	Thu Mar 23 15:36:06 2000
@@ -2,6 +2,7 @@
 #define _I386_STRING_H_
 
 #ifdef __KERNEL__
+#include <linux/config.h>
 /*
  * On a 486 or Pentium, we are better off not using the
  * byte string operations. But on a 386 or a PPro the
@@ -11,7 +12,7 @@
  * Also, the byte strings actually work correctly. Forget
  * the i486 routines for now as they may be broken..
  */
-#if FIXED_486_STRING && (CPU == 486 || CPU == 586)
+#if FIXED_486_STRING && defined(CONFIG_X86_USE_STRING_486)
 #include <asm/string-486.h>
 #else
 
@@ -284,8 +285,6 @@
 
 #define __HAVE_ARCH_MEMCPY
 
-#include <linux/config.h>
-
 #ifdef CONFIG_X86_USE_3DNOW
 
 /* All this just for in_interrupt() ... */
@@ -529,7 +528,7 @@
 	return addr;
 }
 
+#endif /* CONFIG_X86_USE_STRING_486 */
 #endif /* __KERNEL__ */
 
-#endif
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.3.99-pre2/linux/include/asm-i386/system.h	Fri Mar 10 16:40:49 2000
+++ linux/include/asm-i386/system.h	Thu Mar 23 15:36:06 2000
@@ -1,6 +1,7 @@
 #ifndef __ASM_SYSTEM_H
 #define __ASM_SYSTEM_H
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <asm/segment.h>
 #include <linux/bitops.h> /* for LOCK_PREFIX */
@@ -215,7 +216,7 @@
  * indicated by comparing RETURN with OLD.
  */
 
-#if CPU != 386
+#ifdef CONFIG_X86_CMPXCHG
 #define __HAVE_ARCH_CMPXCHG 1
 
 static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.3.99-pre2/linux/include/asm-i386/uaccess.h	Mon Oct 11 15:38:15 1999
+++ linux/include/asm-i386/uaccess.h	Thu Mar 23 15:36:06 2000
@@ -56,7 +56,7 @@
 			 segment_eq(get_fs(),KERNEL_DS) || \
 			  __verify_write((void *)(addr),(size))))
 
-#endif /* CPU */
+#endif
 
 extern inline int verify_area(int type, const void * addr, unsigned long size)
 {
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-ia64/smplock.h linux/include/asm-ia64/smplock.h
--- v2.3.99-pre2/linux/include/asm-ia64/smplock.h	Thu Feb 10 17:11:20 2000
+++ linux/include/asm-ia64/smplock.h	Thu Mar 23 12:50:09 2000
@@ -10,6 +10,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-m68k/smplock.h linux/include/asm-m68k/smplock.h
--- v2.3.99-pre2/linux/include/asm-m68k/smplock.h	Fri Sep 10 23:57:36 1999
+++ linux/include/asm-m68k/smplock.h	Thu Mar 23 12:50:09 2000
@@ -8,6 +8,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-mips/smplock.h linux/include/asm-mips/smplock.h
--- v2.3.99-pre2/linux/include/asm-mips/smplock.h	Sat Feb 26 22:31:56 2000
+++ linux/include/asm-mips/smplock.h	Thu Mar 23 12:50:09 2000
@@ -11,6 +11,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-ppc/elf.h linux/include/asm-ppc/elf.h
--- v2.3.99-pre2/linux/include/asm-ppc/elf.h	Sun Feb 13 19:29:04 2000
+++ linux/include/asm-ppc/elf.h	Tue Mar 21 10:49:32 2000
@@ -38,9 +38,11 @@
 typedef double elf_fpreg_t;
 typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 
+#ifdef __KERNEL__
 /* Altivec registers */
 typedef vector128 elf_vrreg_t;
 typedef elf_vrreg_t elf_vrregset_t[ELF_NVRREG];
+#endif /* __KERNEL__ */
 
 #define ELF_CORE_COPY_REGS(gregs, regs) \
 	memcpy(gregs, regs, \
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-ppc/mman.h linux/include/asm-ppc/mman.h
--- v2.3.99-pre2/linux/include/asm-ppc/mman.h	Wed Dec 18 00:54:09 1996
+++ linux/include/asm-ppc/mman.h	Tue Mar 21 10:49:32 2000
@@ -16,14 +16,20 @@
 
 #define MAP_GROWSDOWN	0x0100		/* stack-like segment */
 #define MAP_DENYWRITE	0x0800		/* ETXTBSY */
-#define MAP_EXECUTABLE	0x1000		/* mark it as a executable */
+#define MAP_EXECUTABLE	0x1000		/* mark it as an executable */
 
 #define MS_ASYNC	1		/* sync memory asynchronously */
 #define MS_INVALIDATE	2		/* invalidate the caches */
 #define MS_SYNC		4		/* synchronous memory sync */
 
-#define MCL_CURRENT     0x2000          /* lock all currently mapped pages */
-#define MCL_FUTURE      0x4000          /* lock all additions to address space */
+#define MCL_CURRENT	1		/* lock all current mappings */
+#define MCL_FUTURE	2		/* lock all future mappings */
+
+#define MADV_NORMAL	0x0		/* default page-in behavior */
+#define MADV_RANDOM	0x1		/* page-in minimum required */
+#define MADV_SEQUENTIAL	0x2		/* read-ahead aggressively */
+#define MADV_WILLNEED	0x3		/* pre-fault pages */
+#define MADV_DONTNEED	0x4		/* discard these pages */
 
 /* compatibility flags */
 #define MAP_ANON	MAP_ANONYMOUS
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-ppc/smplock.h linux/include/asm-ppc/smplock.h
--- v2.3.99-pre2/linux/include/asm-ppc/smplock.h	Fri Sep 10 23:57:37 1999
+++ linux/include/asm-ppc/smplock.h	Thu Mar 23 12:50:09 2000
@@ -8,6 +8,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-ppc/types.h linux/include/asm-ppc/types.h
--- v2.3.99-pre2/linux/include/asm-ppc/types.h	Tue Mar  7 14:32:26 2000
+++ linux/include/asm-ppc/types.h	Tue Mar 21 10:49:32 2000
@@ -2,7 +2,6 @@
 #define _PPC_TYPES_H
 
 #ifndef __ASSEMBLY__
-#ifdef __KERNEL__
 
 typedef unsigned short umode_t;
 
@@ -20,6 +19,11 @@
 typedef unsigned long long __u64;
 #endif
 
+typedef struct {
+	__u32 u[4];
+} __attribute((aligned(16))) vector128;
+
+#ifdef __KERNEL__
 /*
  * These aren't exported outside the kernel to avoid name space clashes
  */
@@ -34,10 +38,6 @@
 
 typedef signed long long s64;
 typedef unsigned long long u64;
-
-typedef struct {
-	u32 u[4];
-} __attribute((aligned(16))) vector128;
 
 #define BITS_PER_LONG 32
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-sparc/asm_offsets.h linux/include/asm-sparc/asm_offsets.h
--- v2.3.99-pre2/linux/include/asm-sparc/asm_offsets.h	Sun Feb 20 21:12:39 2000
+++ linux/include/asm-sparc/asm_offsets.h	Tue Mar 21 23:38:25 2000
@@ -142,21 +142,21 @@
 #define ASIZ_task_cap_inheritable	0x00000004
 #define AOFF_task_cap_permitted	0x000001c8
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x000001cc
+#define AOFF_task_user	0x000001d0
 #define ASIZ_task_user	0x00000004
-#define AOFF_task_rlim	0x000001d0
+#define AOFF_task_rlim	0x000001d4
 #define ASIZ_task_rlim	0x00000050
-#define AOFF_task_used_math	0x00000220
+#define AOFF_task_used_math	0x00000224
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000222
+#define AOFF_task_comm	0x00000226
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000234
+#define AOFF_task_link_count	0x00000238
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000238
+#define AOFF_task_tty	0x0000023c
 #define ASIZ_task_tty	0x00000004
-#define AOFF_task_semundo	0x0000023c
+#define AOFF_task_semundo	0x00000240
 #define ASIZ_task_semundo	0x00000004
-#define AOFF_task_semsleeping	0x00000240
+#define AOFF_task_semsleeping	0x00000244
 #define ASIZ_task_semsleeping	0x00000004
 #define AOFF_task_thread	0x00000248
 #define ASIZ_task_thread	0x00000380
@@ -423,49 +423,49 @@
 #define ASIZ_task_cap_inheritable	0x00000004
 #define AOFF_task_cap_permitted	0x000002c4
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x000002c8
+#define AOFF_task_user	0x000002cc
 #define ASIZ_task_user	0x00000004
-#define AOFF_task_rlim	0x000002cc
+#define AOFF_task_rlim	0x000002d0
 #define ASIZ_task_rlim	0x00000050
-#define AOFF_task_used_math	0x0000031c
+#define AOFF_task_used_math	0x00000320
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x0000031e
+#define AOFF_task_comm	0x00000322
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000330
+#define AOFF_task_link_count	0x00000334
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000334
+#define AOFF_task_tty	0x00000338
 #define ASIZ_task_tty	0x00000004
-#define AOFF_task_semundo	0x00000338
+#define AOFF_task_semundo	0x0000033c
 #define ASIZ_task_semundo	0x00000004
-#define AOFF_task_semsleeping	0x0000033c
+#define AOFF_task_semsleeping	0x00000340
 #define ASIZ_task_semsleeping	0x00000004
-#define AOFF_task_thread	0x00000340
+#define AOFF_task_thread	0x00000348
 #define ASIZ_task_thread	0x00000380
-#define AOFF_task_fs	0x000006c0
+#define AOFF_task_fs	0x000006c8
 #define ASIZ_task_fs	0x00000004
-#define AOFF_task_files	0x000006c4
+#define AOFF_task_files	0x000006cc
 #define ASIZ_task_files	0x00000004
-#define AOFF_task_sigmask_lock	0x000006c8
+#define AOFF_task_sigmask_lock	0x000006d0
 #define ASIZ_task_sigmask_lock	0x00000008
-#define AOFF_task_sig	0x000006d0
+#define AOFF_task_sig	0x000006d8
 #define ASIZ_task_sig	0x00000004
-#define AOFF_task_signal	0x000006d4
+#define AOFF_task_signal	0x000006dc
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x000006dc
+#define AOFF_task_blocked	0x000006e4
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x000006e4
+#define AOFF_task_sigqueue	0x000006ec
 #define ASIZ_task_sigqueue	0x00000004
-#define AOFF_task_sigqueue_tail	0x000006e8
+#define AOFF_task_sigqueue_tail	0x000006f0
 #define ASIZ_task_sigqueue_tail	0x00000004
-#define AOFF_task_sas_ss_sp	0x000006ec
+#define AOFF_task_sas_ss_sp	0x000006f4
 #define ASIZ_task_sas_ss_sp	0x00000004
-#define AOFF_task_sas_ss_size	0x000006f0
+#define AOFF_task_sas_ss_size	0x000006f8
 #define ASIZ_task_sas_ss_size	0x00000004
-#define AOFF_task_parent_exec_id	0x000006f4
+#define AOFF_task_parent_exec_id	0x000006fc
 #define ASIZ_task_parent_exec_id	0x00000004
-#define AOFF_task_self_exec_id	0x000006f8
+#define AOFF_task_self_exec_id	0x00000700
 #define ASIZ_task_self_exec_id	0x00000004
-#define AOFF_task_exit_sem	0x000006fc
+#define AOFF_task_exit_sem	0x00000704
 #define ASIZ_task_exit_sem	0x00000024
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.3.99-pre2/linux/include/asm-sparc/pgtable.h	Tue Mar  7 14:32:26 2000
+++ linux/include/asm-sparc/pgtable.h	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.92 2000/03/02 20:37:37 davem Exp $ */
+/* $Id: pgtable.h,v 1.93 2000/03/21 01:04:53 anton Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
@@ -96,9 +96,12 @@
 #define VMALLOC_START (0xfe300000)
 #define VMALLOC_END   ~0x0UL
 
-#define pte_ERROR(e)	__builtin_trap()
-#define pmd_ERROR(e)	__builtin_trap()
-#define pgd_ERROR(e)	__builtin_trap()
+#define pte_ERROR(e) \
+	printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
+#define pmd_ERROR(e) \
+	printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+#define pgd_ERROR(e) \
+	printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
 
 BTFIXUPDEF_INT(page_none)
 BTFIXUPDEF_INT(page_shared)
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-sparc/smplock.h linux/include/asm-sparc/smplock.h
--- v2.3.99-pre2/linux/include/asm-sparc/smplock.h	Fri Sep 10 23:57:37 1999
+++ linux/include/asm-sparc/smplock.h	Thu Mar 23 12:50:09 2000
@@ -8,6 +8,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h
--- v2.3.99-pre2/linux/include/asm-sparc64/page.h	Sun Mar 19 18:35:31 2000
+++ linux/include/asm-sparc64/page.h	Tue Mar 21 23:38:25 2000
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.32 2000/03/15 07:19:28 davem Exp $ */
+/* $Id: page.h,v 1.33 2000/03/22 02:48:04 davem Exp $ */
 
 #ifndef _SPARC64_PAGE_H
 #define _SPARC64_PAGE_H
@@ -96,8 +96,7 @@
 
 #ifndef __ASSEMBLY__
 /* Do prdele, look what happens to be in %g4... */
-register unsigned long page_offset asm("g4");
-#define PAGE_OFFSET		page_offset
+register unsigned long PAGE_OFFSET asm("g4");
 #else
 #define PAGE_OFFSET		0xFFFFF80000000000
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/asm-sparc64/smplock.h linux/include/asm-sparc64/smplock.h
--- v2.3.99-pre2/linux/include/asm-sparc64/smplock.h	Fri Sep 10 23:57:37 1999
+++ linux/include/asm-sparc64/smplock.h	Thu Mar 23 12:50:09 2000
@@ -9,6 +9,8 @@
 
 extern spinlock_t kernel_flag;
 
+#define kernel_locked()		spin_is_locked(&kernel_flag)
+
 /*
  * Release global kernel lock and global interrupt lock
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/atm_idt77105.h linux/include/linux/atm_idt77105.h
--- v2.3.99-pre2/linux/include/linux/atm_idt77105.h	Thu Feb 10 17:11:21 2000
+++ linux/include/linux/atm_idt77105.h	Tue Mar 21 23:38:26 2000
@@ -9,6 +9,7 @@
 
 #include <asm/types.h>
 #include <linux/atmioc.h>
+#include <linux/atmdev.h>
 
 /*
  * Structure for IDT77105_GETSTAT and IDT77105_GETSTATZ ioctls.
@@ -21,20 +22,7 @@
         __u32 rx_hec_errors;  /* Header Error Check errors on receive */
 };
 
-#define IDT77105_GETLOOP	_IOW('a',ATMIOC_PHYPRV,struct atmif_sioc)	/* get loopback mode */
-#define IDT77105_SETLOOP	_IOW('a',ATMIOC_PHYPRV+1,struct atmif_sioc)	/* set loopback mode */
 #define IDT77105_GETSTAT	_IOW('a',ATMIOC_PHYPRV+2,struct atmif_sioc)	/* get stats */
 #define IDT77105_GETSTATZ	_IOW('a',ATMIOC_PHYPRV+3,struct atmif_sioc)	/* get stats and zero */
-
-
-/*
- * TODO: what we need is a global loopback mode get/set ioctl for
- * all devices, not these device-specific hacks -- Greg Banks
- */
-#define IDT77105_LM_NONE	0	/* no loopback */
-#define IDT77105_LM_DIAG	1	/* diagnostic (i.e. loop TX to RX)
-					 * (a.k.a. local loopback) */
-#define IDT77105_LM_LOOP	2	/* line (i.e. loop RX to TX)
-					 * (a.k.a. remote loopback) */
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/atm_suni.h linux/include/linux/atm_suni.h
--- v2.3.99-pre2/linux/include/linux/atm_suni.h	Thu Aug 26 13:05:41 1999
+++ linux/include/linux/atm_suni.h	Tue Mar 21 23:38:26 2000
@@ -1,19 +1,12 @@
 /* atm_suni.h - Driver-specific declarations of the SUNI driver (for use by
 		driver-specific utilities) */
 
-/* Written 1998 by Werner Almesberger, EPFL ICA */
+/* Written 1998,2000 by Werner Almesberger, EPFL ICA */
 
 
 #ifndef LINUX_ATM_SUNI_H
 #define LINUX_ATM_SUNI_H
 
-#include <linux/atmioc.h>
-
-#define SUNI_GETLOOP	_IOR('a',ATMIOC_PHYPRV,int)	/* get loopback mode */
-#define SUNI_SETLOOP	_IO('a',ATMIOC_PHYPRV+1)	/* set loopback mode */
-
-#define SUNI_LM_NONE	0	/* no loopback */
-#define SUNI_LM_DIAG	1	/* diagnostic (i.e. loop TX to RX) */
-#define SUNI_LM_LOOP	2	/* line (i.e. loop RX to TX) */
+/* everything obsoleted */
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/atmdev.h linux/include/linux/atmdev.h
--- v2.3.99-pre2/linux/include/linux/atmdev.h	Thu Feb 10 17:11:21 2000
+++ linux/include/linux/atmdev.h	Thu Mar 23 15:41:40 2000
@@ -1,6 +1,6 @@
 /* atmdev.h - ATM device driver declarations and various related items */
  
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
  
 
 #ifndef LINUX_ATMDEV_H
@@ -20,6 +20,8 @@
 			   SONET overhead: /270*260 (9 section, 1 path)
 			   bits per cell:  /8/53
 			   max cell rate:  353207.547 cells/sec */
+#define ATM_25_PCR	((25600000/8-8000)/54)
+			/* 25 Mbps ATM cell rate (59111) */
 
 #define ATM_PDU_OVHD	0	/* number of bytes to charge against buffer
 				   quota per PDU */
@@ -27,10 +29,17 @@
 #define ATM_SD(s)	((s)->sk->protinfo.af_atm)
 
 
+#define __AAL_STAT_ITEMS \
+    __HANDLE_ITEM(tx);			/* TX okay */ \
+    __HANDLE_ITEM(tx_err);		/* TX errors */ \
+    __HANDLE_ITEM(rx);			/* RX okay */ \
+    __HANDLE_ITEM(rx_err);		/* RX errors */ \
+    __HANDLE_ITEM(rx_drop);		/* RX out of memory */
+
 struct atm_aal_stats {
-	int		tx,tx_err;	/* TX okay and errors */
-	int		rx,rx_err;	/* RX okay and errors */
-	int		rx_drop;	/* RX out of memory */
+#define __HANDLE_ITEM(i) int i
+	__AAL_STAT_ITEMS
+#undef __HANDLE_ITEM
 };
 
 
@@ -69,12 +78,53 @@
 					/* get AAL layer statistics */
 #define ATM_GETSTATZ	_IOW('a',ATMIOC_SARCOM+1,struct atmif_sioc)
 					/* get AAL layer statistics and zero */
+#define ATM_GETLOOP	_IOW('a',ATMIOC_SARCOM+2,struct atmif_sioc)
+					/* get loopback mode */
+#define ATM_SETLOOP	_IOW('a',ATMIOC_SARCOM+3,struct atmif_sioc)
+					/* set loopback mode */
+#define ATM_QUERYLOOP	_IOW('a',ATMIOC_SARCOM+4,struct atmif_sioc)
+					/* query supported loopback modes */
 #define ATM_SETSC	_IOW('a',ATMIOC_SPECIAL+1,int)
 					/* enable or disable single-copy */
 
 /* for ATM_GETTYPE */
 #define ATM_ITFTYP_LEN	8	/* maximum length of interface type name */
 
+/*
+ * Loopback modes for ATM_{PHY,SAR}_{GET,SET}LOOP
+ */
+
+/* Point of loopback				CPU-->SAR-->PHY-->line--> ... */
+#define __ATM_LM_NONE	0	/* no loop back     ^     ^     ^      ^      */
+#define __ATM_LM_AAL	1	/* loop back PDUs --'     |     |      |      */
+#define __ATM_LM_ATM	2	/* loop back ATM cells ---'     |      |      */
+/* RESERVED		4	loop back on PHY side  ---'		      */
+#define __ATM_LM_PHY	8	/* loop back bits (digital) ----'      |      */
+#define __ATM_LM_ANALOG 16	/* loop back the analog signal --------'      */
+
+/* Direction of loopback */
+#define __ATM_LM_MKLOC(n)	((n))	    /* Local (i.e. loop TX to RX) */
+#define __ATM_LM_MKRMT(n)	((n) << 8)  /* Remote (i.e. loop RX to TX) */
+
+#define __ATM_LM_XTLOC(n)	((n) & 0xff)
+#define __ATM_LM_XTRMT(n)	(((n) >> 8) & 0xff)
+
+#define ATM_LM_NONE	0	/* no loopback */
+
+#define ATM_LM_LOC_AAL	__ATM_LM_MKLOC(__ATM_LM_AAL)
+#define ATM_LM_LOC_ATM	__ATM_LM_MKLOC(__ATM_LM_ATM)
+#define ATM_LM_LOC_PHY	__ATM_LM_MKLOC(__ATM_LM_PHY)
+#define ATM_LM_LOC_ANALOG __ATM_LM_MKLOC(__ATM_LM_ANALOG)
+
+#define ATM_LM_RMT_AAL	__ATM_LM_MKRMT(__ATM_LM_AAL)
+#define ATM_LM_RMT_ATM	__ATM_LM_MKRMT(__ATM_LM_ATM)
+#define ATM_LM_RMT_PHY	__ATM_LM_MKRMT(__ATM_LM_PHY)
+#define ATM_LM_RMT_ANALOG __ATM_LM_MKRMT(__ATM_LM_ANALOG)
+
+/*
+ * Note: ATM_LM_LOC_* and ATM_LM_RMT_* can be combined, provided that
+ * __ATM_LM_XTLOC(x) <= __ATM_LM_XTRMT(x)
+ */
 
 
 struct atm_iobuf {
@@ -132,7 +182,9 @@
     "SESSION",	"HASSAP",	"BOUND",	"CLOSE"
 
 
-#ifdef __KERNEL__
+#ifndef __KERNEL__
+#undef __AAL_STAT_ITEMS
+#else
 
 #include <linux/sched.h> /* wait_queue_head_t */
 #include <linux/time.h> /* struct timeval */
@@ -147,39 +199,56 @@
 #endif
 
 
-#define ATM_VF_ADDR	1	/* Address is in use. Set by anybody, cleared
+struct k_atm_aal_stats {
+#define __HANDLE_ITEM(i) atomic_t i
+	__AAL_STAT_ITEMS
+#undef __HANDLE_ITEM
+};
+
+
+struct k_atm_dev_stats {
+	struct k_atm_aal_stats aal0;
+	struct k_atm_aal_stats aal34;
+	struct k_atm_aal_stats aal5;
+};
+
+
+enum {
+	ATM_VF_ADDR,		/* Address is in use. Set by anybody, cleared
 				   by device driver. */
-#define ATM_VF_READY	2	/* VC is ready to transfer data. Set by device
+	ATM_VF_READY,		/* VC is ready to transfer data. Set by device
 				   driver, cleared by anybody. */
-#define ATM_VF_PARTIAL	4	/* resources are bound to PVC (partial PVC
+	ATM_VF_PARTIAL,		/* resources are bound to PVC (partial PVC
 				   setup), controlled by socket layer */
-#define ATM_VF_BOUND	16384	/* local SAP is set, controlled by SVC socket
-				   layer */
-#define ATM_VF_REGIS	8	/* registered with demon, controlled by SVC
+	ATM_VF_REGIS,		/* registered with demon, controlled by SVC
 				   socket layer */
-#define ATM_VF_RELEASED 16	/* demon has indicated/requested release,
+	ATM_VF_BOUND,		/* local SAP is set, controlled by SVC socket
+				   layer */
+	ATM_VF_RELEASED,	/* demon has indicated/requested release,
 				   controlled by SVC socket layer */
-#define ATM_VF_HASQOS	32	/* QOS parameters have been set */
-#define ATM_VF_LISTEN	64	/* socket is used for listening */
-#define ATM_VF_META	128	/* SVC socket isn't used for normal data
+	ATM_VF_HASQOS,		/* QOS parameters have been set */
+	ATM_VF_LISTEN,		/* socket is used for listening */
+	ATM_VF_META,		/* SVC socket isn't used for normal data
 				   traffic and doesn't depend on signaling
 				   to be available */
-				/*  256; unused */
-				/*  512; unused */
-				/* 1024; unused */
-				/* 2048; unused */
-#define ATM_VF_SESSION	4096	/* VCC is p2mp session control descriptor */
-#define ATM_VF_HASSAP	8192	/* SAP has been set */
-#define ATM_VF_CLOSE	32768	/* asynchronous close - treat like VF_RELEASED*/
+	ATM_VF_SESSION,		/* VCC is p2mp session control descriptor */
+	ATM_VF_HASSAP,		/* SAP has been set */
+	ATM_VF_CLOSE,		/* asynchronous close - treat like VF_RELEASED*/
+};
+
 
 #define ATM_VF2VS(flags) \
-    ((flags) & ATM_VF_READY ? ATM_VS_CONNECTED : \
-     (flags) & ATM_VF_RELEASED ? ATM_VS_CLOSING : \
-     (flags) & ATM_VF_LISTEN ? ATM_VS_LISTEN : \
-     (flags) & ATM_VF_REGIS ? ATM_VS_INUSE : \
-     (flags) & ATM_VF_BOUND ? ATM_VS_BOUND : ATM_VS_IDLE)
+    (test_bit(ATM_VF_READY,&(flags)) ? ATM_VS_CONNECTED : \
+     test_bit(ATM_VF_RELEASED,&(flags)) ? ATM_VS_CLOSING : \
+     test_bit(ATM_VF_LISTEN,&(flags)) ? ATM_VS_LISTEN : \
+     test_bit(ATM_VF_REGIS,&(flags)) ? ATM_VS_INUSE : \
+     test_bit(ATM_VF_BOUND,&(flags)) ? ATM_VS_BOUND : ATM_VS_IDLE)
+
+
+enum {
+	ATM_DF_CLOSE,		/* close device when last VCC is closed */
+};
 
-#define ATM_DF_CLOSE	1	/* close device when last VCC is closed */
 
 #define ATM_PHY_SIG_LOST    0	/* no carrier/light */
 #define ATM_PHY_SIG_UNKNOWN 1	/* carrier/light status is unknown */
@@ -188,8 +257,11 @@
 #define ATM_ATMOPT_CLP	1	/* set CLP bit */
 
 
+typedef struct { unsigned short bits; } atm_vcc_flags_t;
+
+
 struct atm_vcc {
-	unsigned short	flags;		/* VCC flags (ATM_VF_*) */
+	atm_vcc_flags_t flags;		/* VCC flags (ATM_VF_*) */
 	unsigned char	family;		/* address family; 0 if unused */
 	short		vpi;		/* VPI and VCI (types must be equal */
 					/* with sockaddr) */
@@ -207,11 +279,12 @@
 					/* modified by protocol or by driver.*/
 					/* NOTE: this interface will change */
 	int (*push_oam)(struct atm_vcc *vcc,void *cell);
+	int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
 	void		*dev_data;	/* per-device data */
 	void		*proto_data;	/* per-protocol data */
 	struct timeval	timestamp;	/* AAL timestamps */
 	struct sk_buff_head recvq;	/* receive queue */
-	struct atm_aal_stats *stats;	/* pointer to AAL stats group */
+	struct k_atm_aal_stats *stats;	/* pointer to AAL stats group */
 	wait_queue_head_t sleep;	/* if socket is busy */
 	wait_queue_head_t wsleep;	/* if waiting for write buffer space */
 	struct sock	*sk;		/* socket backpointer */
@@ -240,6 +313,9 @@
 };
 
 
+typedef struct { unsigned int bits; } atm_dev_flags_t;
+
+
 struct atm_dev {
 	const struct atmdev_ops *ops;	/* device operations; NULL if unused */
 	const struct atmphy_ops *phy;	/* PHY operations, may be undefined */
@@ -250,11 +326,11 @@
 	struct atm_vcc	*last;		/* last VCC (or undefined) */
 	void		*dev_data;	/* per-device data */
 	void		*phy_data;	/* private PHY date */
-	unsigned long	flags;		/* device flags (ATM_DF_*) */
+	atm_dev_flags_t flags;		/* device flags (ATM_DF_*) */
 	struct atm_dev_addr *local;	/* local ATM addresses */
 	unsigned char	esi[ESI_LEN];	/* ESI ("MAC" addr) */
 	struct atm_cirange ci_range;	/* VPI/VCI range */
-	struct atm_dev_stats stats;	/* statistics */
+	struct k_atm_dev_stats stats;	/* statistics */
 	char		signal;		/* signal status (ATM_PHY_SIG_*) */
 	int		link_rate;	/* link rate (default: OC3) */
 #ifdef CONFIG_PROC_FS
@@ -307,6 +383,7 @@
 	int (*start)(struct atm_dev *dev);
 	int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void *arg);
 	void (*interrupt)(struct atm_dev *dev);
+	int (*stop)(struct atm_dev *dev);
 };
 
 struct atm_skb_data {
@@ -318,7 +395,7 @@
 #define ATM_SKB(skb) (((struct atm_skb_data *) (skb)->cb))
 
 struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
-    int number,unsigned long flags); /* number == -1: pick first available */
+    int number,atm_dev_flags_t *flags); /* number == -1: pick first available */
 struct atm_dev *atm_find_dev(int number);
 void atm_dev_deregister(struct atm_dev *dev);
 void shutdown_atm_dev(struct atm_dev *dev);
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/awe_voice.h linux/include/linux/awe_voice.h
--- v2.3.99-pre2/linux/include/linux/awe_voice.h	Sun Mar  7 15:25:23 1999
+++ linux/include/linux/awe_voice.h	Tue Mar 21 11:10:45 2000
@@ -3,9 +3,9 @@
  *
  * Voice information definitions for the low level driver for the 
  * AWE32/SB32/AWE64 wave table synth.
- *   version 0.4.3; Feb. 1, 1999
+ *   version 0.4.4; Jan. 4, 2000
  *
- * Copyright (C) 1996-1999 Takashi Iwai
+ * Copyright (C) 1996-2000 Takashi Iwai
  *
  * 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
@@ -57,6 +57,7 @@
 #define AWE_MAP_PRESET		6	/* awe_voice_map */
 /*#define AWE_PROBE_INFO	7*/	/* awe_voice_map (pat only) */
 #define AWE_PROBE_DATA		8	/* optarg=sample */
+#define AWE_REMOVE_INFO		9	/* optarg=(bank<<8)|instr */
 #define AWE_LOAD_CHORUS_FX	0x10	/* awe_chorus_fx_rec (optarg=mode) */
 #define AWE_LOAD_REVERB_FX	0x11	/* awe_reverb_fx_rec (optarg=mode) */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/binfmts.h linux/include/linux/binfmts.h
--- v2.3.99-pre2/linux/include/linux/binfmts.h	Sun Mar 19 18:35:31 2000
+++ linux/include/linux/binfmts.h	Thu Mar 23 15:36:06 2000
@@ -21,7 +21,7 @@
 	struct page *page[MAX_ARG_PAGES];
 	unsigned long p; /* current top of mem */
 	int sh_bang;
-	struct dentry * dentry;
+	struct file * file;
 	int e_uid, e_gid;
 	kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
 	int argc, envc;
@@ -44,11 +44,6 @@
 
 extern int register_binfmt(struct linux_binfmt *);
 extern int unregister_binfmt(struct linux_binfmt *);
-
-extern int read_exec(struct dentry *, unsigned long offset,
-	char * addr, unsigned long count, int to_kmem);
-
-extern int open_dentry(struct dentry *, int mode);
 
 extern int prepare_binprm(struct linux_binprm *);
 extern void remove_arg_zero(struct linux_binprm *);
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/capi.h linux/include/linux/capi.h
--- v2.3.99-pre2/linux/include/linux/capi.h	Mon Nov  3 10:09:21 1997
+++ linux/include/linux/capi.h	Thu Mar 23 08:38:57 2000
@@ -1,11 +1,28 @@
 /*
- * $Id: capi.h,v 1.1 1997/03/04 21:27:33 calle Exp $
+ * $Id: capi.h,v 1.3 2000/03/08 17:06:34 calle Exp $
  * 
  * CAPI 2.0 Interface for Linux
  * 
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: capi.h,v $
+ * Revision 1.3  2000/03/08 17:06:34  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.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:27:33  calle
  * First version in isdn4linux
  *
@@ -108,6 +125,7 @@
  */
 #define CAPI_INSTALLED		_IOR('C',0x22, __u16)
 
+
 /*
  * member contr is input for
  * CAPI_GET_MANUFACTURER, CAPI_VERSION, CAPI_GET_SERIAL
@@ -123,5 +141,19 @@
 	capi_manufacturer_cmd cmd;
 	__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 --recursive --new-file v2.3.99-pre2/linux/include/linux/circ_buf.h linux/include/linux/circ_buf.h
--- v2.3.99-pre2/linux/include/linux/circ_buf.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/circ_buf.h	Tue Mar 21 13:04:53 2000
@@ -0,0 +1,32 @@
+#ifndef _LINUX_CIRC_BUF_H
+#define _LINUX_CIRC_BUF_H 1
+
+struct circ_buf {
+	char *buf;
+	int head;
+	int tail;
+};
+
+/* Return count in buffer.  */
+#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))
+
+/* Return space available, 0..size-1.  We always leave one free char
+   as a completely full buffer has head == tail, which is the same as
+   empty.  */
+#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
+
+/* Return count up to the end of the buffer.  Carefully avoid
+   accessing head and tail more than once, so they can change
+   underneath us without returning inconsistent results.  */
+#define CIRC_CNT_TO_END(head,tail,size) \
+	({int end = (size) - (tail); \
+	  int n = ((head) + end) & ((size)-1); \
+	  n < end ? n : end;})
+
+/* Return space available up to the end of the buffer.  */
+#define CIRC_SPACE_TO_END(head,tail,size) \
+	({int end = (size) - 1 - (head); \
+	  int n = (end + (tail)) & ((size)-1); \
+	  n <= end ? n : end+1;})
+
+#endif /* _LINUX_CIRC_BUF_H  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.3.99-pre2/linux/include/linux/dcache.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/dcache.h	Mon Mar 20 08:15:21 2000
@@ -157,6 +157,9 @@
 /* test whether root is busy without destroying dcache */
 extern int is_root_busy(struct dentry *);
 
+/* test whether we have any submounts in a subdir tree */
+extern int have_submounts(struct dentry *);
+
 /*
  * This adds the entry to the hash queues.
  */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v2.3.99-pre2/linux/include/linux/ext2_fs.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/ext2_fs.h	Thu Mar 23 15:36:06 2000
@@ -306,10 +306,7 @@
 /*
  * Mount flags
  */
-#define EXT2_MOUNT_CHECK_NORMAL		0x0001	/* Do some more checks */
-#define EXT2_MOUNT_CHECK_STRICT		0x0002	/* Do again more checks */
-#define EXT2_MOUNT_CHECK		(EXT2_MOUNT_CHECK_NORMAL | \
-					 EXT2_MOUNT_CHECK_STRICT)
+#define EXT2_MOUNT_CHECK		0x0001	/* Do mount-time checks */
 #define EXT2_MOUNT_GRPID		0x0004	/* Create files with directory's group */
 #define EXT2_MOUNT_DEBUG		0x0008	/* Some debugging messages */
 #define EXT2_MOUNT_ERRORS_CONT		0x0010	/* Continue on errors */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/fb.h linux/include/linux/fb.h
--- v2.3.99-pre2/linux/include/linux/fb.h	Tue Mar 14 19:10:40 2000
+++ linux/include/linux/fb.h	Thu Mar 23 10:07:42 2000
@@ -435,12 +435,41 @@
     u32 vmode;
 };
 
+#ifdef MODULE
+static inline int fb_find_mode(struct fb_var_screeninfo *var,
+			       struct fb_info *info, const char *mode_option,
+			       const struct fb_videomode *db,
+			       unsigned int dbsize,
+			       const struct fb_videomode *default_mode,
+			       unsigned int default_bpp)
+{
+    extern int __fb_try_mode(struct fb_var_screeninfo *var,
+	    		     struct fb_info *info,
+			     const struct fb_videomode *mode,
+			     unsigned int bpp);
+    /*
+     *  FIXME: How to make the compiler optimize vga640x400 away if
+     *         default_mode is non-NULL?
+     */
+    static const struct fb_videomode vga640x400 = {
+	/* 640x400 @ 70 Hz, 31.5 kHz hsync */
+	NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
+	0, FB_VMODE_NONINTERLACED
+    };
+    if (!default_mode)
+	default_mode = &vga640x400;
+    if (!default_bpp)
+	default_bpp = 8;
+    return __fb_try_mode(var, info, default_mode, default_bpp);
+}
+#else
 extern int __init fb_find_mode(struct fb_var_screeninfo *var,
 			       struct fb_info *info, const char *mode_option,
 			       const struct fb_videomode *db,
 			       unsigned int dbsize,
 			       const struct fb_videomode *default_mode,
 			       unsigned int default_bpp);
+#endif
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.3.99-pre2/linux/include/linux/fs.h	Sun Mar 19 18:35:31 2000
+++ linux/include/linux/fs.h	Thu Mar 23 15:36:06 2000
@@ -339,7 +339,7 @@
 struct address_space_operations {
 	int (*writepage) (struct dentry *, struct page *);
 	int (*readpage)(struct dentry *, struct page *);
-	int (*prepare_write)(struct page *, unsigned, unsigned);
+	int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
 	int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
 	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
 	int (*bmap)(struct address_space *, long);
@@ -751,8 +751,7 @@
 		return -ENODEV;
 	if (!sb->s_op || !sb->s_op->statfs)
 		return -ENOSYS;
-	memset(buf, 0xff, sizeof(struct statfs));
-	buf->f_blocks = 0;	/* Darn GNU df... */
+	memset(buf, 0, sizeof(struct statfs));
 	return sb->s_op->statfs(sb, buf);
 }
 
@@ -814,6 +813,7 @@
 extern void put_unused_fd(unsigned int);
 
 extern struct file *filp_open(const char *, int, int, struct dentry *);
+extern struct file *dentry_open(struct dentry *, int);
 extern int filp_close(struct file *, fl_owner_t id);
 
 extern char * getname(const char *);
@@ -938,6 +938,9 @@
 	return __open_namei(pathname, 0, 0, NULL);
 }
 
+extern int kernel_read(struct file *, unsigned long, char *, unsigned long);
+extern struct file * open_exec(const char *);
+ 
 /* fs/dcache.c -- generic fs support functions */
 extern int is_subdir(struct dentry *, struct dentry *);
 extern ino_t find_inode_number(struct dentry *, struct qstr *);
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.3.99-pre2/linux/include/linux/hdreg.h	Sun Mar 19 18:35:31 2000
+++ linux/include/linux/hdreg.h	Thu Mar 23 15:36:08 2000
@@ -193,8 +193,20 @@
 #define HDIO_SET_NICE		0x0329	/* set nice flags */
 #define HDIO_UNREGISTER_HWIF	0x032a  /* unregister interface */
 
-#define __NEW_HD_DRIVE_ID
+/* BIG GEOMETRY */
+struct hd_big_geometry {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned int cylinders;
+	unsigned long start;
+};
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x033n/0x033n */
+#define HDIO_GETGEO_BIG		0x0330	/* */
+#define HDIO_GETGEO_BIG_RAW	0x0331	/* */
 
+
+#define __NEW_HD_DRIVE_ID
 /* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
 struct hd_driveid {
 	unsigned short	config;		/* lots of obsolete bit flags */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/ide.h linux/include/linux/ide.h
--- v2.3.99-pre2/linux/include/linux/ide.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/ide.h	Thu Mar 23 15:36:23 2000
@@ -326,6 +326,7 @@
 		ide_dma_end,	ide_dma_check,		ide_dma_on,
 		ide_dma_off,	ide_dma_off_quietly,	ide_dma_test_irq,
 		ide_dma_bad_drive,			ide_dma_good_drive,
+		ide_dma_verbose,			ide_dma_retune,
 		ide_dma_lostirq,			ide_dma_timeout
 } ide_dma_action_t;
 
@@ -857,6 +858,7 @@
 void ide_destroy_dmatable (ide_drive_t *drive);
 ide_startstop_t ide_dma_intr (ide_drive_t *drive);
 int check_drive_lists (ide_drive_t *drive, int good_bad);
+int report_drive_dmaing (ide_drive_t *drive);
 int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
 int ide_release_dma (ide_hwif_t *hwif);
 void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init;
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/input.h linux/include/linux/input.h
--- v2.3.99-pre2/linux/include/linux/input.h	Thu Mar  2 14:36:23 2000
+++ linux/include/linux/input.h	Sun Mar 19 18:29:40 2000
@@ -302,9 +302,10 @@
 #define BTN_TOOL_AIRBRUSH	0x144
 #define BTN_TOOL_FINGER		0x145
 #define BTN_TOOL_MOUSE		0x146
-#define BTN_TOUCH		0x147
-#define BTN_STYLUS		0x148
-#define BTN_STYLUS2		0x149
+#define BTN_TOOL_LENS		0x147
+#define BTN_TOUCH		0x14a
+#define BTN_STYLUS		0x14b
+#define BTN_STYLUS2		0x14c
 
 #define KEY_MAX			0x1ff
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/isapnp.h linux/include/linux/isapnp.h
--- v2.3.99-pre2/linux/include/linux/isapnp.h	Fri Jan 28 15:09:09 2000
+++ linux/include/linux/isapnp.h	Thu Mar 23 15:39:32 2000
@@ -128,6 +128,24 @@
 	struct isapnp_resources *next;	/* next resource */
 };
 
+#define ISAPNP_ANY_ID		0xffff
+#define ISAPNP_CARD_DEVS	8
+
+#define ISAPNP_CARD_ID(_va, _vb, _vc, _device) \
+		vendor: ISAPNP_VENDOR(_va, _vb, _vc), device: ISAPNP_DEVICE(_device)
+#define ISAPNP_CARD_END \
+		vendor: 0, device: 0
+#define ISAPNP_DEVICE_ID(_va, _vb, _vc, _function) \
+		{ vendor: ISAPNP_VENDOR(_va, _vb, _vc), function: ISAPNP_FUNCTION(_function) }
+
+struct isapnp_card_id {
+	unsigned short vendor, device;
+	struct {
+		unsigned short vendor, function;
+	} devs[ISAPNP_CARD_DEVS];	/* logical devices */
+	unsigned long driver_data;	/* data private to the driver */
+};
+
 #if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE))
 
 #define __ISAPNP__
@@ -158,6 +176,9 @@
 				unsigned short vendor,
 				unsigned short function,
 				struct pci_dev *from);
+int isapnp_probe_cards(const struct isapnp_card_id *ids,
+		       int (*probe)(struct pci_bus *card,
+				    const struct isapnp_card_id *id));
 /* misc */
 void isapnp_resource_change(struct resource *resource,
 			    unsigned long start,
@@ -196,7 +217,10 @@
 extern inline struct pci_dev *isapnp_find_dev(struct pci_bus *card,
 					      unsigned short vendor,
 					      unsigned short function,
-				       struct pci_dev *from) { return NULL; }
+					      struct pci_dev *from) { return NULL; }
+extern inline int isapnp_probe_cards(const struct isapnp_card_id *ids,
+				     int (*probe)(struct pci_bus *card,
+						  const struct isapnp_card_id *id)) { return -ENODEV; }
 extern inline void isapnp_resource_change(struct resource *resource,
 					  unsigned long start,
 					  unsigned long size) { ; }
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.3.99-pre2/linux/include/linux/isdn.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/isdn.h	Thu Mar 23 08:38:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.94 2000/02/26 00:29:40 keil Exp $
+/* $Id: isdn.h,v 1.101 2000/03/20 22:37:47 detabc Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,35 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.101  2000/03/20 22:37:47  detabc
+ * modify abc-extension to work together with the new LL.
+ * remove abc frame-counter (is obsolete now).
+ * use the new lp->super_tx_queue for internal queueing (bsd-rawip-compress).
+ * modify isdn_net_xmit() and isdn_net_write_super().
+ * -- Kai, please have a look to this two function's. Thank's.
+ *
+ * Revision 1.100  2000/03/19 15:27:53  kai
+ * no known bugs left...
+ *
+ * Revision 1.99  2000/03/18 16:20:26  kai
+ * cosmetics / renaming
+ *
+ * Revision 1.98  2000/03/17 18:20:47  kai
+ * moved to frame_cnt based flow control
+ * some races still need to be fixed
+ *
+ * Revision 1.97  2000/03/17 16:22:55  kai
+ * we keep track of outstanding packets (given to HL, but not confirmed yet)
+ * now, but we don't use it for flow control yet.
+ *
+ * Revision 1.96  2000/03/17 12:49:42  kai
+ * calling statcallb with ISDN_STAT_BSENT in hard-IRQ context is now
+ * officially allowed. writebuf_skb() will never be called in hard-IRQ context
+ * anymore.
+ *
+ * Revision 1.95  2000/03/04 16:20:42  detabc
+ * copy frames before rewriting frame's saddr
+ *
  * Revision 1.94  2000/02/26 00:29:40  keil
  * more softnet changes
  *
@@ -395,7 +424,6 @@
 #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 
 
@@ -701,7 +729,6 @@
   ulong                  sqfull_stamp; /* Start-Time of overload           */
   ulong                  slavedelay;   /* Dynamic bundling delaytime       */
   int                    triggercps;   /* BogoCPS needed for trigger slave */
-  struct net_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      */
@@ -711,9 +738,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);
@@ -733,13 +766,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 net_device dev;               /* interface to upper levels        */
 #ifdef CONFIG_ISDN_PPP
   struct mpqueue *mp_last; 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v2.3.99-pre2/linux/include/linux/isdn_ppp.h	Thu Nov 11 20:11:53 1999
+++ linux/include/linux/isdn_ppp.h	Thu Mar 23 08:38:57 2000
@@ -29,9 +29,6 @@
 #define PPPIOCSCOMPRESSOR _IOW('t',135,int)
 #define PPPIOCGIFNAME      _IOR('t',136, char [IFNAMSIZ] )
 
-#define PPP_MP          0x003d
-#define PPP_LINK_COMP   0x00fb
-#define PPP_LINK_CCP    0x80fb
 
 #define SC_MP_PROT       0x00000200
 #define SC_REJ_MP_PROT   0x00000400
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/kernelcapi.h linux/include/linux/kernelcapi.h
--- v2.3.99-pre2/linux/include/linux/kernelcapi.h	Wed Feb 16 17:03:52 2000
+++ linux/include/linux/kernelcapi.h	Thu Mar 23 08:38:57 2000
@@ -1,11 +1,22 @@
 /*
- * $Id: kernelcapi.h,v 1.5 2000/01/28 16:45:40 calle Exp $
+ * $Id: kernelcapi.h,v 1.6 2000/03/03 15:50:42 calle 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.6  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.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.
@@ -94,8 +105,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 +119,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 +165,7 @@
 #define CAPI_MSGNOTINSTALLED 	          0x1109
 #define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP  0x110a
 #define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b
+
 
 #endif				/* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/linkage.h linux/include/linux/linkage.h
--- v2.3.99-pre2/linux/include/linux/linkage.h	Tue Mar  7 14:32:26 2000
+++ linux/include/linux/linkage.h	Wed Mar 22 22:23:55 2000
@@ -1,6 +1,8 @@
 #ifndef _LINUX_LINKAGE_H
 #define _LINUX_LINKAGE_H
 
+#include <linux/config.h>
+
 #ifdef __cplusplus
 #define CPP_ASMLINKAGE extern "C"
 #else
@@ -35,13 +37,13 @@
 #define __ALIGN .balign 4
 #define __ALIGN_STR ".balign 4"
 #else
-#if !defined(__i486__) && !defined(__i586__)
-#define __ALIGN .align 4,0x90
-#define __ALIGN_STR ".align 4,0x90"
-#else  /* __i486__/__i586__ */
+#if defined(__i386__) && defined(CONFIG_X86_ALIGNMENT_16)
 #define __ALIGN .align 16,0x90
 #define __ALIGN_STR ".align 16,0x90"
-#endif /* __i486__/__i586__ */
+#else
+#define __ALIGN .align 4,0x90
+#define __ALIGN_STR ".align 4,0x90"
+#endif
 #endif /* __sh__ */
 #endif /* __mc68000__ */
 #endif /* __arm__ */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.3.99-pre2/linux/include/linux/netdevice.h	Thu Mar  2 14:36:23 2000
+++ linux/include/linux/netdevice.h	Thu Mar 23 15:36:18 2000
@@ -42,6 +42,9 @@
 #define NET_XMIT_DROP		1	/* skb dropped			*/
 #define NET_XMIT_CN		2	/* congestion notification	*/
 #define NET_XMIT_POLICED	3	/* skb is shot by police	*/
+#define NET_XMIT_BYPASS		4	/* packet does not leave via dequeue;
+					   (TC use only - dev_queue_xmit
+					   returns this as NET_XMIT_SUCCESS) */
 
 #define net_xmit_errno(e)	((e) != NET_XMIT_CN ? -ENOBUFS : 0)
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfs.h linux/include/linux/nfs.h
--- v2.3.99-pre2/linux/include/linux/nfs.h	Wed Dec  8 14:11:28 1999
+++ linux/include/linux/nfs.h	Tue Mar 21 11:30:08 2000
@@ -160,11 +160,35 @@
 	__u32			bavail;
 };
 
+/* Arguments to the write call.
+ * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+ */
+#define NFS_WRITE_MAXIOV        8
+
+enum nfs3_stable_how {
+	NFS_UNSTABLE = 0,
+	NFS_DATA_SYNC = 1,
+	NFS_FILE_SYNC = 2
+};
+
 struct nfs_writeargs {
 	struct nfs_fh *		fh;
 	__u32			offset;
 	__u32			count;
-	const void *		buffer;
+	enum nfs3_stable_how	stable;
+	unsigned int		nriov;
+	struct iovec		iov[NFS_WRITE_MAXIOV];
+};
+
+struct nfs_writeverf {
+	enum nfs3_stable_how	committed;
+	__u32			verifier[2];
+};
+
+struct nfs_writeres {
+	struct nfs_fattr *	fattr;
+	struct nfs_writeverf *	verf;
+	__u32			count;
 };
 
 #ifdef NFS_NEED_XDR_TYPES
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfs_flushd.h linux/include/linux/nfs_flushd.h
--- v2.3.99-pre2/linux/include/linux/nfs_flushd.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfs_flushd.h	Thu Mar 23 15:36:44 2000
@@ -0,0 +1,64 @@
+#ifndef NFS_CLUSTER_H
+#define NFS_CLUSTER_H
+
+
+
+#ifdef __KERNEL__
+#include <linux/nfs_fs_sb.h>
+
+/*
+ * Counters of total number and pending number of requests.
+ * When the total number of requests exceeds the soft limit, we start
+ * flushing out requests. If it exceeds the hard limit, we stall until
+ * it drops again.
+ */
+#define MAX_REQUEST_SOFT        192
+#define MAX_REQUEST_HARD        256
+
+/*
+ * Maximum number of requests per write cluster.
+ * 32 requests per cluster account for 128K of data on an intel box.
+ * Note: it's a good idea to make this number smaller than MAX_REQUEST_SOFT.
+ *
+ * For 100Mbps Ethernet, 128 pages (i.e. 256K) per cluster gives much
+ * better performance.
+ */
+#define REQUEST_HASH_SIZE	16
+#define REQUEST_NR(off)		((off) >> PAGE_CACHE_SHIFT)
+#define REQUEST_HASH(ino, off)	(((ino) ^ REQUEST_NR(off)) & (REQUEST_HASH_SIZE - 1))
+
+
+/*
+ * Functions
+ */
+extern int		nfs_reqlist_alloc(struct nfs_server *);
+extern void		nfs_reqlist_free(struct nfs_server *);
+extern int		nfs_reqlist_init(struct nfs_server *);
+extern void		nfs_reqlist_exit(struct nfs_server *);
+extern void		inode_schedule_scan(struct inode *, unsigned long);
+extern void		inode_remove_flushd(struct inode *);
+extern void		nfs_wake_flushd(void);
+
+/*
+ * This is the per-mount writeback cache.
+ */
+struct nfs_reqlist {
+	unsigned int		nr_requests;
+	unsigned long		runat;
+	wait_queue_head_t	request_wait;
+
+	/* The async RPC task that is responsible for scanning the
+	 * requests.
+	 */
+	struct rpc_task		*task;		/* request flush task */
+
+	/* Authentication flavor handle for this NFS client */
+	struct rpc_auth		*auth;
+
+	/* The list of all inodes with pending writebacks.  */
+	struct inode		*inodes;
+};
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.3.99-pre2/linux/include/linux/nfs_fs.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/nfs_fs.h	Thu Mar 23 15:37:00 2000
@@ -9,8 +9,10 @@
 #ifndef _LINUX_NFS_FS_H
 #define _LINUX_NFS_FS_H
 
+#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
+#include <linux/pagemap.h>
 #include <linux/in.h>
 
 #include <linux/sunrpc/sched.h>
@@ -34,13 +36,16 @@
  */
 #define NFS_MAX_DIRCACHE		16
 
-#define NFS_MAX_FILE_IO_BUFFER_SIZE	16384
+#define NFS_MAX_FILE_IO_BUFFER_SIZE	32768
 #define NFS_DEF_FILE_IO_BUFFER_SIZE	4096
 
 /*
  * The upper limit on timeouts for the exponential backoff algorithm.
  */
 #define NFS_MAX_RPC_TIMEOUT		(6*HZ)
+#define NFS_WRITEBACK_DELAY		(5*HZ)
+#define NFS_WRITEBACK_LOCKDELAY		(60*HZ)
+#define NFS_COMMIT_DELAY		(5*HZ)
 
 /*
  * Size of the lookup cache in units of number of entries cached.
@@ -58,11 +63,13 @@
 #define NFS_DSERVER(dentry)		(&(dentry)->d_sb->u.nfs_sb.s_server)
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
 #define NFS_CLIENT(inode)		(NFS_SERVER(inode)->client)
+#define NFS_REQUESTLIST(inode)		(NFS_SERVER(inode)->rw_requests)
 #define NFS_ADDR(inode)			(RPC_PEERADDR(NFS_CLIENT(inode)))
 #define NFS_CONGESTED(inode)		(RPC_CONGESTED(NFS_CLIENT(inode)))
 
 #define NFS_READTIME(inode)		((inode)->u.nfs_i.read_cache_jiffies)
 #define NFS_OLDMTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_NEXTSCAN(inode)		((inode)->u.nfs_i.nextscan)
 #define NFS_CACHEINV(inode) \
 do { \
 	NFS_READTIME(inode) = jiffies - 1000000; \
@@ -78,7 +85,6 @@
 
 #define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
 #define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
-#define NFS_WRITEBACK(inode)		((inode)->u.nfs_i.writeback)
 #define NFS_COOKIES(inode)		((inode)->u.nfs_i.cookies)
 #define NFS_DIREOF(inode)		((inode)->u.nfs_i.direof)
 
@@ -93,46 +99,31 @@
 /* Flags in the RPC client structure */
 #define NFS_CLNTF_BUFSIZE	0x0001	/* readdir buffer in longwords */
 
-#ifdef __KERNEL__
+#define NFS_RW_SYNC		0x0001	/* O_SYNC handling */
+#define NFS_RW_SWAP		0x0002	/* This is a swap request */
 
 /*
- * This struct describes a file region to be written.
- * It's kind of a pity we have to keep all these lists ourselves, rather
- * than sticking an extra pointer into struct page.
- */
-struct nfs_wreq {
-	struct rpc_listitem	wb_list;	/* linked list of req's */
-	struct rpc_task		wb_task;	/* RPC task */
-	struct file *		wb_file;	/* dentry referenced */
-	struct page *		wb_page;	/* page to be written */
-	wait_queue_head_t	wb_wait;	/* wait for completion */
-	unsigned int		wb_offset;	/* offset within page */
-	unsigned int		wb_bytes;	/* dirty range */
-	unsigned int		wb_count;	/* user count */
-	int			wb_status;
-	pid_t			wb_pid;		/* owner process */
-	unsigned short		wb_flags;	/* status flags */
-
-	struct nfs_writeargs	wb_args;	/* NFS RPC stuff */
-	struct nfs_fattr	wb_fattr;	/* file attributes */
-};
-
-#define WB_NEXT(req)		((struct nfs_wreq *) ((req)->wb_list.next))
-
-/*
- * Various flags for wb_flags
- */
-#define NFS_WRITE_CANCELLED	0x0004	/* has been cancelled */
-#define NFS_WRITE_UNCOMMITTED	0x0008	/* written but uncommitted (NFSv3) */
-#define NFS_WRITE_INVALIDATE	0x0010	/* invalidate after write */
-#define NFS_WRITE_INPROGRESS	0x0100	/* RPC call in progress */
-#define NFS_WRITE_COMPLETE	0x0200	/* RPC call completed */
-
-#define WB_CANCELLED(req)	((req)->wb_flags & NFS_WRITE_CANCELLED)
-#define WB_UNCOMMITTED(req)	((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
-#define WB_INVALIDATE(req)	((req)->wb_flags & NFS_WRITE_INVALIDATE)
-#define WB_INPROGRESS(req)	((req)->wb_flags & NFS_WRITE_INPROGRESS)
-#define WB_COMPLETE(req)	((req)->wb_flags & NFS_WRITE_COMPLETE)
+ * When flushing a cluster of dirty pages, there can be different
+ * strategies:
+ */
+#define FLUSH_AGING		0	/* only flush old buffers */
+#define FLUSH_SYNC		1	/* file being synced, or contention */
+#define FLUSH_WAIT		2	/* wait for completion */
+#define FLUSH_STABLE		4	/* commit to stable storage */
+
+static inline
+loff_t page_offset(struct page *page)
+{
+	return ((loff_t)page->index) << PAGE_CACHE_SHIFT;
+}
+
+static inline
+unsigned long page_index(struct page *page)
+{
+	return page->index;
+}
+
+#ifdef __KERNEL__
 
 /*
  * linux/fs/nfs/proc.c
@@ -218,21 +209,54 @@
  */
 extern int  nfs_writepage(struct dentry *, struct page *);
 extern int  nfs_check_failed_request(struct inode *);
-
+extern struct nfs_page* nfs_find_request(struct inode *, struct page *);
+extern void nfs_release_request(struct nfs_page *req);
+extern int  nfs_flush_incompatible(struct file *file, struct page *page);
+extern int  nfs_updatepage(struct file *, struct page *, unsigned long, unsigned int);
 /*
  * Try to write back everything synchronously (but check the
  * return value!)
  */
-extern int  nfs_wb_all(struct inode *);
-extern int  nfs_wb_page(struct inode *, struct page *);
-extern int  nfs_wb_file(struct inode *, struct file *);
+extern int  nfs_sync_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_flush_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_flush_timeout(struct inode *, int);
+#ifdef CONFIG_NFS_V3
+extern int  nfs_commit_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_commit_timeout(struct inode *, int);
+#endif
+
+static inline int
+nfs_have_writebacks(struct inode *inode)
+{
+	return !list_empty(&inode->u.nfs_i.writeback);
+}
+
+static inline int
+nfs_wb_all(struct inode *inode)
+{
+	int error = nfs_sync_file(inode, 0, 0, 0, FLUSH_WAIT);
+	return (error < 0) ? error : 0;
+}
+
+/*
+ * Write back all requests on one page - we do this before reading it.
+ */
+static inline int
+nfs_wb_page(struct inode *inode, struct page* page)
+{
+	int error = nfs_sync_file(inode, 0, page_offset(page), PAGE_CACHE_SIZE, FLUSH_WAIT | FLUSH_STABLE);
+	return (error < 0) ? error : 0;
+}
 
 /*
- * Invalidate write-backs, possibly trying to write them
- * back first..
+ * Write back all pending writes for one user.. 
  */
-extern void nfs_inval(struct inode *);
-extern int  nfs_updatepage(struct file *, struct page *, unsigned long, unsigned int);
+static inline int
+nfs_wb_file(struct inode *inode, struct file *file)
+{
+	int error = nfs_sync_file(inode, file, 0, 0, FLUSH_WAIT);
+	return (error < 0) ? error : 0;
+}
 
 /*
  * linux/fs/nfs/read.c
@@ -260,6 +284,19 @@
 /* NFS root */
 
 extern int nfs_root_mount(struct super_block *sb);
+
+#define nfs_wait_event(clnt, wq, condition)				\
+({									\
+	int __retval = 0;						\
+	if (clnt->cl_intr) {						\
+		sigset_t oldmask;					\
+		rpc_clnt_sigmask(clnt, &oldmask);			\
+		__retval = wait_event_interruptible(wq, condition);	\
+		rpc_clnt_sigunmask(clnt, &oldmask);			\
+	} else								\
+		wait_event(wq, condition);				\
+	__retval;							\
+})
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- v2.3.99-pre2/linux/include/linux/nfs_fs_i.h	Fri Oct 22 13:21:55 1999
+++ linux/include/linux/nfs_fs_i.h	Tue Mar 21 12:53:17 2000
@@ -42,10 +42,19 @@
 
 	/*
 	 * This is the list of dirty unwritten pages.
-	 * NFSv3 will want to add a list for written but uncommitted
-	 * pages.
 	 */
-	struct nfs_wreq *	writeback;
+	struct list_head	dirty;
+	struct list_head	commit;
+	struct list_head	writeback;
+
+	unsigned int		ndirty,
+				ncommit,
+				npages;
+
+	/* Flush daemon info */
+	struct inode		*hash_next,
+				*hash_prev;
+	unsigned long		nextscan;
 
 	/* Readdir caching information. */
 	void *cookies;
@@ -55,8 +64,9 @@
 /*
  * Legal inode flag values
  */
-#define NFS_INO_REVALIDATING	0x0001		/* revalidating attrs */
+#define NFS_INO_REVALIDATING	0x0004		/* revalidating attrs */
 #define NFS_IS_SNAPSHOT		0x0010		/* a snapshot file */
+#define NFS_INO_FLUSH		0x0020		/* inode is due for flushing */
 
 /*
  * NFS lock info
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfs_fs_sb.h linux/include/linux/nfs_fs_sb.h
--- v2.3.99-pre2/linux/include/linux/nfs_fs_sb.h	Wed Aug 18 16:43:29 1999
+++ linux/include/linux/nfs_fs_sb.h	Thu Mar 23 15:36:06 2000
@@ -18,6 +18,7 @@
 	unsigned int		acdirmin;
 	unsigned int		acdirmax;
 	char *			hostname;	/* remote hostname */
+	struct nfs_reqlist *	rw_requests;	/* async read/write requests */
 };
 
 /*
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfsd/stats.h linux/include/linux/nfsd/stats.h
--- v2.3.99-pre2/linux/include/linux/nfsd/stats.h	Tue Nov 23 22:42:21 1999
+++ linux/include/linux/nfsd/stats.h	Tue Mar 21 11:10:48 2000
@@ -16,8 +16,17 @@
 	unsigned int	fh_stale;	/* FH stale error */
 	unsigned int	fh_lookup;	/* dentry cached */
 	unsigned int	fh_anon;	/* anon file dentry returned */
-	unsigned int	fh_nocache_dir;	/* filehandle not foudn in dcache */
-	unsigned int	fh_nocache_nondir;	/* filehandle not foudn in dcache */
+	unsigned int	fh_nocache_dir;	/* filehandle not found in dcache */
+	unsigned int	fh_nocache_nondir;	/* filehandle not found in dcache */
+	unsigned int	io_read;	/* bytes returned to read requests */
+	unsigned int	io_write;	/* bytes passed in write requests */
+	unsigned int	th_cnt;		/* number of available threads */
+	unsigned int	th_usage[10];	/* number of ticks during which n perdeciles
+					 * of available threads were in use */
+	unsigned int	th_fullcnt;	/* number of times last free thread was used */
+	unsigned int	ra_size;	/* size of ra cache */
+	unsigned int	ra_depth[11];	/* number of times ra entry was found that deep
+					 * in the cache (10percentiles). [10] = not found */
 };
 
 #ifdef __KERNEL__
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h
--- v2.3.99-pre2/linux/include/linux/nfsd/syscall.h	Fri Mar 10 16:40:50 2000
+++ linux/include/linux/nfsd/syscall.h	Thu Mar 23 15:36:55 2000
@@ -111,10 +111,7 @@
 		struct nfsctl_uidmap	u_umap;
 		struct nfsctl_fhparm	u_getfh;
 		struct nfsctl_fdparm	u_getfd;
-#ifdef notyet
 		struct nfsctl_fsparm	u_getfs;
-#endif
-		unsigned int		u_debug;
 	} u;
 #define ca_svc		u.u_svc
 #define ca_client	u.u_client
@@ -124,15 +121,11 @@
 #define ca_getfd	u.u_getfd
 #define	ca_getfs	u.u_getfs
 #define ca_authd	u.u_authd
-#define ca_debug	u.u_debug
 };
 
 union nfsctl_res {
 	__u8			cr_getfh[NFS_FHSIZE];
-#ifdef notyet
 	struct knfsd_fh		cr_getfs;
-#endif
-	unsigned int		cr_debug;
 };
 
 #ifdef __KERNEL__
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.3.99-pre2/linux/include/linux/pci.h	Sun Mar 19 18:35:31 2000
+++ linux/include/linux/pci.h	Thu Mar 23 15:36:18 2000
@@ -551,6 +551,8 @@
 
 #ifndef CONFIG_PCI
 extern inline int pcibios_present(void) { return 0; }
+extern inline int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn) 
+{ 	return PCIBIOS_DEVICE_NOT_FOUND; }
 
 #define _PCI_NOP(o,s,t) \
 	extern inline int pcibios_##o##_config_##s## (u8 bus, u8 dfn, u8 where, t val) \
@@ -583,6 +585,7 @@
 extern inline int pci_register_driver(struct pci_driver *drv) { return 0;}
 extern inline void pci_unregister_driver(struct pci_driver *drv) { }
 extern inline int scsi_to_pci_dma_dir(unsigned char scsi_dir) { return scsi_dir; }
+extern inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
 
 #else
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/pci_ids.h linux/include/linux/pci_ids.h
--- v2.3.99-pre2/linux/include/linux/pci_ids.h	Fri Mar 10 16:40:50 2000
+++ linux/include/linux/pci_ids.h	Tue Mar 21 13:04:53 2000
@@ -1052,6 +1052,12 @@
 #define PCI_DEVICE_ID_OXSEMI_16PCI952	0x950A
 #define PCI_DEVICE_ID_OXSEMI_16PCI95N	0x9511
 
+#define PCI_VENDOR_ID_TITAN		0x14D2
+#define PCI_DEVICE_ID_TITAN_100		0xA001
+#define PCI_DEVICE_ID_TITAN_200		0xA005
+#define PCI_DEVICE_ID_TITAN_400		0xA003
+#define PCI_DEVICE_ID_TITAN_800B	0xA004
+
 #define PCI_VENDOR_ID_PANACOM		0x14d4
 #define PCI_DEVICE_ID_PANACOM_QUADMODEM	0x0400
 #define PCI_DEVICE_ID_PANACOM_DUALMODEM	0x0402
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/pipe_fs_i.h linux/include/linux/pipe_fs_i.h
--- v2.3.99-pre2/linux/include/linux/pipe_fs_i.h	Tue Nov 23 22:42:21 1999
+++ linux/include/linux/pipe_fs_i.h	Tue Mar 21 12:47:12 2000
@@ -9,6 +9,8 @@
 	unsigned int writers;
 	unsigned int waiting_readers;
 	unsigned int waiting_writers;
+	unsigned int r_counter;
+	unsigned int w_counter;
 };
 
 /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
@@ -24,6 +26,8 @@
 #define PIPE_WRITERS(inode)	((inode).i_pipe->writers)
 #define PIPE_WAITING_READERS(inode)	((inode).i_pipe->waiting_readers)
 #define PIPE_WAITING_WRITERS(inode)	((inode).i_pipe->waiting_writers)
+#define PIPE_RCOUNTER(inode)	((inode).i_pipe->r_counter)
+#define PIPE_WCOUNTER(inode)	((inode).i_pipe->w_counter)
 
 #define PIPE_EMPTY(inode)	(PIPE_LEN(inode) == 0)
 #define PIPE_FULL(inode)	(PIPE_LEN(inode) == PIPE_SIZE)
@@ -31,5 +35,10 @@
 #define PIPE_END(inode)	((PIPE_START(inode) + PIPE_LEN(inode)) & (PIPE_SIZE-1))
 #define PIPE_MAX_RCHUNK(inode)	(PIPE_SIZE - PIPE_START(inode))
 #define PIPE_MAX_WCHUNK(inode)	(PIPE_SIZE - PIPE_END(inode))
+
+/* Drop the inode semaphore and wait for a pipe event, atomically */
+void pipe_wait(struct inode * inode);
+
+struct inode* pipe_new(struct inode* inode);
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/serial.h linux/include/linux/serial.h
--- v2.3.99-pre2/linux/include/linux/serial.h	Tue Feb  1 01:35:44 2000
+++ linux/include/linux/serial.h	Thu Mar 23 15:36:08 2000
@@ -10,6 +10,8 @@
 #ifndef _LINUX_SERIAL_H
 #define _LINUX_SERIAL_H
 
+#include <asm/page.h>
+
 /*
  * Counters of the input lines (CTS, DSR, RI, CD) interrupts
  */
@@ -23,12 +25,12 @@
 /*
  * The size of the serial xmit buffer is 1 page, or 4096 bytes
  */
-#define SERIAL_XMIT_SIZE 4096
+#define SERIAL_XMIT_SIZE PAGE_SIZE
 
 struct serial_struct {
 	int	type;
 	int	line;
-	int	port;
+	unsigned long	port;
 	int	irq;
 	int	flags;
 	int	xmit_fifo_size;
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/serialP.h linux/include/linux/serialP.h
--- v2.3.99-pre2/linux/include/linux/serialP.h	Tue Feb  1 01:35:44 2000
+++ linux/include/linux/serialP.h	Thu Mar 23 15:37:31 2000
@@ -22,12 +22,13 @@
 #include <linux/config.h>
 #include <linux/termios.h>
 #include <linux/tqueue.h>
+#include <linux/circ_buf.h>
 #include <linux/wait.h>
 
 struct serial_state {
 	int	magic;
 	int	baud_base;
-	int	port;
+	unsigned long	port;
 	int	irq;
 	int	flags;
 	int	hub6;
@@ -50,7 +51,7 @@
 
 struct async_struct {
 	int			magic;
-	int			port;
+	unsigned long		port;
 	int			hub6;
 	int			flags;
 	int			xmit_fifo_size;
@@ -74,10 +75,8 @@
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
 	long			pgrp; /* pgrp of opening process */
-	unsigned char 		*xmit_buf;
-	int			xmit_head;
-	int			xmit_tail;
-	int			xmit_cnt;
+ 	struct circ_buf		xmit;
+ 	spinlock_t		xmit_lock;
 	u8			*iomem_base;
 	u16			iomem_reg_shift;
 	int			io_type;
@@ -101,11 +100,6 @@
 #define SSTATE_MAGIC 0x5302
 
 /*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
  * Events are used to schedule things to happen at timer-interrupt
  * time, instead of at rs interrupt time.
  */
@@ -180,19 +174,22 @@
 #define SPCI_FL_IRQBASE3       (0x0003 << 4)
 #define SPCI_FL_IRQBASE4       (0x0004 << 4)
 #define SPCI_FL_GET_IRQBASE(x)        ((x & SPCI_FL_IRQ_MASK) >> 4)
-	
+
 /* Use sucessiveentries base resource table */
 #define SPCI_FL_BASE_TABLE	0x0100
-	
+
 /* Use successive entries in the irq resource table */
 #define SPCI_FL_IRQ_TABLE	0x0200
-	
+
 /* Use the irq resource table instead of dev->irq */
 #define SPCI_FL_IRQRESOURCE	0x0400
 
 /* Use the Base address register size to cap number of ports */
 #define SPCI_FL_REGION_SZ_CAP	0x0800
-	
+
+/* Do not use irq sharing for this device */
+#define SPCI_FL_NO_SHIRQ	0x1000
+
 #define SPCI_FL_PNPDEFAULT	(SPCI_FL_IRQRESOURCE)
-	
+
 #endif /* _LINUX_SERIAL_H */
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/smp_lock.h linux/include/linux/smp_lock.h
--- v2.3.99-pre2/linux/include/linux/smp_lock.h	Wed Aug 18 16:43:33 1999
+++ linux/include/linux/smp_lock.h	Thu Mar 23 15:36:06 2000
@@ -7,6 +7,7 @@
 #define unlock_kernel()				do { } while(0)
 #define release_kernel_lock(task, cpu)		do { } while(0)
 #define reacquire_kernel_lock(task)		do { } while(0)
+#define kernel_locked() 1
 
 #else
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/sonet.h linux/include/linux/sonet.h
--- v2.3.99-pre2/linux/include/linux/sonet.h	Thu Feb 10 17:11:22 2000
+++ linux/include/linux/sonet.h	Tue Mar 21 23:38:26 2000
@@ -1,23 +1,29 @@
 /* sonet.h - SONET/SHD physical layer control */
  
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
  
 
 #ifndef LINUX_SONET_H
 #define LINUX_SONET_H
 
+#define __SONET_ITEMS \
+    __HANDLE_ITEM(section_bip); 	/* section parity errors (B1) */ \
+    __HANDLE_ITEM(line_bip);		/* line parity errors (B2) */ \
+    __HANDLE_ITEM(path_bip);		/* path parity errors (B3) */ \
+    __HANDLE_ITEM(line_febe);		/* line parity errors at remote */ \
+    __HANDLE_ITEM(path_febe);		/* path parity errors at remote */ \
+    __HANDLE_ITEM(corr_hcs);		/* correctable header errors */ \
+    __HANDLE_ITEM(uncorr_hcs);		/* uncorrectable header errors */ \
+    __HANDLE_ITEM(tx_cells);		/* cells sent */ \
+    __HANDLE_ITEM(rx_cells);		/* cells received */
+
 struct sonet_stats {
-	int section_bip;		/* section parity errors (B1) */
-	int line_bip;			/* line parity errors (B2) */
-	int path_bip;			/* path parity errors (B3) */
-	int line_febe;			/* line parity errors at remote */
-	int path_febe;			/* path parity errors at remote */
-	int corr_hcs;			/* correctable header errors */
-	int uncorr_hcs;			/* uncorrectable header errors */
-	int tx_cells;			/* cells sent */
-	int rx_cells;			/* cells received */
+#define __HANDLE_ITEM(i) int i
+	__SONET_ITEMS
+#undef __HANDLE_ITEM
 } __attribute__ ((packed));
 
+
 #define SONET_GETSTAT	_IOR('a',ATMIOC_PHYTYP,struct sonet_stats)
 					/* get statistics */
 #define SONET_GETSTATZ	_IOR('a',ATMIOC_PHYTYP+1,struct sonet_stats)
@@ -48,5 +54,24 @@
 #define SONET_FRAME_SDH   1		/* SDH STM-1 framing */
 
 #define SONET_FRSENSE_SIZE 6		/* C1[3],H1[3] (0xff for unknown) */
+
+
+#ifndef __KERNEL__
+#undef __SONET_ITEMS
+#else
+
+#include <asm/atomic.h>
+
+struct k_sonet_stats {
+#define __HANDLE_ITEM(i) atomic_t i
+	__SONET_ITEMS
+#undef __HANDLE_ITEM
+};
+
+extern void sonet_copy_stats(struct k_sonet_stats *from,struct sonet_stats *to);
+extern void sonet_subtract_stats(struct k_sonet_stats *from,
+    struct sonet_stats *to);
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/sunrpc/auth.h linux/include/linux/sunrpc/auth.h
--- v2.3.99-pre2/linux/include/linux/sunrpc/auth.h	Wed Aug 18 16:45:08 1999
+++ linux/include/linux/sunrpc/auth.h	Thu Mar 23 15:37:04 2000
@@ -64,10 +64,10 @@
 	struct rpc_auth *	(*create)(struct rpc_clnt *);
 	void			(*destroy)(struct rpc_auth *);
 
-	struct rpc_cred *	(*crcreate)(struct rpc_task *);
+	struct rpc_cred *	(*crcreate)(int);
 	void			(*crdestroy)(struct rpc_cred *);
 
-	int			(*crmatch)(struct rpc_task *, struct rpc_cred*);
+	int			(*crmatch)(struct rpc_cred *, int);
 	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
 	int			(*crrefresh)(struct rpc_task *);
 	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
@@ -83,10 +83,14 @@
 int			rpcauth_unregister(struct rpc_authops *);
 struct rpc_auth *	rpcauth_create(unsigned int, struct rpc_clnt *);
 void			rpcauth_destroy(struct rpc_auth *);
-struct rpc_cred *	rpcauth_lookupcred(struct rpc_task *);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
+struct rpc_cred *	rpcauth_bindcred(struct rpc_task *);
 void			rpcauth_holdcred(struct rpc_task *);
-void			rpcauth_releasecred(struct rpc_task *);
-int			rpcauth_matchcred(struct rpc_task *, struct rpc_cred *);
+void			rpcauth_releasecred(struct rpc_auth *,
+					    struct rpc_cred *);
+void			rpcauth_unbindcred(struct rpc_task *);
+int			rpcauth_matchcred(struct rpc_auth *,
+					  struct rpc_cred *, int);
 u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
 u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
 int			rpcauth_refreshcred(struct rpc_task *);
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- v2.3.99-pre2/linux/include/linux/sunrpc/clnt.h	Wed Aug 18 16:45:08 1999
+++ linux/include/linux/sunrpc/clnt.h	Thu Mar 23 15:37:04 2000
@@ -111,21 +111,23 @@
 void		rpc_getport(struct rpc_task *, struct rpc_clnt *);
 int		rpc_register(u32, u32, int, unsigned short, int *);
 
-int		rpc_call(struct rpc_clnt *clnt, u32 proc,
-				void *argp, void *resp, int flags);
-int		rpc_call_async(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags);
-void		rpc_call_setup(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags);
-int		rpc_do_call(struct rpc_clnt *clnt, u32 proc,
-				void *argp, void *resp, int flags,
-				rpc_action callback, void *clntdata);
+void		rpc_call_setup(struct rpc_task *, struct rpc_message *, int);
+
+int		rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg,
+			       int flags, rpc_action callback, void *clntdata);
+int		rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg,
+			      int flags);
 void		rpc_restart_call(struct rpc_task *);
 void		rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
 void		rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
 
-#define rpc_call(clnt, proc, argp, resp, flags)	\
-		rpc_do_call(clnt, proc, argp, resp, flags, NULL, NULL)
+static __inline__
+int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
+{
+	struct rpc_message msg = { proc, argp, resp, NULL };
+	return rpc_call_sync(clnt, &msg, flags);
+}
+		
 
 extern __inline__ void
 rpc_set_timeout(struct rpc_clnt *clnt, unsigned int retr, unsigned long incr)
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h
--- v2.3.99-pre2/linux/include/linux/sunrpc/sched.h	Wed Aug 18 16:44:20 1999
+++ linux/include/linux/sunrpc/sched.h	Thu Mar 23 15:37:00 2000
@@ -21,6 +21,16 @@
 #undef  CONFIG_RPC_FASTSCHED
 
 /*
+ * This is the actual RPC procedure call info.
+ */
+struct rpc_message {
+	__u32			rpc_proc;	/* Procedure number */
+	void *			rpc_argp;	/* Arguments */
+	void *			rpc_resp;	/* Result */
+	struct rpc_cred *	rpc_cred;	/* Credentials */
+};
+
+/*
  * This is the RPC task struct
  */
 struct rpc_task {
@@ -33,17 +43,14 @@
 	struct rpc_task *	tk_prev_task;	/* global list of tasks */
 	struct rpc_clnt *	tk_client;	/* RPC client */
 	struct rpc_rqst *	tk_rqstp;	/* RPC request */
-	struct rpc_cred *	tk_cred;	/* RPC credentials */
 	int			tk_status;	/* result of last operation */
 	struct rpc_wait_queue *	tk_rpcwait;	/* RPC wait queue we're on */
 
 	/*
 	 * RPC call state
 	 */
-	__u32			tk_proc;	/* procedure number */
+	struct rpc_message	tk_msg;		/* RPC call info */
 	__u32 *			tk_buffer;	/* XDR buffer */
-	void *			tk_argp;	/* argument storage */
-	void *			tk_resp;	/* result storage */
 	__u8			tk_garb_retry,
 				tk_cred_retry,
 				tk_suid_retry;
@@ -67,6 +74,10 @@
 	wait_queue_head_t	tk_wait;	/* sync: sleep on this q */
 	unsigned long		tk_timeout;	/* timeout for rpc_sleep() */
 	unsigned short		tk_flags;	/* misc flags */
+	unsigned short		tk_lock;	/* Task lock counter */
+	unsigned int		tk_wakeup   : 1,/* Task waiting to wake up */
+				tk_sleeping : 1,/* Task is truly asleep */
+				tk_active   : 1;/* Task has been activated */
 #ifdef RPC_DEBUG
 	unsigned short		tk_pid;		/* debugging aid */
 #endif
@@ -100,6 +111,8 @@
 #define RPC_DO_CALLBACK(t)	((t)->tk_flags & RPC_TASK_CALLBACK)
 #define RPC_DO_ROOTOVERRIDE(t)	((t)->tk_flags & RPC_TASK_ROOTCREDS)
 #define RPC_ASSASSINATED(t)	((t)->tk_flags & RPC_TASK_KILLED)
+#define RPC_IS_SLEEPING(t)	((t)->tk_sleeping)
+#define RPC_IS_ACTIVATED(t)	((t)->tk_active)
 
 /*
  * RPC synchronization objects
@@ -126,29 +139,28 @@
 					rpc_action exitfunc, int flags);
 void		rpc_release_task(struct rpc_task *);
 void		rpc_killall_tasks(struct rpc_clnt *);
-void		rpc_execute(struct rpc_task *);
+int		rpc_execute(struct rpc_task *);
 void		rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
 					rpc_action action);
 int		rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
 void		rpc_remove_wait_queue(struct rpc_task *);
 void		rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
 					rpc_action action, rpc_action timer);
-void		rpc_cond_wait(struct rpc_wait_queue *, struct rpc_task *,
-					unsigned char *,
-					rpc_action action, rpc_action timer);
+void		rpc_sleep_locked(struct rpc_wait_queue *, struct rpc_task *,
+				 rpc_action action, rpc_action timer);
+void		rpc_add_timer(struct rpc_task *, rpc_action);
 void		rpc_wake_up_task(struct rpc_task *);
 void		rpc_wake_up(struct rpc_wait_queue *);
 struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
 void		rpc_wake_up_status(struct rpc_wait_queue *, int);
-void		rpc_add_timer(struct rpc_task *, rpc_action);
-void		rpc_del_timer(struct rpc_task *);
+int		rpc_lock_task(struct rpc_task *);
+void		rpc_unlock_task(struct rpc_task *);
 void		rpc_delay(struct rpc_task *, unsigned long);
 void *		rpc_allocate(unsigned int flags, unsigned int);
 void		rpc_free(void *);
 int		rpciod_up(void);
 void		rpciod_down(void);
 void		rpciod_wake_up(void);
-void		rpciod_tcp_dispatcher(void);
 #ifdef RPC_DEBUG
 void		rpc_show_tasks(void);
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/sunrpc/xprt.h linux/include/linux/sunrpc/xprt.h
--- v2.3.99-pre2/linux/include/linux/sunrpc/xprt.h	Fri Oct 15 15:25:14 1999
+++ linux/include/linux/sunrpc/xprt.h	Thu Mar 23 15:37:04 2000
@@ -47,7 +47,7 @@
 	((xprt)->cong >= (xprt)->cwnd)
 
 /* Default timeout values */
-#define RPC_MAX_UDP_TIMEOUT	(6*HZ)
+#define RPC_MAX_UDP_TIMEOUT	(60*HZ)
 #define RPC_MAX_TCP_TIMEOUT	(600*HZ)
 
 /* RPC call and reply header size as number of 32bit words (verifier
@@ -96,8 +96,7 @@
 	struct rpc_task *	rq_task;	/* RPC task data */
 	__u32			rq_xid;		/* request XID */
 	struct rpc_rqst *	rq_next;	/* free list */
-	unsigned char		rq_gotit;	/* reply received */
-	unsigned char		rq_damaged;	/* being received */
+	unsigned char		rq_damaged;	/* reply being received */
 
 	/*
 	 * For authentication (e.g. auth_des)
@@ -122,12 +121,6 @@
 #define rq_rlen			rq_rcv_buf.io_len
 
 struct rpc_xprt {
-	struct rpc_xprt *	link;		/* list of all clients */
-	struct rpc_xprt *	rx_pending;	/* receive pending list */
-	
-	int 			rx_pending_flag;/* are we on the rcv pending list ? */
-
-	struct file *		file;		/* VFS layer */
 	struct socket *		sock;		/* BSD socket layer */
 	struct sock *		inet;		/* INET layer */
 
@@ -145,9 +138,9 @@
 	struct rpc_wait_queue	reconn;		/* waiting for reconnect */
 	struct rpc_rqst *	free;		/* free slots */
 	struct rpc_rqst		slot[RPC_MAXREQS];
-	unsigned char		connected;	/* TCP: connected */
-	unsigned char		write_space;	/* TCP: can send */
-	unsigned int		shutdown   : 1,	/* being shut down */
+	unsigned int		connected  : 1,	/* TCP: connected */
+				write_space: 1,	/* TCP: can send */
+				shutdown   : 1,	/* being shut down */
 				nocong	   : 1,	/* no congestion control */
 				stream     : 1,	/* TCP */
 				tcp_more   : 1,	/* more record fragments */
@@ -156,16 +149,12 @@
 	/*
 	 * State of TCP reply receive stuff
 	 */
-	union {					/* record marker & XID */
-		u32		header[2];
-		u8 		data[8];
-	}			tcp_recm;
-	struct rpc_rqst *	tcp_rqstp;
-	struct iovec		tcp_iovec[MAX_IOVEC];
-	u32			tcp_total;	/* overall record length */
-	u32			tcp_reclen;	/* fragment length */
-	u32			tcp_offset;	/* fragment offset */
-	u32			tcp_copied;	/* copied to request */
+	u32			tcp_recm;	/* Fragment header */
+	u32			tcp_xid;	/* Current XID */
+	unsigned int		tcp_reclen,	/* fragment length */
+				tcp_offset,	/* fragment offset */
+				tcp_copied;	/* copied to request */
+	struct list_head	rx_pending;	/* receive pending list */
 
 	/*
 	 * Send stuff
@@ -179,17 +168,13 @@
 
 	wait_queue_head_t	cong_wait;
 };
-#define tcp_reclen		tcp_recm.header[0]
-#define tcp_xid			tcp_recm.header[1]
 
 #ifdef __KERNEL__
 
-struct rpc_xprt *	xprt_create(struct file *socket,
-					struct sockaddr_in *addr,
-					struct rpc_timeout *toparms);
 struct rpc_xprt *	xprt_create_proto(int proto, struct sockaddr_in *addr,
 					struct rpc_timeout *toparms);
 int			xprt_destroy(struct rpc_xprt *);
+void			xprt_shutdown(struct rpc_xprt *);
 void			xprt_default_timeout(struct rpc_timeout *, int);
 void			xprt_set_timeout(struct rpc_timeout *, unsigned int,
 					unsigned long);
@@ -201,8 +186,22 @@
 void			xprt_release(struct rpc_task *);
 void			xprt_reconnect(struct rpc_task *);
 int			xprt_clear_backlog(struct rpc_xprt *);
+void			__rpciod_tcp_dispatcher(void);
+
+extern struct list_head	rpc_xprt_pending;
 
-int			xprt_tcp_pending(void);
+static inline
+int xprt_tcp_pending(void)
+{
+	return !list_empty(&rpc_xprt_pending);
+}
+
+static inline
+void rpciod_tcp_dispatcher(void)
+{
+	if (xprt_tcp_pending())
+		__rpciod_tcp_dispatcher();
+}
 
 #endif /* __KERNEL__*/
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/udf_fs.h linux/include/linux/udf_fs.h
--- v2.3.99-pre2/linux/include/linux/udf_fs.h	Tue Mar 14 19:10:40 2000
+++ linux/include/linux/udf_fs.h	Mon Mar 20 08:17:43 2000
@@ -60,6 +60,7 @@
 /*
  * Function prototypes (all other prototypes included in udfdecl.h)
  */
+extern int init_udf_fs(void);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/udf_fs_sb.h linux/include/linux/udf_fs_sb.h
--- v2.3.99-pre2/linux/include/linux/udf_fs_sb.h	Thu Mar  2 14:36:23 2000
+++ linux/include/linux/udf_fs_sb.h	Mon Mar 20 08:17:43 2000
@@ -49,7 +49,16 @@
 
 struct udf_part_map
 {
-	__u32	s_uspace_bitmap;
+	union
+	{
+		__u32			bitmap;
+		struct inode	*table;
+	}		s_uspace;
+	union
+	{
+		__u32			bitmap;
+		struct inode	*table;
+	}		s_fspace;
 	__u32	s_partition_root;
 	__u32	s_partition_len;
 	__u16	s_partition_type;
@@ -61,6 +70,7 @@
 	} s_type_specific;
 	__u32	(*s_partition_func)(struct super_block *, __u32, __u16, __u32);
 	__u16	s_volumeseqnum;
+	__u16	s_partition_flags;
 };
 
 #pragma pack()
diff -u --recursive --new-file v2.3.99-pre2/linux/include/linux/usb.h linux/include/linux/usb.h
--- v2.3.99-pre2/linux/include/linux/usb.h	Tue Mar 14 19:10:40 2000
+++ linux/include/linux/usb.h	Thu Mar 23 14:59:09 2000
@@ -604,6 +604,7 @@
 extern int usb_terminate_bulk(struct usb_device *, void *);
 
 extern void usb_init_root_hub(struct usb_device *dev);
+extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len);
 extern void usb_connect(struct usb_device *dev);
 extern void usb_disconnect(struct usb_device **);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/net/irda/irda_device.h linux/include/net/irda/irda_device.h
--- v2.3.99-pre2/linux/include/net/irda/irda_device.h	Tue Feb  1 01:35:44 2000
+++ linux/include/net/irda/irda_device.h	Thu Mar 23 15:39:32 2000
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Apr 14 12:41:42 1998
- * Modified at:   Fri Jan 14 10:46:56 2000
+ * Modified at:   Mon Mar 20 09:08:57 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
@@ -73,18 +73,18 @@
 	IRDA_TASK_CHILD_INIT,  /* Initializing child task */
 	IRDA_TASK_CHILD_WAIT,  /* Waiting for child task to finish */
 	IRDA_TASK_CHILD_DONE   /* Child task is finished */
-} TASK_STATE;
+} IRDA_TASK_STATE;
 
 struct irda_task;
-typedef int (*TASK_CALLBACK) (struct irda_task *task);
+typedef int (*IRDA_TASK_CALLBACK) (struct irda_task *task);
 
 struct irda_task {
 	queue_t q;
 	magic_t magic;
 
-	TASK_STATE state;
-	TASK_CALLBACK function;
-	TASK_CALLBACK finished;
+	IRDA_TASK_STATE state;
+	IRDA_TASK_CALLBACK function;
+	IRDA_TASK_CALLBACK finished;
 
 	struct irda_task *parent;
 	struct timer_list timer;
@@ -180,10 +180,11 @@
 
 void irda_task_delete(struct irda_task *task);
 int  irda_task_kick(struct irda_task *task);
-struct irda_task *irda_task_execute(void *instance, TASK_CALLBACK function, 
-				    TASK_CALLBACK finished, 
+struct irda_task *irda_task_execute(void *instance, 
+				    IRDA_TASK_CALLBACK function, 
+				    IRDA_TASK_CALLBACK finished, 
 				    struct irda_task *parent, void *param);
-void irda_task_next_state(struct irda_task *task, TASK_STATE state);
+void irda_task_next_state(struct irda_task *task, IRDA_TASK_STATE state);
 
 extern const char *infrared_mode[];
 
diff -u --recursive --new-file v2.3.99-pre2/linux/include/net/irda/parameters.h linux/include/net/irda/parameters.h
--- v2.3.99-pre2/linux/include/net/irda/parameters.h	Wed Dec 29 13:13:21 1999
+++ linux/include/net/irda/parameters.h	Tue Mar 21 11:17:28 2000
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Jun  7 08:47:28 1999
- * Modified at:   Mon Dec 13 11:51:59 1999
+ * Modified at:   Sun Jan 30 14:05:14 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -61,15 +61,15 @@
 	__u8  *bp;
 	__u16 *sp;
 	__u32 *ip;
-} pv_t;
+} irda_pv_t;
 
 typedef struct {
 	__u8 pi;
 	__u8 pl;
-	pv_t pv;
-} param_t;
+	irda_pv_t pv;
+} irda_param_t;
 
-typedef int (*PI_HANDLER)(void *self, param_t *param, int get);
+typedef int (*PI_HANDLER)(void *self, irda_param_t *param, int get);
 typedef int (*PV_HANDLER)(void *self, __u8 *buf, int len, __u8 pi,
 			  PV_TYPE type, PI_HANDLER func);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/ipc/shm.c linux/ipc/shm.c
--- v2.3.99-pre2/linux/ipc/shm.c	Sun Mar 19 18:35:31 2000
+++ linux/ipc/shm.c	Tue Mar 21 12:30:00 2000
@@ -30,6 +30,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/malloc.h>
 #include <linux/shm.h>
 #include <linux/swap.h>
@@ -1046,12 +1047,10 @@
 		 */
 		if ((shmid % SEQ_MULTIPLIER) == zero_id)
 			return -EINVAL;
-		lock_kernel();
 		down(&shm_ids.sem);
 		shp = shm_lock(shmid);
 		if (shp == NULL) {
 			up(&shm_ids.sem);
-			unlock_kernel();
 			return -EINVAL;
 		}
 		err = -EIDRM;
@@ -1060,14 +1059,12 @@
 				int id=shp->id;
 				shm_unlock(shmid);
 				up(&shm_ids.sem);
-				/* The kernel lock prevents new attaches from
-				 * being happening.  We can't hold shm_lock here
-				 * else we will deadlock in shm_lookup when we
+				/*
+				 * We can't hold shm_lock here else we
+				 * will deadlock in shm_lookup when we
 				 * try to recursively grab it.
 				 */
-				err = shm_remove_name(id);
-				unlock_kernel();
-				return err;
+				return shm_remove_name(id);
 			}
 			/* Do not find me any more */
 			shp->shm_perm.mode |= SHM_DEST;
@@ -1077,7 +1074,6 @@
 		/* Unlock */
 		shm_unlock(shmid);
 		up(&shm_ids.sem);
-		unlock_kernel();
 		return err;
 	}
 
@@ -1139,8 +1135,8 @@
 
 static int shm_mmap(struct file * file, struct vm_area_struct * vma)
 {
-	if (!(vma->vm_flags & VM_SHARED))
-		return -EINVAL; /* we cannot do private mappings */
+	if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
+		return -EINVAL; /* we cannot do private writable mappings */
 	UPDATE_ATIME(file->f_dentry->d_inode);
 	vma->vm_ops = &shm_vm_ops;
 	shm_inc(file->f_dentry->d_inode->i_ino);
@@ -1155,15 +1151,16 @@
 	unsigned long addr;
 	struct file * file;
 	int    err;
-	int    flags;
+	unsigned long flags;
+	unsigned long prot;
+	unsigned long o_flags;
 	char   name[SHM_FMT_LEN+1];
 
 	if (!shm_sb || (shmid % SEQ_MULTIPLIER) == zero_id)
 		return -EINVAL;
 
-	if ((addr = (ulong)shmaddr))
-	{
-		if(addr & (SHMLBA-1)) {
+	if ((addr = (ulong)shmaddr)) {
+		if (addr & (SHMLBA-1)) {
 			if (shmflg & SHM_RND)
 				addr &= ~(SHMLBA-1);	   /* round down */
 			else
@@ -1173,14 +1170,22 @@
 	} else
 		flags = MAP_SHARED;
 
-	sprintf (name, SHM_FMT, shmid); 
+	if (shmflg & SHM_RDONLY) {
+		prot = PROT_READ;
+		o_flags = O_RDONLY;
+	} else {
+		prot = PROT_READ | PROT_WRITE;
+		o_flags = O_RDWR;
+	}
+
+	sprintf (name, SHM_FMT, shmid);
+
 	lock_kernel();
-	file = filp_open(name, O_RDWR, 0, dget(shm_sb->s_root));
+	file = filp_open(name, o_flags, 0, dget(shm_sb->s_root));
 	if (IS_ERR (file))
 		goto bad_file;
 	*raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
-			  (shmflg & SHM_RDONLY ? PROT_READ :
-			   PROT_READ | PROT_WRITE), flags, 0);
+			  prot, flags, 0);
 	unlock_kernel();
 	if (IS_ERR(*raddr))
 		err = PTR_ERR(*raddr);
@@ -1203,14 +1208,18 @@
 }
 
 /*
- *	Remove a name. Must be called with lock_kernel
+ *	Remove a name.
  */
  
 static int shm_remove_name(int id)
 {
+	int err;
 	char name[SHM_FMT_LEN+1];
 	sprintf (name, SHM_FMT, id);
-	return do_unlink (name, dget(shm_sb->s_root));
+	lock_kernel();
+	err = do_unlink (name, dget(shm_sb->s_root));
+	unlock_kernel();
+	return err;
 }
 
 /*
@@ -1224,8 +1233,6 @@
 	int id = shmd->vm_file->f_dentry->d_inode->i_ino;
 	struct shmid_kernel *shp;
 
-	lock_kernel();
-
 	/* remove from the list of attaches of the shm segment */
 	if(!(shp = shm_lock(id)))
 		BUG();
@@ -1243,14 +1250,12 @@
 		 * try to recursively grab it.
 		 */
 		err = shm_remove_name(pid);
-		if(err && err != -ENOENT)
+		if(err && err != -EINVAL && err != -ENOENT) 
 			printk(KERN_ERR "Unlink of SHM id %d failed (%d).\n", pid, err);
 
 	} else {
 		shm_unlock(id);
 	}
-
-	unlock_kernel();
 }
 
 /*
diff -u --recursive --new-file v2.3.99-pre2/linux/kernel/Makefile linux/kernel/Makefile
--- v2.3.99-pre2/linux/kernel/Makefile	Thu Mar  2 14:36:23 2000
+++ linux/kernel/Makefile	Wed Mar 22 09:39:11 2000
@@ -7,9 +7,6 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CPPFLAGS) -traditional $< -o $*.s
-
 O_TARGET := kernel.o
 O_OBJS    = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \
 	    module.o exit.o itimer.o info.o time.o softirq.o resource.o \
@@ -29,7 +26,7 @@
 OX_OBJS  += ksyms.o
 endif
 
-ifdef CONFIG_PM
+ifeq ($(CONFIG_PM),y)
 OX_OBJS += pm.o
 endif
 
diff -u --recursive --new-file v2.3.99-pre2/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.3.99-pre2/linux/kernel/ksyms.c	Sun Mar 19 18:35:31 2000
+++ linux/kernel/ksyms.c	Wed Mar 22 22:15:57 2000
@@ -207,6 +207,7 @@
 EXPORT_SYMBOL(locks_mandatory_area);
 EXPORT_SYMBOL(dput);
 EXPORT_SYMBOL(is_root_busy);
+EXPORT_SYMBOL(have_submounts);
 EXPORT_SYMBOL(prune_dcache);
 EXPORT_SYMBOL(shrink_dcache_sb);
 EXPORT_SYMBOL(shrink_dcache_parent);
@@ -324,12 +325,20 @@
 EXPORT_SYMBOL(proc_doulongvec_minmax);
 
 /* interrupt handling */
+EXPORT_SYMBOL(add_timer);
+EXPORT_SYMBOL(del_timer);
 EXPORT_SYMBOL(request_irq);
 EXPORT_SYMBOL(free_irq);
+
+/* The notion of irq probe/assignment is foreign to S/390 */
+
+#if !defined(CONFIG_ARCH_S390)
 EXPORT_SYMBOL(probe_irq_on);
 EXPORT_SYMBOL(probe_irq_off);
-EXPORT_SYMBOL(add_timer);
-EXPORT_SYMBOL(del_timer);
+EXPORT_SYMBOL(autoirq_setup);
+EXPORT_SYMBOL(autoirq_report);
+#endif
+
 #ifdef __SMP__
 EXPORT_SYMBOL(del_timer_sync);
 #endif
@@ -366,10 +375,6 @@
 EXPORT_SYMBOL(unlock_kiovec);
 EXPORT_SYMBOL(brw_kiovec);
 
-/* autoirq from  drivers/net/auto_irq.c */
-EXPORT_SYMBOL(autoirq_setup);
-EXPORT_SYMBOL(autoirq_report);
-
 /* dma handling */
 EXPORT_SYMBOL(request_dma);
 EXPORT_SYMBOL(free_dma);
@@ -437,8 +442,8 @@
 EXPORT_SYMBOL(copy_strings_kernel);
 EXPORT_SYMBOL(do_execve);
 EXPORT_SYMBOL(flush_old_exec);
-EXPORT_SYMBOL(open_dentry);
-EXPORT_SYMBOL(read_exec);
+EXPORT_SYMBOL(kernel_read);
+EXPORT_SYMBOL(open_exec);
 
 /* Miscellaneous access points */
 EXPORT_SYMBOL(si_meminfo);
diff -u --recursive --new-file v2.3.99-pre2/linux/kernel/ptrace.c linux/kernel/ptrace.c
--- v2.3.99-pre2/linux/kernel/ptrace.c	Sun Feb 13 19:29:04 2000
+++ linux/kernel/ptrace.c	Tue Mar 21 11:01:25 2000
@@ -44,9 +44,9 @@
 	mapnr = pte_pagenr(*pgtable);
 	if (write && (!pte_write(*pgtable) || !pte_dirty(*pgtable)))
 		goto fault_in_page;
-	if (mapnr >= max_mapnr)
-		return 0;
 	page = mem_map + mapnr;
+	if ((mapnr >= max_mapnr) || PageReserved(page))
+		return 0;
 	flush_cache_page(vma, addr);
 
 	if (write) {
diff -u --recursive --new-file v2.3.99-pre2/linux/kernel/sched.c linux/kernel/sched.c
--- v2.3.99-pre2/linux/kernel/sched.c	Thu Mar  2 14:36:23 2000
+++ linux/kernel/sched.c	Tue Mar 21 14:43:39 2000
@@ -618,6 +618,11 @@
 	goto handle_softirq_back;
 
 handle_tq_scheduler:
+	/*
+	 * do not run the task queue with disabled interrupts,
+	 * cli() wouldn't work on SMP
+	 */
+	sti();
 	run_task_queue(&tq_scheduler);
 	goto tq_scheduler_back;
 
@@ -630,7 +635,7 @@
 
 scheduling_in_interrupt:
 	printk("Scheduling in interrupt\n");
-	*(int *)0 = 0;
+	BUG();
 	return;
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/kernel/signal.c linux/kernel/signal.c
--- v2.3.99-pre2/linux/kernel/signal.c	Fri Mar 10 16:40:50 2000
+++ linux/kernel/signal.c	Thu Mar 23 08:15:01 2000
@@ -89,7 +89,7 @@
  * Dequeue a signal and return the element to the caller, which is 
  * expected to free it.
  *
- * All callers of must be holding current->sigmask_lock.
+ * All callers must be holding current->sigmask_lock.
  */
 
 int
@@ -148,19 +148,19 @@
 			kmem_cache_free(signal_queue_cachep,q);
 			atomic_dec(&nr_queued_signals);
 
-			/* then see if this signal is still pending. */
-			q = *pp;
-			while (q) {
-				if (q->info.si_signo == sig) {
-					reset = 0;
-					break;
-				}
-				q = q->next;
-			}
+			/* Then see if this signal is still pending.
+			   (Non rt signals may not be queued twice.)
+			 */
+			if (sig >= SIGRTMIN)
+				for (q = *pp; q; q = q->next)
+					if (q->info.si_signo == sig) {
+						reset = 0;
+						break;
+					}
+					
 		} else {
-			/* Ok, it wasn't in the queue.  It must have
-			   been sent either by a non-rt mechanism and
-			   we ran out of queue space.  So zero out the
+			/* Ok, it wasn't in the queue.  We must have
+			   been out of queue space.  So zero out the
 			   info.  */
 			info->si_signo = sig;
 			info->si_errno = 0;
@@ -169,9 +169,10 @@
 			info->si_uid = 0;
 		}
 
-		if (reset)
+		if (reset) {
 			sigdelset(&current->signal, sig);
-		recalc_sigpending(current);
+			recalc_sigpending(current);
+		}
 
 		/* XXX: Once POSIX.1b timers are in, if si_code == SI_TIMER,
 		   we need to xchg out the timer overrun values.  */
@@ -195,6 +196,43 @@
 }
 
 /*
+ * Remove signal sig from queue and from t->signal.
+ * Returns 1 if sig was found in t->signal.
+ *
+ * All callers must be holding t->sigmask_lock.
+ */
+static int rm_sig_from_queue(int sig, struct task_struct *t)
+{
+	struct signal_queue *q, **pp;
+
+	if (sig >= SIGRTMIN) {
+		printk(KERN_CRIT "SIG: rm_sig_from_queue() doesn't support rt signals\n");
+		return 0;
+	}
+
+	if (!sigismember(&t->signal, sig))
+		return 0;
+
+	sigdelset(&t->signal, sig);
+
+	pp = &t->sigqueue;
+	q = t->sigqueue;
+
+	/* Find the one we're interested in ...
+	   It may appear only once. */
+	for ( ; q ; pp = &q->next, q = q->next)
+		if (q->info.si_signo == sig)
+			break;
+	if (q) {
+		if ((*pp = q->next) == NULL)
+			t->sigqueue_tail = pp;
+		kmem_cache_free(signal_queue_cachep,q);
+		atomic_dec(&nr_queued_signals);
+	}
+	return 1;
+}
+
+/*
  * Determine whether a signal should be posted or not.
  *
  * Signals with SIG_IGN can be ignored, except for the
@@ -272,18 +310,16 @@
 		if (t->state == TASK_STOPPED)
 			wake_up_process(t);
 		t->exit_code = 0;
-		sigdelsetmask(&t->signal, (sigmask(SIGSTOP)|sigmask(SIGTSTP)|
-					   sigmask(SIGTTOU)|sigmask(SIGTTIN)));
-		/* Inflict this corner case with recalculations, not mainline */
-		recalc_sigpending(t);
+		if (rm_sig_from_queue(SIGSTOP, t) || rm_sig_from_queue(SIGTSTP, t) ||
+		    rm_sig_from_queue(SIGTTOU, t) || rm_sig_from_queue(SIGTTIN, t))
+			recalc_sigpending(t);
 		break;
 
 	case SIGSTOP: case SIGTSTP:
 	case SIGTTIN: case SIGTTOU:
 		/* If we're stopping again, cancel SIGCONT */
-		sigdelset(&t->signal, SIGCONT);
-		/* Inflict this corner case with recalculations, not mainline */
-		recalc_sigpending(t);
+		if (rm_sig_from_queue(SIGCONT, t))
+			recalc_sigpending(t);
 		break;
 	}
 
@@ -337,8 +373,12 @@
 				q->info = *info;
 				break;
 		}
-	} else {
-		/* Queue overflow, we have to abort. */
+	} else if (sig >= SIGRTMIN && info && (unsigned long)info != 1
+		   && info->si_code < 0) {
+		/*
+		 * Queue overflow, abort.  We may abort if the signal was rt
+		 * and sent by user using something other than kill().
+		 */
 		ret = -EAGAIN;
 		goto out;
 	}
@@ -405,7 +445,7 @@
 }
 
 /*
- * kill_pg() sends a signal to a process group: this is what the tty
+ * kill_pg_info() sends a signal to a process group: this is what the tty
  * control characters do (^C, ^Z etc)
  */
 
@@ -436,7 +476,7 @@
 }
 
 /*
- * kill_sl() sends a signal to the session leader: this is used
+ * kill_sl_info() sends a signal to the session leader: this is used
  * to send SIGHUP to the controlling process of a terminal when
  * the connection is lost.
  */
@@ -483,7 +523,7 @@
 }
 
 /*
- * kill_something() interprets pid in interesting ways just like kill(2).
+ * kill_something_info() interprets pid in interesting ways just like kill(2).
  *
  * POSIX specifies that kill(-1,sig) is unspecified, but what we have
  * is probably wrong.  Should make it like BSD or SYSV.
diff -u --recursive --new-file v2.3.99-pre2/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.3.99-pre2/linux/kernel/sysctl.c	Sun Mar 19 18:35:31 2000
+++ linux/kernel/sysctl.c	Thu Mar 23 08:40:45 2000
@@ -14,6 +14,8 @@
  * Added proc_doulongvec_minmax, 09/08/99, Carlos H. Bauer.
  * Changed linked lists to use list.h instead of lists.h, 02/24/00, Bill
  *  Wendling.
+ * The list_for_each() macro wasn't appropriate for the sysctl loop.
+ *  Removed it and replaced it with older style, 03/23/00, Bill Wendling
  */
 
 #include <linux/config.h>
@@ -84,9 +86,9 @@
 static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
 		  void *buffer, size_t *lenp);
 
-
 static ctl_table root_table[];
-static LIST_HEAD(root_table_header);
+static struct ctl_table_header root_table_header =
+	{ root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) };
 
 static ctl_table kern_table[];
 static ctl_table vm_table[];
@@ -99,7 +101,6 @@
 static ctl_table dev_table[];
 extern ctl_table random_table[];
 
-
 /* /proc declarations: */
 
 #ifdef CONFIG_PROC_FS
@@ -108,14 +109,12 @@
 static ssize_t proc_writesys(struct file *, const char *, size_t, loff_t *);
 static int proc_sys_permission(struct inode *, int);
 
-struct file_operations proc_sys_file_operations =
-{
+struct file_operations proc_sys_file_operations = {
 	read:		proc_readsys,
 	write:		proc_writesys,
 };
 
-static struct inode_operations proc_sys_inode_operations =
-{
+static struct inode_operations proc_sys_inode_operations = {
 	permission:	proc_sys_permission,
 };
 
@@ -124,6 +123,7 @@
 static void register_proc_table(ctl_table *, struct proc_dir_entry *);
 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
 #endif
+
 extern int inodes_stat[];
 extern int dentry_stat[];
 
@@ -294,39 +294,33 @@
 	register_proc_table(root_table, proc_sys_root);
 	init_irq_proc();
 #endif
-
 }
 
-int do_sysctl (int *name, int nlen,
-	       void *oldval, size_t *oldlenp,
+int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,
 	       void *newval, size_t newlen)
 {
-	int error;
 	struct list_head *tmp;
-	void *context;
-	
+
 	if (nlen == 0 || nlen >= CTL_MAXNAME)
 		return -ENOTDIR;
-	
-	if (oldval) 
-	{
+	if (oldval) {
 		int old_len;
-		if (!oldlenp)
-			return -EFAULT;
-		if(get_user(old_len, oldlenp))
+		if (!oldlenp || get_user(old_len, oldlenp))
 			return -EFAULT;
 	}
-	list_for_each(tmp, &root_table_header) {
+	tmp = &root_table_header.ctl_entry;
+	do {
 		struct ctl_table_header *head =
 			list_entry(tmp, struct ctl_table_header, ctl_entry);
-		context = NULL;
-		error = parse_table(name, nlen, oldval, oldlenp, 
-				    newval, newlen, head->ctl_table, &context);
+		void *context = NULL;
+		int error = parse_table(name, nlen, oldval, oldlenp, 
+					newval, newlen, head->ctl_table,
+					&context);
 		if (context)
 			kfree(context);
 		if (error != -ENOTDIR)
 			return error;
-	}
+	} while (tmp != &root_table_header.ctl_entry);
 	return -ENOTDIR;
 }
 
@@ -335,7 +329,7 @@
 	struct __sysctl_args tmp;
 	int error;
 
-	if(copy_from_user(&tmp, args, sizeof(tmp)))
+	if (copy_from_user(&tmp, args, sizeof(tmp)))
 		return -EFAULT;
 		
 	lock_kernel();
@@ -345,9 +339,10 @@
 	return error;
 }
 
-
-/* ctl_perm does NOT grant the superuser all rights automatically, because
-   some sysctl variables are readonly even to root. */
+/*
+ * ctl_perm does NOT grant the superuser all rights automatically, because
+ * some sysctl variables are readonly even to root.
+ */
 
 static int test_perm(int mode, int op)
 {
@@ -370,17 +365,16 @@
 		       void *newval, size_t newlen,
 		       ctl_table *table, void **context)
 {
-	int error;
 repeat:
 	if (!nlen)
 		return -ENOTDIR;
 
 	for ( ; table->ctl_name; table++) {
 		int n;
-		if(get_user(n,name))
+		if (get_user(n, name))
 			return -EFAULT;
-		if (n == table->ctl_name ||
-		    table->ctl_name == CTL_ANY) {
+		if (n == table->ctl_name || table->ctl_name == CTL_ANY) {
+			int error;
 			if (table->child) {
 				if (ctl_perm(table, 001))
 					return -EPERM;
@@ -389,8 +383,8 @@
 						table, name, nlen,
 						oldval, oldlenp,
 						newval, newlen, context);
-				if (error)
-					return error;
+					if (error)
+						return error;
 				}
 				name++;
 				nlen--;
@@ -465,9 +459,9 @@
 	tmp->ctl_table = table;
 	INIT_LIST_HEAD(&tmp->ctl_entry);
 	if (insert_at_head)
-		list_add(&tmp->ctl_entry, &root_table_header);
+		list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
 	else
-		list_add_tail(&tmp->ctl_entry, &root_table_header);
+		list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
 #ifdef CONFIG_PROC_FS
 	register_proc_table(table, proc_sys_root);
 #endif
@@ -694,6 +688,7 @@
 #define OP_OR	2
 #define OP_MAX	3
 #define OP_MIN	4
+
 static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
 		  void *buffer, size_t *lenp, int conv, int op)
 {
@@ -812,7 +807,6 @@
 		(current->pid == 1) ? OP_SET : OP_AND);
 }
 
-
 int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
 		  void *buffer, size_t *lenp)
 {
@@ -910,7 +904,6 @@
 	filp->f_pos += *lenp;
 	return 0;
 }
-
 
 /*
  * an unsigned long function version
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/filemap.c linux/mm/filemap.c
--- v2.3.99-pre2/linux/mm/filemap.c	Sun Mar 19 18:35:31 2000
+++ linux/mm/filemap.c	Tue Mar 21 11:30:08 2000
@@ -2465,7 +2465,7 @@
 			PAGE_BUG(page);
 		}
 
-		status = mapping->a_ops->prepare_write(page, offset, offset+bytes);
+		status = mapping->a_ops->prepare_write(file, page, offset, offset+bytes);
 		if (status)
 			goto unlock;
 		kaddr = (char*)page_address(page);
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/highmem.c linux/mm/highmem.c
--- v2.3.99-pre2/linux/mm/highmem.c	Fri Jan 21 18:19:17 2000
+++ linux/mm/highmem.c	Thu Mar 23 11:25:02 2000
@@ -234,11 +234,20 @@
 {
 	struct page *p_from;
 	unsigned long vfrom;
+	unsigned long flags;
 
 	p_from = from->b_page;
+
+	/*
+	 * Since this can be executed from IRQ context, reentrance
+	 * on the same CPU must be avoided:
+	 */
+	__save_flags(flags);
+	__cli();
 	vfrom = kmap_atomic(p_from, KM_BOUNCE_WRITE);
 	memcpy(to->b_data, (char *)vfrom + bh_offset(from), to->b_size);
 	kunmap_atomic(vfrom, KM_BOUNCE_WRITE);
+	__restore_flags(flags);
 }
 
 static inline void copy_to_high_bh_irq (struct buffer_head *to,
@@ -246,11 +255,15 @@
 {
 	struct page *p_to;
 	unsigned long vto;
+	unsigned long flags;
 
 	p_to = to->b_page;
+	__save_flags(flags);
+	__cli();
 	vto = kmap_atomic(p_to, KM_BOUNCE_READ);
 	memcpy((char *)vto + bh_offset(to), from->b_data, to->b_size);
 	kunmap_atomic(vto, KM_BOUNCE_READ);
+	__restore_flags(flags);
 }
 
 static inline void bounce_end_io (struct buffer_head *bh, int uptodate)
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/memory.c linux/mm/memory.c
--- v2.3.99-pre2/linux/mm/memory.c	Sun Mar 19 18:35:31 2000
+++ linux/mm/memory.c	Thu Mar 23 11:35:32 2000
@@ -428,6 +428,7 @@
 	struct vm_area_struct *	vma = 0;
 	struct page *		map;
 	int			i;
+	int			datain = (rw == READ);
 	
 	/* Make sure the iobuf is not already mapped somewhere. */
 	if (iobuf->nr_pages)
@@ -459,8 +460,19 @@
 			vma = find_vma(current->mm, ptr);
 			if (!vma) 
 				goto out_unlock;
+			if (vma->vm_start > ptr) {
+				if (!(vma->vm_flags & VM_GROWSDOWN))
+					goto out_unlock;
+				if (expand_stack(vma, ptr))
+					goto out_unlock;
+			}
+			if (((datain) && (!(vma->vm_flags & VM_WRITE))) ||
+					(!(vma->vm_flags & VM_READ))) {
+				err = -EACCES;
+				goto out_unlock;
+			}
 		}
-		if (handle_mm_fault(current, vma, ptr, (rw==READ)) <= 0) 
+		if (handle_mm_fault(current, vma, ptr, datain) <= 0) 
 			goto out_unlock;
 		spin_lock(&mm->page_table_lock);
 		map = follow_page(ptr);
@@ -774,6 +786,15 @@
 	update_mmu_cache(vma, address, entry);
 }
 
+static inline void break_cow(struct vm_area_struct * vma, struct page *	old_page, struct page * new_page, unsigned long address, 
+		pte_t *page_table)
+{
+	copy_cow_page(old_page,new_page,address);
+	flush_page_to_ram(new_page);
+	flush_cache_page(vma, address);
+	establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
+}
+
 /*
  * This routine handles present pages, when users try to write
  * to a shared page. It is done by copying the page to a new address
@@ -852,10 +873,7 @@
 	if (pte_val(*page_table) == pte_val(pte)) {
 		if (PageReserved(old_page))
 			++vma->vm_mm->rss;
-		copy_cow_page(old_page, new_page, address);
-		flush_page_to_ram(new_page);
-		flush_cache_page(vma, address);
-		establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
+		break_cow(vma, old_page, new_page, address, page_table);
 
 		/* Free the old page.. */
 		new_page = old_page;
@@ -903,7 +921,7 @@
 		return;
 	flush_cache_page(vma, address);
 	page = pte_page(pte);
-	if (page-mem_map >= max_mapnr)
+	if ((page-mem_map >= max_mapnr) || PageReserved(page))
 		return;
 	offset = address & ~PAGE_MASK;
 	memclear_highpage_flush(page, offset, PAGE_SIZE - offset);
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/mmap.c linux/mm/mmap.c
--- v2.3.99-pre2/linux/mm/mmap.c	Tue Mar 14 19:10:40 2000
+++ linux/mm/mmap.c	Thu Mar 23 10:08:45 2000
@@ -200,11 +200,11 @@
 	if (file != NULL) {
 		switch (flags & MAP_TYPE) {
 		case MAP_SHARED:
-			if ((prot & PROT_WRITE) && !(file->f_mode & 2))
+			if ((prot & PROT_WRITE) && !(file->f_mode & FMODE_WRITE))
 				return -EACCES;
 
 			/* Make sure we don't allow writing to an append-only file.. */
-			if (IS_APPEND(file->f_dentry->d_inode) && (file->f_mode & 2))
+			if (IS_APPEND(file->f_dentry->d_inode) && (file->f_mode & FMODE_WRITE))
 				return -EACCES;
 
 			/* make sure there are no mandatory locks on the file. */
@@ -213,7 +213,7 @@
 
 			/* fall through */
 		case MAP_PRIVATE:
-			if (!(file->f_mode & 1))
+			if (!(file->f_mode & FMODE_READ))
 				return -EACCES;
 			break;
 
@@ -252,7 +252,7 @@
 		VM_ClearReadHint(vma);
 		vma->vm_raend = 0;
 
-		if (file->f_mode & 1)
+		if (file->f_mode & FMODE_READ)
 			vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
 		if (flags & MAP_SHARED) {
 			vma->vm_flags |= VM_SHARED | VM_MAYSHARE;
@@ -266,7 +266,7 @@
 			 * We leave the VM_MAYSHARE bit on, just to get correct output
 			 * from /proc/xxx/maps..
 			 */
-			if (!(file->f_mode & 2))
+			if (!(file->f_mode & FMODE_WRITE))
 				vma->vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
 		}
 	} else
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.3.99-pre2/linux/mm/page_alloc.c	Sun Mar 19 18:35:31 2000
+++ linux/mm/page_alloc.c	Mon Mar 20 14:33:26 2000
@@ -266,11 +266,19 @@
 /*
  * This is the 'heart' of the zoned buddy allocator:
  */
-struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
+struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order)
 {
 	zone_t **zone = zonelist->zones;
 
 	/*
+	 * If this is a recursive call, we'd better
+	 * do our best to just allocate things without
+	 * further thought.
+	 */
+	if (current->flags & PF_MEMALLOC)
+		goto allocate_ok;
+
+	/*
 	 * (If anyone calls gfp from interrupts nonatomically then it
 	 * will sooner or later tripped up by a schedule().)
 	 *
@@ -283,32 +291,22 @@
 			break;
 		if (!z->size)
 			BUG();
-		/*
-		 * If this is a recursive call, we'd better
-		 * do our best to just allocate things without
-		 * further thought.
-		 */
-		if (!(current->flags & PF_MEMALLOC)) {
-			/* Are we low on memory? */
-			if (z->free_pages <= z->pages_low)
-				continue;
-		}
-		/*
-		 * This is an optimization for the 'higher order zone
-		 * is empty' case - it can happen even in well-behaved
-		 * systems, think the page-cache filling up all RAM.
-		 * We skip over empty zones. (this is not exact because
-		 * we do not take the spinlock and it's not exact for
-		 * the higher order case, but will do it for most things.)
-		 */
-		if (z->free_pages) {
+
+		/* Are we supposed to free memory? Don't make it worse.. */
+		if (!z->zone_wake_kswapd && z->free_pages > z->pages_low) {
 			struct page *page = rmqueue(z, order);
 			if (page)
 				return page;
 		}
 	}
+
+	/*
+	 * Ok, no obvious zones were available, start
+	 * balancing things a bit..
+	 */
 	if (zone_balance_memory(zonelist)) {
 		zone = zonelist->zones;
+allocate_ok:
 		for (;;) {
 			zone_t *z = *(zone++);
 			if (!z)
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/vmalloc.c linux/mm/vmalloc.c
--- v2.3.99-pre2/linux/mm/vmalloc.c	Tue Nov 23 22:42:21 1999
+++ linux/mm/vmalloc.c	Tue Mar 21 11:01:25 2000
@@ -39,7 +39,8 @@
 			continue;
 		if (pte_present(page)) {
 			unsigned long map_nr = pte_pagenr(page);
-			if (map_nr < max_mapnr)
+			if ((map_nr < max_mapnr) && 
+						(!PageReserved(mem_map + map_nr)))
 				__free_page(mem_map + map_nr);
 			continue;
 		}
@@ -206,7 +207,7 @@
 	struct vm_struct *area;
 
 	size = PAGE_ALIGN(size);
-	if (!size || (size >> PAGE_SHIFT) > max_mapnr) {
+	if (!size || (size >> PAGE_SHIFT) > num_physpages) {
 		BUG();
 		return NULL;
 	}
diff -u --recursive --new-file v2.3.99-pre2/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.3.99-pre2/linux/mm/vmscan.c	Tue Mar 14 19:10:40 2000
+++ linux/mm/vmscan.c	Tue Mar 21 11:01:25 2000
@@ -45,7 +45,7 @@
 	if (!pte_present(pte))
 		goto out_failed;
 	page = pte_page(pte);
-	if (page-mem_map >= max_mapnr)
+	if ((page-mem_map >= max_mapnr) || PageReserved(page))
 		goto out_failed;
 
 	/* Don't look at this pte if it's been accessed recently. */
@@ -59,7 +59,7 @@
 		goto out_failed;
 	}
 
-	if (PageReserved(page) || PageLocked(page))
+	if (PageLocked(page))
 		goto out_failed;
 
 	/*
diff -u --recursive --new-file v2.3.99-pre2/linux/net/802/tr.c linux/net/802/tr.c
--- v2.3.99-pre2/linux/net/802/tr.c	Tue Nov 23 22:42:21 1999
+++ linux/net/802/tr.c	Wed Mar 22 09:39:38 2000
@@ -237,8 +237,9 @@
 	rif_cache entry;
 	unsigned char *olddata;
 	unsigned char mcast_func_addr[] = {0xC0,0x00,0x00,0x04,0x00,0x00};
+	unsigned long flags ; 
 	
-	spin_lock_bh(&rif_lock);
+	spin_lock_irqsave(&rif_lock,flags);
 
 	/*
 	 *	Broadcasts are single route as stated in RFC 1042 
@@ -308,7 +309,7 @@
 	else 
 		slack = 18 - ((ntohs(trh->rcf) & TR_RCF_LEN_MASK)>>8);
 	olddata = skb->data;
-	spin_unlock_bh(&rif_lock);
+	spin_unlock_irqrestore(&rif_lock,flags);
 
 	skb_pull(skb, slack);
 	memmove(skb->data, olddata, sizeof(struct trh_hdr) - slack);
@@ -418,8 +419,9 @@
 {
 	int i;
 	unsigned long now=jiffies;
+	unsigned long flags ; 
 
-	spin_lock(&rif_lock);
+	spin_lock_irqsave(&rif_lock,flags);
 	
 	for(i=0; i < RIF_TABLE_SIZE;i++) 
 	{
@@ -439,7 +441,7 @@
 		}
 	}
 	
-	spin_unlock(&rif_lock);
+	spin_unlock_irqrestore(&rif_lock,flags);
 
 	/*
 	 *	Reset the timer
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/Makefile linux/net/atm/Makefile
--- v2.3.99-pre2/linux/net/atm/Makefile	Tue Mar 14 19:10:40 2000
+++ linux/net/atm/Makefile	Tue Mar 21 23:38:26 2000
@@ -16,10 +16,6 @@
 O_OBJS	= addr.o pvc.o signaling.o svc.o
 OX_OBJS = common.o atm_misc.o raw.o resources.o
 
-ifeq ($(CONFIG_MMU_HACKS),y)
-O_OBJS += mmuio.o
-endif
-
 ifeq ($(CONFIG_ATM_CLIP),y)
 O_OBJS += clip.o
 NEED_IPCOM = ipcommon.o
@@ -36,10 +32,11 @@
 endif
 
 ifeq ($(CONFIG_ATM_LANE),y)
-O_OBJS += lec.o lane_mpoa_init.o
+O_OBJS += lec.o
+OX_OBJS += lane_mpoa_init.o
 else
   ifeq ($(CONFIG_ATM_LANE),m)
-  O_OBJS += lane_mpoa_init.o
+  OX_OBJS += lane_mpoa_init.o
   M_OBJS += lec.o
   endif
 endif
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/addr.c linux/net/atm/addr.c
--- v2.3.99-pre2/linux/net/atm/addr.c	Fri Sep 10 23:57:37 1999
+++ linux/net/atm/addr.c	Tue Mar 21 23:38:26 2000
@@ -1,11 +1,11 @@
 /* net/atm/addr.c - Local ATM address registry */
 
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
 #include <linux/atm.h>
 #include <linux/atmdev.h>
-#include <linux/wait.h>
+#include <linux/sched.h>
 #include <asm/uaccess.h>
 
 #include "signaling.h"
@@ -41,23 +41,7 @@
  * (which may involve page faults and therefore rescheduling)
  */
 
-
-static volatile int local_lock = 0;
-static wait_queue_head_t local_wait;
-
-
-static void lock_local(void)
-{
-	while (local_lock) sleep_on(&local_wait);
-	local_lock = 1;
-}
-
-
-static void unlock_local(void)
-{
-	local_lock = 0;
-	wake_up(&local_wait);
-}
+static DECLARE_MUTEX(local_lock);
 
 
 static void notify_sigd(struct atm_dev *dev)
@@ -73,13 +57,13 @@
 {
 	struct atm_dev_addr *this;
 
-	lock_local();
+	down(&local_lock);
 	while (dev->local) {
 		this = dev->local;
 		dev->local = this->next;
 		kfree(this);
 	}
-	unlock_local();
+	up(&local_lock);
 	notify_sigd(dev);
 }
 
@@ -91,20 +75,20 @@
 
 	error = check_addr(addr);
 	if (error) return error;
-	lock_local();
+	down(&local_lock);
 	for (walk = &dev->local; *walk; walk = &(*walk)->next)
 		if (identical(&(*walk)->addr,addr)) {
-			unlock_local();
+			up(&local_lock);
 			return -EEXIST;
 		}
 	*walk = kmalloc(sizeof(struct atm_dev_addr),GFP_KERNEL);
 	if (!*walk) {
-		unlock_local();
+		up(&local_lock);
 		return -ENOMEM;
 	}
 	(*walk)->addr = *addr;
 	(*walk)->next = NULL;
-	unlock_local();
+	up(&local_lock);
 	notify_sigd(dev);
 	return 0;
 }
@@ -117,17 +101,17 @@
 
 	error = check_addr(addr);
 	if (error) return error;
-	lock_local();
+	down(&local_lock);
 	for (walk = &dev->local; *walk; walk = &(*walk)->next)
 		if (identical(&(*walk)->addr,addr)) break;
 	if (!*walk) {
-		unlock_local();
+		up(&local_lock);
 		return -ENOENT;
 	}
 	this = *walk;
 	*walk = this->next;
 	kfree(this);
-	unlock_local();
+	up(&local_lock);
 	notify_sigd(dev);
 	return 0;
 }
@@ -138,27 +122,21 @@
 	struct atm_dev_addr *walk;
 	int total;
 
-	lock_local();
+	down(&local_lock);
 	total = 0;
 	for (walk = dev->local; walk; walk = walk->next) {
 		total += sizeof(struct sockaddr_atmsvc);
 		if (total > size) {
-			unlock_local();
+			up(&local_lock);
 			return -E2BIG;
 		}
 		if (copy_to_user(u_buf,&walk->addr,
 		    sizeof(struct sockaddr_atmsvc))) {
-			unlock_local();
+			up(&local_lock);
 			return -EFAULT;
 		}
 		u_buf++;
 	}
-	unlock_local();
+	up(&local_lock);
 	return total;
-}
-
-
-void init_addr(void)
-{
-	init_waitqueue_head(&local_wait);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/addr.h linux/net/atm/addr.h
--- v2.3.99-pre2/linux/net/atm/addr.h	Thu Aug 26 13:05:42 1999
+++ linux/net/atm/addr.h	Tue Mar 21 23:38:26 2000
@@ -1,6 +1,6 @@
 /* net/atm/addr.h - Local ATM address registry */
 
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
 #ifndef NET_ATM_ADDR_H
@@ -14,6 +14,5 @@
 int add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
 int del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
 int get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,int size);
-void init_addr(void);
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/atm_misc.c linux/net/atm/atm_misc.c
--- v2.3.99-pre2/linux/net/atm/atm_misc.c	Thu Feb 10 17:11:23 2000
+++ linux/net/atm/atm_misc.c	Tue Mar 21 23:38:26 2000
@@ -1,12 +1,14 @@
 /* net/atm/atm_misc.c - Various functions for use by ATM drivers */
 
-/* Written 1995-1999 by Werner Almesberger, EPFL ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL ICA */
 
 
 #include <linux/module.h>
 #include <linux/atm.h>
 #include <linux/atmdev.h>
 #include <linux/skbuff.h>
+#include <linux/sonet.h>
+#include <linux/bitops.h>
 #include <asm/atomic.h>
 #include <asm/errno.h>
 
@@ -16,7 +18,7 @@
 	atm_force_charge(vcc,truesize);
 	if (atomic_read(&vcc->rx_inuse) <= vcc->sk->rcvbuf) return 1;
 	atm_return(vcc,truesize);
-	vcc->stats->rx_drop++;
+	atomic_inc(&vcc->stats->rx_drop);
 	return 0;
 }
 
@@ -36,7 +38,7 @@
 		}
 	}
 	atm_return(vcc,guess);
-	vcc->stats->rx_drop++;
+	atomic_inc(&vcc->stats->rx_drop);
 	return NULL;
 }
 
@@ -46,7 +48,7 @@
 	struct atm_vcc *walk;
 
 	for (walk = vcc->dev->vccs; walk; walk = walk->next)
-		if ((walk->flags & ATM_VF_ADDR) && walk->vpi == vpi &&
+		if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vpi == vpi &&
 		    walk->vci == vci && ((walk->qos.txtp.traffic_class !=
 		    ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) ||
 		    (walk->qos.rxtp.traffic_class != ATM_NONE &&
@@ -135,7 +137,25 @@
 }
 
 
+void sonet_copy_stats(struct k_sonet_stats *from,struct sonet_stats *to)
+{
+#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
+	__SONET_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
+void sonet_subtract_stats(struct k_sonet_stats *from,struct sonet_stats *to)
+{
+#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i)
+	__SONET_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
 EXPORT_SYMBOL(atm_charge);
 EXPORT_SYMBOL(atm_alloc_charge);
 EXPORT_SYMBOL(atm_find_ci);
 EXPORT_SYMBOL(atm_pcr_goal);
+EXPORT_SYMBOL(sonet_copy_stats);
+EXPORT_SYMBOL(sonet_subtract_stats);
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/clip.c linux/net/atm/clip.c
--- v2.3.99-pre2/linux/net/atm/clip.c	Sat Feb 26 22:32:07 2000
+++ linux/net/atm/clip.c	Tue Mar 21 23:38:26 2000
@@ -21,6 +21,7 @@
 #include <linux/in.h> /* for struct sockaddr_in */
 #include <linux/if.h> /* for IFF_UP */
 #include <linux/inetdevice.h>
+#include <linux/bitops.h>
 #include <net/route.h> /* for struct rtable and routing */
 #include <net/icmp.h> /* icmp_send */
 #include <asm/param.h> /* for HZ */
@@ -196,6 +197,7 @@
 	atm_return(vcc,skb->truesize);
 	skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs;
 		/* clip_vcc->entry == NULL if we don't have an IP address yet */
+	skb->rx_dev = NULL;
 	if (!skb->dev) {
 		kfree_skb(skb);
 		return;
@@ -431,7 +433,7 @@
 	}
 	clip_priv->stats.tx_packets++;
 	clip_priv->stats.tx_bytes += skb->len;
-	(void) vcc->dev->ops->send(vcc,skb);
+	(void) vcc->send(vcc,skb);
 	if (atm_may_send(vcc,0)) {
 		entry->vccs->xoff = 0;
 		return 0;
@@ -462,7 +464,6 @@
 	struct clip_vcc *clip_vcc;
 	struct sk_buff_head copy;
 	struct sk_buff *skb;
-	unsigned long flags;
 
 	if (!vcc->push) return -EBADFD;
 	clip_vcc = kmalloc(sizeof(struct clip_vcc),GFP_KERNEL);
@@ -477,12 +478,9 @@
 	clip_vcc->idle_timeout = timeout*HZ;
 	clip_vcc->old_push = vcc->push;
 	clip_vcc->old_pop = vcc->pop;
-	save_flags(flags);
-	cli();
 	vcc->push = clip_push;
 	vcc->pop = clip_pop;
 	skb_migrate(&vcc->recvq,&copy);
-	restore_flags(flags);
 	/* re-process everything received between connection setup and MKIP */
 	while ((skb = skb_dequeue(&copy)))
 		if (!clip_devs) {
@@ -710,7 +708,7 @@
 	999,		/* dummy device number */
 	NULL,NULL,	/* pretend not to have any VCCs */
 	NULL,NULL,	/* no data */
-	0,		/* no flags */
+	{ 0 },		/* no flags */
 	NULL,		/* no local address */
 	{ 0 }		/* no ESI, no statistics */
 };
@@ -729,7 +727,8 @@
 		add_timer(&idle_timer);
 	}
 	atmarpd = vcc;
-	vcc->flags |= ATM_VF_READY | ATM_VF_META;
+	set_bit(ATM_VF_META,&vcc->flags);
+	set_bit(ATM_VF_READY,&vcc->flags);
 	    /* allow replies and avoid getting closed if signaling dies */
 	bind_vcc(vcc,&atmarpd_dev);
 	vcc->push = NULL;
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/common.c linux/net/atm/common.c
--- v2.3.99-pre2/linux/net/atm/common.c	Thu Feb 10 17:11:23 2000
+++ linux/net/atm/common.c	Tue Mar 21 23:38:26 2000
@@ -19,9 +19,11 @@
 #include <linux/sched.h>
 #include <linux/time.h>		/* struct timeval */
 #include <linux/skbuff.h>
+#include <linux/bitops.h>
 #include <net/sock.h>		/* struct sock */
 
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 #include <asm/poll.h>
 
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
@@ -96,7 +98,7 @@
 	if (sock->type == SOCK_STREAM) return -EINVAL;
 	if (!(sk = alloc_atm_vcc_sk(family))) return -ENOMEM;
 	vcc = sk->protinfo.af_atm;
-	vcc->flags = 0;
+	memset(&vcc->flags,0,sizeof(vcc->flags));
 	vcc->dev = NULL;
 	vcc->family = sock->ops->family;
 	vcc->alloc_tx = alloc_tx;
@@ -126,7 +128,7 @@
 	struct sk_buff *skb;
 
 	vcc = sk->protinfo.af_atm;
-	vcc->flags &= ~ATM_VF_READY;
+	clear_bit(ATM_VF_READY,&vcc->flags);
 	if (vcc->dev) {
 		if (vcc->dev->ops->close) vcc->dev->ops->close(vcc);
 		if (vcc->push) vcc->push(vcc,NULL); /* atmarpd has no push */
@@ -156,9 +158,8 @@
 
 void atm_async_release_vcc(struct atm_vcc *vcc,int reply)
 {
-	vcc->flags |= ATM_VF_CLOSE;
+	set_bit(ATM_VF_CLOSE,&vcc->flags);
 	vcc->reply = reply;
-	/*vcc->flags &= ~ATM_VF_READY;*/
 	wake_up(&vcc->sleep);
 }
 
@@ -204,6 +205,7 @@
 	if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE))
 		return -EPERM;
 	error = 0;
+	bind_vcc(vcc,dev);
 	switch (vcc->qos.aal) {
 		case ATM_AAL0:
 			error = atm_init_aal0(vcc);
@@ -226,8 +228,10 @@
 	}
 	if (!error) error = adjust_tp(&vcc->qos.txtp,vcc->qos.aal);
 	if (!error) error = adjust_tp(&vcc->qos.rxtp,vcc->qos.aal);
-	if (error) return error;
-	bind_vcc(vcc,dev);
+	if (error) {
+		bind_vcc(vcc,NULL);
+		return error;
+	}
 	DPRINTK("VCC %d.%d, AAL %d\n",vpi,vci,vcc->qos.aal);
 	DPRINTK("  TX: %d, PCR %d..%d, SDU %d\n",vcc->qos.txtp.traffic_class,
 	    vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu);
@@ -257,8 +261,8 @@
 int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci)
 {
 	if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC)
-		vcc->flags &= ~ATM_VF_PARTIAL;
-	else if (vcc->flags & ATM_VF_PARTIAL) return -EINVAL;
+		clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+	else if (test_bit(ATM_VF_PARTIAL,&vcc->flags)) return -EINVAL;
 	printk(KERN_DEBUG "atm_connect (TX: cl %d,bw %d-%d,sdu %d; "
 	    "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n",
 	    vcc->qos.txtp.traffic_class,vcc->qos.txtp.min_pcr,
@@ -267,7 +271,7 @@
 	    vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu,
 	    vcc->qos.aal == ATM_AAL5 ? "" : vcc->qos.aal == ATM_AAL0 ? "" :
 	    " ??? code ",vcc->qos.aal == ATM_AAL0 ? 0 : vcc->qos.aal);
-	if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
+	if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) return -EBADFD;
 	if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
 	    vcc->qos.rxtp.traffic_class == ATM_ANYCLASS)
 		return -EINVAL;
@@ -285,7 +289,7 @@
 		if (!dev) return -ENODEV;
 	}
 	if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
-		vcc->flags |= ATM_VF_PARTIAL;
+		set_bit(ATM_VF_PARTIAL,&vcc->flags);
 	return 0;
 }
 
@@ -300,7 +304,8 @@
 	if (!(vpi || vci)) return -EINVAL;
 	error = atm_connect_vcc(ATM_SD(sock),itf,vpi,vci);
 	if (error) return error;
-	if (ATM_SD(sock)->flags & ATM_VF_READY) sock->state = SS_CONNECTED;
+	if (test_bit(ATM_VF_READY,&ATM_SD(sock)->flags))
+		sock->state = SS_CONNECTED;
 	return 0;
 }
 
@@ -308,11 +313,10 @@
 int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len,
     int flags,struct scm_cookie *scm)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct atm_vcc *vcc;
 	struct sk_buff *skb;
-	unsigned long cpu_flags;
 	int eff_len,error;
-
 	void *buff;
 	int size;
 
@@ -322,28 +326,33 @@
 	buff = m->msg_iov->iov_base;
 	size = m->msg_iov->iov_len;
 	vcc = ATM_SD(sock);
-	save_flags(cpu_flags);
-	cli();
+	add_wait_queue(&vcc->sleep,&wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	error = 1; /* <= 0 is error */
 	while (!(skb = skb_dequeue(&vcc->recvq))) {
-		if (vcc->flags & (ATM_VF_RELEASED | ATM_VF_CLOSE)) {
-			restore_flags(cpu_flags);
-			return vcc->reply;
+		if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+		    test_bit(ATM_VF_CLOSE,&vcc->flags)) {
+			error = vcc->reply;
+			break;
 		}
-		if (!(vcc->flags & ATM_VF_READY)) {
-			restore_flags(cpu_flags);
-			return 0;
+		if (!test_bit(ATM_VF_READY,&vcc->flags)) {
+			error = 0;
+			break;
 		}
 		if (flags & MSG_DONTWAIT) {
-			restore_flags(cpu_flags);
-			return -EAGAIN;
+			error = -EAGAIN;
+			break;
 		}
-		interruptible_sleep_on(&vcc->sleep);
+		schedule();
+		set_current_state(TASK_INTERRUPTIBLE);
 		if (signal_pending(current)) {
-			restore_flags(cpu_flags);
-			return -ERESTARTSYS;
+			error = -ERESTARTSYS;
+			break;
 		}
 	}
-	restore_flags(cpu_flags);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&vcc->sleep,&wait);
+	if (error <= 0) return error;
 	vcc->timestamp = skb->stamp;
 	eff_len = skb->len > size ? size : skb->len;
 	if (vcc->dev->ops->feedback)
@@ -383,10 +392,10 @@
 int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len,
     struct scm_cookie *scm)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct atm_vcc *vcc;
 	struct sk_buff *skb;
 	int eff,error;
-
 	const void *buff;
 	int size;
 
@@ -396,19 +405,40 @@
 	buff = m->msg_iov->iov_base;
 	size = m->msg_iov->iov_len;
 	vcc = ATM_SD(sock);
-	if (vcc->flags & (ATM_VF_RELEASED | ATM_VF_CLOSE)) return vcc->reply;
-	if (!(vcc->flags & ATM_VF_READY)) return -EPIPE;
+	if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+	    test_bit(ATM_VF_CLOSE,&vcc->flags))
+		return vcc->reply;
+	if (!test_bit(ATM_VF_READY,&vcc->flags)) return -EPIPE;
 	if (!size) return 0;
 	/* verify_area is done by net/socket.c */
 	eff = (size+3) & ~3; /* align to word boundary */
+	add_wait_queue(&vcc->wsleep,&wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	error = 0;
 	while (!(skb = vcc->alloc_tx(vcc,eff))) {
-		if (m->msg_flags & MSG_DONTWAIT) return -EAGAIN;
-		interruptible_sleep_on(&vcc->wsleep);
-		if (signal_pending(current)) return -ERESTARTSYS;
-		if (vcc->flags & (ATM_VF_RELEASED | ATM_VF_CLOSE))
-			return vcc->reply;
-		if (!(vcc->flags & ATM_VF_READY)) return -EPIPE;
+		if (m->msg_flags & MSG_DONTWAIT) {
+			error = -EAGAIN;
+			break;
+		}
+		schedule();
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (signal_pending(current)) {
+			error = -ERESTARTSYS;
+			break;
+		}
+		if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+		    test_bit(ATM_VF_CLOSE,&vcc->flags)) {
+			error = vcc->reply;
+			break;
+		}
+		if (!test_bit(ATM_VF_READY,&vcc->flags)) {
+			error = -EPIPE;
+			break;
+		}
 	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&vcc->wsleep,&wait);
+	if (error) return error;
 	skb->dev = NULL; /* for paths shared with net_device interfaces */
 	ATM_SKB(skb)->iovcnt = 0;
 	ATM_SKB(skb)->atm_options = vcc->atm_options;
@@ -433,7 +463,9 @@
 	mask = 0;
 	if (skb_peek(&vcc->recvq) || skb_peek(&vcc->listenq))
 		mask |= POLLIN | POLLRDNORM;
-	if (vcc->flags & (ATM_VF_RELEASED | ATM_VF_CLOSE)) mask |= POLLHUP;
+	if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+	    test_bit(ATM_VF_CLOSE,&vcc->flags))
+		mask |= POLLHUP;
 	if (sock->state != SS_CONNECTING) {
 		if (vcc->qos.txtp.traffic_class != ATM_NONE &&
 		    vcc->qos.txtp.max_sdu+atomic_read(&vcc->tx_inuse)+
@@ -448,20 +480,38 @@
 }
 
 
+static void copy_aal_stats(struct k_atm_aal_stats *from,
+    struct atm_aal_stats *to)
+{
+#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
+	__AAL_STAT_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
+static void subtract_aal_stats(struct k_atm_aal_stats *from,
+    struct atm_aal_stats *to)
+{
+#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i)
+	__AAL_STAT_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
 static int fetch_stats(struct atm_dev *dev,struct atm_dev_stats *arg,int zero)
 {
-	unsigned long flags;
-	int error;
+	struct atm_dev_stats tmp;
+	int error = 0;
 
-	error = 0;
-	save_flags(flags);
-	cli();
-	if (arg)
-		error = copy_to_user(arg,&dev->stats,
-		    sizeof(struct atm_dev_stats));
-        if (zero && !error)
-                memset(&dev->stats,0,sizeof(struct atm_dev_stats));
-        restore_flags(flags);
+	copy_aal_stats(&dev->stats.aal0,&tmp.aal0);
+	copy_aal_stats(&dev->stats.aal34,&tmp.aal34);
+	copy_aal_stats(&dev->stats.aal5,&tmp.aal5);
+	if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
+	if (zero && !error) {
+		subtract_aal_stats(&dev->stats.aal0,&tmp.aal0);
+		subtract_aal_stats(&dev->stats.aal34,&tmp.aal34);
+		subtract_aal_stats(&dev->stats.aal5,&tmp.aal5);
+	}
 	return error ? -EFAULT : 0;
 }
 
@@ -478,7 +528,8 @@
 	switch (cmd) {
 		case SIOCOUTQ:
 			if (sock->state != SS_CONNECTED ||
-			    !(vcc->flags & ATM_VF_READY)) return -EINVAL;
+			    !test_bit(ATM_VF_READY,&vcc->flags))
+				return -EINVAL;
 			return put_user(vcc->sk->sndbuf-
 			    atomic_read(&vcc->tx_inuse)-ATM_PDU_OVHD,
 			    (int *) arg) ? -EFAULT : 0;
@@ -522,6 +573,14 @@
 			return 0;
 		case ATMSIGD_CTRL:
 			if (!capable(CAP_NET_ADMIN)) return -EPERM;
+			/*
+			 * The user/kernel protocol for exchanging signalling
+			 * info uses kernel pointers as opaque references,
+			 * so the holder of the file descriptor can scribble
+			 * on the kernel... so we should make sure that we
+			 * have the same privledges that /proc/kcore needs
+			 */
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
 			error = sigd_attach(vcc);
 			if (!error) sock->state = SS_CONNECTED;
 			return error;
@@ -668,6 +727,12 @@
 			   write the length" */
 			return put_user(size,
 			    &((struct atmif_sioc *) arg)->length) ? -EFAULT : 0;
+		case ATM_SETLOOP:
+			if (__ATM_LM_XTRMT((int) (long) buf) &&
+			    __ATM_LM_XTLOC((int) (long) buf) >
+			    __ATM_LM_XTRMT((int) (long) buf))
+				return -EINVAL;
+			/* fall through */
 		case ATM_SETCIRANGE:
 		case SONET_GETSTATZ:
 		case SONET_SETDIAG:
@@ -689,6 +754,14 @@
 
 int atm_change_qos(struct atm_vcc *vcc,struct atm_qos *qos)
 {
+	/*
+	 * Don't let the QoS change the already connected AAL type nor the
+	 * traffic class.
+	 */
+	if (qos->aal != vcc->qos.aal ||
+	    qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class ||
+	    qos->txtp.traffic_class != vcc->qos.txtp.traffic_class)
+		return -EINVAL;
 	if (!vcc->dev->ops->change_qos) return -EOPNOTSUPP;
 	if (vcc->family == AF_ATMPVC)
 		return vcc->dev->ops->change_qos(vcc,qos,ATM_MF_SET);
@@ -751,7 +824,7 @@
 				if (sock->state != SS_UNCONNECTED)
 					return -EBADFD;
 				vcc->qos = qos;
-				vcc->flags |= ATM_VF_HASQOS;
+				set_bit(ATM_VF_HASQOS,&vcc->flags);
 				return 0;
 			}
 		case SO_SETCLP:
@@ -777,7 +850,8 @@
 	vcc = ATM_SD(sock);
 	switch (optname) {
 		case SO_ATMQOS:
-			if (!(vcc->flags & ATM_VF_HASQOS)) return -EINVAL;
+			if (!test_bit(ATM_VF_HASQOS,&vcc->flags))
+				return -EINVAL;
 			return copy_to_user(optval,&vcc->qos,sizeof(vcc->qos)) ?
 			    -EFAULT : 0;
 		case SO_SETCLP:
@@ -787,7 +861,8 @@
 			{
 				struct sockaddr_atmpvc pvc;
 
-				if (!vcc->dev || !(vcc->flags & ATM_VF_ADDR))
+				if (!vcc->dev ||
+				    !test_bit(ATM_VF_ADDR,&vcc->flags))
 					return -ENOTCONN;
 				pvc.sap_family = AF_ATMPVC;
 				pvc.sap_addr.itf = vcc->dev->number;
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/ipcommon.c linux/net/atm/ipcommon.c
--- v2.3.99-pre2/linux/net/atm/ipcommon.c	Thu Aug 26 13:05:42 1999
+++ linux/net/atm/ipcommon.c	Tue Mar 21 23:38:26 2000
@@ -1,6 +1,6 @@
 /* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */
 
-/* Written 1996,1997 by Werner Almesberger, EPFL LRC */
+/* Written 1996-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
 #include <linux/string.h>
@@ -32,20 +32,26 @@
 
 /*
  * skb_migrate moves the list at FROM to TO, emptying FROM in the process.
- * This function should live in skbuff.c or skbuff.h. Note that skb_migrate
- * is not atomic, so turn off interrupts when using it.
+ * This function should live in skbuff.c or skbuff.h.
  */
 
 
 void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to)
 {
-	struct sk_buff *skb,*prev;
+	struct sk_buff *skb;
+	unsigned long flags;
 
-	for (skb = ((struct sk_buff *) from)->next;
-	    skb != (struct sk_buff *) from; skb = skb->next) skb->list = to;
-	prev = from->prev;
-	from->next->prev = (struct sk_buff *) to;
-	prev->next = (struct sk_buff *) to;
+	spin_lock_irqsave(&from->lock,flags);
 	*to = *from;
-	skb_queue_head_init(from);
+	from->prev = (struct sk_buff *) from;
+	from->next = (struct sk_buff *) from;
+	from->qlen = 0;
+	spin_unlock_irqrestore(&from->lock,flags);
+	spin_lock_init(&to->lock);
+	for (skb = ((struct sk_buff *) to)->next;
+	    skb != (struct sk_buff *) from; skb = skb->next) skb->list = to;
+	if (to->next == (struct sk_buff *) from)
+		to->next = (struct sk_buff *) to;
+	to->next->prev = (struct sk_buff *) to;
+	to->prev->next = (struct sk_buff *) to;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/ipcommon.h linux/net/atm/ipcommon.h
--- v2.3.99-pre2/linux/net/atm/ipcommon.h	Thu Aug 26 13:05:42 1999
+++ linux/net/atm/ipcommon.h	Tue Mar 21 23:38:26 2000
@@ -1,6 +1,6 @@
 /* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */
 
-/* Written 1996-1998 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1996-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
 #ifndef NET_ATM_IPCOMMON_H
@@ -15,6 +15,10 @@
 
 extern struct net_device *clip_devs;
 
+/*
+ * Moves all skbs from "from" to "to". The operation is atomic for "from", but
+ * not for "to". "to" may only be accessed after skb_migrate finishes.
+ */
 
 void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/lane_mpoa_init.c linux/net/atm/lane_mpoa_init.c
--- v2.3.99-pre2/linux/net/atm/lane_mpoa_init.c	Thu Aug 26 13:05:42 1999
+++ linux/net/atm/lane_mpoa_init.c	Tue Mar 21 23:38:26 2000
@@ -37,6 +37,18 @@
 #endif
 
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+rwlock_t lane_bridge_hook_lock = RW_LOCK_UNLOCKED;
+struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
+						unsigned char *addr) = NULL;
+void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent) = NULL;
+#if defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_BRIDGE_MODULE)
+EXPORT_SYMBOL(lane_bridge_hook_lock);
+EXPORT_SYMBOL(br_fdb_get_hook);
+EXPORT_SYMBOL(br_fdb_put_hook);
+#endif /* defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_BRIDGE_MODULE) */
+#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
+
 void atm_lane_init(void)
 {
 #ifndef CONFIG_ATM_LANE_MODULE /* not module */
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/lec.c linux/net/atm/lec.c
--- v2.3.99-pre2/linux/net/atm/lec.c	Sat Feb 26 22:32:07 2000
+++ linux/net/atm/lec.c	Tue Mar 21 23:38:26 2000
@@ -6,6 +6,7 @@
 
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/bitops.h>
 
 /* We are ethernet device */
 #include <linux/if_ether.h>
@@ -29,9 +30,11 @@
 #include <linux/atmdev.h>
 #include <linux/atmlec.h>
 
-/* Bridge */
-#ifdef CONFIG_BRIDGE
-#include <net/br.h>
+/* Proxy LEC knows about bridging */
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#include <linux/if_bridge.h>
+#include "../bridge/br_private.h"
+unsigned char bridge_ula[] = {0x01, 0x80, 0xc2, 0x00, 0x00};
 #endif
 
 /* Modular too */
@@ -88,23 +91,19 @@
         return &dev_lec[0];
 }
 
-#ifdef CONFIG_BRIDGE
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
 static void handle_bridge(struct sk_buff *skb, struct net_device *dev)
 {
         struct ethhdr *eth;
         char *buff;
         struct lec_priv *priv;
-        unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
 
         /* Check if this is a BPDU. If so, ask zeppelin to send
-         * LE_TOPOLOGY_REQUEST with the value of Topology Change bit
-         * in the Config BPDU*/
+         * LE_TOPOLOGY_REQUEST with the same value of Topology Change bit
+         * as the Config BPDU has */
         eth = (struct ethhdr *)skb->data;
         buff = skb->data + skb->dev->hard_header_len;
-        if ((memcmp(eth->h_dest, bridge_ula, ETH_ALEN) == 0) &&
-            *buff++ == BRIDGE_LLC1_DSAP &&
-            *buff++ == BRIDGE_LLC1_SSAP &&
-            *buff++ == BRIDGE_LLC1_CTRL) {
+        if (*buff++ == 0x42 && *buff++ == 0x42 && *buff++ == 0x03) {
                 struct sk_buff *skb2;
                 struct atmlec_msg *mesg;
 
@@ -113,7 +112,8 @@
                 skb2->len = sizeof(struct atmlec_msg);
                 mesg = (struct atmlec_msg *)skb2->data;
                 mesg->type = l_topology_change;
-                mesg->content.normal.flag = *(skb->nh.raw + BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) & TOPOLOGY_CHANGE;
+                buff += 4;
+                mesg->content.normal.flag = *buff & 0x01; /* 0x01 is topology change */
 
                 priv = (struct lec_priv *)dev->priv;
                 atm_force_charge(priv->lecd, skb2->truesize);
@@ -123,7 +123,7 @@
 
         return;
 }
-#endif /* CONFIG_BRIDGE */
+#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
 
 /*
  * Modelled after tr_type_trans
@@ -219,10 +219,10 @@
         DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
                 (long)skb->head, (long)skb->data, (long)skb->tail,
                 (long)skb->end);
-#ifdef CONFIG_BRIDGE
-        if (skb->pkt_bridged == IS_BRIDGED)
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+        if (memcmp(skb->data, bridge_ula, sizeof(bridge_ula)) == 0)
                 handle_bridge(skb, dev);
-#endif /* CONFIG_BRIDGE */
+#endif
 
         /* Make sure we have room for lec_id */
         if (skb_headroom(skb) < 2) {
@@ -303,7 +303,7 @@
         send_vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry);
         DPRINTK("%s:send_vcc:%p vcc_flags:%x, entry:%p\n", dev->name,
                 send_vcc, send_vcc?send_vcc->flags:0, entry);
-        if (!send_vcc || !(send_vcc->flags & ATM_VF_READY)) {    
+        if (!send_vcc || !test_bit(ATM_VF_READY,&send_vcc->flags)) {    
                 if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
                         DPRINTK("%s:lec_send_packet: queuing packet, ", dev->name);
                         DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
@@ -339,7 +339,7 @@
                         send_vcc->vpi, send_vcc->vci);       
                 priv->stats.tx_packets++;
                 priv->stats.tx_bytes += skb2->len;
-                send_vcc->dev->ops->send(send_vcc, skb2);
+                send_vcc->send(send_vcc, skb2);
         }
 
         ATM_SKB(skb)->vcc = send_vcc;
@@ -348,7 +348,7 @@
         ATM_SKB(skb)->atm_options = send_vcc->atm_options;
         priv->stats.tx_packets++;
         priv->stats.tx_bytes += skb->len;
-        send_vcc->dev->ops->send(send_vcc, skb);
+        send_vcc->send(send_vcc, skb);
 
 #if 0
         /* Should we wait for card's device driver to notify us? */
@@ -463,32 +463,44 @@
                 priv->lecid=(unsigned short)(0xffff&mesg->content.normal.flag);
                 break;
         case l_should_bridge: {
-#ifdef CONFIG_BRIDGE
-                struct fdb *f;
-                extern Port_data port_info[];
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+                struct net_bridge_fdb_entry *f;
 
                 DPRINTK("%s: bridge zeppelin asks about 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
                         dev->name,
                         mesg->content.proxy.mac_addr[0], mesg->content.proxy.mac_addr[1],
                         mesg->content.proxy.mac_addr[2], mesg->content.proxy.mac_addr[3],
                         mesg->content.proxy.mac_addr[4], mesg->content.proxy.mac_addr[5]);
-                f = br_avl_find_addr(mesg->content.proxy.mac_addr); /* bridge/br.c */
+
+                read_lock(&lane_bridge_hook_lock);
+                if (br_fdb_get_hook == NULL || dev->br_port == NULL) {
+                        read_unlock(&lane_bridge_hook_lock);
+                        break;
+                }
+
+                f = br_fdb_get_hook(dev->br_port->br, mesg->content.proxy.mac_addr);
                 if (f != NULL &&
-                    port_info[f->port].dev != dev &&
-                    port_info[f->port].state == Forwarding) {
+                    f->dst->dev != dev &&
+                    f->dst->state == BR_STATE_FORWARDING) {
                                 /* hit from bridge table, send LE_ARP_RESPONSE */
                         struct sk_buff *skb2;
 
                         DPRINTK("%s: entry found, responding to zeppelin\n", dev->name);
                         skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
-                        if (skb2 == NULL) break;
+                        if (skb2 == NULL) {
+                                br_fdb_put_hook(f);
+                                read_unlock(&lane_bridge_hook_lock);
+                                break;
+                        }
                         skb2->len = sizeof(struct atmlec_msg);
                         memcpy(skb2->data, mesg, sizeof(struct atmlec_msg));
                         atm_force_charge(priv->lecd, skb2->truesize);
                         skb_queue_tail(&priv->lecd->recvq, skb2);
                         wake_up(&priv->lecd->sleep);
                 }
-#endif /* CONFIG_BRIDGE */
+                if (f != NULL) br_fdb_put_hook(f);
+                read_unlock(&lane_bridge_hook_lock);
+#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
                 }
                 break;
         default:
@@ -537,7 +549,7 @@
         999,	    /*dummy device number*/
         NULL,NULL,  /*no VCCs*/
         NULL,NULL,  /*no data*/
-        0,	    /*no flags*/
+        { 0 },	    /*no flags*/
         NULL,	    /* no local address*/
         { 0 }	    /*no ESI or rest of the atm_dev struct things*/
 };
@@ -684,6 +696,7 @@
                         lec_arp_check_empties(priv, vcc, skb);
                 }
                 skb->dev = dev;
+		skb->rx_dev = NULL;
                 skb->data += 2; /* skip lec_id */
 #ifdef CONFIG_TR
                 if (priv->is_trdev) skb->protocol = tr_type_trans(skb, dev);
@@ -777,7 +790,8 @@
         bind_vcc(vcc, &lecatm_dev);
         
         vcc->proto_data = dev_lec[i];
-        vcc->flags |= ATM_VF_READY | ATM_VF_META;
+	set_bit(ATM_VF_META,&vcc->flags);
+	set_bit(ATM_VF_READY,&vcc->flags);
 
         /* Set default values to these variables */
         priv->maximum_unknown_frame_count = 1;
@@ -1055,8 +1069,8 @@
         if (entry->vcc) {
                 entry->vcc->push = entry->old_push;
 #if 0 /* August 6, 1998 */
-                entry->vcc->flags |= ATM_VF_RELEASED;
-                entry->vcc->flags &= ~ATM_VF_READY;
+                set_bit(ATM_VF_RELEASED,&entry->vcc->flags);
+		clear_bit(ATM_VF_READY,&entry->vcc->flags);
                 entry->vcc->push(entry->vcc, NULL);
 #endif
 		atm_async_release_vcc(entry->vcc, -EPIPE);
@@ -1065,8 +1079,8 @@
         if (entry->recv_vcc) {
                 entry->recv_vcc->push = entry->old_recv_push;
 #if 0
-                entry->recv_vcc->flags |= ATM_VF_RELEASED;
-                entry->recv_vcc->flags &= ~ATM_VF_READY;
+                set_bit(ATM_VF_RELEASED,&entry->vcc->flags);
+		clear_bit(ATM_VF_READY,&entry->vcc->flags);
                 entry->recv_vcc->push(entry->recv_vcc, NULL);
 #endif
 		atm_async_release_vcc(entry->recv_vcc, -EPIPE);
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/lec.h linux/net/atm/lec.h
--- v2.3.99-pre2/linux/net/atm/lec.h	Fri Sep 10 23:57:38 1999
+++ linux/net/atm/lec.h	Wed Mar 22 22:22:05 2000
@@ -9,9 +9,18 @@
 #ifndef _LEC_H_
 #define _LEC_H_
 
+#include <linux/config.h>
 #include <linux/atmdev.h>
 #include <linux/netdevice.h>
 #include <linux/atmlec.h>
+
+#if defined (CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#include <linux/if_bridge.h>
+extern rwlock_t lane_bridge_hook_lock;
+struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
+                                                unsigned char *addr);
+void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
+#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
 
 #define LEC_HEADER_LEN 16
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/mpc.c linux/net/atm/mpc.c
--- v2.3.99-pre2/linux/net/atm/mpc.c	Thu Feb 10 17:11:23 2000
+++ linux/net/atm/mpc.c	Tue Mar 21 23:38:26 2000
@@ -2,6 +2,7 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/bitops.h>
 
 /* We are an ethernet device */
 #include <linux/if_ether.h>
@@ -522,7 +523,7 @@
         atomic_add(skb->truesize, &entry->shortcut->tx_inuse);
 	ATM_SKB(skb)->iovcnt = 0; /* just to be safe ... */
 	ATM_SKB(skb)->atm_options = entry->shortcut->atm_options;
-        entry->shortcut->dev->ops->send(entry->shortcut, skb);
+        entry->shortcut->send(entry->shortcut, skb);
 	entry->packets_fwded++;
 
         return 0;
@@ -739,7 +740,7 @@
         NULL,           /* last VCC             */
         NULL,           /* per-device data      */
         NULL,           /* private PHY data     */
-        0,              /* device flags         */
+        { 0 },          /* device flags         */
         NULL,           /* local ATM address    */
         { 0 }           /* no ESI               */
         /* rest of the members will be 0 */
@@ -780,7 +781,8 @@
 
         mpc->mpoad_vcc = vcc;
         bind_vcc(vcc, &mpc_dev);
-        vcc->flags |= ATM_VF_READY | ATM_VF_META;
+	set_bit(ATM_VF_META,&vcc->flags);
+	set_bit(ATM_VF_READY,&vcc->flags);
 
         if (mpc->dev) {
                 char empty[ATM_ESA_LEN];
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/proc.c linux/net/atm/proc.c
--- v2.3.99-pre2/linux/net/atm/proc.c	Sat Feb 26 22:32:08 2000
+++ linux/net/atm/proc.c	Tue Mar 21 23:38:26 2000
@@ -33,6 +33,7 @@
 #include <linux/if_arp.h>
 #include <linux/init.h> /* for __init */
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 #include <asm/param.h> /* for HZ */
 #include "resources.h"
 #include "common.h" /* atm_proc_init prototype */
@@ -64,10 +65,12 @@
 };
 
 static void add_stats(char *buf,const char *aal,
-  const struct atm_aal_stats *stats)
+  const struct k_atm_aal_stats *stats)
 {
-	sprintf(strchr(buf,0),"%s ( %d %d %d %d %d )",aal,stats->tx,
-	    stats->tx_err,stats->rx,stats->rx_err,stats->rx_drop);
+	sprintf(strchr(buf,0),"%s ( %d %d %d %d %d )",aal,
+	    atomic_read(&stats->tx),atomic_read(&stats->tx_err),
+	    atomic_read(&stats->rx),atomic_read(&stats->rx_err),
+	    atomic_read(&stats->rx_drop));
 }
 
 
@@ -217,7 +220,7 @@
 		default:
 			here += sprintf(here,"%3d",vcc->family);
 	}
-	here += sprintf(here," %04x  %5d %7d/%7d %7d/%7d\n",vcc->flags,
+	here += sprintf(here," %04x  %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
 	    vcc->reply,
 	    atomic_read(&vcc->tx_inuse),vcc->sk->sndbuf,
 	    atomic_read(&vcc->rx_inuse),vcc->sk->rcvbuf);
@@ -583,7 +586,7 @@
 	struct proc_dir_entry *devices = NULL,*pvc = NULL,*svc = NULL;
 	struct proc_dir_entry *arp = NULL,*lec = NULL,*vc = NULL;
 
-	atm_proc_root = proc_mkdir("atm", &proc_root);
+	atm_proc_root = proc_mkdir("net/atm",NULL);
 	if (!atm_proc_root)
 		return -ENOMEM;
 	CREATE_ENTRY(devices);
@@ -605,6 +608,6 @@
 	if (arp) remove_proc_entry("arp",atm_proc_root);
 	if (lec) remove_proc_entry("lec",atm_proc_root);
 	if (vc) remove_proc_entry("vc",atm_proc_root);
-	remove_proc_entry("atm",&proc_root);
+	remove_proc_entry("net/atm",NULL);
 	return -ENOMEM;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/pvc.c linux/net/atm/pvc.c
--- v2.3.99-pre2/linux/net/atm/pvc.c	Fri Sep 10 23:57:38 1999
+++ linux/net/atm/pvc.c	Tue Mar 21 23:38:26 2000
@@ -1,6 +1,6 @@
 /* net/atm/pvc.c - ATM PVC sockets */
 
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
 #include <linux/config.h>
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>	/* printk */
 #include <linux/init.h>
 #include <linux/skbuff.h>
+#include <linux/bitops.h>
 #include <net/sock.h>		/* for sock_no_* */
 #ifdef CONFIG_ATM_CLIP
 #include <net/atmclip.h>
@@ -42,8 +43,8 @@
 	addr = (struct sockaddr_atmpvc *) sockaddr;
 	if (addr->sap_family != AF_ATMPVC) return -EAFNOSUPPORT;
 	vcc = ATM_SD(sock);
-	if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
-	if (vcc->flags & ATM_VF_PARTIAL) {
+	if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) return -EBADFD;
+	if (test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
 		if (vcc->vpi != ATM_VPI_UNSPEC) addr->sap_addr.vpi = vcc->vpi;
 		if (vcc->vci != ATM_VCI_UNSPEC) addr->sap_addr.vci = vcc->vci;
 	}
@@ -65,7 +66,7 @@
 	struct sockaddr_atmpvc *addr;
 	struct atm_vcc *vcc = ATM_SD(sock);
 
-	if (!vcc->dev || !(vcc->flags & ATM_VF_ADDR)) return -ENOTCONN;
+	if (!vcc->dev || !test_bit(ATM_VF_ADDR,&vcc->flags)) return -ENOTCONN;
         *sockaddr_len = sizeof(struct sockaddr_atmpvc);
 	addr = (struct sockaddr_atmpvc *) sockaddr;
 	addr->sap_family = AF_ATMPVC;
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/raw.c linux/net/atm/raw.c
--- v2.3.99-pre2/linux/net/atm/raw.c	Sat Feb 26 22:32:08 2000
+++ linux/net/atm/raw.c	Tue Mar 21 23:38:26 2000
@@ -38,16 +38,34 @@
 {
 	DPRINTK("APopR (%d) %d -= %d\n",vcc->vci,vcc->tx_inuse,skb->truesize);
 	atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
-	dev_kfree_skb_irq(skb);
+	dev_kfree_skb_any(skb);
 	wake_up(&vcc->wsleep);
 }
 
 
+static int atm_send_aal0(struct atm_vcc *vcc,struct sk_buff *skb)
+{
+	/*
+	 * Note that if vpi/vci are _ANY or _UNSPEC the below will
+	 * still work
+	 */
+	if (!capable(CAP_NET_ADMIN) &&
+            (((u32 *) skb->data)[0] & (ATM_HDR_VPI_MASK | ATM_HDR_VCI_MASK)) !=
+            ((vcc->vpi << ATM_HDR_VPI_SHIFT) | (vcc->vci << ATM_HDR_VCI_SHIFT)))
+	    {
+		kfree_skb(skb);
+		return -EADDRNOTAVAIL;
+        }
+	return vcc->dev->ops->send(vcc,skb);
+}
+
+
 int atm_init_aal0(struct atm_vcc *vcc)
 {
 	vcc->push = atm_push_raw;
 	vcc->pop = atm_pop_raw;
 	vcc->push_oam = NULL;
+	vcc->send = atm_send_aal0;
 	return 0;
 }
 
@@ -57,6 +75,7 @@
 	vcc->push = atm_push_raw;
 	vcc->pop = atm_pop_raw;
 	vcc->push_oam = NULL;
+	vcc->send = vcc->dev->ops->send;
 	return 0;
 }
 
@@ -66,6 +85,7 @@
 	vcc->push = atm_push_raw;
 	vcc->pop = atm_pop_raw;
 	vcc->push_oam = NULL;
+	vcc->send = vcc->dev->ops->send;
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/resources.c linux/net/atm/resources.c
--- v2.3.99-pre2/linux/net/atm/resources.c	Thu Feb 10 17:11:24 2000
+++ linux/net/atm/resources.c	Tue Mar 21 23:38:26 2000
@@ -1,6 +1,6 @@
 /* net/atm/resources.c - Staticly allocated resources */
 
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
 #include <linux/config.h>
@@ -9,6 +9,7 @@
 #include <linux/atmdev.h>
 #include <linux/kernel.h> /* for barrier */
 #include <linux/module.h>
+#include <linux/bitops.h>
 #include <net/sock.h>	 /* for struct sock */
 #include <asm/segment.h> /* for get_fs_long and put_fs_long */
 
@@ -66,7 +67,7 @@
 
 
 struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
-    int number,unsigned long flags)
+    int number,atm_dev_flags_t *flags)
 {
 	struct atm_dev *dev;
 
@@ -91,8 +92,9 @@
 	dev->dev_data = NULL;
 	barrier();
 	dev->ops = ops;
-	dev->flags = flags;
-	memset((void *) &dev->stats,0,sizeof(struct atm_dev_stats));
+	if (flags) dev->flags = *flags;
+	else memset(&dev->flags,0,sizeof(dev->flags));
+	memset((void *) &dev->stats,0,sizeof(dev->stats));
 #ifdef CONFIG_PROC_FS
 	if (ops->proc_read)
 		if (atm_proc_dev_register(dev) < 0) {
@@ -118,7 +120,7 @@
 void shutdown_atm_dev(struct atm_dev *dev)
 {
 	if (dev->vccs) {
-		dev->flags |= ATM_DF_CLOSE;
+		set_bit(ATM_DF_CLOSE,&dev->flags);
 		return;
 	}
 	if (dev->ops->dev_close) dev->ops->dev_close(dev);
@@ -165,7 +167,7 @@
 	if (vcc->next) vcc->next->prev = vcc->prev;
 	else if (vcc->dev) vcc->dev->last = vcc->prev;
 	if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs &&
-	    (vcc->dev->flags & ATM_DF_CLOSE))
+	    test_bit(ATM_DF_CLOSE,&vcc->dev->flags))
 		shutdown_atm_dev(vcc->dev);
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/signaling.c linux/net/atm/signaling.c
--- v2.3.99-pre2/linux/net/atm/signaling.c	Thu Feb 10 17:11:24 2000
+++ linux/net/atm/signaling.c	Tue Mar 21 23:38:26 2000
@@ -12,6 +12,7 @@
 #include <linux/atmsap.h>
 #include <linux/atmsvc.h>
 #include <linux/atmdev.h>
+#include <linux/bitops.h>
 
 #include "resources.h"
 #include "signaling.h"
@@ -30,29 +31,33 @@
 
 
 struct atm_vcc *sigd = NULL;
-static wait_queue_head_t sigd_sleep;
+static DECLARE_WAIT_QUEUE_HEAD(sigd_sleep);
 
 
 static void sigd_put_skb(struct sk_buff *skb)
 {
 #ifdef WAIT_FOR_DEMON
 	static unsigned long silence = 0;
-#endif
+	DECLARE_WAITQUEUE(wait,current);
 
+	add_wait_queue(&sigd_sleep,&wait);
 	while (!sigd) {
-#ifdef WAIT_FOR_DEMON
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (time_after(jiffies, silence) || silence == 0) {
 			printk(KERN_INFO "atmsvc: waiting for signaling demon "
 			    "...\n");
 			silence = (jiffies+30*HZ)|1;
 		}
-		sleep_on(&sigd_sleep);
+		schedule();
+	}
+	remove_wait_queue(&sigd_sleep,&wait);
 #else
+	if (!sigd) {
 		printk(KERN_WARNING "atmsvc: no signaling demon\n");
 		kfree_skb(skb);
 		return;
-#endif
 	}
+#endif
 	atm_force_charge(sigd,skb->truesize);
 	skb_queue_tail(&sigd->recvq,skb);
 	wake_up(&sigd->sleep);
@@ -63,7 +68,8 @@
 {
 	struct sk_buff *skb;
 
-	if ((vcc->flags & ATM_VF_RELEASED) || !(vcc->flags & ATM_VF_READY))
+	if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+	    !test_bit(ATM_VF_READY,&vcc->flags))
 		return;
 	msg->type = as_error;
 	if (!vcc->dev->ops->change_qos) msg->reply = -EOPNOTSUPP;
@@ -114,7 +120,8 @@
 				session_vcc->qos = msg->qos;
 			break;
 		case as_error:
-			vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_READY);
+			clear_bit(ATM_VF_REGIS,&vcc->flags);
+			clear_bit(ATM_VF_READY,&vcc->flags);
 			vcc->reply = msg->reply;
 			break;
 		case as_indicate:
@@ -133,8 +140,8 @@
 			}
 			return 0;
 		case as_close:
-			vcc->flags |= ATM_VF_RELEASED;
-			vcc->flags &= ~ATM_VF_READY;
+			set_bit(ATM_VF_RELEASED,&vcc->flags);
+			clear_bit(ATM_VF_READY,&vcc->flags);
 			vcc->reply = msg->reply;
 			break;
 		case as_modify:
@@ -177,7 +184,7 @@
 	if (!pvc) memset(&msg->pvc,0,sizeof(msg->pvc));
 	else msg->pvc = *pvc;
 	sigd_put_skb(skb);
-	if (vcc) vcc->flags |= ATM_VF_REGIS;
+	if (vcc) set_bit(ATM_VF_REGIS,&vcc->flags);
 }
 
 
@@ -185,8 +192,8 @@
 {
 	while (vcc) {
 		if (vcc->family == PF_ATMSVC &&
-		    !(vcc->flags & ATM_VF_META)) {
-			vcc->flags |= ATM_VF_RELEASED;
+		    !test_bit(ATM_VF_META,&vcc->flags)) {
+			set_bit(ATM_VF_RELEASED,&vcc->flags);
 			vcc->reply = -EUNATCH;
 			wake_up(&vcc->sleep);
 		}
@@ -223,7 +230,7 @@
 	999,		/* dummy device number */
 	NULL,NULL,	/* pretend not to have any VCCs */
 	NULL,NULL,	/* no data */
-	0,		/* no flags */
+	{ 0 },		/* no flags */
 	NULL,		/* no local address */
 	{ 0 }		/* no ESI, no statistics */
 };
@@ -235,13 +242,8 @@
 	DPRINTK("sigd_attach\n");
 	sigd = vcc;
 	bind_vcc(vcc,&sigd_dev);
-	vcc->flags |= ATM_VF_READY | ATM_VF_META;
+	set_bit(ATM_VF_META,&vcc->flags);
+	set_bit(ATM_VF_READY,&vcc->flags);
 	wake_up(&sigd_sleep);
 	return 0;
-}
-
-
-void signaling_init(void)
-{
-	init_waitqueue_head(&sigd_sleep);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/signaling.h linux/net/atm/signaling.h
--- v2.3.99-pre2/linux/net/atm/signaling.h	Thu Feb 10 17:11:24 2000
+++ linux/net/atm/signaling.h	Tue Mar 21 23:38:26 2000
@@ -21,6 +21,5 @@
     struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
     const struct sockaddr_atmsvc *svc);
 int sigd_attach(struct atm_vcc *vcc);
-void signaling_init(void);
 
 #endif
diff -u --recursive --new-file v2.3.99-pre2/linux/net/atm/svc.c linux/net/atm/svc.c
--- v2.3.99-pre2/linux/net/atm/svc.c	Thu Feb 10 17:11:24 2000
+++ linux/net/atm/svc.c	Tue Mar 21 23:38:26 2000
@@ -17,6 +17,7 @@
 #include <linux/atmsap.h>
 #include <linux/atmsvc.h>
 #include <linux/atmdev.h>
+#include <linux/bitops.h>
 #include <net/sock.h>		/* for sock_no_* */
 #include <asm/uaccess.h>
 
@@ -59,13 +60,18 @@
 
 static void svc_disconnect(struct atm_vcc *vcc)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct sk_buff *skb;
 
 	DPRINTK("svc_disconnect %p\n",vcc);
-	if (vcc->flags & ATM_VF_REGIS) {
+	if (test_bit(ATM_VF_REGIS,&vcc->flags)) {
 		sigd_enq(vcc,as_close,NULL,NULL,NULL);
-		while (!(vcc->flags & ATM_VF_RELEASED) && sigd)
-		    sleep_on(&vcc->sleep);
+		add_wait_queue(&vcc->sleep,&wait);
+		while (!test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule();
+		}
+		remove_wait_queue(&vcc->sleep,&wait);
 	}
 	/* beware - socket is still in use by atmsigd until the last
 	   as_indicate has been answered */
@@ -75,8 +81,10 @@
 							       the reason */
 		dev_kfree_skb(skb);
 	}
-	vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_RELEASED | ATM_VF_CLOSE);
-	    /* may retry later */
+	clear_bit(ATM_VF_REGIS,&vcc->flags);
+	clear_bit(ATM_VF_RELEASED,&vcc->flags);
+	clear_bit(ATM_VF_CLOSE,&vcc->flags);
+	/* ... may retry later */
 }
 
 
@@ -87,7 +95,7 @@
 	if (!sock->sk) return 0;
 	vcc = ATM_SD(sock);
 	DPRINTK("svc_release %p\n",vcc);
-	vcc->flags &= ~ATM_VF_READY;
+	clear_bit(ATM_VF_READY,&vcc->flags);
 	atm_release_vcc_sk(sock->sk,0);
 	svc_disconnect(vcc);
 	    /* VCC pointer is used as a reference, so we must not free it
@@ -101,6 +109,7 @@
 static int svc_bind(struct socket *sock,struct sockaddr *sockaddr,
     int sockaddr_len)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct sockaddr_atmsvc *addr;
 	struct atm_vcc *vcc;
 
@@ -108,19 +117,25 @@
 	if (sock->state == SS_CONNECTED) return -EISCONN;
 	if (sock->state != SS_UNCONNECTED) return -EINVAL;
 	vcc = ATM_SD(sock);
-	if (vcc->flags & ATM_VF_SESSION) return -EINVAL;
+	if (test_bit(ATM_VF_SESSION,&vcc->flags)) return -EINVAL;
 	addr = (struct sockaddr_atmsvc *) sockaddr;
 	if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
-	vcc->flags &= ~ATM_VF_BOUND; /* failing rebind will kill old binding */
+	clear_bit(ATM_VF_BOUND,&vcc->flags);
+	    /* failing rebind will kill old binding */
 	/* @@@ check memory (de)allocation on rebind */
-	if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
+	if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) return -EBADFD;
 	vcc->local = *addr;
 	vcc->reply = WAITING;
 	sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local);
-	while (vcc->reply == WAITING && sigd) sleep_on(&vcc->sleep);
-	vcc->flags &= ~ATM_VF_REGIS; /* doesn't count */
+	add_wait_queue(&vcc->sleep,&wait);
+	while (vcc->reply == WAITING && sigd) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	remove_wait_queue(&vcc->sleep,&wait);
+	clear_bit(ATM_VF_REGIS,&vcc->flags); /* doesn't count */
 	if (!sigd) return -EUNATCH;
-        if (!vcc->reply) vcc->flags |= ATM_VF_BOUND;
+        if (!vcc->reply) set_bit(ATM_VF_BOUND,&vcc->flags);
 	return vcc->reply;
 }
 
@@ -128,6 +143,7 @@
 static int svc_connect(struct socket *sock,struct sockaddr *sockaddr,
     int sockaddr_len,int flags)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct sockaddr_atmsvc *addr;
 	struct atm_vcc *vcc = ATM_SD(sock);
 	int error;
@@ -141,11 +157,13 @@
 		if (vcc->reply) return vcc->reply;
 	}
 	else {
+		int error;
+
 		if (sock->state != SS_UNCONNECTED) return -EINVAL;
-		if (vcc->flags & ATM_VF_SESSION) return -EINVAL;
+		if (test_bit(ATM_VF_SESSION,&vcc->flags)) return -EINVAL;
 		addr = (struct sockaddr_atmsvc *) sockaddr;
 		if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
-		if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
+		if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) return -EBADFD;
 		if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
 		    vcc->qos.rxtp.traffic_class == ATM_ANYCLASS)
 			return -EINVAL;
@@ -158,34 +176,46 @@
 			sock->state = SS_CONNECTING;
 			return -EINPROGRESS;
 		}
+		add_wait_queue(&vcc->sleep,&wait);
+		error = 0;
 		while (vcc->reply == WAITING && sigd) {
-			interruptible_sleep_on(&vcc->sleep);
-			if (signal_pending(current)) {
-				DPRINTK("*ABORT*\n");
-				/*
-				 * This is tricky:
-				 *   Kernel ---close--> Demon
-				 *   Kernel <--close--- Demon
-			         * or
-				 *   Kernel ---close--> Demon
-				 *   Kernel <--error--- Demon
-				 * or
-				 *   Kernel ---close--> Demon
-				 *   Kernel <--okay---- Demon
-				 *   Kernel <--close--- Demon
-				 */
-				sigd_enq(vcc,as_close,NULL,NULL,NULL);
-				while (vcc->reply == WAITING && sigd)
-					sleep_on(&vcc->sleep);
-				if (!vcc->reply)
-					while (!(vcc->flags & ATM_VF_RELEASED)
-					    && sigd) sleep_on(&vcc->sleep);
-				vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_RELEASED
-				    | ATM_VF_CLOSE);
-				    /* we're gone now but may connect later */
-				return -EINTR;
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (!signal_pending(current)) continue;
+			DPRINTK("*ABORT*\n");
+			/*
+			 * This is tricky:
+			 *   Kernel ---close--> Demon
+			 *   Kernel <--close--- Demon
+		         * or
+			 *   Kernel ---close--> Demon
+			 *   Kernel <--error--- Demon
+			 * or
+			 *   Kernel ---close--> Demon
+			 *   Kernel <--okay---- Demon
+			 *   Kernel <--close--- Demon
+			 */
+			sigd_enq(vcc,as_close,NULL,NULL,NULL);
+			while (vcc->reply == WAITING && sigd) {
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule();
 			}
+			if (!vcc->reply)
+				while (!test_bit(ATM_VF_RELEASED,&vcc->flags)
+				    && sigd) {
+					set_current_state(TASK_UNINTERRUPTIBLE);
+					schedule();
+				}
+			remove_wait_queue(&vcc->sleep,&wait);
+			clear_bit(ATM_VF_REGIS,&vcc->flags);
+			clear_bit(ATM_VF_RELEASED,&vcc->flags);
+			clear_bit(ATM_VF_CLOSE,&vcc->flags);
+			    /* we're gone now but may connect later */
+			error = -EINTR;
+			break;
 		}
+		remove_wait_queue(&vcc->sleep,&wait);
+		if (error) return error;
 		if (!sigd) return -EUNATCH;
 		if (vcc->reply) return vcc->reply;
 	}
@@ -209,16 +239,22 @@
 
 static int svc_listen(struct socket *sock,int backlog)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct atm_vcc *vcc = ATM_SD(sock);
 
 	DPRINTK("svc_listen %p\n",vcc);
 	/* let server handle listen on unbound sockets */
-	if (vcc->flags & ATM_VF_SESSION) return -EINVAL;
+	if (test_bit(ATM_VF_SESSION,&vcc->flags)) return -EINVAL;
 	vcc->reply = WAITING;
 	sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local);
-	while (vcc->reply == WAITING && sigd) sleep_on(&vcc->sleep);
+	add_wait_queue(&vcc->sleep,&wait);
+	while (vcc->reply == WAITING && sigd) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	remove_wait_queue(&vcc->sleep,&wait);
 	if (!sigd) return -EUNATCH;
-	vcc->flags |= ATM_VF_LISTEN;
+	set_bit(ATM_VF_LISTEN,&vcc->flags);
 	vcc->backlog_quota = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT;
 	return vcc->reply;
 }
@@ -240,18 +276,32 @@
 
 	DPRINTK("svc_accept %p -> %p\n",old_vcc,new_vcc);
 	while (1) {
+		DECLARE_WAITQUEUE(wait,current);
+
+		add_wait_queue(&old_vcc->sleep,&wait);
 		while (!(skb = skb_dequeue(&old_vcc->listenq)) && sigd) {
-			if (old_vcc->flags & ATM_VF_RELEASED) break;
-			if (old_vcc->flags & ATM_VF_CLOSE)
-				return old_vcc->reply;
-			if (flags & O_NONBLOCK) return -EAGAIN;
-			interruptible_sleep_on(&old_vcc->sleep);
-			if (signal_pending(current)) return -ERESTARTSYS;
+			if (test_bit(ATM_VF_RELEASED,&old_vcc->flags)) break;
+			if (test_bit(ATM_VF_CLOSE,&old_vcc->flags)) {
+				error = old_vcc->reply;
+				break;
+			}
+			if (flags & O_NONBLOCK) {
+				error = -EAGAIN;
+				break;
+			}
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				error = -ERESTARTSYS;
+				break;
+			}
 		}
+		remove_wait_queue(&old_vcc->sleep,&wait);
+		if (error) return error;
 		if (!skb) return -EUNATCH;
 		msg = (struct atmsvc_msg *) skb->data;
 		new_vcc->qos = msg->qos;
-		new_vcc->flags |= ATM_VF_HASQOS;
+		set_bit(ATM_VF_HASQOS,&new_vcc->flags);
 		new_vcc->remote = msg->svc;
 		new_vcc->local = msg->local;
 		new_vcc->sap = msg->sap;
@@ -267,8 +317,12 @@
 		/* wait should be short, so we ignore the non-blocking flag */
 		new_vcc->reply = WAITING;
 		sigd_enq(new_vcc,as_accept,old_vcc,NULL,NULL);
-		while (new_vcc->reply == WAITING && sigd)
-			sleep_on(&new_vcc->sleep);
+		add_wait_queue(&new_vcc->sleep,&wait);
+		while (new_vcc->reply == WAITING && sigd) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule();
+		}
+		remove_wait_queue(&new_vcc->sleep,&wait);
 		if (!sigd) return -EUNATCH;
 		if (!new_vcc->reply) break;
 		if (new_vcc->reply != -ERESTARTSYS) return new_vcc->reply;
@@ -293,6 +347,7 @@
 
 int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos)
 {
+	DECLARE_WAITQUEUE(wait,current);
 	struct atm_qos save_qos;
 
 	vcc->reply = WAITING;
@@ -300,8 +355,13 @@
 	vcc->qos = *qos;
 	sigd_enq(vcc,as_modify,NULL,NULL,&vcc->local);
 	vcc->qos = save_qos;
-	while (vcc->reply == WAITING && !(vcc->flags & ATM_VF_RELEASED) &&
-	    sigd) sleep_on(&vcc->sleep);
+	add_wait_queue(&vcc->sleep,&wait);
+	while (vcc->reply == WAITING && !test_bit(ATM_VF_RELEASED,&vcc->flags)
+	    && sigd) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	remove_wait_queue(&vcc->sleep,&wait);
 	if (!sigd) return -EUNATCH;
 	return vcc->reply;
 }
@@ -317,7 +377,7 @@
 		return atm_setsockopt(sock,level,optname,optval,optlen);
 	vcc = ATM_SD(sock);
 	if (copy_from_user(&vcc->sap,optval,optlen)) return -EFAULT;
-	vcc->flags |= ATM_VF_HASSAP;
+	set_bit(ATM_VF_HASSAP,&vcc->flags);
 	return 0;
 }
 
@@ -393,6 +453,4 @@
 		printk(KERN_ERR "ATMSVC: can't register");
 		return;
 	}
-	signaling_init();
-	init_addr();
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.3.99-pre2/linux/net/bridge/br.c	Sun Feb 20 21:12:40 2000
+++ linux/net/bridge/br.c	Wed Mar 22 22:22:05 2000
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br.c,v 1.39 2000/02/18 16:47:11 davem Exp $
+ *	$Id: br.c,v 1.40 2000/03/21 21:08:47 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -13,6 +13,7 @@
  *	2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
@@ -23,6 +24,10 @@
 #include <asm/uaccess.h>
 #include "br_private.h"
 
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#include "../atm/lec.h"
+#endif
+
 void br_dec_use_count()
 {
 	MOD_DEC_USE_COUNT;
@@ -39,6 +44,12 @@
 
 	br_handle_frame_hook = br_handle_frame;
 	br_ioctl_hook = br_ioctl_deviceless_stub;
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	write_lock(&lane_bridge_hook_lock);
+	br_fdb_get_hook = br_fdb_get;
+	br_fdb_put_hook = br_fdb_put;
+	write_unlock(&lane_bridge_hook_lock);
+#endif
 	register_netdevice_notifier(&br_device_notifier);
 
 	return 0;
@@ -59,6 +70,12 @@
 	unregister_netdevice_notifier(&br_device_notifier);
 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
 	net_call_rx_atomic(__br_clear_frame_hook);
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	write_lock(&lane_bridge_hook_lock);
+	br_fdb_get_hook = NULL;
+	br_fdb_put_hook = NULL;
+	write_unlock(&lane_bridge_hook_lock);
+#endif
 }
 
 EXPORT_NO_SYMBOLS;
diff -u --recursive --new-file v2.3.99-pre2/linux/net/bridge/br_input.c linux/net/bridge/br_input.c
--- v2.3.99-pre2/linux/net/bridge/br_input.c	Sat Feb 26 22:32:08 2000
+++ linux/net/bridge/br_input.c	Tue Mar 21 23:38:26 2000
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_input.c,v 1.3 2000/02/24 19:48:06 davem Exp $
+ *	$Id: br_input.c,v 1.4 2000/03/21 21:08:47 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -19,7 +19,7 @@
 #include <linux/if_bridge.h>
 #include "br_private.h"
 
-unsigned char bridge_ula[5] = { 0x01, 0x80, 0xc2, 0x00, 0x00 };
+unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
 
 static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
 {
@@ -53,6 +53,8 @@
 	    p->state == BR_STATE_DISABLED)
 		goto freeandout;
 
+	skb_push(skb, skb->data - skb->mac.raw);
+
 	if (br->dev.flags & IFF_PROMISC) {
 		struct sk_buff *skb2;
 
@@ -80,8 +82,6 @@
 
 	if (!memcmp(dest, bridge_ula, 5) && !(dest[5] & 0xF0))
 		goto handle_special_frame;
-
-	skb_push(skb, skb->data - skb->mac.raw);
 
 	if (p->state == BR_STATE_LEARNING ||
 	    p->state == BR_STATE_FORWARDING)
diff -u --recursive --new-file v2.3.99-pre2/linux/net/bridge/br_private.h linux/net/bridge/br_private.h
--- v2.3.99-pre2/linux/net/bridge/br_private.h	Sun Feb 20 21:12:40 2000
+++ linux/net/bridge/br_private.h	Tue Mar 21 23:38:26 2000
@@ -4,7 +4,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_private.h,v 1.1 2000/02/18 16:47:12 davem Exp $
+ *	$Id: br_private.h,v 1.2 2000/03/21 21:08:47 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -114,7 +114,7 @@
 };
 
 struct notifier_block br_device_notifier;
-unsigned char bridge_ula[5];
+unsigned char bridge_ula[6];
 
 /* br.c */
 void br_dec_use_count(void);
diff -u --recursive --new-file v2.3.99-pre2/linux/net/core/dev.c linux/net/core/dev.c
--- v2.3.99-pre2/linux/net/core/dev.c	Thu Mar  2 14:36:23 2000
+++ linux/net/core/dev.c	Tue Mar 21 23:38:27 2000
@@ -651,7 +651,7 @@
 		qdisc_run(dev);
 
 		spin_unlock_bh(&dev->queue_lock);
-		return ret;
+		return ret == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : ret;
 	}
 
 	/* The device has no queue. Common case for software devices:
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.3.99-pre2/linux/net/ipv4/ipip.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/ipip.c	Tue Mar 21 23:38:27 2000
@@ -1,7 +1,7 @@
 /*
  *	Linux NET3:	IP/IP protocol decoder. 
  *
- *	Version: $Id: ipip.c,v 1.31 2000/03/17 14:41:51 davem Exp $
+ *	Version: $Id: ipip.c,v 1.32 2000/03/21 06:13:54 davem Exp $
  *
  *	Authors:
  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile
--- v2.3.99-pre2/linux/net/ipv4/netfilter/Makefile	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/netfilter/Makefile	Sun Mar 19 18:19:35 2000
@@ -21,6 +21,8 @@
 O_OBJS += $(IP_NF_CONNTRACK_OBJ)
 else
   ifeq ($(CONFIG_IP_NF_CONNTRACK),m)
+  MI_OBJS += $(IP_NF_CONNTRACK_OBJ)
+  MIX_OBJS += ip_conntrack_standalone.o
   M_OBJS += ip_conntrack.o
   endif
 endif
@@ -37,7 +39,7 @@
 OX_OBJS += ip_conntrack_ftp.o
 else
   ifeq ($(CONFIG_IP_NF_FTP),m)
-  M_OBJS += ip_conntrack_ftp.o
+  MX_OBJS += ip_conntrack_ftp.o
   endif
 endif
 
@@ -129,6 +131,8 @@
   endif
 else
   ifeq ($(CONFIG_IP_NF_NAT),m)
+  MI_OBJS += ip_nat_rule.o $(IP_NF_NAT_OBJ) 
+  MIX_OBJS += ip_nat_standalone.o
   M_OBJS += iptable_nat.o
     ifeq ($(CONFIG_IP_NF_FTP),m)
     M_OBJS += ip_nat_ftp.o
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/netfilter/ip_conntrack_ftp.c linux/net/ipv4/netfilter/ip_conntrack_ftp.c
--- v2.3.99-pre2/linux/net/ipv4/netfilter/ip_conntrack_ftp.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/netfilter/ip_conntrack_ftp.c	Sun Mar 19 18:19:35 2000
@@ -1,7 +1,4 @@
 /* FTP extension for IP connection tracking. */
-#ifdef MODULE
-#define EXPORT_SYMTAB
-#endif
 #include <linux/module.h>
 #include <linux/netfilter.h>
 #include <linux/ip.h>
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/netfilter/ip_conntrack_standalone.c linux/net/ipv4/netfilter/ip_conntrack_standalone.c
--- v2.3.99-pre2/linux/net/ipv4/netfilter/ip_conntrack_standalone.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/netfilter/ip_conntrack_standalone.c	Sun Mar 19 18:19:35 2000
@@ -7,9 +7,6 @@
 /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
    Public Licence. */
 
-#ifdef MODULE
-#define EXPORT_SYMTAB
-#endif
 #include <linux/types.h>
 #include <linux/ip.h>
 #include <linux/netfilter.h>
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/netfilter/ip_nat_standalone.c linux/net/ipv4/netfilter/ip_nat_standalone.c
--- v2.3.99-pre2/linux/net/ipv4/netfilter/ip_nat_standalone.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/netfilter/ip_nat_standalone.c	Sun Mar 19 18:19:35 2000
@@ -7,9 +7,6 @@
 /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
    Public Licence. */
 
-#ifdef MODULE
-#define EXPORT_SYMTAB
-#endif
 #include <linux/types.h>
 #include <linux/ip.h>
 #include <linux/netfilter.h>
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/netfilter/ip_queue.c linux/net/ipv4/netfilter/ip_queue.c
--- v2.3.99-pre2/linux/net/ipv4/netfilter/ip_queue.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/netfilter/ip_queue.c	Tue Mar 21 23:38:27 2000
@@ -491,7 +491,7 @@
 	skb = netlink_build_message(e, &status);
 	if (skb == NULL)
 		return status;
-	return netlink_unicast(nfnl, skb, nlq->peer.pid, 0);
+	return netlink_unicast(nfnl, skb, nlq->peer.pid, MSG_DONTWAIT);
 }
 
 static struct sk_buff *
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.3.99-pre2/linux/net/ipv4/route.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/route.c	Wed Mar 22 21:51:32 2000
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.82 2000/03/17 14:41:52 davem Exp $
+ * Version:	$Id: route.c,v 1.83 2000/03/23 05:34:13 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -205,9 +205,6 @@
 	return (hash^(hash>>8)) & rt_hash_mask;
 }
 
-#ifndef CONFIG_PROC_FS
-static int rt_cache_get_info(char *buffer, char **start, off_t offset, int length) { return 0; }
-#else
 static int rt_cache_get_info(char *buffer, char **start, off_t offset, int length)
 {
 	int len=0;
@@ -267,7 +264,6 @@
   		len = length;
   	return len;
 }
-#endif
   
 static __inline__ void rt_free(struct rtable *rt)
 {
@@ -2204,7 +2200,6 @@
 #ifdef CONFIG_NET_CLS_ROUTE
 struct ip_rt_acct *ip_rt_acct;
 
-#ifdef CONFIG_PROC_FS
 static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
 			   int length, int *eof, void *data)
 {
@@ -2242,7 +2237,6 @@
 	}
 	return 0;
 }
-#endif
 #endif
 
 void __init ip_rt_init(void)
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.3.99-pre2/linux/net/ipv4/tcp.c	Fri Mar 10 16:40:51 2000
+++ linux/net/ipv4/tcp.c	Wed Mar 22 21:51:32 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.164 2000/03/08 19:36:40 davem Exp $
+ * Version:	$Id: tcp.c,v 1.165 2000/03/23 05:30:32 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1337,19 +1337,20 @@
 				break;
 			}
 
-			if (sk->done) {
-				copied = -ENOTCONN;
+			if (sk->shutdown & RCV_SHUTDOWN) {
+				if (!(flags&MSG_PEEK))
+					sk->done = 1;
 				break;
 			}
 
 			if (sk->state == TCP_CLOSE) {
-				if (!(flags&MSG_PEEK))
+				if (sk->done) {
+					copied = -ENOTCONN;
+					break;
+				} else if (!(flags&MSG_PEEK))
 					sk->done = 1;
 				break;
 			}
-
-			if (sk->shutdown & RCV_SHUTDOWN)
-				break;
 
 			if (!timeo) {
 				copied = -EAGAIN;
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.3.99-pre2/linux/net/ipv4/tcp_input.c	Thu Mar  2 14:36:23 2000
+++ linux/net/ipv4/tcp_input.c	Tue Mar 21 23:38:27 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.189 2000/02/27 19:52:55 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.190 2000/03/21 19:34:23 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1944,10 +1944,8 @@
 
 		if (eaten) {
 			kfree_skb(skb);
-		} else if (!sk->dead) {
-			wake_up_interruptible(sk->sleep);
-			sock_wake_async(sk->socket,1, POLL_IN);
-		}
+		} else
+			sk->data_ready(sk, 0);
 		return;
 	}
 
@@ -2531,8 +2529,7 @@
 				/* FIN bit check is not done since if FIN is set in
 				 * this frame, the pred_flags won't match up. -DaveM
 				 */
-				wake_up_interruptible(sk->sleep);
-				sock_wake_async(sk->socket,1, POLL_IN);
+				sk->data_ready(sk, 0);
 			}
 
 			tcp_event_data_recv(tp, skb);
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.3.99-pre2/linux/net/ipv4/tcp_ipv4.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv4/tcp_ipv4.c	Wed Mar 22 21:51:32 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.202 2000/03/17 14:41:53 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.203 2000/03/22 17:55:03 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -2015,7 +2015,7 @@
 		timer_expires = jiffies;
 
 	sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X"
-		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld %d %p %u %u %u %u",
+		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %u %u %u %u",
 		i, src, srcp, dest, destp, sp->state, 
 		tp->write_seq-tp->snd_una, tp->rcv_nxt-tp->copied_seq,
 		timer_active, timer_expires-jiffies,
diff -u --recursive --new-file v2.3.99-pre2/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.3.99-pre2/linux/net/ipv6/sit.c	Sun Mar 19 18:35:31 2000
+++ linux/net/ipv6/sit.c	Tue Mar 21 23:38:27 2000
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.36 2000/03/17 14:42:08 davem Exp $
+ *	$Id: sit.c,v 1.37 2000/03/21 06:14:00 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.3.99-pre2/linux/net/irda/ircomm/ircomm_param.c linux/net/irda/ircomm/ircomm_param.c
--- v2.3.99-pre2/linux/net/irda/ircomm/ircomm_param.c	Wed Dec 29 13:13:21 1999
+++ linux/net/irda/ircomm/ircomm_param.c	Tue Mar 21 11:17:28 2000
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Jun  7 10:25:11 1999
- * Modified at:   Tue Dec 14 15:26:30 1999
+ * Modified at:   Sun Jan 30 14:32:03 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -40,19 +40,27 @@
 
 #include <net/irda/ircomm_param.h>
 
-static int ircomm_param_service_type(void *instance, param_t *param, int get);
-static int ircomm_param_port_type(void *instance, param_t *param, int get);
-static int ircomm_param_port_name(void *instance, param_t *param, int get);
-static int ircomm_param_service_type(void *instance, param_t *param, int get);
-static int ircomm_param_data_rate(void *instance, param_t *param, int get);
-static int ircomm_param_data_format(void *instance, param_t *param, int get);
-static int ircomm_param_flow_control(void *instance, param_t *param, int get);
-static int ircomm_param_xon_xoff(void *instance, param_t *param, int get);
-static int ircomm_param_enq_ack(void *instance, param_t *param, int get);
-static int ircomm_param_line_status(void *instance, param_t *param, int get);
-static int ircomm_param_dte(void *instance, param_t *param, int get);
-static int ircomm_param_dce(void *instance, param_t *param, int get);
-static int ircomm_param_poll(void *instance, param_t *param, int get);
+static int ircomm_param_service_type(void *instance, irda_param_t *param, 
+				     int get);
+static int ircomm_param_port_type(void *instance, irda_param_t *param, 
+				  int get);
+static int ircomm_param_port_name(void *instance, irda_param_t *param, 
+				  int get);
+static int ircomm_param_service_type(void *instance, irda_param_t *param, 
+				     int get);
+static int ircomm_param_data_rate(void *instance, irda_param_t *param, 
+				  int get);
+static int ircomm_param_data_format(void *instance, irda_param_t *param, 
+				    int get);
+static int ircomm_param_flow_control(void *instance, irda_param_t *param, 
+				     int get);
+static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
+static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
+static int ircomm_param_line_status(void *instance, irda_param_t *param, 
+				    int get);
+static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
+static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
+static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
 
 static pi_minor_info_t pi_minor_call_table_common[] = {
 	{ ircomm_param_service_type, PV_INT_8_BITS },
@@ -170,7 +178,8 @@
  *    query and then the remote device sends its initial paramters
  *
  */
-static int ircomm_param_service_type(void *instance, param_t *param, int get)
+static int ircomm_param_service_type(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	__u8 service_type = param->pv.b; /* We know it's a one byte integer */
@@ -229,7 +238,7 @@
  *    Since we only advertise serial service, this parameter should only
  *    be equal to IRCOMM_SERIAL.
  */
-static int ircomm_param_port_type(void *instance, param_t *param, int get)
+static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -253,7 +262,7 @@
  *    Exchange port name
  *
  */
-static int ircomm_param_port_name(void *instance, param_t *param, int get)
+static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	
@@ -276,7 +285,7 @@
  *    Exchange data rate to be used in this settings
  *
  */
-static int ircomm_param_data_rate(void *instance, param_t *param, int get)
+static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	
@@ -299,7 +308,8 @@
  *    Exchange data format to be used in this settings
  *
  */
-static int ircomm_param_data_format(void *instance, param_t *param, int get)
+static int ircomm_param_data_format(void *instance, irda_param_t *param, 
+				    int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -320,7 +330,8 @@
  *    Exchange flow control settings to be used in this settings
  *
  */
-static int ircomm_param_flow_control(void *instance, param_t *param, int get)
+static int ircomm_param_flow_control(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -343,7 +354,7 @@
  *    Exchange XON/XOFF characters
  *
  */
-static int ircomm_param_xon_xoff(void *instance, param_t *param, int get)
+static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -370,7 +381,7 @@
  *    Exchange ENQ/ACK characters
  *
  */
-static int ircomm_param_enq_ack(void *instance, param_t *param, int get)
+static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -397,7 +408,8 @@
  *    
  *
  */
-static int ircomm_param_line_status(void *instance, param_t *param, int get)
+static int ircomm_param_line_status(void *instance, irda_param_t *param, 
+				    int get)
 {
 	IRDA_DEBUG(2, __FUNCTION__ "(), not impl.\n");
 
@@ -410,7 +422,7 @@
  *    If we get here, there must be some sort of null-modem connection, and
  *    we are probably working in server mode as well.
  */
-static int ircomm_param_dte(void *instance, param_t *param, int get)
+static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	__u8 dte;
@@ -453,7 +465,7 @@
  *    
  *
  */
-static int ircomm_param_dce(void *instance, param_t *param, int get)
+static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	__u8 dce;
@@ -485,7 +497,7 @@
  *    Called when the peer device is polling for the line settings
  *
  */
-static int ircomm_param_poll(void *instance, param_t *param, int get)
+static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/irda/irda_device.c linux/net/irda/irda_device.c
--- v2.3.99-pre2/linux/net/irda/irda_device.c	Thu Mar  2 14:36:23 2000
+++ linux/net/irda/irda_device.c	Tue Mar 21 11:17:28 2000
@@ -254,7 +254,7 @@
 	return req.ifr_receiving;
 }
 
-void irda_task_next_state(struct irda_task *task, TASK_STATE state)
+void irda_task_next_state(struct irda_task *task, IRDA_TASK_STATE state)
 {
 	IRDA_DEBUG(2, __FUNCTION__ "(), state = %s\n", task_state[state]);
 
@@ -354,8 +354,9 @@
  *    called from interrupt context, so it's not possible to use
  *    schedule_timeout() 
  */
-struct irda_task *irda_task_execute(void *instance, TASK_CALLBACK function, 
-				    TASK_CALLBACK finished, 
+struct irda_task *irda_task_execute(void *instance, 
+				    IRDA_TASK_CALLBACK function, 
+				    IRDA_TASK_CALLBACK finished, 
 				    struct irda_task *parent, void *param)
 {
 	struct irda_task *task;
diff -u --recursive --new-file v2.3.99-pre2/linux/net/irda/irlan/irlan_eth.c linux/net/irda/irlan/irlan_eth.c
--- v2.3.99-pre2/linux/net/irda/irlan/irlan_eth.c	Sun Feb 13 19:29:04 2000
+++ linux/net/irda/irlan/irlan_eth.c	Tue Mar 21 11:17:28 2000
@@ -6,13 +6,13 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Oct 15 08:37:58 1998
- * Modified at:   Thu Nov  4 14:50:52 1999
+ * Modified at:   Tue Mar 21 09:06:41 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
  *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  * 
- *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -25,6 +25,7 @@
  *     
  ********************************************************************/
 
+#include <linux/config.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/inetdevice.h>
@@ -331,6 +332,7 @@
 	 * is useful if we have changed access points on the same
 	 * subnet.  
 	 */
+#ifdef CONFIG_INET
 	IRDA_DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
 	in_dev = in_dev_get(dev);
 	if (in_dev == NULL)
@@ -345,6 +347,7 @@
 		 NULL, dev->dev_addr, NULL);
 	read_unlock(&in_dev->lock);
 	in_dev_put(in_dev);
+#endif /* CONFIG_INET */
 }
 
 /*
diff -u --recursive --new-file v2.3.99-pre2/linux/net/irda/irttp.c linux/net/irda/irttp.c
--- v2.3.99-pre2/linux/net/irda/irttp.c	Fri Jan  7 19:13:23 2000
+++ linux/net/irda/irttp.c	Tue Mar 21 11:17:28 2000
@@ -60,7 +60,8 @@
 static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
 static void irttp_start_todo_timer(struct tsap_cb *self, int timeout);
 static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
-static int irttp_param_max_sdu_size(void *instance, param_t *param, int get);
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, 
+				    int get);
 
 /* Information for parsing parameters in IrTTP */
 static pi_minor_info_t pi_minor_call_table[] = {
@@ -1405,7 +1406,8 @@
  *    will be called both when this parameter needs to be inserted into, and
  *    extracted from the connect frames
  */
-static int irttp_param_max_sdu_size(void *instance, param_t *param, int get)
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, 
+				    int get)
 {
 	struct tsap_cb *self;
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/irda/parameters.c linux/net/irda/parameters.c
--- v2.3.99-pre2/linux/net/irda/parameters.c	Thu Mar  2 14:36:23 2000
+++ linux/net/irda/parameters.c	Tue Mar 21 11:17:28 2000
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Jun  7 10:25:11 1999
- * Modified at:   Tue Dec 14 16:03:57 1999
+ * Modified at:   Sun Jan 30 14:08:39 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -78,7 +78,7 @@
 static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, 
 				PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int ret;
 
 	p.pi = pi;
@@ -105,7 +105,7 @@
 static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, 
 				 PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int ret;
 
 	/* Extract values anyway, since handler may need them */
@@ -129,7 +129,7 @@
 static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, 
 			       PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int n = 0;
 	int err;
 
@@ -202,7 +202,7 @@
 static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, 
 				PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int n = 0;
 	int err;
 
@@ -273,7 +273,7 @@
 			       PV_TYPE type, PI_HANDLER func)
 {
 	char str[33];
-	param_t p;
+	irda_param_t p;
 	int err;
 
 	IRDA_DEBUG(2, __FUNCTION__ "()\n");
@@ -319,7 +319,7 @@
 static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
 			       PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 
 	p.pi = pi;     /* In case handler needs to know */
 	p.pl = buf[1]; /* Extract lenght of value */
@@ -346,10 +346,10 @@
  */
 int irda_param_pack(__u8 *buf, char *fmt, ...)
 {
+	irda_pv_t arg;
 	va_list args;
 	char *p;
 	int n = 0;
-	pv_t arg;
 	
 	va_start(args, fmt);
 
@@ -392,10 +392,10 @@
  */
 int irda_param_unpack(__u8 *buf, char *fmt, ...)
 {
+	irda_pv_t arg;
 	va_list args;
 	char *p;
 	int n = 0;
-	pv_t arg;
 
 	va_start(args, fmt);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/irda/qos.c linux/net/irda/qos.c
--- v2.3.99-pre2/linux/net/irda/qos.c	Wed Dec 29 13:13:21 1999
+++ linux/net/irda/qos.c	Tue Mar 21 11:17:28 2000
@@ -6,10 +6,10 @@
  * Status:        Stable
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Sep  9 00:00:26 1997
- * Modified at:   Sun Dec 12 13:47:09 1999
+ * Modified at:   Sun Jan 30 14:29:16 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -43,13 +43,18 @@
 #define CI_BZIP2  27 /* Random pick */
 #endif
 
-static int irlap_param_baud_rate(void *instance, param_t *param, int get);
-static int irlap_param_link_disconnect(void *instance, param_t *parm, int get);
-static int irlap_param_max_turn_time(void *instance, param_t *param, int get);
-static int irlap_param_data_size(void *instance, param_t *param, int get);
-static int irlap_param_window_size(void *instance, param_t *param, int get);
-static int irlap_param_additional_bofs(void *instance, param_t *parm, int get);
-static int irlap_param_min_turn_time(void *instance, param_t *param, int get);
+static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
+static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, 
+				       int get);
+static int irlap_param_max_turn_time(void *instance, irda_param_t *param, 
+				     int get);
+static int irlap_param_data_size(void *instance, irda_param_t *param, int get);
+static int irlap_param_window_size(void *instance, irda_param_t *param, 
+				   int get);
+static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, 
+				       int get);
+static int irlap_param_min_turn_time(void *instance, irda_param_t *param, 
+				     int get);
 
 __u32 min_turn_times[]  = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
 __u32 baud_rates[]      = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, 
@@ -345,7 +350,7 @@
  *    Negotiate data-rate
  *
  */
-static int irlap_param_baud_rate(void *instance, param_t *param, int get)
+static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
 {
 	__u16 final;
 
@@ -380,7 +385,8 @@
  *    Negotiate link disconnect/threshold time. 
  *
  */
-static int irlap_param_link_disconnect(void *instance, param_t *param, int get)
+static int irlap_param_link_disconnect(void *instance, irda_param_t *param, 
+				       int get)
 {
 	__u16 final;
 	
@@ -413,7 +419,8 @@
  *    will be negotiated independently for each station
  *
  */
-static int irlap_param_max_turn_time(void *instance, param_t *param, int get)
+static int irlap_param_max_turn_time(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -435,7 +442,7 @@
  *    will be negotiated independently for each station
  *
  */
-static int irlap_param_data_size(void *instance, param_t *param, int get)
+static int irlap_param_data_size(void *instance, irda_param_t *param, int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -457,7 +464,8 @@
  *    will be negotiated independently for each station
  *
  */
-static int irlap_param_window_size(void *instance, param_t *param, int get)
+static int irlap_param_window_size(void *instance, irda_param_t *param, 
+				   int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -478,7 +486,7 @@
  *    Negotiate additional BOF characters. This is a type 1 parameter and
  *    will be negotiated independently for each station.
  */
-static int irlap_param_additional_bofs(void *instance, param_t *param, int get)
+static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -499,7 +507,8 @@
  *    Negotiate the minimum turn around time. This is a type 1 parameter and
  *    will be negotiated independently for each station
  */
-static int irlap_param_min_turn_time(void *instance, param_t *param, int get)
+static int irlap_param_min_turn_time(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
diff -u --recursive --new-file v2.3.99-pre2/linux/net/khttpd/main.c linux/net/khttpd/main.c
--- v2.3.99-pre2/linux/net/khttpd/main.c	Tue Dec  7 09:32:52 1999
+++ linux/net/khttpd/main.c	Mon Mar 20 12:25:46 2000
@@ -198,7 +198,6 @@
 	
 	DECLARE_WAIT_QUEUE_HEAD(WQ);
 	
-	MOD_INC_USE_COUNT;
 	
 	sprintf(current->comm,"khttpd manager");
 	lock_kernel();   /* This seems to be required for exit_mm */
@@ -347,6 +346,8 @@
 int __init khttpd_init(void)
 {
 	int I;
+
+	MOD_INC_USE_COUNT;
 	
 	I=0;
 	while (I<CONFIG_KHTTPD_NUMCPU)
diff -u --recursive --new-file v2.3.99-pre2/linux/net/khttpd/security.c linux/net/khttpd/security.c
--- v2.3.99-pre2/linux/net/khttpd/security.c	Sun Mar 19 18:35:31 2000
+++ linux/net/khttpd/security.c	Mon Mar 20 12:25:46 2000
@@ -113,10 +113,11 @@
 #endif
 	/* Rule no. 3 -- Does the file exist ? */
 
-	
+	lock_kernel();
 		
 	filp = filp_open(Filename, 0, O_RDONLY, NULL);
 	
+	unlock_kernel();
 	
 	if ((IS_ERR(filp))||(filp==NULL)||(filp->f_dentry==NULL))
 	{
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sched/sch_atm.c linux/net/sched/sch_atm.c
--- v2.3.99-pre2/linux/net/sched/sch_atm.c	Sat Feb 26 22:32:08 2000
+++ linux/net/sched/sch_atm.c	Tue Mar 21 23:38:27 2000
@@ -276,7 +276,7 @@
                 goto err_out;
 	}
 	/* @@@ should check if the socket is really operational or we'll crash
-	   on vcc->dev->ops->send */
+	   on vcc->send */
 	if (classid) {
 		if (TC_H_MAJ(classid ^ sch->handle)) {
 			DPRINTK("atm_tc_change: classid mismatch\n");
@@ -449,8 +449,21 @@
 	sch->stats.packets++;
 	flow->stats.bytes += skb->len;
 	flow->stats.packets++;
-	sch->q.qlen++;
-	return 0;
+	/*
+	 * Okay, this may seem weird. We pretend we've dropped the packet if
+	 * it goes via ATM. The reason for this is that the outer qdisc
+	 * expects to be able to q->dequeue the packet later on if we return
+	 * success at this place. Also, sch->q.qdisc needs to reflect whether
+	 * there is a packet egligible for dequeuing or not. Note that the
+	 * statistics of the outer qdisc are necessarily wrong because of all
+	 * this. There's currently no correct solution for this.
+	 */
+	if (flow == &p->link) {
+		sch->q.qlen++;
+		return 0;
+	}
+	tasklet_schedule(&p->task);
+	return NET_XMIT_BYPASS;
 }
 
 
@@ -477,11 +490,9 @@
 		 */
 		while ((skb = flow->q->dequeue(flow->q))) {
 			if (!atm_may_send(flow->vcc,skb->truesize)) {
-				if (flow->q->ops->requeue(skb,flow->q))
-					sch->q.qlen--;
+				(void) flow->q->ops->requeue(skb,flow->q);
 				break;
 			}
-			sch->q.qlen--;
 			D2PRINTK("atm_tc_deqeueue: sending on class %p\n",flow);
 			/* remove any LL header somebody else has attached */
 			skb_pull(skb,(char *) skb->nh.iph-(char *) skb->data);
@@ -501,7 +512,7 @@
 			atomic_add(skb->truesize,&flow->vcc->tx_inuse);
 			ATM_SKB(skb)->iovcnt = 0;
 			/* atm.atm_options are already set by atm_tc_enqueue */
-			(void) flow->vcc->dev->ops->send(flow->vcc,skb);
+			(void) flow->vcc->send(flow->vcc,skb);
 		}
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/auth.c linux/net/sunrpc/auth.c
--- v2.3.99-pre2/linux/net/sunrpc/auth.c	Fri Oct 15 15:25:14 1999
+++ linux/net/sunrpc/auth.c	Mon Mar 20 08:14:04 2000
@@ -4,12 +4,9 @@
  * Generic RPC authentication API.
  *
  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
- *
- * Modified May 1999, Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
  */
 
 #include <linux/types.h>
-#include <linux/string.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/errno.h>
@@ -84,6 +81,15 @@
 	auth->au_nextgc = jiffies + (auth->au_expire >> 1);
 }
 
+static inline void
+rpcauth_crdestroy(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+	if (auth->au_ops->crdestroy)
+		auth->au_ops->crdestroy(cred);
+	else
+		rpc_free(cred);
+}
+
 /*
  * Clear the RPC credential cache
  */
@@ -115,18 +121,15 @@
 rpcauth_gc_credcache(struct rpc_auth *auth)
 {
 	struct rpc_cred	**q, *cred, *free = NULL;
-	int		i, safe = 0;
+	int		i;
 
 	dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
 	spin_lock(&rpc_credcache_lock);
 	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
 		q = &auth->au_credcache[i];
 		while ((cred = *q) != NULL) {
-			if (++safe > 500) {
-				printk("RPC: rpcauth_gc_credcache looping!\n");
-				break;
-			}
-			if (!cred->cr_count && time_before(cred->cr_expire, jiffies)) {
+			if (!cred->cr_count &&
+			    time_before(cred->cr_expire, jiffies)) {
 				*q = cred->cr_next;
 				cred->cr_next = free;
 				free = cred;
@@ -138,7 +141,7 @@
 	spin_unlock(&rpc_credcache_lock);
 	while ((cred = free) != NULL) {
 		free = cred->cr_next;
-		rpc_free(cred);
+		rpcauth_crdestroy(auth, cred);
 	}
 	auth->au_nextgc = jiffies + auth->au_expire;
 }
@@ -146,7 +149,7 @@
 /*
  * Insert credential into cache
  */
-inline void
+void
 rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	int		nr;
@@ -155,8 +158,8 @@
 	spin_lock(&rpc_credcache_lock);
 	cred->cr_next = auth->au_credcache[nr];
 	auth->au_credcache[nr] = cred;
-	cred->cr_expire = jiffies + auth->au_expire;
 	cred->cr_count++;
+	cred->cr_expire = jiffies + auth->au_expire;
 	spin_unlock(&rpc_credcache_lock);
 }
 
@@ -164,13 +167,13 @@
  * Look up a process' credentials in the authentication cache
  */
 static struct rpc_cred *
-rpcauth_lookup_credcache(struct rpc_task *task)
+rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags)
 {
-	struct rpc_auth	*auth = task->tk_auth;
 	struct rpc_cred	**q, *cred = NULL;
-	int		nr;
+	int		nr = 0;
 
-	nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR);
+	if (!(taskflags & RPC_TASK_ROOTCREDS))
+		nr = current->uid % RPC_CREDCACHE_NR;
 
 	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth);
@@ -178,7 +181,8 @@
 	spin_lock(&rpc_credcache_lock);
 	q = &auth->au_credcache[nr];
 	while ((cred = *q) != NULL) {
-		if (auth->au_ops->crmatch(task, cred)) {
+		if (!(cred->cr_flags & RPCAUTH_CRED_DEAD) &&
+		    auth->au_ops->crmatch(cred, taskflags)) {
 			*q = cred->cr_next;
 			break;
 		}
@@ -187,7 +191,7 @@
 	spin_unlock(&rpc_credcache_lock);
 
 	if (!cred)
-		cred = auth->au_ops->crcreate(task);
+		cred = auth->au_ops->crcreate(taskflags);
 
 	if (cred)
 		rpcauth_insert_credcache(auth, cred);
@@ -198,7 +202,7 @@
 /*
  * Remove cred handle from cache
  */
-static inline void
+static void
 rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	struct rpc_cred	**q, *cr;
@@ -210,7 +214,8 @@
 	while ((cr = *q) != NULL) {
 		if (cred == cr) {
 			*q = cred->cr_next;
-			return;
+			cred->cr_next = NULL;
+			break;
 		}
 		q = &cred->cr_next;
 	}
@@ -218,58 +223,78 @@
 }
 
 struct rpc_cred *
-rpcauth_lookupcred(struct rpc_task *task)
+rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
 {
+	dprintk("RPC:     looking up %s cred\n",
+		auth->au_ops->au_name);
+	return rpcauth_lookup_credcache(auth, taskflags);
+}
+
+struct rpc_cred *
+rpcauth_bindcred(struct rpc_task *task)
+{
+	struct rpc_auth *auth = task->tk_auth;
+
 	dprintk("RPC: %4d looking up %s cred\n",
 		task->tk_pid, task->tk_auth->au_ops->au_name);
-	return task->tk_cred = rpcauth_lookup_credcache(task);
+	task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, task->tk_flags);
+	if (task->tk_msg.rpc_cred == 0)
+		task->tk_status = -ENOMEM;
+	return task->tk_msg.rpc_cred;
 }
 
 int
-rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred)
+rpcauth_matchcred(struct rpc_auth *auth, struct rpc_cred *cred, int taskflags)
 {
-	struct rpc_auth	*auth = task->tk_auth;
-
-	dprintk("RPC: %4d matching %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
-	return auth->au_ops->crmatch(task, cred);
+	dprintk("RPC:     matching %s cred %d\n",
+		auth->au_ops->au_name, taskflags);
+	return auth->au_ops->crmatch(cred, taskflags);
 }
 
 void
 rpcauth_holdcred(struct rpc_task *task)
 {
 	dprintk("RPC: %4d holding %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
-	if (task->tk_cred)
-		task->tk_cred->cr_count++;
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	if (task->tk_msg.rpc_cred) {
+		task->tk_msg.rpc_cred->cr_count++;
+		task->tk_msg.rpc_cred->cr_expire = jiffies + task->tk_auth->au_expire;
+	}
 }
 
 void
-rpcauth_releasecred(struct rpc_task *task)
+rpcauth_releasecred(struct rpc_auth *auth, struct rpc_cred *cred)
 {
-	struct rpc_auth	*auth = task->tk_auth;
-	struct rpc_cred	*cred;
-
-	dprintk("RPC: %4d releasing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
-	if ((cred = task->tk_cred) != NULL) {
+	if (cred != NULL && cred->cr_count > 0) {
 		cred->cr_count--;
 		if (cred->cr_flags & RPCAUTH_CRED_DEAD) {
 			rpcauth_remove_credcache(auth, cred);
 			if (!cred->cr_count)
-				auth->au_ops->crdestroy(cred);
+				rpcauth_crdestroy(auth, cred);
 		}
-		task->tk_cred = NULL;
 	}
 }
 
+void
+rpcauth_unbindcred(struct rpc_task *task)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
+
+	dprintk("RPC: %4d releasing %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, cred);
+
+	rpcauth_releasecred(auth, cred);
+	task->tk_msg.rpc_cred = NULL;
+}
+
 u32 *
 rpcauth_marshcred(struct rpc_task *task, u32 *p)
 {
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d marshaling %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	return auth->au_ops->crmarshal(task, p,
 				task->tk_flags & RPC_CALL_REALUID);
 }
@@ -280,7 +305,7 @@
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d validating %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	return auth->au_ops->crvalidate(task, p);
 }
 
@@ -290,7 +315,7 @@
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d refreshing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	task->tk_status = auth->au_ops->crrefresh(task);
 	return task->tk_status;
 }
@@ -299,14 +324,14 @@
 rpcauth_invalcred(struct rpc_task *task)
 {
 	dprintk("RPC: %4d invalidating %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
-	if (task->tk_cred)
-		task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	if (task->tk_msg.rpc_cred)
+		task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
 }
 
 int
 rpcauth_uptodatecred(struct rpc_task *task)
 {
-	return !(task->tk_cred) ||
-		(task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
+	return !(task->tk_msg.rpc_cred) ||
+		(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/auth_null.c linux/net/sunrpc/auth_null.c
--- v2.3.99-pre2/linux/net/sunrpc/auth_null.c	Mon Mar 22 08:00:15 1999
+++ linux/net/sunrpc/auth_null.c	Mon Mar 20 08:14:04 2000
@@ -38,6 +38,7 @@
 nul_destroy(struct rpc_auth *auth)
 {
 	dprintk("RPC: destroying NULL authenticator %p\n", auth);
+	rpcauth_free_credcache(auth);
 	rpc_free(auth);
 }
 
@@ -45,15 +46,12 @@
  * Create NULL creds for current process
  */
 static struct rpc_cred *
-nul_create_cred(struct rpc_task *task)
+nul_create_cred(int flags)
 {
 	struct rpc_cred	*cred;
 
-	if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred)))) {
-		task->tk_status = -ENOMEM;
+	if (!(cred = (struct rpc_cred *) rpc_allocate(flags, sizeof(*cred))))
 		return NULL;
-	}
-
 	cred->cr_count = 0;
 	cred->cr_flags = RPCAUTH_CRED_UPTODATE;
 
@@ -73,7 +71,7 @@
  * Match cred handle against current process
  */
 static int
-nul_match(struct rpc_task *task, struct rpc_cred *cred)
+nul_match(struct rpc_cred *cred, int taskflags)
 {
 	return 1;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/auth_unix.c linux/net/sunrpc/auth_unix.c
--- v2.3.99-pre2/linux/net/sunrpc/auth_unix.c	Tue Jun  1 23:25:48 1999
+++ linux/net/sunrpc/auth_unix.c	Mon Mar 20 08:14:04 2000
@@ -4,12 +4,9 @@
  * UNIX-style authentication; no AUTH_SHORT support
  *
  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
- *
- * Modified May 1999 Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
  */
 
 #include <linux/types.h>
-#include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/socket.h>
 #include <linux/in.h>
@@ -63,7 +60,7 @@
 }
 
 static struct rpc_cred *
-unx_create_cred(struct rpc_task *task)
+unx_create_cred(int flags)
 {
 	struct unx_cred	*cred;
 	int		i;
@@ -71,14 +68,12 @@
 	dprintk("RPC:      allocating UNIX cred for uid %d gid %d\n",
 				current->uid, current->gid);
 
-	if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred)))) {
-		task->tk_status = -ENOMEM;
+	if (!(cred = (struct unx_cred *) rpc_allocate(flags, sizeof(*cred))))
 		return NULL;
-	}
 
 	cred->uc_count = 0;
 	cred->uc_flags = RPCAUTH_CRED_UPTODATE;
-	if (RPC_DO_ROOTOVERRIDE(task)) {
+	if (flags & RPC_TASK_ROOTCREDS) {
 		cred->uc_uid = cred->uc_fsuid = 0;
 		cred->uc_gid = cred->uc_fsgid = 0;
 		cred->uc_gids[0] = NOGROUP;
@@ -119,7 +114,7 @@
 	cred->uc_fsgid = gid;
 	cred->uc_gids[0] = (gid_t) NOGROUP;
 
-	return task->tk_cred = (struct rpc_cred *) cred;
+	return task->tk_msg.rpc_cred = (struct rpc_cred *) cred;
 }
 
 static void
@@ -134,12 +129,12 @@
  * request root creds (e.g. for NFS swapping).
  */
 static int
-unx_match(struct rpc_task * task, struct rpc_cred *rcred)
+unx_match(struct rpc_cred *rcred, int taskflags)
 {
 	struct unx_cred	*cred = (struct unx_cred *) rcred;
 	int		i;
 
-	if (!RPC_DO_ROOTOVERRIDE(task)) {
+	if (!(taskflags & RPC_TASK_ROOTCREDS)) {
 		int groups;
 
 		if (cred->uc_uid != current->uid
@@ -169,7 +164,7 @@
 unx_marshal(struct rpc_task *task, u32 *p, int ruid)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
-	struct unx_cred	*cred = (struct unx_cred *) task->tk_cred;
+	struct unx_cred	*cred = (struct unx_cred *) task->tk_msg.rpc_cred;
 	u32		*base, *hold;
 	int		i, n;
 
@@ -210,7 +205,7 @@
 static int
 unx_refresh(struct rpc_task *task)
 {
-	task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
+	task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
 	return task->tk_status = -EACCES;
 }
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.3.99-pre2/linux/net/sunrpc/clnt.c	Wed Dec  8 14:11:28 1999
+++ linux/net/sunrpc/clnt.c	Wed Mar 22 16:26:49 2000
@@ -42,14 +42,13 @@
 static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);
 
 
-static void	call_bind(struct rpc_task *task);
 static void	call_reserve(struct rpc_task *task);
 static void	call_reserveresult(struct rpc_task *task);
 static void	call_allocate(struct rpc_task *task);
 static void	call_encode(struct rpc_task *task);
 static void	call_decode(struct rpc_task *task);
+static void	call_bind(struct rpc_task *task);
 static void	call_transmit(struct rpc_task *task);
-static void	call_receive(struct rpc_task *task);
 static void	call_status(struct rpc_task *task);
 static void	call_refresh(struct rpc_task *task);
 static void	call_refreshresult(struct rpc_task *task);
@@ -98,7 +97,7 @@
 	clnt->cl_port     = xprt->addr.sin_port;
 	clnt->cl_prog     = program->number;
 	clnt->cl_vers     = version->number;
-	clnt->cl_prot     = IPPROTO_UDP;
+	clnt->cl_prot     = xprt->prot;
 	clnt->cl_stats    = program->stats;
 	clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait");
 
@@ -117,10 +116,10 @@
 	return clnt;
 
 out_no_clnt:
-	printk("RPC: out of memory in rpc_create_client\n");
+	printk(KERN_INFO "RPC: out of memory in rpc_create_client\n");
 	goto out;
 out_no_auth:
-	printk("RPC: Couldn't create auth handle (flavor %d)\n",
+	printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %d)\n",
 		flavor);
 	rpc_free(clnt);
 	clnt = NULL;
@@ -140,7 +139,7 @@
 		clnt->cl_protname, clnt->cl_server);
 	while (clnt->cl_users) {
 #ifdef RPC_DEBUG
-		printk("rpc_shutdown_client: client %s, tasks=%d\n",
+		dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n",
 			clnt->cl_protname, clnt->cl_users);
 #endif
 		/* Don't let rpc_release_client destroy us */
@@ -240,42 +239,72 @@
 /*
  * New rpc_call implementation
  */
-int
-rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
-				int flags, rpc_action func, void *data)
+int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
 {
 	struct rpc_task	my_task, *task = &my_task;
 	sigset_t	oldset;
-	int		async, status;
+	int		status;
 
 	/* If this client is slain all further I/O fails */
 	if (clnt->cl_dead) 
 		return -EIO;
 
+	if (flags & RPC_TASK_ASYNC) {
+		printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
+		flags &= ~RPC_TASK_ASYNC;
+	}
+
 	rpc_clnt_sigmask(clnt, &oldset);		
 
 	/* Create/initialize a new RPC task */
-	if ((async = (flags & RPC_TASK_ASYNC)) != 0) {
-		if (!func)
-			func = rpc_default_callback;
-		status = -ENOMEM;
-		if (!(task = rpc_new_task(clnt, func, flags)))
-			goto out;
-		task->tk_calldata = data;
-	} else {
-		rpc_init_task(task, clnt, NULL, flags);
-	}
+	rpc_init_task(task, clnt, NULL, flags);
+	rpc_call_setup(task, msg, 0);
 
-	/* Bind the user cred, set up the call info struct and
-	 * execute the task */
-	if (rpcauth_lookupcred(task) != NULL) {
-		rpc_call_setup(task, proc, argp, resp, 0);
-		rpc_execute(task);
-	} else
-		async = 0;
+	/* Set up the call info struct and execute the task */
+	if (task->tk_status == 0)
+		status = rpc_execute(task);
+	else
+		status = task->tk_status;
+	rpc_release_task(task);
+
+	rpc_clnt_sigunmask(clnt, &oldset);		
+
+	return status;
+}
 
-	status = 0;
-	if (!async) {
+/*
+ * New rpc_call implementation
+ */
+int
+rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
+	       rpc_action callback, void *data)
+{
+	struct rpc_task	*task;
+	sigset_t	oldset;
+	int		status;
+
+	/* If this client is slain all further I/O fails */
+	if (clnt->cl_dead) 
+		return -EIO;
+
+	flags |= RPC_TASK_ASYNC;
+
+	rpc_clnt_sigmask(clnt, &oldset);		
+
+	/* Create/initialize a new RPC task */
+	if (!callback)
+		callback = rpc_default_callback;
+	status = -ENOMEM;
+	if (!(task = rpc_new_task(clnt, callback, flags)))
+		goto out;
+	task->tk_calldata = data;
+
+	rpc_call_setup(task, msg, 0);
+
+	/* Set up the call info struct and execute the task */
+	if (task->tk_status == 0)
+		status = rpc_execute(task);
+	else {
 		status = task->tk_status;
 		rpc_release_task(task);
 	}
@@ -288,18 +317,24 @@
 
 
 void
-rpc_call_setup(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags)
+rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
 {
-	task->tk_action = call_bind;
-	task->tk_proc   = proc;
-	task->tk_argp   = argp;
-	task->tk_resp   = resp;
+	task->tk_msg   = *msg;
 	task->tk_flags |= flags;
+	/* Bind the user cred */
+	if (task->tk_msg.rpc_cred != NULL) {
+		rpcauth_holdcred(task);
+	} else
+		rpcauth_bindcred(task);
+
+	if (task->tk_status == 0)
+		task->tk_action = call_reserve;
+	else
+		task->tk_action = NULL;
 
 	/* Increment call count */
-	if (task->tk_proc < task->tk_client->cl_maxproc)
-		rpcproc_count(task->tk_client, proc)++;
+	if (task->tk_msg.rpc_proc < task->tk_client->cl_maxproc)
+		rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 /*
@@ -313,27 +348,8 @@
 		rpc_release_task(task);
 		return;
 	}
-	task->tk_action = call_bind;
-	rpcproc_count(task->tk_client, task->tk_proc)++;
-}
-
-/*
- * 0.	Get the server port number if not yet set
- */
-static void
-call_bind(struct rpc_task *task)
-{
-	struct rpc_clnt	*clnt = task->tk_client;
-	struct rpc_xprt *xprt = clnt->cl_xprt;
-
-	if (xprt->stream && !xprt->connected)
-		task->tk_action = call_reconnect;
-	else
-		task->tk_action = call_reserve;
-	task->tk_status = 0;
-
-	if (!clnt->cl_port)
-		rpc_getport(task, clnt);
+	task->tk_action = call_reserve;
+	rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 /*
@@ -344,26 +360,22 @@
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 
-	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
-	if (!clnt->cl_port) {
-		printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n",
-			clnt->cl_protname, clnt->cl_server,
-			clnt->cl_softrtry? "giving up" : "retrying");
-		if (!clnt->cl_softrtry) {
-			task->tk_action = call_bind;
-			rpc_delay(task, 5*HZ);
-			return;
-		}
+	if (task->tk_msg.rpc_proc > clnt->cl_maxproc) {
+		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
+			clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc);
 		rpc_exit(task, -EIO);
 		return;
 	}
+
+	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
 	if (!rpcauth_uptodatecred(task)) {
 		task->tk_action = call_refresh;
 		return;
 	}
+
+	task->tk_status  = 0;
 	task->tk_action  = call_reserveresult;
 	task->tk_timeout = clnt->cl_timeout.to_resrvval;
-	task->tk_status  = 0;
 	clnt->cl_stats->rpccnt++;
 	xprt_reserve(task);
 }
@@ -374,6 +386,8 @@
 static void
 call_reserveresult(struct rpc_task *task)
 {
+	int status = task->tk_status;
+
 	dprintk("RPC: %4d call_reserveresult (status %d)\n",
 				task->tk_pid, task->tk_status);
 	/*
@@ -382,7 +396,7 @@
 	 */
 	if ((task->tk_status >= 0 && !task->tk_rqstp) ||
 	    (task->tk_status < 0 && task->tk_rqstp))
-		printk("call_reserveresult: status=%d, request=%p??\n",
+		printk(KERN_ERR "call_reserveresult: status=%d, request=%p??\n",
 		 task->tk_status, task->tk_rqstp);
 
 	if (task->tk_status >= 0) {
@@ -390,11 +404,11 @@
 		return;
 	}
 
-	switch (task->tk_status) {
+	task->tk_status = 0;
+	switch (status) {
 	case -EAGAIN:
 	case -ENOBUFS:
 		task->tk_timeout = task->tk_client->cl_timeout.to_resrvval;
-		task->tk_status = 0;
 		task->tk_action = call_reserve;
 		break;
 	case -ETIMEDOUT:
@@ -402,11 +416,11 @@
 		task->tk_action = call_timeout;
 		break;
 	default:
-		task->tk_action = NULL;
 		if (!task->tk_rqstp) {
-			printk("RPC: task has no request, exit EIO\n");
+			printk(KERN_INFO "RPC: task has no request, exit EIO\n");
 			rpc_exit(task, -EIO);
-		}
+		} else
+			rpc_exit(task, status);
 	}
 }
 
@@ -428,13 +442,13 @@
 
 	/* FIXME: compute buffer requirements more exactly using
 	 * auth->au_wslack */
-	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE;
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE;
 
 	if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL)
 		return;
-	printk("RPC: buffer allocation failed for task %p\n", task); 
+	printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task); 
 
-	if (!signalled()) {
+	if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) {
 		xprt_release(task);
 		task->tk_action = call_reserve;
 		rpc_delay(task, HZ>>4);
@@ -460,10 +474,10 @@
 	dprintk("RPC: %4d call_encode (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
-	task->tk_action = call_transmit;
+	task->tk_action = call_bind;
 
 	/* Default buffer setup */
-	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE;
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE;
 	req->rq_svec[0].iov_base = task->tk_buffer;
 	req->rq_svec[0].iov_len  = bufsiz;
 	req->rq_slen		 = 0;
@@ -474,23 +488,16 @@
 	req->rq_rnr		 = 1;
 	req->rq_damaged		 = 0;
 
-	if (task->tk_proc > clnt->cl_maxproc) {
-		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
-			clnt->cl_protname, clnt->cl_vers, task->tk_proc);
-		rpc_exit(task, -EIO);
-		return;
-	}
-
 	/* Zero buffer so we have automatic zero-padding of opaque & string */
 	memset(task->tk_buffer, 0, bufsiz);
 
 	/* Encode header and provided arguments */
-	encode = rpcproc_encode(clnt, task->tk_proc);
+	encode = rpcproc_encode(clnt, task->tk_msg.rpc_proc);
 	if (!(p = call_header(task))) {
-		printk("RPC: call_header failed, exit EIO\n");
+		printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
 		rpc_exit(task, -EIO);
 	} else
-	if (encode && (status = encode(req, p, task->tk_argp)) < 0) {
+	if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) {
 		printk(KERN_WARNING "%s: can't encode arguments: %d\n",
 				clnt->cl_protname, -status);
 		rpc_exit(task, status);
@@ -498,38 +505,59 @@
 }
 
 /*
- * 4.	Transmit the RPC request
+ * 4.	Get the server port number if not yet set
  */
 static void
-call_transmit(struct rpc_task *task)
+call_bind(struct rpc_task *task)
 {
-	dprintk("RPC: %4d call_transmit (status %d)\n", 
-				task->tk_pid, task->tk_status);
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_xprt *xprt = clnt->cl_xprt;
 
-	task->tk_action = call_receive;
-	task->tk_status = 0;
-	xprt_transmit(task);
+	task->tk_action = (xprt->connected) ? call_transmit : call_reconnect;
+
+	if (!clnt->cl_port) {
+		task->tk_action = call_reconnect;
+		task->tk_timeout = clnt->cl_timeout.to_maxval;
+		rpc_getport(task, clnt);
+	}
 }
 
 /*
- * 5.	Wait for the RPC reply
+ * 4a.	Reconnect to the RPC server (TCP case)
  */
 static void
-call_receive(struct rpc_task *task)
+call_reconnect(struct rpc_task *task)
 {
-	dprintk("RPC: %4d call_receive (status %d)\n", 
-		task->tk_pid, task->tk_status);
+	struct rpc_clnt *clnt = task->tk_client;
 
-	task->tk_action = call_status;
+	dprintk("RPC: %4d call_reconnect status %d\n",
+				task->tk_pid, task->tk_status);
 
-	/* Need to ensure cleanups are performed by xprt_receive_status() */
-	xprt_receive(task);
+	task->tk_action = call_transmit;
+	if (task->tk_status < 0 || !clnt->cl_xprt->stream)
+		return;
+	clnt->cl_stats->netreconn++;
+	xprt_reconnect(task);
+}
 
-	/* If we have no decode function, this means we're performing
-	 * a void call (a la lockd message passing). */
-	if (!rpcproc_decode(task->tk_client, task->tk_proc)) {
-		task->tk_action = NULL;
+/*
+ * 5.	Transmit the RPC request, and wait for reply
+ */
+static void
+call_transmit(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+
+	dprintk("RPC: %4d call_transmit (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	task->tk_action = call_status;
+	if (task->tk_status < 0)
 		return;
+	xprt_transmit(task);
+	if (!rpcproc_decode(clnt, task->tk_msg.rpc_proc)) {
+		task->tk_action = NULL;
+		rpc_wake_up_task(task);
 	}
 }
 
@@ -558,34 +586,30 @@
 	case -ETIMEDOUT:
 		task->tk_action = call_timeout;
 		break;
-	case -EAGAIN:
-		if (!req)
-			task->tk_action = call_reserve;
-		else if (!task->tk_buffer)
-			task->tk_action = call_allocate;
-		else if (req->rq_damaged) {
-			task->tk_action = call_encode;
-			clnt->cl_stats->rpcretrans++;
-		} else {
-			task->tk_action = call_transmit;
-			clnt->cl_stats->rpcretrans++;
-		}
-		break;
 	case -ECONNREFUSED:
 	case -ENOTCONN:
+		req->rq_bytes_sent = 0;
 		if (clnt->cl_autobind || !clnt->cl_port) {
 			clnt->cl_port = 0;
 			task->tk_action = call_bind;
-		} else if (xprt->stream)
+			break;
+		}
+		if (xprt->stream) {
 			task->tk_action = call_reconnect;
-		else {
-			rpc_delay(task, 5*HZ); /* Hope it all wears off */
-			if (req->rq_damaged)
-				task->tk_action = call_encode;
-			else
-				task->tk_action = call_transmit;
-			clnt->cl_stats->rpcretrans++;
+			break;
 		}
+		/*
+		 * Sleep and dream of an open connection
+		 */
+		task->tk_timeout = 5 * HZ;
+		rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+	case -ENOMEM:
+	case -EAGAIN:
+		if (req->rq_damaged)
+			task->tk_action = call_encode;
+		else
+			task->tk_action = call_transmit;
+		clnt->cl_stats->rpcretrans++;
 		break;
 	default:
 		if (clnt->cl_chatty)
@@ -614,15 +638,13 @@
 				task->tk_pid);
 			goto minor_timeout;
 		}
-		to->to_initval <<= 1;
-		if (to->to_initval > to->to_maxval)
-			to->to_initval = to->to_maxval;
+		to->to_retries = clnt->cl_timeout.to_retries;
 	}
 
 	dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid);
 	if (clnt->cl_softrtry) {
 		if (clnt->cl_chatty && !task->tk_exit)
-			printk("%s: server %s not responding, timed out\n",
+			printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
 				clnt->cl_protname, clnt->cl_server);
 		rpc_exit(task, -EIO);
 		return;
@@ -630,24 +652,26 @@
 	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
 		task->tk_flags |= RPC_CALL_MAJORSEEN;
 		if (req)
-			printk("%s: server %s not responding, still trying\n",
+			printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
 				clnt->cl_protname, clnt->cl_server);
 		else
-			printk("%s: task %d can't get a request slot\n",
+			printk(KERN_NOTICE "%s: task %d can't get a request slot\n",
 				clnt->cl_protname, task->tk_pid);
 	}
 	if (clnt->cl_autobind)
 		clnt->cl_port = 0;
 
 minor_timeout:
-	if (!clnt->cl_port) {
+	if (!req)
+		task->tk_action = call_reserve;
+	else if (req->rq_damaged) {
+		task->tk_action = call_encode;
+		clnt->cl_stats->rpcretrans++;
+	} else if (!clnt->cl_port) {
 		task->tk_action = call_bind;
+		clnt->cl_stats->rpcretrans++;
 	} else if (clnt->cl_xprt->stream && !clnt->cl_xprt->connected) {
 		task->tk_action = call_reconnect;
-	} else if (!req) {
-		task->tk_action = call_reserve;
-	} else if (req->rq_damaged) {
-		task->tk_action = call_encode;
 		clnt->cl_stats->rpcretrans++;
 	} else {
 		task->tk_action = call_transmit;
@@ -657,21 +681,6 @@
 }
 
 /*
- * 6b.	Reconnect to the RPC server (TCP case)
- */
-static void
-call_reconnect(struct rpc_task *task)
-{
-	struct rpc_clnt *clnt = task->tk_client;
-	dprintk("RPC: %4d call_reconnect status %d\n",
-				task->tk_pid, task->tk_status);
-	task->tk_action = call_reserve;
-	task->tk_status = 0;
-	clnt->cl_stats->netreconn++;
-	xprt_reconnect(task);
-}
-
-/*
  * 7.	Decode the RPC reply
  */
 static void
@@ -679,20 +688,20 @@
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 	struct rpc_rqst	*req = task->tk_rqstp;
-	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_proc);
+	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_msg.rpc_proc);
 	u32		*p;
 
 	dprintk("RPC: %4d call_decode (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
 	if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
-		printk("%s: server %s OK\n",
+		printk(KERN_NOTICE "%s: server %s OK\n",
 			clnt->cl_protname, clnt->cl_server);
 		task->tk_flags &= ~RPC_CALL_MAJORSEEN;
 	}
 
 	if (task->tk_status < 12) {
-		printk("%s: too small RPC reply size (%d bytes)\n",
+		printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n",
 			clnt->cl_protname, task->tk_status);
 		rpc_exit(task, -EIO);
 		return;
@@ -708,10 +717,11 @@
 	 */
 	if (task->tk_client->cl_prog == 100003 && 
             (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
-		if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) {
+		if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
 			dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
 			task->tk_flags ^= RPC_CALL_REALUID;
 			task->tk_action = call_encode;
+			task->tk_suid_retry--;
 			return;
 		}
 	}
@@ -719,7 +729,7 @@
 	task->tk_action = NULL;
 
 	if (decode)
-		task->tk_status = decode(req, p, task->tk_resp);
+		task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
 	dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
 					task->tk_status);
 }
@@ -751,7 +761,7 @@
 	if (task->tk_status < 0)
 		rpc_exit(task, -EACCES);
 	else
-		task->tk_action = call_bind;
+		task->tk_action = call_reserve;
 }
 
 /*
@@ -772,7 +782,7 @@
 	*p++ = htonl(RPC_VERSION);	/* RPC version */
 	*p++ = htonl(clnt->cl_prog);	/* program number */
 	*p++ = htonl(clnt->cl_vers);	/* program version */
-	*p++ = htonl(task->tk_proc);	/* procedure */
+	*p++ = htonl(task->tk_msg.rpc_proc);	/* procedure */
 	return rpcauth_marshcred(task, p);
 }
 
@@ -787,20 +797,21 @@
 	p += 1;	/* skip XID */
 
 	if ((n = ntohl(*p++)) != RPC_REPLY) {
-		printk("call_verify: not an RPC reply: %x\n", n);
+		printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n);
 		goto garbage;
 	}
 	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
 		int	error = -EACCES;
 
 		if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
-			printk("call_verify: RPC call rejected: %x\n", n);
+			printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
 		} else
 		switch ((n = ntohl(*p++))) {
 		case RPC_AUTH_REJECTEDCRED:
 		case RPC_AUTH_REJECTEDVERF:
-			if (!task->tk_cred_retry--)
+			if (!task->tk_cred_retry)
 				break;
+			task->tk_cred_retry--;
 			dprintk("RPC: %4d call_verify: retry stale creds\n",
 							task->tk_pid);
 			rpcauth_invalcred(task);
@@ -809,17 +820,18 @@
 		case RPC_AUTH_BADCRED:
 		case RPC_AUTH_BADVERF:
 			/* possibly garbled cred/verf? */
-			if (!task->tk_garb_retry--)
+			if (!task->tk_garb_retry)
 				break;
+			task->tk_garb_retry--;
 			dprintk("RPC: %4d call_verify: retry garbled creds\n",
 							task->tk_pid);
 			task->tk_action = call_encode;
 			return NULL;
 		case RPC_AUTH_TOOWEAK:
-			printk("call_verify: server requires stronger "
+			printk(KERN_NOTICE "call_verify: server requires stronger "
 			       "authentication.\n");
 		default:
-			printk("call_verify: unknown auth error: %x\n", n);
+			printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
 			error = -EIO;
 		}
 		dprintk("RPC: %4d call_verify: call rejected %d\n",
@@ -828,7 +840,7 @@
 		return NULL;
 	}
 	if (!(p = rpcauth_checkverf(task, p))) {
-		printk("call_verify: auth check failed\n");
+		printk(KERN_WARNING "call_verify: auth check failed\n");
 		goto garbage;		/* bad verifier, retry */
 	}
 	switch ((n = ntohl(*p++))) {
@@ -837,19 +849,20 @@
 	case RPC_GARBAGE_ARGS:
 		break;			/* retry */
 	default:
-		printk("call_verify: server accept status: %x\n", n);
+		printk(KERN_WARNING "call_verify: server accept status: %x\n", n);
 		/* Also retry */
 	}
 
 garbage:
 	dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
 	task->tk_client->cl_stats->rpcgarbage++;
-	if (task->tk_garb_retry--) {
-		printk("RPC: garbage, retrying %4d\n", task->tk_pid);
+	if (task->tk_garb_retry) {
+		task->tk_garb_retry--;
+		printk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
 		task->tk_action = call_encode;
 		return NULL;
 	}
-	printk("RPC: garbage, exit EIO\n");
+	printk(KERN_WARNING "RPC: garbage, exit EIO\n");
 	rpc_exit(task, -EIO);
 	return NULL;
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/pmap_clnt.c linux/net/sunrpc/pmap_clnt.c
--- v2.3.99-pre2/linux/net/sunrpc/pmap_clnt.c	Sun Oct 12 10:17:46 1997
+++ linux/net/sunrpc/pmap_clnt.c	Mon Mar 20 08:14:04 2000
@@ -41,6 +41,7 @@
 {
 	struct rpc_portmap *map = &clnt->cl_pmap;
 	struct sockaddr_in *sap = &clnt->cl_xprt->addr;
+	struct rpc_message msg = { PMAP_GETPORT, map, &clnt->cl_port, NULL };
 	struct rpc_clnt	*pmap_clnt;
 	struct rpc_task	*child;
 
@@ -66,7 +67,7 @@
 		goto bailout;
 
 	/* Setup the call info struct */
-	rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
+	rpc_call_setup(child, &msg, 0);
 
 	/* ... and run the child task */
 	rpc_run_child(task, child, pmap_getport_done);
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.3.99-pre2/linux/net/sunrpc/sched.c	Thu Feb 10 17:11:25 2000
+++ linux/net/sunrpc/sched.c	Tue Mar 21 11:30:08 2000
@@ -69,6 +69,16 @@
 static int			rpc_inhibit = 0;
 
 /*
+ * Spinlock for wait queues. Access to the latter also has to be
+ * interrupt-safe in order to allow timers to wake up sleeping tasks.
+ */
+spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED;
+/*
+ * Spinlock for other critical sections of code.
+ */
+spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED;
+
+/*
  * This is the last-ditch buffer for NFS swap requests
  */
 static u32			swap_buffer[PAGE_SIZE >> 2];
@@ -87,14 +97,52 @@
 }
 
 /*
- * Spinlock for wait queues. Access to the latter also has to be
- * interrupt-safe in order to allow timers to wake up sleeping tasks.
+ * Set up a timer for the current task.
  */
-spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED;
+static inline void
+__rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	if (!task->tk_timeout)
+		return;
+
+	dprintk("RPC: %4d setting alarm for %lu ms\n",
+			task->tk_pid, task->tk_timeout * 1000 / HZ);
+
+	if (timer_pending(&task->tk_timer)) {
+		printk(KERN_ERR "RPC: Bug! Overwriting active timer\n");
+		del_timer(&task->tk_timer);
+	}
+	if (!timer)
+		timer = __rpc_default_timer;
+	init_timer(&task->tk_timer);
+	task->tk_timer.expires  = jiffies + task->tk_timeout;
+	task->tk_timer.data     = (unsigned long) task;
+	task->tk_timer.function = (void (*)(unsigned long)) timer;
+	add_timer(&task->tk_timer);
+}
+
 /*
- * Spinlock for other critical sections of code.
+ * Set up a timer for an already sleeping task.
  */
-spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED;
+void rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	spin_lock_bh(&rpc_queue_lock);
+	if (!(RPC_IS_RUNNING(task) || task->tk_wakeup))
+		__rpc_add_timer(task, timer);
+	spin_unlock_bh(&rpc_queue_lock);
+}
+
+/*
+ * Delete any timer for the current task.
+ */
+static inline void
+__rpc_del_timer(struct rpc_task *task)
+{
+	dprintk("RPC: %4d deleting timer\n", task->tk_pid);
+	if (timer_pending(&task->tk_timer))
+		del_timer(&task->tk_timer);
+	task->tk_timeout = 0;
+}
 
 /*
  * Add new request to wait queue.
@@ -104,16 +152,15 @@
  * improve overall performance.
  * Everyone else gets appended to the queue to ensure proper FIFO behavior.
  */
-static int
+static inline int
 __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
 {
-	if (task->tk_rpcwait) {
-		if (task->tk_rpcwait != queue)
-		{
-			printk(KERN_WARNING "RPC: doubly enqueued task!\n");
-			return -EWOULDBLOCK;
-		}
+	if (task->tk_rpcwait == queue)
 		return 0;
+
+	if (task->tk_rpcwait) {
+		printk(KERN_WARNING "RPC: doubly enqueued task!\n");
+		return -EWOULDBLOCK;
 	}
 	if (RPC_IS_SWAPPER(task))
 		rpc_insert_list(&queue->task, task);
@@ -130,7 +177,7 @@
 int
 rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task)
 {
-	int result;
+	int		result;
 
 	spin_lock_bh(&rpc_queue_lock);
 	result = __rpc_add_wait_queue(q, task);
@@ -142,13 +189,14 @@
  * Remove request from queue.
  * Note: must be called with spin lock held.
  */
-static void
+static inline void
 __rpc_remove_wait_queue(struct rpc_task *task)
 {
-	struct rpc_wait_queue *queue;
+	struct rpc_wait_queue *queue = task->tk_rpcwait;
 
-	if (!(queue = task->tk_rpcwait))
+	if (!queue)
 		return;
+
 	rpc_remove_list(&queue->task, task);
 	task->tk_rpcwait = NULL;
 
@@ -159,51 +207,14 @@
 void
 rpc_remove_wait_queue(struct rpc_task *task)
 {
+	if (!task->tk_rpcwait)
+		return;
 	spin_lock_bh(&rpc_queue_lock);
 	__rpc_remove_wait_queue(task);
 	spin_unlock_bh(&rpc_queue_lock);
 }
 
 /*
- * Set up a timer for the current task.
- */
-inline void
-rpc_add_timer(struct rpc_task *task, rpc_action timer)
-{
-	unsigned long	expires = jiffies + task->tk_timeout;
-
-	dprintk("RPC: %4d setting alarm for %lu ms\n",
-			task->tk_pid, task->tk_timeout * 1000 / HZ);
-	if (!timer)
-		timer = __rpc_default_timer;
-	if (time_before(expires, jiffies)) {
-		printk(KERN_ERR "RPC: bad timeout value %ld - setting to 10 sec!\n",
-					task->tk_timeout);
-		expires = jiffies + 10 * HZ;
-	}
-	task->tk_timer.expires  = expires;
-	task->tk_timer.data     = (unsigned long) task;
-	task->tk_timer.function = (void (*)(unsigned long)) timer;
-	task->tk_timer.prev     = NULL;
-	task->tk_timer.next     = NULL;
-	add_timer(&task->tk_timer);
-}
-
-/*
- * Delete any timer for the current task.
- * Must be called with interrupts off.
- */
-inline void
-rpc_del_timer(struct rpc_task *task)
-{
-	if (task->tk_timeout) {
-		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
-		del_timer(&task->tk_timer);
-		task->tk_timeout = 0;
-	}
-}
-
-/*
  * Make an RPC task runnable.
  *
  * Note: If the task is ASYNC, this must be called with 
@@ -218,31 +229,44 @@
 	}
 	task->tk_flags |= RPC_TASK_RUNNING;
 	if (RPC_IS_ASYNC(task)) {
-		int status;
-		status = __rpc_add_wait_queue(&schedq, task);
-		if (status)
-		{
-			printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
-			task->tk_status = status;
+		if (RPC_IS_SLEEPING(task)) {
+			int status;
+			status = __rpc_add_wait_queue(&schedq, task);
+			if (status < 0) {
+				printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
+				task->tk_status = status;
+			} else
+				task->tk_sleeping = 0;
 		}
 		wake_up(&rpciod_idle);
 	} else {
+		task->tk_sleeping = 0;
 		wake_up(&task->tk_wait);
 	}
 }
 
+/*
+ * Place a newly initialized task on the schedq.
+ */
+static inline void
+rpc_schedule_run(struct rpc_task *task)
+{
+	/* Don't run a child twice! */
+	if (RPC_IS_ACTIVATED(task))
+		return;
+	task->tk_active = 1;
+	task->tk_sleeping = 1;
+	rpc_make_runnable(task);
+}
 
 /*
  *	For other people who may need to wake the I/O daemon
  *	but should (for now) know nothing about its innards
  */
- 
 void rpciod_wake_up(void)
 {
 	if(rpciod_pid==0)
-	{
 		printk(KERN_ERR "rpciod: wot no daemon?\n");
-	}
 	wake_up(&rpciod_idle);
 }
 
@@ -261,19 +285,25 @@
 	dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid,
 				rpc_qname(q), jiffies);
 
+	if (!RPC_IS_ASYNC(task) && !RPC_IS_ACTIVATED(task)) {
+		printk(KERN_ERR "RPC: Inactive synchronous task put to sleep!\n");
+		return;
+	}
+
+	/* Mark the task as being activated if so needed */
+	if (!RPC_IS_ACTIVATED(task)) {
+		task->tk_active = 1;
+		task->tk_sleeping = 1;
+	}
+
 	status = __rpc_add_wait_queue(q, task);
-	if (status)
-	{
+	if (status) {
 		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
 		task->tk_status = status;
-		task->tk_flags |= RPC_TASK_RUNNING;
-	}
-	else
-	{
-		task->tk_callback = action;
-		if (task->tk_timeout)
-			rpc_add_timer(task, timer);
+	} else {
 		task->tk_flags &= ~RPC_TASK_RUNNING;
+		task->tk_callback = action;
+		__rpc_add_timer(task, timer);
 	}
 
 	return;
@@ -291,6 +321,19 @@
 	spin_unlock_bh(&rpc_queue_lock);
 }
 
+void
+rpc_sleep_locked(struct rpc_wait_queue *q, struct rpc_task *task,
+		 rpc_action action, rpc_action timer)
+{
+	/*
+	 * Protect the queue operations.
+	 */
+	spin_lock_bh(&rpc_queue_lock);
+	__rpc_sleep_on(q, task, action, timer);
+	rpc_lock_task(task);
+	spin_unlock_bh(&rpc_queue_lock);
+}
+
 /*
  * Wake up a single task -- must be invoked with spin lock held.
  *
@@ -307,16 +350,33 @@
 	if (task->tk_magic != 0xf00baa) {
 		printk(KERN_ERR "RPC: attempt to wake up non-existing task!\n");
 		rpc_debug = ~0;
+		rpc_show_tasks();
 		return;
 	}
 #endif
-	rpc_del_timer(task);
+	/* Has the task been executed yet? If not, we cannot wake it up! */
+	if (!RPC_IS_ACTIVATED(task)) {
+		printk(KERN_ERR "RPC: Inactive task (%p) being woken up!\n", task);
+		return;
+	}
+	if (RPC_IS_RUNNING(task))
+		return;
+
+	__rpc_del_timer(task);
+
+	/* If the task has been locked, then set tk_wakeup so that
+	 * rpc_unlock_task() wakes us up... */
+	if (task->tk_lock) {
+		task->tk_wakeup = 1;
+		return;
+	} else
+		task->tk_wakeup = 0;
+
 	if (task->tk_rpcwait != &schedq)
 		__rpc_remove_wait_queue(task);
-	if (!RPC_IS_RUNNING(task)) {
-		task->tk_flags |= RPC_TASK_CALLBACK;
-		rpc_make_runnable(task);
-	}
+	task->tk_flags |= RPC_TASK_CALLBACK;
+	rpc_make_runnable(task);
+
 	dprintk("RPC:      __rpc_wake_up done\n");
 }
 
@@ -338,6 +398,8 @@
 void
 rpc_wake_up_task(struct rpc_task *task)
 {
+	if (RPC_IS_RUNNING(task))
+		return;
 	spin_lock_bh(&rpc_queue_lock);
 	__rpc_wake_up(task);
 	spin_unlock_bh(&rpc_queue_lock);
@@ -389,6 +451,30 @@
 }
 
 /*
+ * Lock down a sleeping task to prevent it from waking up
+ * and disappearing from beneath us.
+ *
+ * This function should always be called with the
+ * rpc_queue_lock held.
+ */
+int
+rpc_lock_task(struct rpc_task *task)
+{
+	if (!RPC_IS_RUNNING(task))
+		return ++task->tk_lock;
+	return 0;
+}
+
+void
+rpc_unlock_task(struct rpc_task *task)
+{
+	spin_lock_bh(&rpc_queue_lock);
+	if (task->tk_lock && !--task->tk_lock && task->tk_wakeup)
+		__rpc_wake_up(task);
+	spin_unlock_bh(&rpc_queue_lock);
+}
+
+/*
  * Run a task at a later time
  */
 static void	__rpc_atrun(struct rpc_task *);
@@ -426,7 +512,7 @@
 		/*
 		 * Execute any pending callback.
 		 */
-		if (task->tk_flags & RPC_TASK_CALLBACK) {
+		if (RPC_DO_CALLBACK(task)) {
 			/* Define a callback save pointer */
 			void (*save_callback)(struct rpc_task *);
 	
@@ -446,101 +532,89 @@
 		}
 
 		/*
-		 * No handler for next step means exit.
-		 */
-		if (!task->tk_action)
-			break;
-
-		/*
 		 * Perform the next FSM step.
 		 * tk_action may be NULL when the task has been killed
 		 * by someone else.
 		 */
-		if (RPC_IS_RUNNING(task) && task->tk_action)
+		if (RPC_IS_RUNNING(task)) {
+			if (!task->tk_action)
+				break;
 			task->tk_action(task);
+		}
 
 		/*
 		 * Check whether task is sleeping.
-		 * Note that if the task may go to sleep in tk_action,
+		 * Note that if the task goes to sleep in tk_action,
 		 * and the RPC reply arrives before we get here, it will
 		 * have state RUNNING, but will still be on schedq.
+		 * 27/9/99: The above has been attempted fixed by
+		 *          introduction of task->tk_sleeping.
 		 */
 		spin_lock_bh(&rpc_queue_lock);
-		if (RPC_IS_RUNNING(task)) {
-			if (task->tk_rpcwait == &schedq)
-				__rpc_remove_wait_queue(task);
-		} else while (!RPC_IS_RUNNING(task)) {
+		if (!RPC_IS_RUNNING(task)) {
+			task->tk_sleeping = 1;
 			if (RPC_IS_ASYNC(task)) {
 				spin_unlock_bh(&rpc_queue_lock);
 				return 0;
 			}
+		} else
+			task->tk_sleeping = 0;
+		spin_unlock_bh(&rpc_queue_lock);
 
+		while (RPC_IS_SLEEPING(task)) {
 			/* sync task: sleep here */
 			dprintk("RPC: %4d sync task going to sleep\n",
 							task->tk_pid);
 			if (current->pid == rpciod_pid)
 				printk(KERN_ERR "RPC: rpciod waiting on sync task!\n");
 
-			spin_unlock_bh(&rpc_queue_lock);
-			__wait_event(task->tk_wait, RPC_IS_RUNNING(task));
-			spin_lock_bh(&rpc_queue_lock);
+			__wait_event(task->tk_wait, !RPC_IS_SLEEPING(task));
+			dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
 
 			/*
-			 * When the task received a signal, remove from
-			 * any queues etc, and make runnable again.
+			 * When a sync task receives a signal, it exits with
+			 * -ERESTARTSYS. In order to catch any callbacks that
+			 * clean up after sleeping on some queue, we don't
+			 * break the loop here, but go around once more.
 			 */
-			if (signalled())
-				__rpc_wake_up(task);
-
-			dprintk("RPC: %4d sync task resuming\n",
-							task->tk_pid);
-		}
-		spin_unlock_bh(&rpc_queue_lock);
-
-		/*
-		 * When a sync task receives a signal, it exits with
-		 * -ERESTARTSYS. In order to catch any callbacks that
-		 * clean up after sleeping on some queue, we don't
-		 * break the loop here, but go around once more.
-		 */
-		if (!RPC_IS_ASYNC(task) && signalled()) {
-			dprintk("RPC: %4d got signal\n", task->tk_pid);
-			rpc_exit(task, -ERESTARTSYS);
+			if (task->tk_client->cl_intr && signalled()) {
+				dprintk("RPC: %4d got signal\n", task->tk_pid);
+				task->tk_flags |= RPC_TASK_KILLED;
+				rpc_exit(task, -ERESTARTSYS);
+				rpc_wake_up_task(task);
+			}
 		}
 	}
 
 	dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
-	if (task->tk_exit) {
-		status = task->tk_status;
+	status = task->tk_status;
+	if (task->tk_exit)
 		task->tk_exit(task);
-	}
 
 	return status;
 }
 
 /*
  * User-visible entry point to the scheduler.
- * The recursion protection is for debugging. It should go away once
- * the code has stabilized.
+ *
+ * This may be called recursively if e.g. an async NFS task updates
+ * the attributes and finds that dirty pages must be flushed.
  */
-void
+int
 rpc_execute(struct rpc_task *task)
 {
-	static int	executing = 0;
-	int		incr = RPC_IS_ASYNC(task)? 1 : 0;
-
-	if (incr) {
-		if (rpc_inhibit) {
-			printk(KERN_INFO "RPC: execution inhibited!\n");
-			return;
-		}
-		if (executing)
-			printk(KERN_WARNING "RPC: %d tasks executed\n", executing);
+	if (rpc_inhibit) {
+		printk(KERN_INFO "RPC: execution inhibited!\n");
+		return -EIO;
 	}
+	task->tk_flags |= RPC_TASK_RUNNING;
+	if (task->tk_active) {
+		printk(KERN_ERR "RPC: active task was run twice!\n");
+		return -EWOULDBLOCK;
+	}
+	task->tk_active = 1;
 	
-	executing += incr;
-	__rpc_execute(task);
-	executing -= incr;
+	return __rpc_execute(task);
 }
 
 /*
@@ -551,28 +625,33 @@
 {
 	struct rpc_task	*task;
 	int		count = 0;
-	int need_resched = current->need_resched;
 
 	dprintk("RPC:      rpc_schedule enter\n");
 	while (1) {
+		/* Ensure equal rights for tcp tasks... */
+		rpciod_tcp_dispatcher();
+
 		spin_lock_bh(&rpc_queue_lock);
 		if (!(task = schedq.task)) {
 			spin_unlock_bh(&rpc_queue_lock);
 			break;
 		}
-		rpc_del_timer(task);
+		if (task->tk_lock) {
+			spin_unlock_bh(&rpc_queue_lock);
+			printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n");
+			rpc_debug = ~0;
+			rpc_show_tasks();
+			break;
+		}
 		__rpc_remove_wait_queue(task);
-		task->tk_flags |= RPC_TASK_RUNNING;
 		spin_unlock_bh(&rpc_queue_lock);
 
 		__rpc_execute(task);
 
-		if (++count >= 200) {
+		if (++count >= 200 || current->need_resched) {
 			count = 0;
-			need_resched = 1;
-		}
-		if (need_resched)
 			schedule();
+		}
 	}
 	dprintk("RPC:      rpc_schedule leave\n");
 }
@@ -646,8 +725,9 @@
 				rpc_action callback, int flags)
 {
 	memset(task, 0, sizeof(*task));
+	init_timer(&task->tk_timer);
 	task->tk_client = clnt;
-	task->tk_flags  = RPC_TASK_RUNNING | flags;
+	task->tk_flags  = flags;
 	task->tk_exit   = callback;
 	init_waitqueue_head(&task->tk_wait);
 	if (current->uid != current->fsuid || current->gid != current->fsgid)
@@ -717,6 +797,15 @@
 
 	dprintk("RPC: %4d release task\n", task->tk_pid);
 
+#ifdef RPC_DEBUG
+	if (task->tk_magic != 0xf00baa) {
+		printk(KERN_ERR "RPC: attempt to release a non-existing task!\n");
+		rpc_debug = ~0;
+		rpc_show_tasks();
+		return;
+	}
+#endif
+
 	/* Remove from global task list */
 	spin_lock(&rpc_sched_lock);
 	prev = task->tk_prev_task;
@@ -734,18 +823,20 @@
 	spin_lock_bh(&rpc_queue_lock);
 
 	/* Delete any running timer */
-	rpc_del_timer(task);
+	__rpc_del_timer(task);
 
 	/* Remove from any wait queue we're still on */
 	__rpc_remove_wait_queue(task);
 
+	task->tk_active = 0;
+
 	spin_unlock_bh(&rpc_queue_lock);
 
 	/* Release resources */
 	if (task->tk_rqstp)
 		xprt_release(task);
-	if (task->tk_cred)
-		rpcauth_releasecred(task);
+	if (task->tk_msg.rpc_cred)
+		rpcauth_unbindcred(task);
 	if (task->tk_buffer) {
 		rpc_free(task->tk_buffer);
 		task->tk_buffer = NULL;
@@ -824,7 +915,7 @@
 	spin_lock_bh(&rpc_queue_lock);
 	/* N.B. Is it possible for the child to have already finished? */
 	__rpc_sleep_on(&childq, task, func, NULL);
-	rpc_make_runnable(child);
+	rpc_schedule_run(child);
 	spin_unlock_bh(&rpc_queue_lock);
 }
 
@@ -903,8 +994,6 @@
 			schedule();
 			rounds = 0;
 		}
-		dprintk("RPC: rpciod running checking dispatch\n");
-		rpciod_tcp_dispatcher();
 
 		if (!rpciod_task_pending()) {
 			dprintk("RPC: rpciod back to sleep\n");
@@ -1032,11 +1121,9 @@
 }
 
 #ifdef RPC_DEBUG
-#include <linux/nfs_fs.h>
 void rpc_show_tasks(void)
 {
 	struct rpc_task *t = all_tasks, *next;
-	struct nfs_wreq *wreq;
 
 	spin_lock(&rpc_sched_lock);
 	t = all_tasks;
@@ -1049,22 +1136,11 @@
 	for (; t; t = next) {
 		next = t->tk_next_task;
 		printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n",
-			t->tk_pid, t->tk_proc, t->tk_flags, t->tk_status,
+			t->tk_pid, t->tk_msg.rpc_proc, t->tk_flags, t->tk_status,
 			t->tk_client, t->tk_client->cl_prog,
 			t->tk_rqstp, t->tk_timeout,
 			t->tk_rpcwait ? rpc_qname(t->tk_rpcwait) : " <NULL> ",
 			t->tk_action, t->tk_exit);
-
-		if (!(t->tk_flags & RPC_TASK_NFSWRITE))
-			continue;
-		/* NFS write requests */
-		wreq = (struct nfs_wreq *) t->tk_calldata;
-		printk("     NFS: flgs=%08x, pid=%d, pg=%p, off=(%d, %d)\n",
-			wreq->wb_flags, wreq->wb_pid, wreq->wb_page,
-			wreq->wb_offset, wreq->wb_bytes);
-		printk("          name=%s/%s\n",
-			wreq->wb_file->f_dentry->d_parent->d_name.name,
-			wreq->wb_file->f_dentry->d_name.name);
 	}
 	spin_unlock(&rpc_sched_lock);
 }
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c
--- v2.3.99-pre2/linux/net/sunrpc/sunrpc_syms.c	Wed Dec  8 14:11:28 1999
+++ linux/net/sunrpc/sunrpc_syms.c	Wed Mar 22 09:47:47 2000
@@ -35,13 +35,16 @@
 EXPORT_SYMBOL(rpc_run_child);
 EXPORT_SYMBOL(rpciod_down);
 EXPORT_SYMBOL(rpciod_up);
+EXPORT_SYMBOL(rpc_new_task);
+EXPORT_SYMBOL(rpc_wake_up_status);
 
 /* RPC client functions */
 EXPORT_SYMBOL(rpc_create_client);
 EXPORT_SYMBOL(rpc_destroy_client);
 EXPORT_SYMBOL(rpc_shutdown_client);
 EXPORT_SYMBOL(rpc_killall_tasks);
-EXPORT_SYMBOL(rpc_do_call);
+EXPORT_SYMBOL(rpc_call_sync);
+EXPORT_SYMBOL(rpc_call_async);
 EXPORT_SYMBOL(rpc_call_setup);
 EXPORT_SYMBOL(rpc_clnt_sigmask);
 EXPORT_SYMBOL(rpc_clnt_sigunmask);
@@ -60,6 +63,7 @@
 EXPORT_SYMBOL(rpcauth_free_credcache);
 EXPORT_SYMBOL(rpcauth_insert_credcache);
 EXPORT_SYMBOL(rpcauth_lookupcred);
+EXPORT_SYMBOL(rpcauth_bindcred);
 EXPORT_SYMBOL(rpcauth_matchcred);
 EXPORT_SYMBOL(rpcauth_releasecred);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.3.99-pre2/linux/net/sunrpc/xprt.c	Thu Feb 10 17:11:25 2000
+++ linux/net/sunrpc/xprt.c	Mon Mar 20 08:13:51 2000
@@ -68,7 +68,14 @@
 /* Following value should be > 32k + RPC overhead */
 #define XPRT_MIN_WRITE_SPACE 35000
 
+extern spinlock_t rpc_queue_lock;
+
+/*
+ * Local variables
+ */
+
 /* Spinlock for critical sections in the code. */
+spinlock_t xprt_sock_lock = SPIN_LOCK_UNLOCKED;
 spinlock_t xprt_lock = SPIN_LOCK_UNLOCKED;
 
 #ifdef RPC_DEBUG
@@ -86,14 +93,12 @@
  */
 static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
 static void	do_xprt_transmit(struct rpc_task *);
-static void	xprt_transmit_status(struct rpc_task *task);
-static void	xprt_transmit_timeout(struct rpc_task *task);
-static void	xprt_receive_status(struct rpc_task *task);
 static void	xprt_reserve_status(struct rpc_task *task);
 static void	xprt_disconnect(struct rpc_xprt *);
-static void	xprt_reconn_timeout(struct rpc_task *task);
-static struct socket *xprt_create_socket(int, struct sockaddr_in *,
-					struct rpc_timeout *);
+static void	xprt_reconn_status(struct rpc_task *task);
+static struct socket *xprt_create_socket(int, struct rpc_timeout *);
+static int	xprt_bind_socket(struct rpc_xprt *, struct socket *);
+static void	xprt_remove_pending(struct rpc_xprt *);
 
 #ifdef RPC_DEBUG_DATA
 /*
@@ -140,7 +145,7 @@
  */
  
 extern inline void
-xprt_move_iov(struct msghdr *msg, struct iovec *niv, int amount)
+xprt_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount)
 {
 	struct iovec *iv=msg->msg_iov;
 	int i;
@@ -148,7 +153,7 @@
 	/*
 	 *	Eat any sent iovecs
 	 */
-	while(iv->iov_len <= amount) {
+	while (iv->iov_len <= amount) {
 		amount -= iv->iov_len;
 		iv++;
 		msg->msg_iovlen--;
@@ -184,14 +189,17 @@
 	int		slen = req->rq_slen - req->rq_bytes_sent;
 	struct iovec	niv[MAX_IOVEC];
 
-	if (slen == 0)
+	if (slen <= 0)
 		return 0;
 
+	if (!sock)
+		return -ENOTCONN;
+
 	xprt_pktdump("packet data:",
 				req->rq_svec->iov_base,
 				req->rq_svec->iov_len);
 
-	msg.msg_flags   = MSG_DONTWAIT;
+	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
 	msg.msg_iov	= req->rq_svec;
 	msg.msg_iovlen	= req->rq_snr;
 	msg.msg_name	= (struct sockaddr *) &xprt->addr;
@@ -238,23 +246,30 @@
 /*
  * Read data from socket
  */
-static inline int
-xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len)
+static int
+xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, unsigned len, unsigned shift)
 {
 	struct socket	*sock = xprt->sock;
-	struct sockaddr_in sin;
 	struct msghdr	msg;
 	mm_segment_t	oldfs;
+	struct iovec	niv[MAX_IOVEC];
 	int		result;
 
-	msg.msg_flags   = MSG_DONTWAIT;
+	if (!sock)
+		return -ENOTCONN;
+
+	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
 	msg.msg_iov	= iov;
 	msg.msg_iovlen	= nr;
-	msg.msg_name	= &sin;
-	msg.msg_namelen = sizeof(sin);
+	msg.msg_name	= NULL;
+	msg.msg_namelen = 0;
 	msg.msg_control = NULL;
 	msg.msg_controllen = 0;
 
+	/* Adjust the iovec if we've already filled it */
+	if (shift)
+		xprt_move_iov(&msg, niv, shift);
+
 	oldfs = get_fs(); set_fs(get_ds());
 	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
 	set_fs(oldfs);
@@ -309,21 +324,30 @@
 int
 xprt_adjust_timeout(struct rpc_timeout *to)
 {
-	if (to->to_exponential)
-		to->to_current <<= 1;
-	else
-		to->to_current += to->to_increment;
-	if (to->to_maxval && to->to_current >= to->to_maxval) {
-		to->to_current = to->to_maxval;
-		to->to_retries = 0;
+	if (to->to_retries > 0) {
+		if (to->to_exponential)
+			to->to_current <<= 1;
+		else
+			to->to_current += to->to_increment;
+		if (to->to_maxval && to->to_current >= to->to_maxval)
+			to->to_current = to->to_maxval;
+	} else {
+		if (to->to_exponential)
+			to->to_initval <<= 1;
+		else
+			to->to_initval += to->to_increment;
+		if (to->to_maxval && to->to_initval >= to->to_maxval)
+			to->to_initval = to->to_maxval;
+		to->to_current = to->to_initval;
 	}
+
 	if (!to->to_current) {
 		printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n");
 		to->to_current = 5 * HZ;
 	}
 	pprintk("RPC: %lu %s\n", jiffies,
 			to->to_retries? "retrans" : "timeout");
-	return (to->to_retries)--;
+	return to->to_retries-- > 0;
 }
 
 /*
@@ -332,22 +356,29 @@
 static void
 xprt_close(struct rpc_xprt *xprt)
 {
+	struct socket	*sock = xprt->sock;
 	struct sock	*sk = xprt->inet;
 
-	xprt_disconnect(xprt);
+	if (!sk)
+		return;
+
+	xprt->inet = NULL;
+	xprt->sock = NULL;
 
 	sk->user_data    = NULL;
 	sk->data_ready   = xprt->old_data_ready;
 	sk->state_change = xprt->old_state_change;
 	sk->write_space  = xprt->old_write_space;
+
+	xprt_disconnect(xprt);
 	sk->no_check	 = 0;
 
-	sock_release(xprt->sock);
+	sock_release(sock);
 	/*
 	 *	TCP doesnt require the rpciod now - other things may
 	 *	but rpciod handles that not us.
 	 */
-	if(xprt->stream && !xprt->connecting)
+	if(xprt->stream)
 		rpciod_down();
 }
 
@@ -360,14 +391,10 @@
 	dprintk("RPC:      disconnected transport %p\n", xprt);
 	xprt->connected = 0;
 	xprt->tcp_offset = 0;
-	xprt->tcp_more = 0;
-	xprt->tcp_total = 0;
-	xprt->tcp_reclen = 0;
 	xprt->tcp_copied = 0;
-	xprt->tcp_rqstp  = NULL;
-	xprt->rx_pending_flag = 0;
+	xprt->tcp_more = 0;
+	xprt_remove_pending(xprt);
 	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
-	rpc_wake_up_status(&xprt->sending, -ENOTCONN);
 }
 
 /*
@@ -377,85 +404,87 @@
 xprt_reconnect(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
-	struct socket	*sock;
-	struct sock	*inet;
+	struct socket	*sock = xprt->sock;
+	struct sock	*inet = xprt->inet;
 	int		status;
 
 	dprintk("RPC: %4d xprt_reconnect %p connected %d\n",
 				task->tk_pid, xprt, xprt->connected);
-	task->tk_status = 0;
-
 	if (xprt->shutdown)
 		return;
 
 	if (!xprt->stream)
 		return;
 
-	spin_lock_bh(&xprt_lock);
-	if (xprt->connected) {
-		spin_unlock_bh(&xprt_lock);
+	if (!xprt->addr.sin_port) {
+		task->tk_status = -EIO;
 		return;
 	}
+
+	spin_lock(&xprt_lock);
 	if (xprt->connecting) {
-		task->tk_timeout = xprt->timeout.to_maxval;
+		task->tk_timeout = 0;
 		rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
-		spin_unlock_bh(&xprt_lock);
+		spin_unlock(&xprt_lock);
 		return;
 	}
 	xprt->connecting = 1;
-	spin_unlock_bh(&xprt_lock);
-
-	/* Create an unconnected socket */
-	if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout))) {
-		xprt->connecting = 0;
-		goto defer;
-	}
-
-	inet = sock->sk;
-	inet->data_ready   = xprt->inet->data_ready;
-	inet->state_change = xprt->inet->state_change;
-	inet->write_space  = xprt->inet->write_space;
-	inet->user_data    = xprt;
+	spin_unlock(&xprt_lock);
 
-	dprintk("RPC: %4d closing old socket\n", task->tk_pid);
-	xprt_close(xprt);
+	status = -ENOTCONN;
+	if (!inet) {
+		/* Create an unconnected socket */
+		if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout)))
+			goto defer;
+		xprt_bind_socket(xprt, sock);
+		inet = sock->sk;
+	}
 
-	/* Reset to new socket */
-	xprt->sock = sock;
-	xprt->inet = inet;
+	xprt_disconnect(xprt);
 
 	/* Now connect it asynchronously. */
 	dprintk("RPC: %4d connecting new socket\n", task->tk_pid);
 	status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
 				sizeof(xprt->addr), O_NONBLOCK);
 
-	xprt->connecting = 0;
 	if (status < 0) {
-		if (status != -EINPROGRESS && status != -EALREADY) {
+		switch (status) {
+		case -EALREADY:
+		case -EINPROGRESS:
+			status = 0;
+			break;
+		case -EISCONN:
+		case -EPIPE:
+			status = 0;
+			xprt_close(xprt);
+			goto defer;
+		default:
 			printk("RPC: TCP connect error %d!\n", -status);
+			xprt_close(xprt);
 			goto defer;
 		}
 
 		dprintk("RPC: %4d connect status %d connected %d\n",
 				task->tk_pid, status, xprt->connected);
-		task->tk_timeout = 60 * HZ;
 
-		spin_lock_bh(&xprt_lock);
+		spin_lock_bh(&xprt_sock_lock);
 		if (!xprt->connected) {
-			rpc_sleep_on(&xprt->reconn, task,
-				NULL, xprt_reconn_timeout);
-			spin_unlock_bh(&xprt_lock);
+			task->tk_timeout = xprt->timeout.to_maxval;
+			rpc_sleep_on(&xprt->reconn, task, xprt_reconn_status, NULL);
+			spin_unlock_bh(&xprt_sock_lock);
 			return;
 		}
-		spin_unlock_bh(&xprt_lock);
+		spin_unlock_bh(&xprt_sock_lock);
 	}
-
-
 defer:
-	spin_lock_bh(&xprt_lock);
-	if (!xprt->connected)
-		rpc_wake_up_next(&xprt->reconn);
-	spin_unlock_bh(&xprt_lock);
+	spin_lock(&xprt_lock);
+	xprt->connecting = 0;
+	if (status < 0) {
+		rpc_delay(task, 5*HZ);
+		task->tk_status = -ENOTCONN;
+	}
+	rpc_wake_up(&xprt->reconn);
+	spin_unlock(&xprt_lock);
 }
 
 /*
@@ -463,29 +492,21 @@
  * process of reconnecting, and leave the rest to the upper layers.
  */
 static void
-xprt_reconn_timeout(struct rpc_task *task)
+xprt_reconn_status(struct rpc_task *task)
 {
-	spin_lock_bh(&xprt_lock);
+	struct rpc_xprt	*xprt = task->tk_xprt;
+
 	dprintk("RPC: %4d xprt_reconn_timeout %d\n",
 				task->tk_pid, task->tk_status);
-	task->tk_status = -ENOTCONN;
-	if (task->tk_xprt->connecting)
-		task->tk_xprt->connecting = 0;
-	if (!task->tk_xprt->connected)
-		task->tk_status = -ENOTCONN;
-	else
-		task->tk_status = -ETIMEDOUT;
-	task->tk_timeout = 0;
-	rpc_wake_up_task(task);
-	spin_unlock_bh(&xprt_lock);
+
+	spin_lock(&xprt_lock);
+	xprt->connecting = 0;
+	rpc_wake_up(&xprt->reconn);
+	spin_unlock(&xprt_lock);
 }
 
-extern spinlock_t rpc_queue_lock;
 /*
- * Look up the RPC request corresponding to a reply.
- *
- * RED-PEN: Niiice... Guys, when will we learn finally that locking
- * in this manner is NOOP? --ANK
+ * Look up the RPC request corresponding to a reply, and then lock it.
  */
 static inline struct rpc_rqst *
 xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid)
@@ -511,6 +532,8 @@
  out_bad:
 	req = NULL;
  out:
+	if (req && !rpc_lock_task(req->rq_task))
+		req = NULL;
 	spin_unlock_bh(&rpc_queue_lock);
 	return req;
 }
@@ -524,9 +547,6 @@
 {
 	struct rpc_task	*task = req->rq_task;
 
-	req->rq_rlen   = copied;
-	req->rq_gotit  = 1;
-
 	/* Adjust congestion window */
 	xprt_adjust_cwnd(xprt, copied);
 
@@ -549,12 +569,11 @@
 	}
 #endif
 
-	/* ... and wake up the process. */
 	dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied);
 	task->tk_status = copied;
 
-	if (!RPC_IS_RUNNING(task))
-		rpc_wake_up_task(task);
+	/* ... and wake up the process. */
+	rpc_wake_up_task(task);
 	return;
 }
 
@@ -612,6 +631,7 @@
 static inline void
 udp_data_ready(struct sock *sk, int len)
 {
+	struct rpc_task	*task;
 	struct rpc_xprt	*xprt;
 	struct rpc_rqst *rovr;
 	struct sk_buff	*skb;
@@ -626,7 +646,10 @@
 	dprintk("RPC:      udp_data_ready client %p\n", xprt);
 
 	if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL)
-		goto out_err;
+		return;
+
+	if (xprt->shutdown)
+		goto dropit;
 
 	repsize = skb->len - sizeof(struct udphdr);
 	if (repsize < 4) {
@@ -634,14 +657,15 @@
 		goto dropit;
 	}
 
-	/* Look up the request corresponding to the given XID */
-	if (!(rovr = xprt_lookup_rqst(xprt,
-				      *(u32 *) (skb->h.raw + sizeof(struct udphdr)))))
+	/* Look up and lock the request corresponding to the given XID */
+	rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + sizeof(struct udphdr)));
+	if (!rovr)
 		goto dropit;
+	task = rovr->rq_task;
 
-	dprintk("RPC: %4d received reply\n", rovr->rq_task->tk_pid);
+	dprintk("RPC: %4d received reply\n", task->tk_pid);
 	xprt_pktdump("packet data:",
-		     (u32 *) (skb->h.raw + sizeof(struct udphdr)), repsize);
+		     (u32 *) (skb->h.raw+sizeof(struct udphdr)), repsize);
 
 	if ((copied = rovr->rq_rlen) > repsize)
 		copied = repsize;
@@ -649,213 +673,287 @@
 	rovr->rq_damaged  = 1;
 	/* Suck it into the iovec, verify checksum if not done by hw. */
 	if (csum_partial_copy_to_page_cache(rovr->rq_rvec, skb, copied))
-		goto dropit;
+		goto out_unlock;
 
 	/* Something worked... */
 	dst_confirm(skb->dst);
 
 	xprt_complete_rqst(xprt, rovr, copied);
 
-dropit:
+ out_unlock:
+	rpc_unlock_task(task);
+
+ dropit:
 	skb_free_datagram(sk, skb);
-	return;
-out_err:
-	return;
 }
 
 /*
- * TCP record receive routine
- * This is not the most efficient code since we call recvfrom twice--
- * first receiving the record marker and XID, then the data.
- * 
- * The optimal solution would be a RPC support in the TCP layer, which
- * would gather all data up to the next record marker and then pass us
- * the list of all TCP segments ready to be copied.
+ * TCP read fragment marker
  */
 static inline int
-tcp_input_record(struct rpc_xprt *xprt)
+tcp_read_fraghdr(struct rpc_xprt *xprt)
 {
-	struct rpc_rqst	*req;
-	struct iovec	*iov;
 	struct iovec	riov;
-	u32		offset;
-	int		result, maxcpy, reclen, avail, want;
+	int		want, result;
 
-	dprintk("RPC:      tcp_input_record\n");
+	if (xprt->tcp_offset >= xprt->tcp_reclen + sizeof(xprt->tcp_recm)) {
+		xprt->tcp_offset = 0;
+		xprt->tcp_reclen = 0;
+	}
+	if (xprt->tcp_offset >= sizeof(xprt->tcp_recm))
+		goto done;
 
-	offset = xprt->tcp_offset;
-	result = -EAGAIN;
-	if (offset < 4 || (!xprt->tcp_more && offset < 8)) {
-		want = (xprt->tcp_more? 4 : 8) - offset;
-		dprintk("RPC:      reading header (%d bytes)\n", want);
-		riov.iov_base = xprt->tcp_recm.data + offset;
+	want = sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+	dprintk("RPC:      reading header (%d bytes)\n", want);
+	do {
+		riov.iov_base = ((u8*) &xprt->tcp_recm) + xprt->tcp_offset;
 		riov.iov_len  = want;
-		result = xprt_recvmsg(xprt, &riov, 1, want);
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
 		if (result < 0)
-			goto done;
-		offset += result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
+			return result;
+		xprt->tcp_offset += result;
+		want -= result;
+	} while (want);
 
-		/* Get the record length and mask out the more_fragments bit */
-		reclen = ntohl(xprt->tcp_reclen);
-		dprintk("RPC:      reclen %08x\n", reclen);
-		xprt->tcp_more = (reclen & 0x80000000)? 0 : 1;
-		reclen &= 0x7fffffff;
-		xprt->tcp_total += reclen;
-		xprt->tcp_reclen = reclen;
-
-		dprintk("RPC:      got xid %08x reclen %d morefrags %d\n",
-			xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more);
-		if (!xprt->tcp_copied
-		 && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) {
-			iov = xprt->tcp_iovec;
-			memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
-#if 0
-*(u32 *)iov->iov_base = req->rq_xid;
-#endif
-			iov->iov_base += 4;
-			iov->iov_len  -= 4;
-			xprt->tcp_copied = 4;
-			xprt->tcp_rqstp  = req;
-		}
-	} else {
-		reclen = xprt->tcp_reclen;
-	}
+	/* Is this another fragment in the last message */
+	if (!xprt->tcp_more)
+		xprt->tcp_copied = 0; /* No, so we're reading a new message */
+
+	/* Get the record length and mask out the last fragment bit */
+	xprt->tcp_reclen = ntohl(xprt->tcp_recm);
+	xprt->tcp_more = (xprt->tcp_reclen & 0x80000000) ? 0 : 1;
+	xprt->tcp_reclen &= 0x7fffffff;
+
+	dprintk("RPC:      New record reclen %d morefrags %d\n",
+				   xprt->tcp_reclen, xprt->tcp_more);
+ done:
+	return xprt->tcp_reclen + sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+}
+
+/*
+ * TCP read xid
+ */
+static inline int
+tcp_read_xid(struct rpc_xprt *xprt, int avail)
+{
+	struct iovec	riov;
+	int		want, result;
+
+	if (xprt->tcp_copied >= sizeof(xprt->tcp_xid) || !avail)
+		goto done;
+	want = MIN(sizeof(xprt->tcp_xid) - xprt->tcp_copied, avail);
+	do {
+		dprintk("RPC:      reading xid (%d bytes)\n", want);
+		riov.iov_base = ((u8*) &xprt->tcp_xid) + xprt->tcp_copied;
+		riov.iov_len  = want;
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
+		if (result < 0)
+			return result;
+		xprt->tcp_copied += result;
+		xprt->tcp_offset += result;
+		want  -= result;
+		avail -= result;
+	} while (want);
+ done:
+	return avail;
+}
 
-	avail = reclen - (offset - 4);
-	if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid
-	 && req->rq_task->tk_rpcwait == &xprt->pending) {
-		want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+/*
+ * TCP read and complete request
+ */
+static inline int
+tcp_read_request(struct rpc_xprt *xprt, struct rpc_rqst *req, int avail)
+{
+	int	want, result;
 
+	if (req->rq_rlen <= xprt->tcp_copied || !avail)
+		goto done;
+	want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+	do {
 		dprintk("RPC: %4d TCP receiving %d bytes\n",
-					req->rq_task->tk_pid, want);
-		/* Request must be re-encoded before retransmit */
-		req->rq_damaged = 1;
-		result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want);
+			req->rq_task->tk_pid, want);
+
+		result = xprt_recvmsg(xprt, req->rq_rvec, req->rq_rnr, want, xprt->tcp_copied);
 		if (result < 0)
-			goto done;
+			return result;
 		xprt->tcp_copied += result;
-		offset += result;
+		xprt->tcp_offset += result;
 		avail  -= result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
+		want   -= result;
+	} while (want);
 
-		maxcpy = MIN(req->rq_rlen, xprt->tcp_total);
-		if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) {
-			dprintk("RPC: %4d received reply complete\n",
-					req->rq_task->tk_pid);
-			xprt_complete_rqst(xprt, req, xprt->tcp_total);
-			xprt->tcp_copied = 0;
-			xprt->tcp_rqstp  = NULL;
-		}
-	}
+ done:
+	if (req->rq_rlen > xprt->tcp_copied && xprt->tcp_more)
+		return avail;
+	dprintk("RPC: %4d received reply complete\n", req->rq_task->tk_pid);
+	xprt_complete_rqst(xprt, req, xprt->tcp_copied);
 
-	/* Skip over any trailing bytes on short reads */
-	while (avail > 0) {
-		static u8	dummy[64];
+	return avail;
+}
 
+/*
+ * TCP discard extra bytes from a short read
+ */
+static inline int
+tcp_read_discard(struct rpc_xprt *xprt, int avail)
+{
+	struct iovec	riov;
+	static u8	dummy[64];
+	int		want, result = 0;
+
+	while (avail) {
 		want = MIN(avail, sizeof(dummy));
 		riov.iov_base = dummy;
 		riov.iov_len  = want;
 		dprintk("RPC:      TCP skipping %d bytes\n", want);
-		result = xprt_recvmsg(xprt, &riov, 1, want);
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
 		if (result < 0)
-			goto done;
-		offset += result;
+			return result;
+		xprt->tcp_offset += result;
 		avail  -= result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
+	}
+	return avail;
+}
+
+/*
+ * TCP record receive routine
+ * This is not the most efficient code since we call recvfrom thrice--
+ * first receiving the record marker, then the XID, then the data.
+ * 
+ * The optimal solution would be a RPC support in the TCP layer, which
+ * would gather all data up to the next record marker and then pass us
+ * the list of all TCP segments ready to be copied.
+ */
+static int
+tcp_input_record(struct rpc_xprt *xprt)
+{
+	struct rpc_rqst	*req = NULL;
+	struct rpc_task	*task = NULL;
+	int		avail, result;
+
+	dprintk("RPC:      tcp_input_record\n");
+
+	if (xprt->shutdown)
+		return -EIO;
+	if (!xprt->connected)
+		return -ENOTCONN;
+
+	/* Read in a new fragment marker if necessary */
+	/* Can we ever really expect to get completely empty fragments? */
+	if ((result = tcp_read_fraghdr(xprt)) <= 0)
+		return result;
+	avail = result;
+
+	/* Read in the xid if necessary */
+	if ((result = tcp_read_xid(xprt, avail)) <= 0)
+		return result;
+	avail = result;
+
+	/* Find and lock the request corresponding to this xid */
+	req = xprt_lookup_rqst(xprt, xprt->tcp_xid);
+	if (req) {
+		task = req->rq_task;
+		if (xprt->tcp_copied == sizeof(xprt->tcp_xid) || req->rq_damaged) {
+			req->rq_damaged = 1;
+			/* Read in the request data */
+			result = tcp_read_request(xprt,  req, avail);
 		}
+		rpc_unlock_task(task);
+		if (result < 0)
+			return result;
+		avail = result;
 	}
-	if (!xprt->tcp_more)
-		xprt->tcp_total = 0;
-	offset = 0;
 
-done:
-	dprintk("RPC:      tcp_input_record done (off %d total %d copied %d)\n",
-			offset, xprt->tcp_total, xprt->tcp_copied);
-	xprt->tcp_offset = offset;
+	/* Skip over any trailing bytes on short reads */
+	if ((result = tcp_read_discard(xprt, avail)) < 0)
+		return result;
+
+	dprintk("RPC:      tcp_input_record done (off %d reclen %d copied %d)\n",
+			xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_copied);
+	result = xprt->tcp_reclen;
 	return result;
 }
 
 /*
  *	TCP task queue stuff
  */
- 
-static struct rpc_xprt *rpc_xprt_pending = NULL;	/* Chain by rx_pending of rpc_xprt's */
+LIST_HEAD(rpc_xprt_pending);	/* List of xprts having pending tcp requests */
+
+static inline
+void tcp_rpciod_queue(void)
+{
+	rpciod_wake_up();
+}
+
+static inline
+void xprt_append_pending(struct rpc_xprt *xprt)
+{
+	if (!list_empty(&xprt->rx_pending))
+		return;
+	spin_lock_bh(&rpc_queue_lock);
+	if (list_empty(&xprt->rx_pending)) {
+		list_add(&xprt->rx_pending, rpc_xprt_pending.prev);
+		dprintk("RPC:     xprt queue %p\n", xprt);
+		tcp_rpciod_queue();
+	}
+	spin_unlock_bh(&rpc_queue_lock);
+}
+
+static
+void xprt_remove_pending(struct rpc_xprt *xprt)
+{
+	spin_lock_bh(&rpc_queue_lock);
+	if (!list_empty(&xprt->rx_pending)) {
+		list_del(&xprt->rx_pending);
+		INIT_LIST_HEAD(&xprt->rx_pending);
+	}
+	spin_unlock_bh(&rpc_queue_lock);
+}
+
+static inline
+struct rpc_xprt *xprt_remove_pending_next(void)
+{
+	struct rpc_xprt	*xprt = NULL;
+
+	spin_lock_bh(&rpc_queue_lock);
+	if (!list_empty(&rpc_xprt_pending)) {
+		xprt = list_entry(rpc_xprt_pending.next, struct rpc_xprt, rx_pending);
+		list_del(&xprt->rx_pending);
+		INIT_LIST_HEAD(&xprt->rx_pending);
+	}
+	spin_unlock_bh(&rpc_queue_lock);
+	return xprt;
+}
 
 /*
  *	This is protected from tcp_data_ready and the stack as its run
  *	inside of the RPC I/O daemon
  */
-static void
-do_rpciod_tcp_dispatcher(void)
+void
+__rpciod_tcp_dispatcher(void)
 {
 	struct rpc_xprt *xprt;
-	int result = 0;
+	int safe_retry = 0, result;
 
 	dprintk("rpciod_tcp_dispatcher: Queue Running\n");
 
 	/*
 	 *	Empty each pending socket
 	 */
- 
-	while(1) {
-		int safe_retry=0;
-
-		if ((xprt = rpc_xprt_pending) == NULL) {
-			break;
-		}
-		xprt->rx_pending_flag = 0;
-		rpc_xprt_pending=xprt->rx_pending;
-		xprt->rx_pending = NULL;
-
+	while ((xprt = xprt_remove_pending_next()) != NULL) {
 		dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt);
 
-		do 
-		{
-			if (safe_retry++ > 50)
-				break;
+		do {
 			result = tcp_input_record(xprt);
-		}
-		while (result >= 0);
+		} while (result >= 0);
 
-		switch (result) {
-			case -EAGAIN:
-			case -ENOTCONN:
-			case -EPIPE:
-				continue;
-			default:
-				printk(KERN_WARNING "RPC: unexpected error %d from tcp_input_record\n",
-					result);
+		if (safe_retry++ > 200) {
+			schedule();
+			safe_retry = 0;
 		}
 	}
 }
 
-void rpciod_tcp_dispatcher(void)
-{
-	/* mama... start_bh_atomic was here...
-	   Calls to sock->ops _are_ _impossible_ with disabled bh. Period. --ANK
-	 */
-	do_rpciod_tcp_dispatcher();
-}
-
-int xprt_tcp_pending(void)
-{
-	return rpc_xprt_pending != NULL;
-}
-
-extern inline void tcp_rpciod_queue(void)
-{
-	rpciod_wake_up();
-}
-
 /*
  *	data_ready callback for TCP. We can't just jump into the
  *	tcp recvmsg functions inside of the network receive bh or
@@ -874,24 +972,15 @@
 		return;
 	}
 
+	if (xprt->shutdown)
+		return;
+
+	xprt_append_pending(xprt);
+
 	dprintk("RPC:      tcp_data_ready client %p\n", xprt);
 	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
 				sk->state, xprt->connected,
 				sk->dead, sk->zapped);
-	/*
-	 *	If we are not waiting for the RPC bh run then
-	 *	we are now
-	 */
-	if (!xprt->rx_pending_flag) {
-		dprintk("RPC:     xprt queue %p\n", rpc_xprt_pending);
-
-		xprt->rx_pending=rpc_xprt_pending;
-		rpc_xprt_pending=xprt;
-		xprt->rx_pending_flag=1;
-	} else
-		dprintk("RPC:     xprt queued already %p\n", xprt);
-	tcp_rpciod_queue();
-
 }
 
 
@@ -907,26 +996,20 @@
 				sk->state, xprt->connected,
 				sk->dead, sk->zapped);
 
-	switch(sk->state) {
+	spin_lock_bh(&xprt_sock_lock);
+	switch (sk->state) {
 	case TCP_ESTABLISHED:
-		if (xprt->connected)
-			break;
 		xprt->connected = 1;
-		xprt->connecting = 0;
+		if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+			rpc_wake_up_task(xprt->snd_task);
 		rpc_wake_up(&xprt->reconn);
-		rpc_wake_up_next(&xprt->sending);
-		tcp_rpciod_queue();
-		break;
-	case TCP_CLOSE:
-		if (xprt->connecting)
-			break;
-		xprt_disconnect(xprt);
-		rpc_wake_up_status(&xprt->reconn,  -ENOTCONN);
 		break;
 	default:
+		xprt->connected = 0;
+		rpc_wake_up_status(&xprt->pending, -ENOTCONN);
 		break;
 	}
-
+	spin_unlock_bh(&xprt_sock_lock);
 }
 
 /*
@@ -940,20 +1023,23 @@
 
 	if (!(xprt = xprt_from_sock(sk)))
 		return;
+	if (xprt->shutdown)
+		return;
 
 	/* Wait until we have enough socket memory */
 	if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE))
 		return;
 
+	spin_lock_bh(&xprt_sock_lock);
 	if (xprt->write_space)
-		return;
+		goto out_unlock;
 
 	xprt->write_space = 1;
 
-	if (!xprt->snd_task)
-		rpc_wake_up_next(&xprt->sending);
-	else if (!RPC_IS_RUNNING(xprt->snd_task))
+	if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
 		rpc_wake_up_task(xprt->snd_task);
+ out_unlock:
+	spin_unlock_bh(&xprt_sock_lock);
 }
 
 static void
@@ -963,20 +1049,24 @@
 
 	if (!(xprt = xprt_from_sock(sk)))
 		return;
+	if (xprt->shutdown)
+		return;
 
 
 	/* Wait until we have enough socket memory */
 	if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE))
 		return;
 
+	spin_lock_bh(&xprt_sock_lock);
 	if (xprt->write_space)
-		return;
+		goto out_unlock;
 
 	xprt->write_space = 1;
-	if (!xprt->snd_task)
-		rpc_wake_up_next(&xprt->sending);
-	else if (!RPC_IS_RUNNING(xprt->snd_task))
+
+	if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
 		rpc_wake_up_task(xprt->snd_task);
+ out_unlock:
+	spin_unlock_bh(&xprt_sock_lock);
 }
 
 /*
@@ -987,9 +1077,8 @@
 {
 	struct rpc_rqst	*req = task->tk_rqstp;
 
-	if (req) {
+	if (req)
 		xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
-	}
 
 	dprintk("RPC: %4d xprt_timer (%s request)\n",
 		task->tk_pid, req ? "pending" : "backlogged");
@@ -1010,12 +1099,13 @@
 	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
 	struct rpc_rqst	*req = task->tk_rqstp;
 
-	spin_lock_bh(&xprt_lock);
+	spin_lock(&xprt_lock);
 	if (xprt->snd_task && xprt->snd_task != task) {
 		dprintk("RPC: %4d TCP write queue full (task %d)\n",
 			task->tk_pid, xprt->snd_task->tk_pid);
-		task->tk_timeout = req->rq_timeout.to_current;
-		rpc_sleep_on(&xprt->sending, task, xprt_transmit, NULL);
+		task->tk_timeout = 0;
+		task->tk_status = -EAGAIN;
+		rpc_sleep_on(&xprt->sending, task, NULL, NULL);
 	} else if (!xprt->snd_task) {
 		xprt->snd_task = task;
 #ifdef RPC_PROFILE
@@ -1023,23 +1113,23 @@
 #endif
 		req->rq_bytes_sent = 0;
 	}
-	spin_unlock_bh(&xprt_lock);
+	spin_unlock(&xprt_lock);
 	return xprt->snd_task == task;
 }
 
 /*
  * Releases the socket for use by other requests.
  */
-static void
+static inline void
 xprt_up_transmit(struct rpc_task *task)
 {
 	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
 
 	if (xprt->snd_task && xprt->snd_task == task) {
-		spin_lock_bh(&xprt_lock);
+		spin_lock(&xprt_lock);
 		xprt->snd_task = NULL;
 		rpc_wake_up_next(&xprt->sending);
-		spin_unlock_bh(&xprt_lock);
+		spin_unlock(&xprt_lock);
 	}
 }
 
@@ -1050,7 +1140,6 @@
 void
 xprt_transmit(struct rpc_task *task)
 {
-	struct rpc_timeout *timeo;
 	struct rpc_rqst	*req = task->tk_rqstp;
 	struct rpc_xprt	*xprt = req->rq_xprt;
 
@@ -1060,26 +1149,21 @@
 	if (xprt->shutdown)
 		task->tk_status = -EIO;
 
+	if (!xprt->connected)
+		task->tk_status = -ENOTCONN;
+
 	if (task->tk_status < 0)
 		return;
 
-	/* Reset timeout parameters */
-	timeo = &req->rq_timeout;
-	if (timeo->to_retries < 0) {
-		dprintk("RPC: %4d xprt_transmit reset timeo\n",
-					task->tk_pid);
-		timeo->to_retries = xprt->timeout.to_retries;
-		timeo->to_current = timeo->to_initval;
-	}
+	if (task->tk_rpcwait)
+		rpc_remove_wait_queue(task);
 
 	/* set up everything as needed. */
 	/* Write the record marker */
 	if (xprt->stream) {
-		u32	marker;
-
-		marker = htonl(0x80000000|(req->rq_slen-4));
-		*((u32 *) req->rq_svec[0].iov_base) = marker;
+		u32	*marker = req->rq_svec[0].iov_base;
 
+		*marker = htonl(0x80000000|(req->rq_slen-sizeof(*marker)));
 	}
 
 	if (!xprt_down_transmit(task))
@@ -1095,24 +1179,14 @@
 	struct rpc_xprt	*xprt = req->rq_xprt;
 	int status, retry = 0;
 
-	if (xprt->shutdown) {
-		task->tk_status = -EIO;
-		goto out_release;
-	}
 
 	/* For fast networks/servers we have to put the request on
 	 * the pending list now:
+	 * Note that we don't want the task timing out during the
+	 * call to xprt_sendmsg(), so we initially disable the timeout,
+	 * and then reset it later...
 	 */
-	req->rq_gotit = 0;
-	status = rpc_add_wait_queue(&xprt->pending, task);
-	if (!status)
-		task->tk_callback = NULL;
-
-	if (status) {
-		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
-		task->tk_status = status;
-		goto out_release;
-	}
+	xprt_receive(task);
 
 	/* Continue transmitting the packet/record. We must be careful
 	 * to cope with writespace callbacks arriving _after_ we have
@@ -1129,80 +1203,67 @@
 			req->rq_bytes_sent += status;
 
 			if (req->rq_bytes_sent >= req->rq_slen)
-				goto out_release;
-		}
-
-		if (status < req->rq_slen)
-			status = -EAGAIN;
-
-		if (status >= 0 || !xprt->stream) {
-			dprintk("RPC: %4d xmit complete\n", task->tk_pid);
-			goto out_release;
+				goto out_receive;
+		} else {
+			if (status >= req->rq_slen)
+				goto out_receive;
+			status = -ENOMEM;
+			break;
 		}
 
 		dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
 				task->tk_pid, req->rq_slen - req->rq_bytes_sent,
 				req->rq_slen);
 
+		status = -EAGAIN;
 		if (retry++ > 50)
 			break;
 	}
+	rpc_unlock_task(task);
 
-	task->tk_status = (status == -ENOMEM) ? -EAGAIN : status;
+	task->tk_status = status;
 
-	/* We don't care if we got a reply, so don't protect
-	 * against bh. */
-	if (task->tk_rpcwait == &xprt->pending)
-		rpc_remove_wait_queue(task);
+	/* Note: at this point, task->tk_sleeping has not yet been set,
+	 *	 hence there is no danger of the waking up task being put on
+	 *	 schedq, and being picked up by a parallel run of rpciod().
+	 */
+	rpc_wake_up_task(task);
+	if (!RPC_IS_RUNNING(task))
+		goto out_release;
 
-	/* Protect against (udp|tcp)_write_space */
-	spin_lock_bh(&xprt_lock);
-	if (status == -ENOMEM || status == -EAGAIN) {
+	switch (status) {
+	case -ENOMEM:
+		/* Protect against (udp|tcp)_write_space */
 		task->tk_timeout = req->rq_timeout.to_current;
+		spin_lock_bh(&xprt_sock_lock);
 		if (!xprt->write_space)
-			rpc_sleep_on(&xprt->sending, task, xprt_transmit_status,
-				     xprt_transmit_timeout);
-		spin_unlock_bh(&xprt_lock);
+			rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+		spin_unlock_bh(&xprt_sock_lock);
 		return;
-	}
-	spin_unlock_bh(&xprt_lock);
-
-out_release:
-	xprt_up_transmit(task);
-}
-
-/*
- * This callback is invoked when the sending task is forced to sleep
- * because the TCP write buffers are full
- */
-static void
-xprt_transmit_status(struct rpc_task *task)
-{
-	struct rpc_xprt	*xprt = task->tk_client->cl_xprt;
-
-	dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status);
-	if (xprt->snd_task == task) {
-		task->tk_status = 0;
-		do_xprt_transmit(task);
+	case -EAGAIN:
+		/* Keep holding the socket if it is blocked */
+		rpc_delay(task, HZ>>4);
 		return;
+	case -ECONNREFUSED:
+	case -ENOTCONN:
+		if (!xprt->stream)
+			return;
+	default:
+		goto out_release;
 	}
-}
 
-/*
- * RPC transmit timeout handler.
- */
-static void
-xprt_transmit_timeout(struct rpc_task *task)
-{
-	dprintk("RPC: %4d transmit_timeout %d\n", task->tk_pid, task->tk_status);
-	task->tk_status  = -ETIMEDOUT;
-	task->tk_timeout = 0;
-	rpc_wake_up_task(task);
+ out_receive:
+	dprintk("RPC: %4d xmit complete\n", task->tk_pid);
+	/* Set the task's receive timeout value */
+	task->tk_timeout = req->rq_timeout.to_current;
+	rpc_add_timer(task, xprt_timer);
+	rpc_unlock_task(task);
+ out_release:
 	xprt_up_transmit(task);
 }
 
 /*
- * Wait for the reply to our call.
+ * Queue the task for a reply to our call.
  * When the callback is invoked, the congestion window should have
  * been updated already.
  */
@@ -1214,42 +1275,8 @@
 
 	dprintk("RPC: %4d xprt_receive\n", task->tk_pid);
 
-	/*
-         * Wait until rq_gotit goes non-null, or timeout elapsed.
-	 */
-	task->tk_timeout = req->rq_timeout.to_current;
-
-	spin_lock_bh(&xprt_lock);
-	if (task->tk_rpcwait)
-		rpc_remove_wait_queue(task);
-
-	if (task->tk_status < 0 || xprt->shutdown) {
-		spin_unlock_bh(&xprt_lock);
-		goto out;
-	}
-
-	if (!req->rq_gotit) {
-		rpc_sleep_on(&xprt->pending, task,
-				xprt_receive_status, xprt_timer);
-		spin_unlock_bh(&xprt_lock);
-		return;
-	}
-	spin_unlock_bh(&xprt_lock);
-
-	dprintk("RPC: %4d xprt_receive returns %d\n",
-				task->tk_pid, task->tk_status);
- out:
-	xprt_receive_status(task);
-}
-
-static void
-xprt_receive_status(struct rpc_task *task)
-{
-	struct rpc_xprt	*xprt = task->tk_xprt;
-
-	if (xprt->tcp_rqstp == task->tk_rqstp)
-		xprt->tcp_rqstp = NULL;
-
+	task->tk_timeout = 0;
+	rpc_sleep_locked(&xprt->pending, task, NULL, NULL);
 }
 
 /*
@@ -1335,7 +1362,6 @@
 
 	dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid);
 	task->tk_status = 0;
-	req->rq_gotit	= 0;
 	req->rq_timeout = xprt->timeout;
 	req->rq_task	= task;
 	req->rq_xprt    = xprt;
@@ -1353,6 +1379,7 @@
 	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst	*req;
 
+	xprt_up_transmit(task);
 	if (!(req = task->tk_rqstp))
 		return;
 	task->tk_rqstp = NULL;
@@ -1363,16 +1390,16 @@
 	spin_lock(&xprt_lock);
 	req->rq_next = xprt->free;
 	xprt->free   = req;
-	spin_unlock(&xprt_lock);
 
 	/* remove slot from queue of pending */
-	spin_lock_bh(&xprt_lock);
 	if (task->tk_rpcwait) {
 		printk("RPC: task of released request still queued!\n");
-		rpc_del_timer(task);
+#ifdef RPC_DEBUG
+		printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait));
+#endif
 		rpc_remove_wait_queue(task);
 	}
-	spin_unlock_bh(&xprt_lock);
+	spin_unlock(&xprt_lock);
 
 	/* Decrease congestion value. */
 	xprt->cong -= RPC_CWNDSCALE;
@@ -1389,7 +1416,7 @@
 	if (proto == IPPROTO_UDP)
 		xprt_set_timeout(to, 5,  5 * HZ);
 	else
-		xprt_set_timeout(to, 5, 15 * HZ);
+		xprt_set_timeout(to, 5, 60 * HZ);
 }
 
 /*
@@ -1416,52 +1443,33 @@
 {
 	struct rpc_xprt	*xprt;
 	struct rpc_rqst	*req;
-	struct sock	*inet;
 	int		i;
 
 	dprintk("RPC:      setting up %s transport...\n",
 				proto == IPPROTO_UDP? "UDP" : "TCP");
 
-	inet = sock->sk;
-
 	if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL)
 		return NULL;
 	memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */
 
-	xprt->file = NULL;
-	xprt->sock = sock;
-	xprt->inet = inet;
 	xprt->addr = *ap;
 	xprt->prot = proto;
 	xprt->stream = (proto == IPPROTO_TCP)? 1 : 0;
-	xprt->congtime = jiffies;
-	init_waitqueue_head(&xprt->cong_wait);
-	inet->user_data = xprt;
-	xprt->old_data_ready = inet->data_ready;
-	xprt->old_state_change = inet->state_change;
-	xprt->old_write_space = inet->write_space;
-	if (proto == IPPROTO_UDP) {
-		inet->data_ready = udp_data_ready;
-		inet->write_space = udp_write_space;
-		inet->no_check = UDP_CSUM_NORCV;
-		xprt->cwnd = RPC_INITCWND;
-	} else {
-		inet->data_ready = tcp_data_ready;
-		inet->state_change = tcp_state_change;
-		inet->write_space = tcp_write_space;
+	if (xprt->stream) {
 		xprt->cwnd = RPC_MAXCWND;
 		xprt->nocong = 1;
-	}
-	xprt->connected = 1;
+	} else
+		xprt->cwnd = RPC_INITCWND;
+	xprt->congtime = jiffies;
+	init_waitqueue_head(&xprt->cong_wait);
 
 	/* Set timeout parameters */
 	if (to) {
 		xprt->timeout = *to;
 		xprt->timeout.to_current = to->to_initval;
 		xprt->timeout.to_resrvval = to->to_maxval << 1;
-	} else {
+	} else
 		xprt_default_timeout(&xprt->timeout, xprt->prot);
-	}
 
 	xprt->pending = RPC_INIT_WAITQ("xprt_pending");
 	xprt->sending = RPC_INIT_WAITQ("xprt_sending");
@@ -1474,13 +1482,11 @@
 	req->rq_next = NULL;
 	xprt->free = xprt->slot;
 
+	INIT_LIST_HEAD(&xprt->rx_pending);
+
 	dprintk("RPC:      created transport %p\n", xprt);
 	
-	/*
-	 *	TCP requires the rpc I/O daemon is present
-	 */
-	if(proto==IPPROTO_TCP)
-		rpciod_up();
+	xprt_bind_socket(xprt, sock);
 	return xprt;
 }
 
@@ -1508,17 +1514,52 @@
 	return err;
 }
 
+static int 
+xprt_bind_socket(struct rpc_xprt *xprt, struct socket *sock)
+{
+	struct sock	*sk = sock->sk;
+
+	if (xprt->inet)
+		return -EBUSY;
+
+	sk->user_data = xprt;
+	xprt->old_data_ready = sk->data_ready;
+	xprt->old_state_change = sk->state_change;
+	xprt->old_write_space = sk->write_space;
+	if (xprt->prot == IPPROTO_UDP) {
+		sk->data_ready = udp_data_ready;
+		sk->write_space = udp_write_space;
+		sk->no_check = UDP_CSUM_NORCV;
+		xprt->connected = 1;
+	} else {
+		sk->data_ready = tcp_data_ready;
+		sk->state_change = tcp_state_change;
+		sk->write_space = tcp_write_space;
+		xprt->connected = 0;
+	}
+
+	/* Reset to new socket */
+	xprt->sock = sock;
+	xprt->inet = sk;
+	/*
+	 *	TCP requires the rpc I/O daemon is present
+	 */
+	if(xprt->stream)
+		rpciod_up();
+
+	return 0;
+}
+
 /*
  * Create a client socket given the protocol and peer address.
  */
 static struct socket *
-xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+xprt_create_socket(int proto, struct rpc_timeout *to)
 {
 	struct socket	*sock;
 	int		type, err;
 
-	dprintk("RPC:      xprt_create_socket(%08x, %s %d)\n",
-			   sap? ntohl(sap->sin_addr.s_addr) : 0,
+	dprintk("RPC:      xprt_create_socket(%s %d)\n",
 			   (proto == IPPROTO_UDP)? "udp" : "tcp", proto);
 
 	type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
@@ -1532,15 +1573,6 @@
 	if (!current->fsuid && xprt_bindresvport(sock) < 0)
 		goto failed;
 
-	if (type == SOCK_STREAM && sap) {
-		err = sock->ops->connect(sock, (struct sockaddr *) sap,
-						sizeof(*sap), 0);
-		if (err < 0) {
-			printk("RPC: TCP connect failed (%d).\n", -err);
-			goto failed;
-		}
-	}
-
 	return sock;
 
 failed:
@@ -1559,7 +1591,7 @@
 
 	dprintk("RPC:      xprt_create_proto called\n");
 
-	if (!(sock = xprt_create_socket(proto, sap, to)))
+	if (!(sock = xprt_create_socket(proto, to)))
 		return NULL;
 
 	if (!(xprt = xprt_setup(sock, proto, sap, to)))
@@ -1587,8 +1619,6 @@
  */
 int
 xprt_clear_backlog(struct rpc_xprt *xprt) {
-	if (!xprt)
-		return 0;
 	if (RPCXPRT_CONGESTED(xprt))
 		return 0;
 	rpc_wake_up_next(&xprt->backlog);
@@ -1603,6 +1633,7 @@
 xprt_destroy(struct rpc_xprt *xprt)
 {
 	dprintk("RPC:      destroying transport %p\n", xprt);
+	xprt_shutdown(xprt);
 	xprt_close(xprt);
 	kfree(xprt);
 
diff -u --recursive --new-file v2.3.99-pre2/linux/scripts/mkdep.c linux/scripts/mkdep.c
--- v2.3.99-pre2/linux/scripts/mkdep.c	Fri Jan 21 18:19:17 2000
+++ linux/scripts/mkdep.c	Wed Mar 22 09:55:28 2000
@@ -18,6 +18,10 @@
  *   the definition is inactivated, but I still used it.  It turns out this
  *   actually happens a few times in the kernel source.  The simple way to
  *   fix this problem is to remove this particular optimization.
+ *
+ * 2.3.99-pre1, Andrew Morton <andrewm@uow.edu.au>
+ * - Changed so that 'filename.o' depends upon 'filename.[cS]'.  This is so that
+ *   missing source files are noticed, rather than silently ignored.
  */
 
 #include <ctype.h>
@@ -47,6 +51,8 @@
 };
 
 
+/* Current input file */
+static const char *g_filename;
 
 /*
  * This records all the configuration options seen.
@@ -58,7 +64,16 @@
 int    size_config = 0;
 int    len_config  = 0;
 
-
+static void
+do_depname(void)
+{
+	if (!hasdep) {
+		hasdep = 1;
+		printf("%s:", depname);
+		if (g_filename)
+			printf(" %s", g_filename);
+	}
+}
 
 /*
  * Grow the configuration string to a desired length.
@@ -193,10 +208,7 @@
 	if (access(path->buffer, F_OK) != 0)
 		return;
 
-	if (!hasdep) {
-		hasdep = 1;
-		printf("%s:", depname);
-	}
+	do_depname();
 	printf(" \\\n   %s", path->buffer);
 }
 
@@ -227,10 +239,7 @@
 
 	define_config(pc, len);
 
-	if (!hasdep) {
-		hasdep = 1;
-		printf("%s: ", depname);
-	}
+	do_depname();
 	printf(" \\\n   $(wildcard %s.h)", path_array[0].buffer);
 }
 
@@ -549,11 +558,13 @@
 	while (--argc > 0) {
 		const char * filename = *++argv;
 		const char * command  = __depname;
+		g_filename = 0;
 		len = strlen(filename);
 		memcpy(depname, filename, len+1);
 		if (len > 2 && filename[len-2] == '.') {
 			if (filename[len-1] == 'c' || filename[len-1] == 'S') {
 			    depname[len-1] = 'o';
+			    g_filename = filename;
 			    command = "";
 			}
 		}
