diff -u --recursive --new-file v2.3.45/linux/CREDITS linux/CREDITS
--- v2.3.45/linux/CREDITS	Sun Feb 13 19:29:03 2000
+++ linux/CREDITS	Wed Feb 16 15:42:05 2000
@@ -41,6 +41,7 @@
 E: tigran@ocston.org
 W: http://www.ocston.org/~tigran
 D: BFS filesystem
+D: Intel P6 CPU microcode update support
 S: United Kingdom
 
 N: Werner Almesberger
@@ -66,6 +67,10 @@
 P: 1024/85AD9EED AD C0 49 08 91 67 DF D7  FA 04 1A EE 09 E8 44 B0
 D: Unix98 pty support.
 D: APM update to 1.2 spec.
+D: /devfs hacking.
+S: 322 N. Riverside Dr.
+S: Neptune, NJ 07753
+S: USA
 
 N: Erik Andersen
 E: andersee@debian.org
@@ -784,6 +789,7 @@
 D: parent process death signal to children
 D: prctl() syscall
 D: /proc/mtrr support to manipulate MTRRs on Intel P6 family
+D: Device FileSystem (devfs)
 S: CSIRO Australia Telescope National Facility
 S: P.O. Box 76, Epping
 S: New South Wales, 2121
@@ -1264,6 +1270,10 @@
 S: Markham, Ontario
 S: L3R 8B2
 S: Canada
+
+N: Andrzej M. Krzysztofowicz
+E: ankry@mif.pg.gda.pl
+D: Some 8-bit XT disk driver and /devfs hacking 
 
 N: Andreas S. Krebs
 E: akrebs@altavista.net
diff -u --recursive --new-file v2.3.45/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.45/linux/Documentation/Configure.help	Thu Feb 10 17:11:01 2000
+++ linux/Documentation/Configure.help	Wed Feb 16 15:42:05 2000
@@ -8079,6 +8079,17 @@
   The module will be called usb-uhci.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
   
+USB-UHCI High Bandwidth support
+CONFIG_USB_UHCI_HIGH_BANDWIDTH
+  This option enables the so called reclamation loop in usb-uhci, thus
+  allowing much higher transfer bandwidth for USB-bulk and control
+  messages; isochronous transfers (audio, video etc.) are not affected.
+  Due to a very simple design of the UHCI controller, this may cause
+  a significant PCI congestion under certain conditions. If you are
+  experiencing a system slowdown, disable this option.
+  
+  If unsure, say N.
+  
 UHCI (intel PIIX4, VIA, ...) alternate (JE) support?
 CONFIG_USB_UHCI_ALT
   This is an alternate driver for UHCI support. It has been commonly
@@ -8247,17 +8258,8 @@
 USB Handspring Visor Driver
 CONFIG_USB_SERIAL_VISOR
   Say Y here if you want to connect to your HandSpring Visor through
-  its USB docking station.
-
-USB Belkin Single Port Serial Driver
-CONFIG_USB_SERIAL_BELKIN
-  Say Y here if you want to use a Belkin single port USB to serial 
-  converter device.
-  
-USB Peracom Single Port Serial Driver
-CONFIG_USB_SERIAL_PERACOM
-  Say Y here if you want to use a Peracom single port USB to serial 
-  converter device.
+  its USB docking station. See http://usbvisor.sourceforge.net for
+  more information on using this driver.
 
 USB Printer support
 CONFIG_USB_PRINTER
@@ -8397,6 +8399,14 @@
   The module will be called dabusb.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
  
+PLUSB driver
+CONFIG_USB_PLUSB
+  A driver for the Prolific PL-2302 USB to USB network device. This 'USB
+  cable' connects two hosts via a point to point network with bandwidth of
+  5Mbit/s.  Configure this driver after connecting the USB cable via
+  ifconfig plusb0 10.0.0.1 pointopoint 10.0.0.2 
+  (and vice versa on the other host).
+ 
 ACPI support
 CONFIG_ACPI
   Advanced Configuration and Power Interface (ACPI) is an interface
@@ -8663,6 +8673,24 @@
   This option will enlarge your kernel by about 67 KB. Several
   programs depend on this, so everyone should say Y here.
 
+/dev filesystem support (EXPERIMENTAL)
+CONFIG_DEVFS_FS
+  This is another virtual filesystem (like /proc) which provides the
+  filesystem interface to device drivers, normally found in /dev.
+  Devfs does not depend on major and minor number allocations. Device
+  drivers register entries in /dev which appear automagically. Without
+  devfs you need to populate /dev with hundreds, even thousands of
+  inodes.
+  This is work in progress. If you want to use this you *must* read
+  Documentation/filesystems/devfs/README
+
+Enable devfs debugging output
+CONFIG_DEVFS_DEBUG
+  This option appears if you have CONFIG_DEVFS_FS enabled. Setting
+  this to 'Y' enables devfs debugging output. See the file
+  Documentation/filesystems/devfs/boot-options for more details.
+  The default is 'N'.
+
 NFS filesystem support
 CONFIG_NFS_FS
   If you are connected to some other (usually local) Unix computer
@@ -9081,6 +9109,8 @@
   mode of operation; you also need client programs that use the Unix98
   API.
 
+  Note that CONFIG_DEVFS_FS is a more general facility.
+
 UnixWare slices support (EXPERIMENTAL)
 CONFIG_UNIXWARE_DISKLABEL
   Like some systems, UnixWare uses its own slice table inside a
@@ -10742,6 +10772,25 @@
   module, say M here and read Documentation/modules.txt. Most people
   will say N.
 
+Intel P6 CPU Microcode Update Support
+CONFIG_MICROCODE
+  If you say Y here you will be able to update microcode on 
+  Intel processors in P6 family, e.g.  Pentium Pro, Pentium II, 
+  Pentium III, Xeon etc. You will obviously need the actual microcode
+  binary data itself which is not shipped with the Linux kernel. 
+  Contact Intel to obtain the latest revision of microcode for 
+  your CPU(s). With this support compiled you can use dd(1) to write
+  microcode, for example:
+
+     # dd if=/etc/microcode of=/proc/driver/microcode bs=98304 count=1
+
+  You need to be superuser to do that.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called microcode.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
 Enhanced Real Time Clock Support
 CONFIG_RTC
   If you say Y here and create a character special file /dev/rtc with
@@ -11730,11 +11779,16 @@
   If you have trouble with some ugly exchanges try to select this
   option.
 
+Disable keypad protocol option
+CONFIG_HISAX_NO_KEYPAD
+  If you like to send special dialstrings including * or # without
+  using the keypad protocol, select this option.
+
 HiSax Support for german 1TR6
 CONFIG_HISAX_1TR6
   Enable this if you have a old german 1TR6 line.
 
-HiSax Support for Teles 16.0/8.0
+Teles 16.0/8.0
 CONFIG_HISAX_16_0
   This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
   and many compatibles. 
@@ -11743,7 +11797,7 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port/shmem settings.
 
-HiSax Support for Teles 16.3 or PNP or PCMCIA
+Teles 16.3 or PNP or PCMCIA
 CONFIG_HISAX_16_3
   This enables HiSax support for the Teles ISDN-cards S0-16.3 the
   Teles/Creatix PnP and the Teles PCMCIA.
@@ -11752,17 +11806,17 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for Teles PCI
+Teles PCI
 CONFIG_HISAX_TELESPCI
   This enables HiSax support for the Teles PCI.
   See Documentation/isdn/README.HiSax on how to configure it.
 
-HiSax Support for Teles S0Box
+Teles S0Box
 CONFIG_HISAX_S0BOX
   This enables HiSax support for the Teles/Creatix parallel port
   S0BOX.  See Documentation/isdn/README.HiSax on how to configure it.
 
-HiSax Support for AVM A1 (Fritz)
+AVM A1 (Fritz)
 CONFIG_HISAX_AVM_A1
   This enables HiSax support for the AVM A1 (aka "Fritz").
   
@@ -11770,17 +11824,17 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for AVM PnP/PCI (Fritz!PNP/PCI)
+AVM PnP/PCI (Fritz!PNP/PCI)
 CONFIG_HISAX_FRITZPCI
   This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
   See Documentation/isdn/README.HiSax on how to configure it.
 
-HiSax Support for AVM A1 PCMCIA (Fritz)
+AVM A1 PCMCIA (Fritz)
 CONFIG_HISAX_AVM_A1_PCMCIA
   This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
   See Documentation/isdn/README.HiSax on how to configure it.
 
-HiSax Support for Elsa cards
+Elsa cards
 CONFIG_HISAX_ELSA
   This enables HiSax support for the Elsa Mircolink ISA cards, for the
   Elsa Quickstep series cards and Elsa PCMCIA.
@@ -11789,7 +11843,7 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for ITK ix1-micro Revision 2
+ITK ix1-micro Revision 2
 CONFIG_HISAX_IX1MICROR2
   This enables HiSax support for the ITK ix1-micro Revision 2 card.
 
@@ -11797,7 +11851,7 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for Eicon.Diehl Diva cards
+Eicon.Diehl Diva cards
 CONFIG_HISAX_DIEHLDIVA
   This enables HiSax support for the Eicon.Diehl Diva none PRO
   versions passive ISDN cards.
@@ -11806,16 +11860,16 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for ASUSCOM cards
+ASUSCOM ISA cards
 CONFIG_HISAX_ASUSCOM
   This enables HiSax support for the AsusCom and their OEM versions
-  passive ISDN cards.
+  passive ISDN ISA cards.
 
   See Documentation/isdn/README.HiSax on how to configure it using the
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for TELEINT cards
+TELEINT cards
 CONFIG_HISAX_TELEINT
   This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
 
@@ -11823,7 +11877,7 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for HFC-S based cards
+HFC-S based cards
 CONFIG_HISAX_HFCS
   This enables HiSax support for the HFC-S 2BDS0 based cards, like
   teles 16.3c.
@@ -11832,7 +11886,7 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for Sedlbauer cards
+Sedlbauer cards
 CONFIG_HISAX_SEDLBAUER
   This enables HiSax support for the Sedlbauer passive ISDN cards.
 
@@ -11840,21 +11894,21 @@
   different cards, a different D-channel protocol, or non-standard
   IRQ/port settings.
 
-HiSax Support for USR Sportster internal TA
+USR Sportster internal TA
 CONFIG_HISAX_SPORTSTER
   This enables HiSax support for the USR Sportster internal TA card.
 
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for MIC card
+MIC card
 CONFIG_HISAX_MIC
   This enables HiSax support for the ITH MIC card. 
 
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for NETjet card
+NETjet card
 CONFIG_HISAX_NETJET
   This enables HiSax support for the NetJet from Traverse
   Technologies.
@@ -11862,62 +11916,67 @@
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for Niccy PnP/PCI card
+Niccy PnP/PCI card
 CONFIG_HISAX_NICCY
   This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI. 
 
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for Siemens I-Surf card
+Siemens I-Surf card
 CONFIG_HISAX_ISURF
   This enables HiSax support for the Siemens I-Talk/I-Surf card with
   ISAR chip.
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for HST Saphir card
+HST Saphir card
 CONFIG_HISAX_HSTSAPHIR
   This enables HiSax support for the HST Saphir card.
   
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for Telekom A4T card
+Telekom A4T card
 CONFIG_HISAX_BKM_A4T
   This enables HiSax support for the Telekom A4T card.
   
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for Scitel Quadro card
+Scitel Quadro card
 CONFIG_HISAX_SCT_QUADRO
   This enables HiSax support for the Scitel Quadro card.
   
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for Gazel cards
+Gazel cards
 CONFIG_HISAX_GAZEL
   This enables HiSax support for the Gazel cards.
 
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for HFC PCI-Bus cards
+HFC PCI-Bus cards
 CONFIG_HISAX_HFC_PCI
   This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
   
   For more informations see under Documentation/isdn/README.hfc-pci.
 
-HiSax Support for Winbond W6692 based cards
+Winbond W6692 based cards
 CONFIG_HISAX_W6692
   This enables HiSax support for Winbond W6692 based PCI ISDN cards.
   
   See Documentation/isdn/README.HiSax on how to configure it using a
   different D-channel protocol, or non-standard IRQ/port settings.
 
-HiSax Support for Am7930 (EXPERIMENTAL)
+HFC-S+, HFC-SP, HFC-PCMCIA cards (EXPERIMENTAL)
+CONFIG_HISAX_HFC_SX
+  This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA cards.
+  This code is not finished yet.
+
+Am7930 (EXPERIMENTAL)
 CONFIG_HISAX_AMD7930
   This enables HiSax support for the AMD7930 chips on some SPARCs.
   This code is not finished yet.
diff -u --recursive --new-file v2.3.45/linux/Documentation/DMA-mapping.txt linux/Documentation/DMA-mapping.txt
--- v2.3.45/linux/Documentation/DMA-mapping.txt	Fri Jan 28 15:09:06 2000
+++ linux/Documentation/DMA-mapping.txt	Mon Feb 14 15:31:14 2000
@@ -55,9 +55,15 @@
 __get_free_pages (but takes size instead of page order).
 It returns two values: the virtual address which you can use to access it
 from the CPU and dma_handle which you pass to the card.
-The return address is guaranteed to be page aligned.
 
-To unmap and free such DMA region, you call:
+The cpu return address and the DMA bus master address are both
+guaranteed to be aligned to the smallest PAGE_SIZE order which
+is greater than or equal to the requested size.  This invariant
+exists (for example) to guarentee that if you allocate a chunk
+which is smaller than or equal to 64 kilobytes, the extent of the
+buffer you receive will not cross a 64K boundry.
+
+To unmap and free such a DMA region, you call:
 
 	pci_free_consistent(dev, size, cpu_addr, dma_handle);
 
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/00-INDEX linux/Documentation/filesystems/00-INDEX
--- v2.3.45/linux/Documentation/filesystems/00-INDEX	Tue Dec  7 09:32:38 1999
+++ linux/Documentation/filesystems/00-INDEX	Wed Feb 16 15:42:05 2000
@@ -8,6 +8,8 @@
 	- info for the SCO UnixWare Boot Filesystem (BFS).
 coda.txt
 	- description of the CODA filesystem.
+devfs
+	- directory containing devfs documentation.
 fat_cvf.txt
 	- info on the Compressed Volume Files extension to the FAT filesystem
 hpfs.txt
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/ChangeLog linux/Documentation/filesystems/devfs/ChangeLog
--- v2.3.45/linux/Documentation/filesystems/devfs/ChangeLog	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/ChangeLog	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,1440 @@
+/* -*- auto-fill -*-                                                         */
+===============================================================================
+Changes for patch v1
+
+- creation of devfs
+
+- modified miscellaneous character devices to support devfs
+===============================================================================
+Changes for patch v2
+
+- bug fix with manual inode creation
+===============================================================================
+Changes for patch v3
+
+- bugfixes
+
+- documentation improvements
+
+- created a couple of scripts (one to save&restore a devfs and the
+  other to set up compatibility symlinks)
+
+- devfs support for SCSI discs. New name format is: sd_hHcCiIlL
+===============================================================================
+Changes for patch v4
+
+- bugfix for the directory reading code
+
+- bugfix for compilation with kerneld
+
+- devfs support for generic hard discs
+
+- rationalisation of the various watchdog drivers
+===============================================================================
+Changes for patch v5
+
+- support for mounting directly from entries in the devfs (it doesn't
+  need to be mounted to do this), including the root filesystem.
+  Mounting of swap partitions also works. Hence, now if you set
+  CONFIG_DEVFS_ONLY to 'Y' then you won't be able to access your discs
+  via ordinary device nodes. Naturally, the default is 'N' so that you
+  can still use your old device nodes.  If you want to mount from devfs
+  entries, make sure you use: append = "root=/dev/sd_..." in your
+  lilo.conf. It seems LILO looks for the device number (major&minor)
+  and writes that into the kernel image :-( 
+
+- support for character memory devices (/dev/null, /dev/zero, /dev/full
+  and so on). Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+===============================================================================
+Changes for patch v6
+
+- support for subdirectories
+
+- support for symbolic links (created by devfs_mk_symlink(), no
+  support yet for creation via symlink(2))
+
+- SCSI disc naming now cast in stone, with the format:
+  /dev/sd/c0b1t2u3	controller=0, bus=1, ID=2, LUN=3, whole disc
+  /dev/sd/c0b1t2u3p4	controller=0, bus=1, ID=2, LUN=3, 4th partition
+
+- loop devices now appear in devfs
+
+- tty devices, console, serial ports, etc. now appear in devfs
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- bugs with mounting devfs-only devices now fixed
+===============================================================================
+Changes for patch v7
+
+- SCSI CD-ROMS, tapes and generic devices now appear in devfs
+===============================================================================
+Changes for patch v8
+
+- bugfix with no-rewind SCSI tapes
+
+- RAMDISCs now appear in devfs
+
+- better cleaning up of devfs entries created by various modules
+
+- interface change to <devfs_register>
+===============================================================================
+Changes for patch v9
+
+- the v8 patch was corrupted somehow, which would affect the patch for
+  linux/fs/filesystems.c
+  I've also fixed the v8 patch file on the WWW
+
+- MetaDevices (/dev/md*) should now appear in devfs
+===============================================================================
+Changes for patch v10
+
+- bugfix in meta device support for devfs
+
+- created this ChangeLog file
+
+- added devfs support to the floppy driver
+
+- added support for creating sockets in a devfs
+===============================================================================
+Changes for patch v11
+
+- added DEVFS_FL_HIDE_UNREG flag
+
+- incorporated better patch for ttyname() in libc 5.4.43 from H.J. Lu.
+
+- interface change to <devfs_mk_symlink>
+
+- support for creating symlinks with symlink(2)
+
+- parallel port printer (/dev/lp*) now appears in devfs
+===============================================================================
+Changes for patch v12
+
+- added inode check to <devfs_fill_file> function
+
+- improved devfs support when mounting from devfs
+
+- added call to <<release>> operation when removing swap areas on
+  devfs devices
+
+- increased NR_SUPER to 128 to support large numbers of devfs mounts
+  (for chroot(2) gaols)
+
+- fixed bug in SCSI disc support: was generating incorrect minors if
+  SCSI ID's did not start at 0 and increase by 1
+
+- support symlink traversal when mounting root
+===============================================================================
+Changes for patch v13
+
+- added devfs support to soundcard driver
+  Thanks to Eric Dumas <dumas@linux.eu.org> and
+  C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- added devfs support to the joystick driver
+
+- loop driver now has it's own subdirectory "/dev/loop/"
+
+- created <devfs_get_flags> and <devfs_set_flags> functions
+
+- fix problem with SCSI disc compatibility names (sd{a,b,c,d,e,f})
+  which assumes ID's start at 0 and increase by 1. Also only create
+  devfs entries for SCSI disc partitions which actually exist
+  Show new names in partition check
+  Thanks to Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
+===============================================================================
+Changes for patch v14
+
+- bug fix in floppy driver: would not compile without
+  CONFIG_DEVFS_FS='Y'
+  Thanks to Jurgen Botz <jbotz@nova.botz.org>
+
+- bug fix in loop driver
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- do not create devfs entries for printers not configured
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- do not create devfs entries for serial ports not present
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- ensure <tty_register_devfs> is exported from tty_io.c
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- allow unregistering of devfs symlink entries
+
+- fixed bug in SCSI disc naming introduced in last patch version
+===============================================================================
+Changes for patch v15
+
+- ported to kernel 2.1.81
+===============================================================================
+Changes for patch v16
+
+- created <devfs_set_symlink_destination> function
+
+- moved DEVFS_SUPER_MAGIC into header file
+
+- added DEVFS_FL_HIDE flag
+
+- created <devfs_get_maj_min>
+
+- created <devfs_get_handle_from_inode>
+
+- fixed bugs in searching by major&minor
+
+- changed interface to <devfs_unregister>, <devfs_fill_file> and
+  <devfs_find_handle>
+
+- fixed inode times when symlink created with symlink(2)
+
+- change tty driver to do auto-creation of devfs entries
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- fixed bug in genhd.c: whole disc (non-SCSI) was not registered to
+  devfs
+
+- updated libc 5.4.43 patch for ttyname()
+===============================================================================
+Changes for patch v17
+
+- added CONFIG_DEVFS_TTY_COMPAT
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- bugfix in devfs support for drivers/char/lp.c
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- clean up serial driver so that PCMCIA devices unregister correctly
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- fixed bug in genhd.c: whole disc (non-SCSI) was not registered to
+  devfs [was missing in patch v16]
+
+- updated libc 5.4.43 patch for ttyname() [was missing in patch v16]
+
+- all SCSI devices now registered in /dev/sg
+
+- support removal of devfs entries via unlink(2)
+===============================================================================
+Changes for patch v18
+
+- added floppy/?u720 floppy entry
+
+- fixed kerneld support for entries in devfs subdirectories
+
+- incorporated latest patch for ttyname() in libc 5.4.43 from H.J. Lu.
+===============================================================================
+Changes for patch v19
+
+- bug fix when looking up unregistered entries: kerneld was not called
+
+- fixes for kernel 2.1.86 (now requires 2.1.86)
+===============================================================================
+Changes for patch v20
+
+- only create available floppy entries
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+
+- new IDE naming scheme following SCSI format (i.e. /dev/id/c0b0t0u0p1
+  instead of /dev/hda1)
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+
+- new XT disc naming scheme following SCSI format (i.e. /dev/xd/c0t0p1
+  instead of /dev/xda1)
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+
+- new non-standard CD-ROM names (i.e. /dev/sbp/c#t#)
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+
+- allow symlink traversal when mounting the root filesystem
+
+- Create entries for MD devices at MD init
+  Thanks to Christophe Leroy <christophe.leroy5@capway.com>
+===============================================================================
+Changes for patch v21
+
+- ported to kernel 2.1.91
+===============================================================================
+Changes for patch v22
+
+- SCSI host number patch ("scsihosts=" kernel option)
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+===============================================================================
+Changes for patch v23
+
+- Fixed persistence bug with device numbers for manually created
+  device files
+
+- Fixed problem with recreating symlinks with different content
+
+- Added CONFIG_DEVFS_MOUNT (mount devfs on /dev at boot time)
+===============================================================================
+Changes for patch v24
+
+- Switched from CONFIG_KERNELD to CONFIG_KMOD: module autoloading
+  should now work again
+
+- Hide entries which are manually unlinked
+
+- Always invalidate devfs dentry cache when registering entries
+
+- Support removal of devfs directories via rmdir(2)
+
+- Ensure directories created by <devfs_mk_dir> are visible
+
+- Default no access for "other" for floppy device
+===============================================================================
+Changes for patch v25
+
+- Updates to CREDITS file and minor IDE numbering change
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+
+- Invalidate devfs dentry cache when making directories
+
+- Invalidate devfs dentry cache when removing entries
+
+- More informative message if root FS mount fails when devfs
+  configured
+
+- Fixed persistence bug with fifos
+===============================================================================
+Changes for patch v26
+
+- ported to kernel 2.1.97
+
+- Changed serial directory from "/dev/serial" to "/dev/tts" and
+  "/dev/consoles" to "/dev/vc" to be more friendly to new procps
+===============================================================================
+Changes for patch v27
+
+- Added support for IDE4 and IDE5
+  Thanks to Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
+
+- Documented "scsihosts=" boot parameter
+
+- Print process command when debugging kerneld/kmod
+
+- Added debugging for register/unregister/change operations
+
+- Added "devfs=" boot options
+
+- Hide unregistered entries by default
+===============================================================================
+Changes for patch v28
+
+- No longer lock/unlock superblock in <devfs_put_super> (cope with
+  recent VFS interface change)
+
+- Do not automatically change ownership/protection of /dev/tty
+
+- Drop negative dentries when they are released
+
+- Manage dcache more efficiently
+===============================================================================
+Changes for patch v29
+
+- Added DEVFS_FL_AUTO_DEVNUM flag
+===============================================================================
+Changes for patch v30
+
+- No longer set unnecessary methods
+
+- Ported to kernel 2.1.99-pre3
+===============================================================================
+Changes for patch v31
+
+- Added PID display to <call_kerneld> debugging message
+
+- Added "diread" and "diwrite" options
+
+- Ported to kernel 2.1.102
+
+- Fixed persistence problem with permissions
+===============================================================================
+Changes for patch v32
+
+- Fixed devfs support in drivers/block/md.c
+===============================================================================
+Changes for patch v33
+
+- Support legacy device nodes
+
+- Fixed bug where recreated inodes were hidden
+
+- New IDE naming scheme: everything is under /dev/ide
+===============================================================================
+Changes for patch v34
+
+- Improved debugging in <get_vfs_inode>
+
+- Prevent duplicate calls to <devfs_mk_dir> in SCSI layer
+
+- No longer free old dentries in <devfs_mk_dir>
+
+- Free all dentries for a given entry when deleting inodes
+===============================================================================
+Changes for patch v35
+
+- Ported to kernel 2.1.105 (sound driver changes)
+===============================================================================
+Changes for patch v36
+
+- Fixed sound driver port
+===============================================================================
+Changes for patch v37
+
+- Minor documentation tweaks
+===============================================================================
+Changes for patch v38
+
+- More documentation tweaks
+
+- Fix for sound driver port
+
+- Removed ttyname-patch (grab libc 5.4.44 instead)
+
+- Ported to kernel 2.1.107-pre2 (loop driver fix)
+===============================================================================
+Changes for patch v39
+
+- Ported to kernel 2.1.107 (hd.c hunk broke due to spelling "fixes"). Sigh
+
+- Removed many #ifdef's, replaced with trickery in include/devfs_fs.h
+===============================================================================
+Changes for patch v40
+
+- Fix for sound driver port
+
+- Limit auto-device numbering to majors 128 to 239
+===============================================================================
+Changes for patch v41
+
+- Fixed inode times persistence problem
+===============================================================================
+Changes for patch v42
+
+- Ported to kernel 2.1.108 (drivers/scsi/hosts.c hunk broke)
+===============================================================================
+Changes for patch v43
+
+- Fixed spelling in <devfs_readlink> debug
+
+- Fixed bug in <devfs_setup> parsing "dilookup"
+
+- More #ifdef's removed
+
+- Supported Sparc keyboard (/dev/kbd)
+
+- Supported DSP56001 digital signal processor (/dev/dsp56k)
+
+- Supported Apple Desktop Bus (/dev/adb)
+
+- Supported Coda network file system (/dev/cfs*)
+===============================================================================
+Changes for patch v44
+
+- Fixed devfs inode leak when manually recreating inodes
+
+- Fixed permission persistence problem when recreating inodes
+===============================================================================
+Changes for patch v45
+
+- Ported to kernel 2.1.110
+===============================================================================
+Changes for patch v46
+
+- Ported to kernel 2.1.112-pre1
+
+- Removed harmless "unused variable" compiler warning
+
+- Fixed modes for manually recreated device nodes
+===============================================================================
+Changes for patch v47
+
+- Added NULL devfs inode warning in <devfs_read_inode>
+
+- Force all inode nlink values to 1
+===============================================================================
+Changes for patch v48
+
+- Added "dimknod" option
+
+- Set inode nlink to 0 when freeing dentries
+
+- Added support for virtual console capture devices (/dev/vcs*)
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Fixed modes for manually recreated symlinks
+===============================================================================
+Changes for patch v49
+
+- Ported to kernel 2.1.113
+===============================================================================
+Changes for patch v50
+
+- Fixed bugs in recreated directories and symlinks
+===============================================================================
+Changes for patch v51
+
+- Improved robustness of rc.devfs script
+  Thanks to Roderich Schupp <rsch@experteam.de>
+
+- Fixed bugs in recreated device nodes
+
+- Fixed bug in currently unused <devfs_get_handle_from_inode>
+
+- Defined new <devfs_handle_t> type
+
+- Improved debugging when getting entries
+
+- Fixed bug where directories could be emptied
+
+- Ported to kernel 2.1.115
+===============================================================================
+Changes for patch v52
+
+- Replaced dummy .epoch inode with .devfsd character device
+
+- Modified rc.devfs to take acount of above change
+
+- Removed spurious driver warning messages when CONFIG_DEVFS_FS=n
+
+- Implemented devfsd protocol revision 0
+===============================================================================
+Changes for patch v53
+
+- Ported to kernel 2.1.116 (kmod change broke hunk)
+
+- Updated Documentation/Configure.help
+
+- Test and tty pattern patch for rc.devfs script
+  Thanks to Roderich Schupp <rsch@experteam.de>
+
+- Added soothing message to warning in <devfs_d_iput>
+===============================================================================
+Changes for patch v54
+
+- Ported to kernel 2.1.117
+
+- Fixed default permissions in sound driver
+
+- Added support for frame buffer devices (/dev/fb*)
+===============================================================================
+Changes for patch v55
+
+- Ported to kernel 2.1.119
+
+- Use GCC extensions for structure initialisations
+
+- Implemented async open notification
+
+- Incremented devfsd protocol revision to 1
+===============================================================================
+Changes for patch v56
+
+- Ported to kernel 2.1.120-pre3
+
+- Moved async open notification to end of <devfs_open>
+===============================================================================
+Changes for patch v57
+
+- Ported to kernel 2.1.121
+
+- Prepended "/dev/" to module load request
+
+- Renamed <call_kerneld> to <call_kmod>
+
+- Created sample modules.conf file
+===============================================================================
+Changes for patch v58
+
+- Fixed typo "AYSNC" -> "ASYNC"
+===============================================================================
+Changes for patch v59
+
+- Added open flag for files
+===============================================================================
+Changes for patch v60
+
+- Ported to kernel 2.1.123-pre2
+===============================================================================
+Changes for patch v61
+
+- Set i_blocks=0 and i_blksize=1024 in <devfs_read_inode>
+===============================================================================
+Changes for patch v62
+
+- Ported to kernel 2.1.123
+===============================================================================
+Changes for patch v63
+
+- Ported to kernel 2.1.124-pre2
+===============================================================================
+Changes for patch v64
+
+- Fixed Unix98 pty support
+
+- Increased buffer size in <get_partition_list> to avoid crash and
+  burn
+===============================================================================
+Changes for patch v65
+
+- More Unix98 pty support fixes
+
+- Added test for empty <<name>> in <devfs_find_handle>
+
+- Renamed <generate_path> to <devfs_generate_path> and published
+
+- Created /dev/root symlink
+  Thanks to Roderich Schupp <rsch@ExperTeam.de>
+  with further modifications by me
+===============================================================================
+Changes for patch v66
+
+- Yet more Unix98 pty support fixes (now tested)
+
+- Created <devfs_get_fops>
+
+- Support media change checks when CONFIG_DEVFS_ONLY=y
+
+- Abolished Unix98-style PTY names for old PTY devices
+===============================================================================
+Changes for patch v67
+
+- Added inline declaration for dummy <devfs_generate_path>
+
+- Removed spurious "unable to register... in devfs" messages when
+  CONFIG_DEVFS_FS=n
+
+- Fixed misc. devices when CONFIG_DEVFS_FS=n
+
+- Limit auto-device numbering to majors 144 to 239
+===============================================================================
+Changes for patch v68
+
+- Hide unopened virtual consoles from directory listings
+
+- Added support for video capture devices
+
+- Ported to kernel 2.1.125
+===============================================================================
+Changes for patch v69
+
+- Fix for CONFIG_VT=n
+===============================================================================
+Changes for patch v70
+
+- Added support for non-OSS/Free sound cards
+===============================================================================
+Changes for patch v71
+
+- Ported to kernel 2.1.126-pre2
+===============================================================================
+Changes for patch v72
+
+- #ifdef's for CONFIG_DEVFS_DISABLE_OLD_NAMES removed
+===============================================================================
+Changes for patch v73
+
+- CONFIG_DEVFS_DISABLE_OLD_NAMES replaced with "nocompat" boot option
+
+- CONFIG_DEVFS_BOOT_OPTIONS removed: boot options always available
+===============================================================================
+Changes for patch v74
+
+- Removed CONFIG_DEVFS_MOUNT and "mount" boot option and replaced with
+  "nomount" boot option
+
+- Documentation updates
+
+- Updated sample modules.conf
+===============================================================================
+Changes for patch v75
+
+- Updated sample modules.conf
+
+- Remount devfs after initrd finishes
+
+- Ported to kernel 2.1.127
+
+- Added support for ISDN
+  Thanks to Christophe Leroy <christophe.leroy5@capway.com>
+===============================================================================
+Changes for patch v76
+
+- Updated an email address in ChangeLog
+
+- CONFIG_DEVFS_ONLY replaced with "only" boot option
+===============================================================================
+Changes for patch v77
+
+- Added DEVFS_FL_REMOVABLE flag
+
+- Check for disc change when listing directories with removable media
+  devices
+
+- Use DEVFS_FL_REMOVABLE in sd.c
+
+- Ported to kernel 2.1.128
+===============================================================================
+Changes for patch v78
+
+- Only call <scan_dir_for_removable> on first call to <devfs_readdir>
+
+- Ported to kernel 2.1.129-pre5
+
+- ISDN support improvements
+  Thanks to Christophe Leroy <christophe.leroy5@capway.com>
+===============================================================================
+Changes for patch v79
+
+- Ported to kernel 2.1.130
+
+- Renamed miscdevice "apm" to "apm_bios" to be consistent with
+  devices.txt
+===============================================================================
+Changes for patch v80
+
+- Ported to kernel 2.1.131
+
+- Updated <devfs_rmdir> for VFS change in 2.1.131
+===============================================================================
+Changes for patch v81
+
+- Fixed permissions on /dev/ptmx
+===============================================================================
+Changes for patch v82
+
+- Ported to kernel 2.1.132-pre4
+
+- Changed initial permissions on /dev/pts/*
+
+- Created <devfs_mk_compat>
+
+- Added "symlinks" boot option
+
+- Changed devfs_register_blkdev() back to register_blkdev() for IDE
+
+- Check for partitions on removable media in <devfs_lookup>
+===============================================================================
+Changes for patch v83
+
+- Fixed support for ramdisc when using string-based root FS name
+
+- Ported to kernel 2.2.0-pre1
+===============================================================================
+Changes for patch v84
+
+- Ported to kernel 2.2.0-pre7
+===============================================================================
+Changes for patch v85
+
+- Compile fixes for driver/sound/sound_common.c (non-module) and
+  drivers/isdn/isdn_common.c
+  Thanks to Christophe Leroy <christophe.leroy5@capway.com>
+
+- Added support for registering regular files
+
+- Created <devfs_set_file_size>
+
+- Added /dev/cpu/mtrr as an alternative interface to /proc/mtrr
+
+- Update devfs inodes from entries if not changed through FS
+===============================================================================
+Changes for patch v86
+
+- Ported to kernel 2.2.0-pre9
+===============================================================================
+Changes for patch v87
+
+- Fixed bug when mounting non-devfs devices in a devfs
+===============================================================================
+Changes for patch v88
+
+- Fixed <devfs_fill_file> to only initialise temporary inodes
+
+- Trap for NULL fops in <devfs_register>
+
+- Return -ENODEV in <devfs_fill_file> for non-driver inodes
+
+- Fixed bug when unswapping non-devfs devices in a devfs
+===============================================================================
+Changes for patch v89
+
+- Switched to C data types in include/linux/devfs_fs.h
+
+- Switched from PATH_MAX to DEVFS_PATHLEN
+
+- Updated Documentation/filesystems/devfs/modules.conf to take account
+  of reverse scanning (!) by modprobe
+
+- Ported to kernel 2.2.0
+===============================================================================
+Changes for patch v90
+
+- CONFIG_DEVFS_DISABLE_OLD_TTY_NAMES replaced with "nottycompat" boot
+  option
+
+- CONFIG_DEVFS_TTY_COMPAT removed: existing "symlinks" boot option now
+  controls this. This means you must have libc 5.4.44 or later, or a
+  recent version of libc 6 if you use the "symlinks" option
+===============================================================================
+Changes for patch v91
+
+- Switch from <devfs_mk_symlink> to <devfs_mk_compat> in
+  drivers/char/vc_screen.c to fix problems with Midnight Commander
+===============================================================================
+Changes for patch v92
+
+- Ported to kernel 2.2.2-pre5
+===============================================================================
+Changes for patch v93
+
+- Modified <sd_name> in drivers/scsi/sd.c to cope with devices that
+  don't exist (which happens with new RAID autostart code printk()s)
+===============================================================================
+Changes for patch v94
+
+- Fixed bug in joystick driver: only first joystick was registered
+===============================================================================
+Changes for patch v95
+
+- Fixed another bug in joystick driver
+
+- Fixed <devfsd_read> to not overrun event buffer
+===============================================================================
+Changes for patch v96
+
+- Ported to kernel 2.2.5-2
+
+- Created <devfs_auto_unregister>
+
+- Fixed bugs: compatibility entries were not unregistered for:
+    loop driver
+    floppy driver
+    RAMDISC driver
+    IDE tape driver
+    SCSI CD-ROM driver
+    SCSI HDD driver
+===============================================================================
+Changes for patch v97
+
+- Fixed bugs: compatibility entries were not unregistered for:
+    ALSA sound driver
+    partitions in generic disc driver
+
+- Don't return unregistred entries in <devfs_find_handle>
+
+- Panic in <devfs_unregister> if entry unregistered
+
+- Don't panic in <devfs_auto_unregister> for duplicates
+===============================================================================
+Changes for patch v98
+
+- Don't unregister already unregistered entries in <unregister>
+
+- Register entry in <sd_detect>
+
+- Unregister entry in <sd_detach>
+
+- Changed to <devfs_*register_chrdev> in drivers/char/tty_io.c
+
+- Ported to kernel 2.2.7
+===============================================================================
+Changes for patch v99
+
+- Ported to kernel 2.2.8
+
+- Fixed bug in drivers/scsi/sd.c when >16 SCSI discs
+
+- Disable warning messages when unable to read partition table for
+  removable media
+===============================================================================
+Changes for patch v100
+
+- Ported to kernel 2.3.1-pre5
+
+- Added "oops-on-panic" boot option
+
+- Improved debugging in <devfs_register> and <devfs_unregister>
+
+- Register entry in <sr_detect>
+
+- Unregister entry in <sr_detach>
+
+- Register entry in <sg_detect>
+
+- Unregister entry in <sg_detach>
+
+- Added support for ALSA drivers
+===============================================================================
+Changes for patch v101
+
+- Ported to kernel 2.3.2
+===============================================================================
+Changes for patch v102
+
+- Update serial driver to register PCMCIA entries
+  Thanks to Roch-Alexandre Nomine-Beguin <roch@samarkand.infini.fr>
+
+- Updated an email address in ChangeLog
+
+- Hide virtual console capture entries from directory listings when
+  corresponding console device is not open
+===============================================================================
+Changes for patch v103
+
+- Ported to kernel 2.3.3
+===============================================================================
+Changes for patch v104
+
+- Added documentation for some functions
+
+- Added "doc" target to fs/devfs/Makefile
+
+- Added "v4l" directory for video4linux devices
+
+- Replaced call to <devfs_unregister> in <sd_detach> with call to
+  <devfs_register_partitions>
+
+- Moved registration for sr and sg drivers from detect() to attach()
+  methods
+
+- Register entries in <st_attach> and unregister in <st_detach>
+
+- Work around IDE driver treating CD-ROM as gendisk
+
+- Use <sed> instead of <tr> in rc.devfs
+
+- Updated ToDo list
+
+- Removed "oops-on-panic" boot option: now always Oops
+===============================================================================
+Changes for patch v105
+
+- Unregister SCSI host from <scsi_host_no_list> in <scsi_unregister>
+  Thanks to Zoltan BOSZORMENYI <zboszor@mol.hu>
+
+- Don't save /dev/log in rc.devfs
+
+- Ported to kernel 2.3.4-pre1
+===============================================================================
+Changes for patch v106
+
+- Fixed silly typo in drivers/scsi/st.c
+
+- Improved debugging in <devfs_register>
+===============================================================================
+Changes for patch v107
+
+- Added "diunlink" and "nokmod" boot options
+
+- Removed superfluous warning message in <devfs_d_iput>
+===============================================================================
+Changes for patch v108
+
+- Remove entries when unloading sound module
+===============================================================================
+Changes for patch v109
+
+- Ported to kernel 2.3.6-pre2
+===============================================================================
+Changes for patch v110
+
+- Took account of change to <d_alloc_root>
+===============================================================================
+Changes for patch v111
+
+- Created separate event queue for each mounted devfs
+
+- Removed <devfs_invalidate_dcache>
+
+- Created new ioctl()s for devfsd
+
+- Incremented devfsd protocol revision to 3
+
+- Fixed bug when re-creating directories: contents were lost
+
+- Block access to inodes until devfsd updates permissions
+===============================================================================
+Changes for patch v112
+
+- Modified patch so it applies against 2.3.5 and 2.3.6
+
+- Updated an email address in ChangeLog
+
+- Do not automatically change ownership/protection of /dev/tty<n>
+
+- Updated sample modules.conf
+
+- Switched to sending process uid/gid to devfsd
+
+- Renamed <call_kmod> to <try_modload>
+
+- Added DEVFSD_NOTIFY_LOOKUP event
+
+- Added DEVFSD_NOTIFY_CHANGE event
+
+- Added DEVFSD_NOTIFY_CREATE event
+
+- Incremented devfsd protocol revision to 4
+
+- Moved kernel-specific stuff to include/linux/devfs_fs_kernel.h
+===============================================================================
+Changes for patch v113
+
+- Ported to kernel 2.3.9
+
+- Restricted permissions on some block devices
+===============================================================================
+Changes for patch v114
+
+- Added support for /dev/netlink
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Return EISDIR rather than EINVAL for read(2) on directories
+
+- Ported to kernel 2.3.10
+===============================================================================
+Changes for patch v115
+
+- Added support for all remaining character devices
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Cleaned up netlink support
+===============================================================================
+Changes for patch v116
+
+- Added support for /dev/parport%d
+  Thanks to Tim Waugh <tim@cyberelk.demon.co.uk>
+
+- Fixed parallel port ATAPI tape driver
+
+- Fixed Atari SLM laser printer driver
+===============================================================================
+Changes for patch v117
+
+- Added support for COSA card
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Fixed drivers/char/ppdev.c: missing #include <linux/init.h>
+
+- Fixed drivers/char/ftape/zftape/zftape-init.c
+  Thanks to Vladimir Popov <mashgrad@usa.net>
+===============================================================================
+Changes for patch v118
+
+- Ported to kernel 2.3.15-pre3
+
+- Fixed bug in loop driver
+
+- Unregister /dev/lp%d entries in drivers/char/lp.c
+  Thanks to Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+===============================================================================
+Changes for patch v119
+
+- Ported to kernel 2.3.16
+===============================================================================
+Changes for patch v120
+
+- Fixed bug in drivers/scsi/scsi.c
+
+- Added /dev/ppp
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Ported to kernel 2.3.17
+===============================================================================
+Changes for patch v121
+
+- Fixed bug in drivers/block/loop.c
+
+- Ported to kernel 2.3.18
+===============================================================================
+Changes for patch v122
+
+- Ported to kernel 2.3.19
+===============================================================================
+Changes for patch v123
+
+- Ported to kernel 2.3.20
+===============================================================================
+Changes for patch v124
+
+- Ported to kernel 2.3.21
+===============================================================================
+Changes for patch v125
+
+- Created <devfs_get_info>, <devfs_set_info>,
+  <devfs_get_first_child> and <devfs_get_next_sibling>
+  Added <<dir>> parameter to <devfs_register>, <devfs_mk_compat>,
+  <devfs_mk_dir> and <devfs_find_handle>
+  Work sponsored by SGI
+
+- Fixed apparent bug in COSA driver
+
+- Re-instated "scsihosts=" boot option
+===============================================================================
+Changes for patch v126
+
+- Always create /dev/pts if CONFIG_UNIX98_PTYS=y
+
+- Fixed call to <devfs_mk_dir> in drivers/block/ide-disk.c
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Allow multiple unregistrations
+
+- Created /dev/scsi hierarchy
+  Work sponsored by SGI
+===============================================================================
+Changes for patch v127
+
+Work sponsored by SGI
+
+- No longer disable devpts if devfs enabled (caveat emptor)
+
+- Added flags array to struct gendisk and removed code from
+  drivers/scsi/sd.c
+
+- Created /dev/discs hierarchy
+===============================================================================
+Changes for patch v128
+
+Work sponsored by SGI
+
+- Created /dev/cdroms hierarchy
+===============================================================================
+Changes for patch v129
+
+Work sponsored by SGI
+
+- Removed compatibility entries for sound devices
+
+- Removed compatibility entries for printer devices
+
+- Removed compatibility entries for video4linux devices
+
+- Removed compatibility entries for parallel port devices
+
+- Removed compatibility entries for frame buffer devices
+===============================================================================
+Changes for patch v130
+
+Work sponsored by SGI
+
+- Added major and minor number to devfsd protocol
+
+- Incremented devfsd protocol revision to 5
+
+- Removed compatibility entries for SoundBlaster CD-ROMs
+
+- Removed compatibility entries for netlink devices
+
+- Removed compatibility entries for SCSI generic devices
+
+- Removed compatibility entries for SCSI tape devices
+===============================================================================
+Changes for patch v131
+
+Work sponsored by SGI
+
+- Support info pointer for all devfs entry types
+
+- Added <<info>> parameter to <devfs_mk_dir> and <devfs_mk_symlink>
+
+- Removed /dev/st hierarchy
+
+- Removed /dev/sg hierarchy
+
+- Removed compatibility entries for loop devices
+
+- Removed compatibility entries for IDE tape devices
+
+- Removed compatibility entries for SCSI CD-ROMs
+
+- Removed /dev/sr hierarchy
+===============================================================================
+Changes for patch v132
+
+Work sponsored by SGI
+
+- Removed compatibility entries for floppy devices
+
+- Removed compatibility entries for RAMDISCs
+
+- Removed compatibility entries for meta-devices
+
+- Removed compatibility entries for SCSI discs
+
+- Created <devfs_make_root>
+
+- Removed /dev/sd hierarchy
+
+- Support "../" when searching devfs namespace
+
+- Created /dev/ide/host* hierarchy
+
+- Supported IDE hard discs in /dev/ide/host* hierarchy
+
+- Removed compatibility entries for IDE discs
+
+- Removed /dev/ide/hd hierarchy
+
+- Supported IDE CD-ROMs in /dev/ide/host* hierarchy
+
+- Removed compatibility entries for IDE CD-ROMs
+
+- Removed /dev/ide/cd hierarchy
+===============================================================================
+Changes for patch v133
+
+Work sponsored by SGI
+
+- Created <devfs_get_unregister_slave>
+
+- Fixed bug in fs/partitions/check.c when rescanning
+===============================================================================
+Changes for patch v134
+
+Work sponsored by SGI
+
+- Removed /dev/sd, /dev/sr, /dev/st and /dev/sg directories
+
+- Removed /dev/ide/hd directory
+
+- Exported <devfs_get_parent>
+
+- Created <devfs_register_tape> and /dev/tapes hierarchy
+
+- Removed /dev/ide/mt hierarchy
+
+- Removed /dev/ide/fd hierarchy
+
+- Ported to kernel 2.3.25
+===============================================================================
+Changes for patch v135
+
+Work sponsored by SGI
+
+- Removed compatibility entries for virtual console capture devices
+
+- Removed unused <devfs_set_symlink_destination>
+
+- Removed compatibility entries for serial devices
+
+- Removed compatibility entries for console devices
+
+- Do not hide entries from devfsd or children
+
+- Removed DEVFS_FL_TTY_COMPAT flag
+
+- Removed "nottycompat" boot option
+
+- Removed <devfs_mk_compat>
+===============================================================================
+Changes for patch v136
+
+Work sponsored by SGI
+
+- Moved BSD pty devices to /dev/pty
+
+- Added DEVFS_FL_WAIT flag
+===============================================================================
+Changes for patch v137
+
+Work sponsored by SGI
+
+- Really fixed bug in fs/partitions/check.c when rescanning
+
+- Support new "disc" naming scheme in <get_removable_partition>
+
+- Allow NULL fops in <devfs_register>
+
+- Removed redundant name functions in SCSI disc and IDE drivers
+===============================================================================
+Changes for patch v138
+
+Work sponsored by SGI
+
+- Fixed old bugs in drivers/block/paride/pt.c, drivers/char/tpqic02.c,
+  drivers/net/wan/cosa.c and drivers/scsi/scsi.c
+  Thanks to Sergey Kubushin <ksi@ksi-linux.com>
+
+- Fall back to major table if NULL fops given to <devfs_register>
+===============================================================================
+Changes for patch v139
+
+Work sponsored by SGI
+
+- Corrected and moved <get_blkfops> and <get_chrfops> declarations
+  from arch/alpha/kernel/osf_sys.c to include/linux/fs.h
+
+- Removed name function from struct gendisk
+
+- Updated devfs FAQ
+===============================================================================
+Changes for patch v140
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.27
+===============================================================================
+Changes for patch v141
+
+Work sponsored by SGI
+
+- Bug fix in arch/m68k/atari/joystick.c
+
+- Moved ISDN and capi devices to /dev/isdn
+===============================================================================
+Changes for patch v142
+
+Work sponsored by SGI
+
+- Bug fix in drivers/block/ide-probe.c (patch confusion)
+===============================================================================
+Changes for patch v143
+
+Work sponsored by SGI
+
+- Bug fix in drivers/block/blkpg.c:partition_name()
+===============================================================================
+Changes for patch v144
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.29
+
+- Removed calls to <devfs_register> from cdu31a, cm206, mcd and mcdx
+  CD-ROM drivers: generic driver handles this now
+
+- Moved joystick devices to /dev/joysticks
+===============================================================================
+Changes for patch v145
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.30-pre3
+
+- Register whole-disc entry even for invalid partition tables
+
+- Fixed bug in mounting root FS when initrd enabled
+
+- Fixed device entry leak with IDE CD-ROMs
+
+- Fixed compile problem with drivers/isdn/isdn_common.c
+
+- Moved COSA devices to /dev/cosa
+
+- Support fifos when unregistering
+
+- Created <devfs_register_series> and used in many drivers
+
+- Moved Coda devices to /dev/coda
+
+- Moved parallel port IDE tapes to /dev/pt
+
+- Moved parallel port IDE generic devices to /dev/pg
+===============================================================================
+Changes for patch v146
+
+Work sponsored by SGI
+
+- Removed obsolete DEVFS_FL_COMPAT and DEVFS_FL_TOLERANT flags
+
+- Fixed compile problem with fs/coda/psdev.c
+
+- Reinstate change to <devfs_register_blkdev> in
+  drivers/block/ide-probe.c now that fs/isofs/inode.c is fixed
+
+- Switched to <devfs_register_blkdev> in drivers/block/floppy.c,
+  drivers/scsi/sr.c and drivers/block/md.c
+
+- Moved DAC960 devices to /dev/dac960
+===============================================================================
+Changes for patch v147
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.32-pre4
+===============================================================================
+Changes for patch v148
+
+Work sponsored by SGI
+
+- Removed kmod support: use devfsd instead
+
+- Moved miscellaneous character devices to /dev/misc
+===============================================================================
+Changes for patch v149
+
+Work sponsored by SGI
+
+- Ensure include/linux/joystick.h is OK for user-space
+
+- Improved debugging in <get_vfs_inode>
+
+- Ensure dentries created by devfsd will be cleaned up
+===============================================================================
+Changes for patch v150
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.34
+===============================================================================
+Changes for patch v151
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.35-pre1
+
+- Created <devfs_get_name>
+===============================================================================
+Changes for patch v152
+
+Work sponsored by SGI
+
+- Updated sample modules.conf
+
+- Ported to kernel 2.3.36-pre1
+===============================================================================
+Changes for patch v153
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.42
+
+- Removed <devfs_fill_file>
+===============================================================================
+Changes for patch v154
+
+Work sponsored by SGI
+
+- Took account of device number changes for /dev/fb*
+===============================================================================
+Changes for patch v155
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.43-pre8
+
+- Moved /dev/tty0 to /dev/vc/0
+
+- Moved sequence number formatting from <_tty_make_name> to drivers
+===============================================================================
+Changes for patch v156
+
+Work sponsored by SGI
+
+- Fixed breakage in drivers/scsi/sd.c due to recent SCSI changes
+===============================================================================
+Changes for patch v157
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.45
+===============================================================================
+Changes for patch v158
+
+Work sponsored by SGI
+
+- Ported to kernel 2.3.46-pre2
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/README linux/Documentation/filesystems/devfs/README
--- v2.3.45/linux/Documentation/filesystems/devfs/README	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/README	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,853 @@
+/* -*- auto-fill -*-                                                         */
+
+		Device File System (devfs) Overview
+
+		Richard Gooch <rgooch@atnf.csiro.au>
+
+			      11-NOV-1999
+
+
+Conventions used in this document                                     <section>
+=================================
+
+Each section in this document will have the string "<section>" at the
+right-hand side of the section title. Each subsection will have
+"<subsection>" at the right-hand side. These strings are meant to make
+it easier to search through the document.
+
+NOTE that the master copy of this document is available online at:
+http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.txt
+
+There is also an optional daemon that may be used with devfs. You can
+find out more about it at:
+http://www.atnf.csiro.au/~rgooch/linux/
+
+
+What is it?                                                           <section>
+===========
+
+Devfs is an alternative to "real" character and block special devices
+on your root filesystem. Kernel device drivers can register devices by
+name rather than major and minor numbers. These devices will appear in
+the devfs automatically, with whatever default ownership and
+protection the driver specified.
+
+NOTE that devfs is entirely optional. If you prefer the old disc-based
+device nodes, then simply leave CONFIG_DEVFS_FS=n (the default). In
+this case, nothing will change.
+ALSO NOTE that if you do enable devfs, the defaults are such that full
+compatibility is maintained with the old devices names.
+
+There are two aspects to devfs: one is the underlying device
+namespace, which is a namespace just like any mounted filesystem. The
+other aspect is the filesystem code which provides a view of the
+device namespace. The reason I make a distinction is because the devfs
+can be mounted many times, with each mount showing the same device
+namespace. Changes made are global to all mounted devfs filesystems.
+Also, because the devfs namespace exists without any devfs mounts, you
+can easily mount the root filesystem by referring to an entry in the
+devfs namespace.
+
+The cost of devfs is a small increase in kernel code size and memory
+usage. On a typical machine, the cost is under 0.2 percent. On a
+modest system with 64 MBytes of RAM, the cost is under 0.1 percent.
+The accusations of "bloatware" levelled at devfs are not justified.
+
+
+Why do it?                                                            <section>
+==========
+
+There are several problems that devfs addresses. Some of these
+problems are more serious than others (depending on your point of
+view), and some can be solved without devfs. However, the totality of
+these problems really calls out for devfs.
+
+The choice is a patchwork of inefficient user space solutions, which
+are complex and likely to be fragile, or to use a simple and efficient
+devfs which is robust.
+
+Major&minor allocation                                             <subsection>
+----------------------
+The existing scheme requires the allocation of major and minor device
+numbers for each and every device. This means that a central
+co-ordinating authority is required to issue these device numbers
+(unless you're developing a "private" device driver), in order to
+preserve uniqueness. Devfs shifts the burden to a namespace. This may
+not seem like a huge benefit, but actually it is. Since driver authors
+will naturally choose a device name which reflects the functionality
+of the device, there is far less potential for namespace conflict.
+Solving this requires a kernel change.
+
+/dev management                                                    <subsection>
+---------------
+Because you currently access devices through device nodes, these must
+be created by the system administrator. For standard devices you can
+usually find a MAKEDEV programme which creates all these (hundreds!)
+of nodes. This means that changes in the kernel must be reflected by
+changes in the MAKEDEV programme, or else the system administrator
+creates device nodes by hand.
+The basic problem is that there are two separate databases of
+major and minor numbers. One is in the kernel and one is in /dev (or
+in a MAKEDEV programme, if you want to look at it that way). This is
+duplication of information, which is not good practice.
+Solving this requires a kernel change.
+
+/dev growth                                                        <subsection>
+-----------
+A typical /dev has over 1200 nodes! Most of these devices simply don't
+exist because the hardware is not available. A huge /dev increases the
+time to access devices (I'm just referring to the dentry lookup times
+and the time taken to read inodes off disc: the next subsection shows
+some more horrors).
+An example of how big /dev can grow is if we consider SCSI devices:
+host           6  bits  (say up to 64 hosts on a really big machine)
+channel        4  bits  (say up to 16 SCSI buses per host)
+id             4  bits
+lun            3  bits
+partition      6  bits
+TOTAL          23 bits
+This requires 8 Mega (1024*1024) inodes if we want to store all
+possible device nodes. Even if we scrap everything but id,partition
+and assume a single host adapter with a single SCSI bus and only one
+logical unit per SCSI target (id), that's still 10 bits or 1024
+inodes. Each VFS inode takes around 256 bytes (kernel 2.1.78), so
+that's 256 kBytes of inode storage on disc (assuming real inodes take
+a similar amount of space as VFS inodes). This is actually not so bad,
+because disc is cheap these days. Embedded systems would care about
+256 kBytes of /dev inodes, but you could argue that embedded systems
+would have hand-tuned /dev directories. I've had to do just that on my
+embedded systems, but I would rather just leave it to devfs.
+Another issue is the time taken to lookup an inode when first
+referenced. Not only does this take time in scanning through a list in
+memory, but also the seek times to read the inodes off disc.
+This could be solved in user-space using a clever programme which
+scanned the kernel logs and deleted /dev entries which are not
+available and created them when they were available. This programme
+would need to be run every time a new module was loaded, which would
+slow things down a lot.
+There is an existing programme called scsidev which will automatically
+create device nodes for SCSI devices. It can do this by scanning files
+in /proc/scsi. Unfortunately, to extend this idea to other device
+nodes would require would require significant modifications to
+existing drivers (so they too would provide information in
+/proc). This is a non-trivial change (I should know: devfs has had to
+do something similar). Once you go to this much effort, you may as
+well use devfs itself (which also provides this information).
+Furthermore, such a system would likely be implemented in an ad-hoc
+fashion, as different drivers will provide their information in
+different ways.
+Devfs is much cleaner, because it (natually) has a uniform mechanism
+to provide this information: the device nodes themselves!
+
+Node to driver file_operations translation                         <subsection>
+------------------------------------------
+There is an important difference between the way disc-based c&b nodes
+and devfs entries make the connection between an entry in /dev and the
+actual device driver.
+
+With the current 8 bit major and minor numbers the connection between
+disc-based c&b nodes and per-major drivers is done through a
+fixed-length table of 128 entries. The various filesystem types set
+the inode operations for c&b nodes to {chr,blk}dev_inode_operations,
+so when a device is opened a few quick levels of indirection bring us
+to the driver file_operations.
+
+For miscellaneous character devices a second step is required: there
+is a scan for the driver entry with the same minor number as the file
+that was opened, and the appropriate minor open method is called. This
+scanning is done *every time* you open a device node. Potentially, you
+may be searching through dozens of misc. entries before you find your
+open method. While not an enormous performance overhead, this does
+seem pointless.
+
+Linux *must* move beyond the 8 bit major and minor barrier,
+somehow. If we simply increase each to 16 bits, then the indexing
+scheme used for major driver lookup becomes untenable, because the
+major tables (one each for character and block devices) would need to
+be 64 k entries long (512 kBytes on x86, 1 MByte for 64 bit
+systems). So we would have to use a scheme like that used for
+miscellaneous character devices, which means the search time goes up
+linearly with the average number of major device drivers on your
+system. Not all "devices" are hardware, some are higher-level drivers
+like KGI, so you can get more "devices" without adding hardware
+You can improve this by creating an ordered (balanced:-)
+binary tree, in which case your search time becomes log(N).
+Alternatively, you can use hashing to speed up the search.
+But why do that search at all if you don't have to? Once again, it
+seems pointless.
+
+Note that the devfs doesn't use the major&minor system. For devfs
+entries, the connection is done when you lookup the /dev entry. When
+devfs_register() is called, an internal table is appended which has
+the entry name and the file_operations. If the dentry cache doesn't
+have the /dev entry already, this internal table is scanned to get the
+file_operations, and an inode is created. If the dentry cache already
+has the entry, there is *no lookup time* (other than the dentry scan
+itself, but we can't avoid that anyway, and besides Linux dentries
+cream other OS's which don't have them:-). Furthermore, the number of
+node entries in a devfs is only the number of available device
+entries, not the number of *conceivable* entries. Even if you remove
+unnecessary entries in a disc-based /dev, the number of conceivable
+entries remains the same: you just limit yourself in order to save
+space.
+Devfs provides a fast connection between a VFS node and the device
+driver, in a scalable way.
+
+/dev as a system administration tool                               <subsection>
+------------------------------------
+Right now /dev contains a list of conceivable devices, most of which I
+don't have. A devfs would only show those devices available on my
+system. This means that listing /dev would be a handy way of checking
+what devices were available.
+
+Major&minor size                                                   <subsection>
+----------------
+Existing major and minor numbers are limited to 8 bits each. This is
+now a limiting factor for some drivers, particularly the SCSI disc
+driver, which consumes a single major number. Only 16 discs are
+supported, and each disc may have only 15 partitions. Maybe this isn't
+a problem for you, but some of us are building huge Linux systems with
+disc arrays. With devfs an arbitrary pointer can be associated with
+each device entry, which can be used to give an effective 32 bit
+device identifier (i.e. that's like having a 32 bit minor
+number). Since this is private to the kernel, there are no C library
+compatibility which you would have with increasing major and minor
+number sizes. See the section on "Allocation of Device Numbers" for
+details on maintaining compatibility with userspace.
+Solving this requires a kernel change.
+
+Read-only root filesystem                                          <subsection>
+------------------------
+Having your device nodes on the root filesystem means that you can't
+operate properly with a read-only root filesystem. This is because you
+want to change ownerships and protections of tty devices. Existing
+practice prevents you using a CD-ROM as your root filesystem for a
+*real* system. Sure, you can boot off a CD-ROM, but you can't change
+tty ownerships, so it's only good for installing.
+Also, you can't use a shared NFS root filesystem for a cluster of
+discless Linux machines (having tty ownerships changed on a common
+/dev is not good). Nor can you embed your root filesystem in a
+ROM-FS.
+You can get around this by creating a RAMDISC at boot time, making
+an ext2 filesystem in it, mounting it somewhere and copying the
+contents of /dev into it, then unmounting it and mounting it over
+/dev. A devfs is a cleaner way of solving this.
+
+Non-Unix root filesystem                                           <subsection>
+------------------------
+Non-Unix filesystems (such as NTFS) can't be used for a root
+filesystem because they variously don't support character and block
+special files or symbolic links. You can't have a separate disc-based
+or RAMDISC-based filesystem mounted on /dev because you need device
+nodes before you can mount these. Devfs can be mounted without any
+device nodes. Devlinks won't work because symlinks aren't supported.
+An alternative solution is to use initrd to mount a RAMDISC initial
+root filesystem (which is populated with a minimal set of device
+nodes), and then construct a new /dev in another RAMDISC, and finally
+switch to your non-Unix root filesystem. This requires clever boot
+scripts and a fragile and conceptually complex boot procedure.
+Devfs solves this in a robust and conceptually simple way.
+
+PTY security                                                       <subsection>
+------------
+Current pseudo-tty (pty) devices are owned by root and read-writable
+by everyone. The user of a pty-pair cannot change
+ownership/protections without being suid-root.
+This could be solved with a secure user-space daemon which runs as
+root and does the actual creation of pty-pairs. Such a daemon would
+require modification to *every* programme that wants to use this new
+mechanism. It also slows down creation of pty-pairs.
+An alternative is to create a new open_pty() syscall which does much
+the same thing as the user-space daemon. Once again, this requires
+modifications to pty-handling programmes.
+The devfs solution allows a device driver to "tag" certain device
+files so that when an unopened device is opened, the ownerships are
+changed to the current euid and egid of the opening process, and the
+protections are changed to the default registered by the driver. When
+the device is closed ownership is set back to root and protections are
+set back to read-write for everybody. No programme need be changed.
+The devpts filesystem provides this auto-ownership feature for Unix98
+ptys. It doesn't support old-style pty devices, nor does it have all
+the other features of devfs.
+
+Intelligent device management                                      <subsection>
+-----------------------------
+Devfs implements a simple yet powerful protocol for communication with
+a device management daemon (devfsd) which runs in user space. It is
+possible to send a message (either synchronously or asynchronously) to
+devfsd on any event, such as registration/unregistration of device
+entries, opening and closing devices, looking up inodes, scanning
+directories and more. This has many possibilities. Some of these are
+already implemented.
+See: http://www.atnf.csiro.au/~rgooch/linux/
+
+Device entry registration events can be used by devfsd to change
+permissions of newly-created device nodes. This is one mechanism to
+control device permissions.
+
+Device entry registration/unregistration events can be used to run
+programmes or scripts. This can be used to provide automatic mounting
+of filesystems when a new block device media is inserted into the
+drive.
+
+Asynchronous device open and close events can be used to implement
+clever permissions management. For example, the default permissions on
+/dev/dsp do not allow everybody to read from the device. This is
+sensible, as you don't want some remote user recording what you say at
+your console. However, the console user is also prevented from
+recording. This behaviour is not desirable. With asynchronous device
+open and close events, you can have devfsd run a programme or script
+when console devices are opened to change the ownerships for *other*
+device nodes (such as /dev/dsp). On closure, you can run a different
+script to restore permissions. An advantage of this scheme over
+modifying the C library tty handling is that this works even if your
+programme crashes (how many times have you seen the utmp database with
+lingering entries for non-existent logins?).
+
+Synchronous device open events can be used to perform intelligent
+device access protections. Before the device driver open() method is
+called, the daemon must first validate the open attempt, by running an
+external programme or script. This is far more flexible than access
+control lists, as access can be determined on the basis of other
+system conditions instead of just the UID and GID.
+
+Inode lookup events can be used to authenticate module autoload
+requests. Instead of using kmod directly, the event can be sent to
+devfsd which can implement an arbitrary authentication before loading
+the module itself.
+Inode lookup events can also be used to construct arbitrary
+namespaces, without having to resort to populating devfs with symlinks
+to devices that don't exist.
+
+Speculative Device Scanning                                        <subsection>
+---------------------------
+Consider an application (like cdparanoia) that wants to find all
+CD-ROM devices on the system (SCSI, IDE and other types), whether or
+not their respective modules are loaded. The application must
+speculatively open certain device nodes (such as /dev/sr0 for the SCSI
+CD-ROMs) in order to make sure the module is loaded. This requires
+that all Linux distributions follow the standard device naming scheme
+(last time I looked RedHat did things differently). Devfs solves the
+naming problem.
+
+The same application also wants to see which devices are actually
+available on the system. With the existing system it needs to read the
+/dev directory and speculatively open each /dev/sr* device to
+determine if the device exists or not. With a large /dev this is an
+inefficient operation, especially if there are many /dev/sr* nodes. A
+solution like scsidev could reduce the number of /dev/sr* entries (but
+of course that also requires all that inefficient directory scanning).
+
+With devfs, the application can open the /dev/sr directory (which
+triggers the module autoloading if required), and proceed to read
+/dev/sr. Since only the available devices will have entries, there are
+no inefficencies in directory scanning or device openings.
+
+
+Who else does it?                                                     <section>
+=================
+
+FreeBSD-current now has a devfs implementation. Solaris 2 has a
+pseudo-devfs (something akin to scsidev but for all devices, with some
+unspecified kernel support). BeOS  and Plan9 also have it. SGI's IRIX
+6.4 and above also have a device filesystem.
+
+While we shouldn't just automatically do something because others do
+it, we should not ignore the work of others either. FreeBSD has a lot
+of competent people working on it, so their opinion should not be
+blithely ignored.
+
+
+How it works                                                          <section>
+============
+
+Registering device entries                                         <subsection>
+--------------------------
+For every entry (device node) in a devfs-based /dev a driver must call
+devfs_register(). This adds the name of the device entry, the
+file_operations structure pointer and a few other things to an
+internal table. Device entries may be added and removed at any
+time. When a device entry is registered, it automagically appears in
+any mounted devfs'.
+
+Inode lookup                                                       <subsection>
+------------
+When a lookup operation on an entry is performed and if there is no
+driver information for that entry devfs will attempt to call devfsd or
+kmod. If still no driver information can be found then a negative
+dentry is yielded and the next stage operation will be called by the
+VFS (such as create() or mknod() inode methods). If driver information
+can be found, an inode is created (if one does not exist already) and
+all is well.
+
+Manually creating device nodes                                     <subsection>
+------------------------------
+The mknod() method allows you to create an ordinary named pipe in the
+devfs, or you can create a character or block special inode if one
+does not already exist. You may wish to create a character or block
+special inode so that you can set permissions and ownership. Later, if
+a device driver registers an entry with the same name, the
+permissions, ownership and times are retained. This is how you can set
+the protections on a device even before the driver is loaded. Once you
+create an inode it appears in the directory listing.
+
+Unregistering device entries                                       <subsection>
+----------------------------
+A device driver calls devfs_unregister() to unregister an entry.
+
+Chroot() gaols                                                     <subsection>
+--------------
+The semantics of inode creation are different when the devfs is
+mounted with the "explicit" option. Now, when a device entry is
+registered, it will not appear until you use mknod() to create the
+device. It doesn't matter if you mknod() before or after the device is
+registered with devfs_register(). The purpose of this behaviour is to
+support chroot(2) gaols, where you want to mount a minimal devfs
+inside the gaol. Only the devices you specifically want to be
+available (through your mknod() setup) will be accessible.
+
+
+Persistence of ownership/permissions across reboots                   <section>
+===================================================
+
+If you don't use mknod(2) to create a device file, nor use chmod(2) or
+chown(2) to change the ownerships/permissions, the inode ctime will
+remain at 0 (the epoch, 12 am, 1-JAN-1970, GMT). Anything with a ctime
+later than this has had it's ownership/permissions changed. Hence, a
+simple script or programme may be used to tar up all changed inodes,
+prior to shutdown.
+
+Upon bootup, simply untar the previously created tarfile, and all your
+ownerships/permissions will be retained. For the paranoid, you can
+save your permissions periodically using a cron job.
+
+NOTE: tar will first unlink(2) an inode before creating a new device
+node. The unlink(2) has the effect of breaking the connection between
+a devfs entry and the device driver. If you use the "devfs=only" boot
+option, you lose access to the device driver, requiring you to reload
+the module. I consider this a bug in tar (there is no real need to
+unlink(2) the inode first).
+
+I've provided a script called "rc.devfs" in this directory which you
+can use to save and restore permissions.
+
+Alternatively, you can use the device management daemon (devfsd) to
+control the permissions of device nodes. This has the advantage of
+being able to store permissions for whole groups of devices with a
+single configuration entry, rather than one (tar) entry per device
+node. Devfsd also receives inode change events, so you could easily
+implement a simple permissions database.
+
+
+Installation during the transition phase                              <section>
+========================================
+
+Currently, not all device drivers in the kernel have been modified to
+use devfs. If you want to boot between kernels with and without devfs
+support, this section discusses some options. Either way is safe,
+although some people will have different preferences.
+
+Note that your old-style (i.e. node-based) chroot /gaol/dev
+directories which you manually created will still work, unless you
+pass the "devfs=only" boot option.
+
+Fail-safe Approach with devfs mounted on /dev                      <subsection>
+---------------------------------------------
+The default is for devfs to be mounted onto /dev at boot time.
+Device drivers which do not yet have devfs support will not
+automagically appear in the devfs. The simplest way to create device
+nodes for these drivers is to unpack a tarfile containing the required
+device nodes. You can do this in your boot scripts. All your drivers
+will now work as before.
+
+Hopefully for most people devfs will have enough support so that they
+can mount devfs directly over /dev without loosing most functionality
+(i.e. loosing access to various devices). As of 22-JAN-1998 (devfs
+patch version 10) I am now running this way. All the devices I have
+are available in the devfs, so I don't lose anything.
+
+WARNING: if your configuration requires the old-style device names
+(i.e. /dev/hda1 or /dev/sda1), you must install devfsd and configure
+it to maintain compatibility entries. It is almost certain that you
+will require this. Note that the kernel creates a compatibility entry
+for the root device, so you don't need initrd.
+
+Fail-safe Approach with real /dev inodes                           <subsection>
+----------------------------------------
+This method involves more work, and is no longer recommended now that
+a large number of drivers have devfs support. You will need to use the
+"devfs=nomount" boot option.
+
+Copy the contents of /dev to /devfs. Then remove entries in /dev
+which are now available in devfs and make them symbolic links to the
+entries in /devfs.  Finally, edit your /etc/fstab or boot scripts so
+that devfs is mounted over /devfs on bootup. If devfs is supported,
+accessing devices supported by devfs will follow the symlinks to
+devfs. If devfs is not supported, accessing those same devices will
+follow the symlinks to /devfs which contains only old-style device
+nodes.  Devices not supported by devfs will be found directly on /dev.
+Simple! You can also follow this principle for chroot gaols.
+
+I've provided a demonstration script called "mk-devlinks" in this
+directory which you can use to create symlinks in /dev and
+/devfs. Note that this script is out of date and should not be run
+without modification.
+
+
+All the way with Devfs                                                <section>
+======================
+
+The devfs kernel patch creates a rationalised device tree. As stated
+above, if you want to keep using the old /dev naming scheme, you just
+need to configure devfsd appopriately (see the man page). People who
+prefer the old names can ignore this section. For those of us who like
+the rationalised names and an uncluttered /dev, read on.
+
+If you don't run devfsd, or don't enable compatibility entry
+management, then you will have to configure your system to use the new
+names. For example, you will then need to edit your /etc/fstab to use
+the new disc naming scheme. If you want to be able to boot non-devfs
+kernels, you will need compatibility symlinks in the underlying
+disc-based /dev pointing back to the old-style names for when you boot
+a kernel without devfs.
+
+You can selectively decide which devices you want compatibility
+entries for. For example, you may only want compatibility entries for
+BSD pseudo-terminal devices (otherwise you'll have to patch you C
+library or use Unix98 ptys instead). It's just a matter of putting in
+the correct regular expression into /dev/devfsd.conf.
+
+
+Other Issues                                                          <section>
+============
+
+Another thing to take note of is whether your init programme creates a
+Unix socket /dev/telinit. Some versions of init create /dev/telinit so
+that the <telinit> programme can communicate with the init process. If
+you have such a system you need to make sure that devfs is mounted
+over /dev *before* init starts. In other words, you can't leave the
+mounting of devfs to /etc/rc, since this is executed after init.
+Other versions of init require a named pipe /dev/initctl which must
+exist *before* init starts. Once again, you need to mount devfs and
+then create the named pipe *before* init starts.
+
+The default behaviour now is to mount devfs onto /dev at boot time.
+You can disable this with the "devfs=nomount" boot option, but you can
+then get harmless but annoying messages about not being able to open
+an initial console.
+
+If you have automatic mounting of devfs onto /dev then you may need to
+create /dev/initctl in your boot scripts. The following lines should
+suffice:
+
+mknod /dev/initctl p
+kill -SIGUSR1 1       # tell init that /dev/initctl now exists
+
+Alternatively, if you don't want the kernel to mount devfs onto /dev
+then you could use the following procedure is a guideline for how to
+get around /dev/initctl problems:
+
+# cd /sbin
+# mv init init.real
+# cat > init
+#! /bin/sh
+mount -n -t devfs none /dev
+mknod /dev/initctl p
+exec /sbin/init.real $*
+[control-D]
+# chmod a+x init
+
+Note that newer versions of init create /dev/initctl automatically, so
+you don't have to worry about this.
+
+Using kmod (module autoloading)                                    <subsection>
+-------------------------------
+Another thing to note is that if you are using kmod then you need to
+edit your /etc/modules.conf so that things work properly. You should
+include the sample modules.conf file in the
+Documentation/filesystems/devfs directory into your /etc/modules.conf
+to ensure correct operation.
+
+Mounting root off a devfs device                                   <subsection>
+--------------------------------
+If you wish to mount root off a devfs device when you pass the
+"devfs=only" boot option, then you need to pass in the "root=<device>"
+option to the kernel when booting. If you use LILO, then you must have
+this in lilo.conf:
+append = "root=<device>"
+
+Surprised? Yep, so was I. It turns out if you have (as most people
+do):
+root = <device>
+
+then LILO will determine the device number of <device> and will write
+that device number into a special place in the kernel image before
+starting the kernel, and the kernel will use that device number to
+mount the root filesystem. So, using the "append" variety ensures that
+LILO passes the root filesystem device as a string, which devfs can
+then use.
+
+Note that this isn't an issue if you don't pass "devfs=only".
+
+TTY issues                                                         <subsection>
+----------
+You may replace your tty devices in /dev with symbolic links to /devfs
+however you will then find that programmes which depend on ttyname(3)
+will no longer work properly. The <tty> programme is a good
+example. I've written a patch to libc 5.4.43 which fixes this. This
+has been included in libc 5.4.44 and glibc 2.1.?
+
+
+Device drivers currently ported                                       <section>
+===============================
+
+- All miscellaneous character devices support devfs (this is done
+  transparently through misc_register())
+
+- SCSI discs and generic hard discs
+
+- Character memory devices (null, zero, full and so on)
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- Loop devices (/dev/loop?)
+ 
+- TTY devices (console, serial ports, terminals and pseudo-terminals)
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- SCSI tapes (/dev/st*)
+
+- SCSI CD-ROMs (/dev/sr*)
+
+- SCSI generic devices (/dev/sg*)
+
+- RAMDISCS (/dev/ram?)
+
+- Meta Devices (/dev/md*)
+
+- Floppy discs (/dev/fd?*)
+
+- Parallel port printers (/dev/lp*)
+
+- Sound devices
+  Thanks to Eric Dumas <dumas@linux.eu.org> and
+  C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- Joysticks (/dev/js*)
+
+- Sparc keyboard (/dev/kbd)
+
+- DSP56001 digital signal processor (/dev/dsp56k)
+
+- Apple Desktop Bus (/dev/adb)
+
+- Coda network file system (/dev/cfs*)
+
+- Virtual console capture devices (/dev/vcs*)
+  Thanks to Dennis Hou <smilax@mindmeld.yi.org>
+
+- Frame buffer devices (/dev/fb*)
+
+- Video capture devices (/dev/video? /dev/vbi?)
+
+
+Naming Scheme                                                         <section>
+=============
+
+Disc Devices                                                       <subsection>
+------------
+
+All discs, whether SCSI, IDE or whatever, are placed under the
+/dev/discs hierarchy:
+	/dev/discs/disc0	first disc
+	/dev/discs/disc1	second disc
+
+Each of these entries is a symbolic link to the directory for that
+device. The device directory contains:
+	disc	for the whole disc
+	part*	for individual partitions
+
+CD-ROM Devices                                                     <subsection>
+--------------
+
+All CD-ROMs, whether SCSI, IDE or whatever, are placed under the
+/dev/cdroms hierarchy:
+	/dev/cdroms/cdrom0	first CD-ROM
+	/dev/cdroms/cdrom1	second CD-ROM
+
+Each of these entries is a symbolic link to the real device entry for
+that device.
+
+Tape Devices                                                       <subsection>
+------------
+
+All tapes, whether SCSI, IDE or whatever, are placed under the
+/dev/tapes hierarchy:
+	/dev/tapes/tape0	first tape
+	/dev/tapes/tape1	second tape
+
+Each of these entries is a symbolic link to the directory for that
+device. The device directory contains:
+	mt			for mode 0
+	mtl			for mode 1
+	mtm			for mode 2
+	mta			for mode 3
+	mtn			for mode 0, no rewind
+	mtln			for mode 1, no rewind
+	mtmn			for mode 2, no rewind
+	mtan			for mode 3, no rewind
+
+SCSI Devices                                                       <subsection>
+------------
+To uniquely identify any SCSI device requires the following
+information:
+  controller	(host adapter)
+  bus		(SCSI channel)
+  target	(SCSI ID)
+  unit		(Logical Unit Number)
+
+All SCSI devices are placed under /dev/scsi (assuming devfs is mounted
+on /dev). Hence, a SCSI device with the following parameters:
+c=1,b=2,t=3,u=4 would appear as:
+	/dev/scsi/host1/bus2/target3/lun4	device directory
+
+Inside this directory, a number of device entries may be created,
+depending on which SCSI device-type drivers were installed.
+
+See the section on the disc naming scheme to see what entries the SCSI
+disc driver creates.
+
+See the section on the tape naming scheme to see what entries the SCSI
+tape driver creates.
+
+The SCSI CD-ROM driver creates:
+	cd
+
+The SCSI generic driver creates:
+	generic
+
+IDE Devices                                                        <subsection>
+-----------
+To uniquely identify any IDE device requires the following
+information:
+  controller
+  bus		(aka. primary/secondary)
+  target	(aka. master/slave)
+  unit
+
+All IDE devices are placed under /dev/ide (assuming devfs is mounted
+on /dev), and uses a similar naming scheme to the SCSI subsystem.
+
+
+XT Hard Discs                                                      <subsection>
+-------------
+All XT discs are placed under /dev/xd (assuming devfs is mounted on
+/dev). The first XT disc has the directory /dev/xd/disc0
+
+TTY devices                                                        <subsection>
+-----------
+The tty devices now appear as:
+  New name                   Old-name                   Device Type
+  --------                   --------                   -----------
+  /dev/tts/{0,1,...}         /dev/ttyS{0,1,...}         Serial ports
+  /dev/cua/{0,1,...}         /dev/cua{0,1,...}          Call out devices
+  /dev/vc/{0,1,...}          /dev/tty{1...63}           Virtual consoles
+  /dev/pty/m{0,1,...}        /dev/ptyp??                PTY masters
+  /dev/pty/s{0,1,...}        /dev/ttyp??                PTY slaves
+
+RAMDISCS                                                           <subsection>
+--------
+The RAMDISCS are placed in their own directory, and are named thus:
+  /dev/rd/{0,1,2,...}
+
+Meta Devices                                                       <subsection>
+------------
+The meta devices are placed in their own directory, and are named
+thus:
+  /dev/md/{0,1,2,...}
+
+Floppy discs                                                       <subsection>
+------------
+Floppy discs are placed in the /dev/floppy directory.
+
+Loop devices                                                       <subsection>
+------------
+Loop devices are placed in the /dev/loop directory.
+
+Sound devices                                                      <subsection>
+-------------
+Sound devices are placed in the /dev/sound directory (audio,
+sequencer, ...).
+
+
+SCSI Host Probing Issues                                              <section>
+========================
+
+Devfs allows you to identify SCSI discs based in part on SCSI host
+numbers. If you have only one SCSI host (card) in your computer, then
+clearly it will be given host number 0. Life is not always that easy
+is you have multiple SCSI hosts. Unfortunately, it can sometimes be
+difficult to guess what the probing order of SCSI hosts is. You need
+to know the probe order before you can use device names. To make this
+easy, there is a kernel boot parameter called "scsihosts". This allows
+you to specify the probe order for different types of SCSI hosts. The
+syntax of this parameter is:
+
+scsihosts=<name_1>:<name_2>:<name_3>:...:<name_n>
+
+where <name_1>,<name_2>,...,<name_n> are the names of drivers used in
+/proc filesystem. For example:
+
+    scsihosts=aha1542:ppa:aha1542::ncr53c7xx
+
+means that devices connected to
+- first aha1542 controller   - will be c0b#t#u#
+- first parallel port ZIP    - will be c1b#t#u#
+- second aha1542 controller  - will be c2b#t#u#
+- first NCR53C7xx controller - will be c4b#t#u#
+- any extra controller       - will be c5b#t#u#, c6b#t#u#, etc
+- if any of above controllers will not be found - the reserved names will
+  not be used by any other device.
+- c3b#t#u# names will never be used
+
+You can use ',' instead of ':' as the separator character if you
+wish.
+
+Note that this scheme does not address the SCSI host order if you have
+multiple cards of the same type (such as NCR53c8xx). In this case you
+need to use the driver-specific boot parameters to control this.
+
+
+Allocation of Device Numbers                                          <section>
+============================
+
+Devfs allows you to write a driver which doesn't need to allocate a
+device number (major&minor numbers) for the internal operation of the
+kernel. However, there are a number of userspace programmes that use
+the device number as a unique handle for a device. An example is the
+<find> programme, which uses device numbers to determine whether an
+inode is on a different filesystem than another inode. The device
+number used is the one for the block device which a filesystem is
+using. To preserve compatibility with userspace programmes, block
+devices using devfs need to have unique device numbers allocated to
+them. Furthermore, POSIX specifies device numbers, so some kind of
+device number needs to be presented to userspace.
+
+The simplest option (especially when porting drivers to devfs) is to
+keep using the old major and minor numbers. Devfs will take whatever
+values are given for major&minor and pass them onto userspace.
+
+Alternatively, you can have devfs choose unique device numbers for
+you. When you register a character or block device using
+<devfs_register> you can provide the optional DEVFS_FL_AUTO_DEVNUM
+flag, which will then automatically allocate a unique device number
+(the allocation is separated for the character and block devices).
+This device number is a 16 bit number, so this leaves plenty of space
+for large numbers of discs and partitions. This scheme can also be
+used for character devices, in particular the tty devices, which are
+currently limited to 256 pseudo-ttys (this limits the total number of
+simultaneous xterms and remote logins).  Note that the device number
+is limited to the range 36864-61439 (majors 144-239), in order to
+avoid any possible conflicts with existing official allocations.
+
+A final note on this scheme: since it doesn't increase the size of
+device numbers, there are no compatibility issues with userspace.
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/ToDo linux/Documentation/filesystems/devfs/ToDo
--- v2.3.45/linux/Documentation/filesystems/devfs/ToDo	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/ToDo	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,40 @@
+		Device File System (devfs) ToDo List
+
+		Richard Gooch <rgooch@atnf.csiro.au>
+
+			      5-DEC-1999
+
+This is a list of things to be done for better devfs support in the
+Linux kernel. If you'd like to contribute to the devfs, please have a
+look at this list for anything that is unallocated. Also, if there are
+items missing (surely), please contact me so I can add them to the
+list (preferably with your name attached to them:-).
+
+
+- >256 ptys
+  Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
+
+- Amiga floppy driver (drivers/block/amiflop.c)
+
+- Atari floppy driver (drivers/block/ataflop.c)
+
+- Network block device (drivers/block/nbd.c)
+
+- SWIM3 (Super Woz Integrated Machine 3) floppy driver (drivers/block/swim3.c)
+
+- Amiga ZorroII ramdisc driver (drivers/block/z2ram.c)
+
+- Parallel port ATAPI CD-ROM (drivers/block/paride/pcd.c)
+
+- Parallel port ATAPI floppy (drivers/block/paride/pf.c)
+
+- AP1000 block driver (drivers/ap1000/ap.c, drivers/ap1000/ddv.c)
+
+- Archimedes floppy (drivers/acorn/block/fd1772.c)
+
+- MFM hard drive (drivers/acorn/block/mfmhd.c)
+
+- I2O block device (drivers/i2o/i2o_block.c)
+
+- ST-RAM device (arch/m68k/atari/stram.c)
+
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/boot-options linux/Documentation/filesystems/devfs/boot-options
--- v2.3.45/linux/Documentation/filesystems/devfs/boot-options	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/boot-options	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,66 @@
+/* -*- auto-fill -*-                                                         */
+
+		Device File System (devfs) Boot Options
+
+		Richard Gooch <rgooch@atnf.csiro.au>
+
+			      14-DEC-1999
+
+
+When either CONFIG_DEVFS_DEBUG or CONFIG_DEVFS_BOOT_OPTIONS are
+enabled, you can pass several boot options to the kernel to control
+devfs behaviour. The boot options are prefixed by "devfs=", and are
+separated by commas. Spaces are not allowed. The syntax looks like
+this:
+
+devfs=<option1>,<option2>,<option3>
+
+and so on. For example, if you wanted to turn on debugging for module
+load requests and device registration, you would do:
+
+devfs=dmod,dreg
+
+
+Debugging Options
+=================
+
+These requires CONFIG_DEVFS_DEBUG to be enabled.
+Note that all debugging options have 'd' as the first character. By
+default all options are off. All debugging output is sent to the
+kernel logs. The debugging options do not take effect until the devfs
+version message appears (just prior to the root filesystem being
+mounted).
+
+These are the options:
+
+dmod		print module load requests to <request_module>
+
+dreg		print device register requests to <devfs_register>
+
+dunreg		print device unregister requests to <devfs_unregister>
+
+dchange		print device change requests to <devfs_set_flags>
+
+dilookup	print inode lookup requests
+
+diread		print inode reads
+
+diunlink	print inode unlinks
+
+diwrite		print inode writes
+
+dimknod		print calls to mknod(2)
+
+dall		some debugging turned on
+
+
+Other Options
+=============
+
+These control the default behaviour of devfs. The options are:
+
+show		show unregistered devices by default
+
+nomount		do not mount devfs onto /dev at boot time
+
+only		disable non-devfs device nodes for devfs-capable drivers
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/mk-devlinks linux/Documentation/filesystems/devfs/mk-devlinks
--- v2.3.45/linux/Documentation/filesystems/devfs/mk-devlinks	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/mk-devlinks	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,123 @@
+#! /bin/csh -f
+
+# WARNING: make sure /devfs is not a mounted devfs
+
+cd /devfs
+if (-e .devfsd) then
+    echo "/devfs must not be a mounted devfs"
+    exit 1
+endif
+if ( ! -e null ) then
+    echo "Cannot find null device"
+    exit 1
+endif
+
+
+# Make SCSI disc links.
+# WARNING: this assumes your SCSI discs are numbered ID=0, ID=1, ID=2 and so on
+set discs = (a b c d e f)
+if ( ! -d sd ) mkdir sd
+ln -sf /devfs/sd /dev
+
+@ scsi_id = 0
+while ($scsi_id < $#discs)
+    @ discnum = $scsi_id + 1
+    ls -lF sd${discs[$discnum]}
+    rm /dev/sd${discs[$discnum]}
+    ln -s /devfs/sd${discs[$discnum]} /dev
+    ln -s ../sd${discs[$discnum]} sd/c0b0t${scsi_id}u0
+    @ partition = 1
+    while ($partition < 16)
+	ls -lF sd${discs[$discnum]}${partition}
+	rm /dev/sd${discs[$discnum]}${partition}
+	ln -s /devfs/sd${discs[$discnum]}${partition} /dev
+	ln -s ../sd${discs[$discnum]}${partition} sd/c0b0t${scsi_id}u0p$partition
+	@ partition ++
+    end
+    @ scsi_id ++
+end
+
+
+# Make IDE disc links
+foreach i (hd*)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make miscellaneous character devices links (character, major=10)
+foreach i (`ls -l * | grep '^c' | fgrep '10,' | awk '{print $10}'`)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make memory devices links (character, major=1)
+foreach i (`ls -l * | grep '^c' | fgrep ' 1,' | awk '{print $10}'`)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make loop device links
+foreach i (loop*)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make various tty device links
+foreach i (tty* pty* cua* console)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make SCSI CD-ROM, tapes and generic links
+ln -s /devfs/sr /dev
+ln -s /devfs/st /dev
+ln -s /devfs/sg /dev
+foreach i (sr* st* nst* sg*)
+    if ("$i" == "stderr") continue
+    if ("$i" == "stdin") continue
+    if ("$i" == "stdout") continue
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make RAMDISC device links
+ln -s /devfs/rd /dev
+foreach i (ram*)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make floppy device links
+ln -s /devfs/floppy /dev
+foreach i (fd?*)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+
+# Make line printer device links
+foreach i (lp*)
+    rm /dev/$i
+    ln -s /devfs/$i /dev
+end
+
+# Make sound devices links
+if ( ! -d sound ) mkdir sound
+ln -sf /devfs/sound /dev
+foreach i (mixer sequencer midi dsp audio sequencer2 mixer1 patmgr0 midi1\
+    dsp1 audio1 patmgr1 midi2 midi3)
+ 
+    if ( -f /dev/$i ) then
+	rm /dev/$i
+	ln -s /devfs/$i /dev
+    endif
+end
+
+
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/modules.conf linux/Documentation/filesystems/devfs/modules.conf
--- v2.3.45/linux/Documentation/filesystems/devfs/modules.conf	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/modules.conf	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,89 @@
+# Sample entries for /etc/modules.conf for devfs
+
+###############################################################################
+#                  Configuration section: change to suit
+#
+alias		/dev/sound	sb
+alias		/dev/v4l	bttv
+alias		gen-watchdog	pcwd
+alias		gen-md		raid0
+alias		/dev/joysticks	joystick
+probeall	scsi-hosts	sym53c8xx
+
+###############################################################################
+#                      Generic section: do not change
+#
+# All HDDs
+probeall	/dev/discs	scsi-hosts sd_mod ide-probe-mod ide-disk DAC960
+
+# All CD-ROMs
+probeall	/dev/cdroms	scsi-hosts sr_mod ide-probe-mod ide-cd
+
+# All tapes
+probeall	/dev/tapes	scsi-hosts st ide-probe-mod ide-tape
+
+# All SCSI devices
+probeall	/dev/scsi	scsi-hosts sd_mod sr_mod st sg
+
+# All IDE devices
+probeall	/dev/ide	ide-probe-mod ide-disk ide-cd ide-tape ide-floppy
+
+# SCSI HDDs
+probeall	/dev/sd		scsi-hosts sd_mod
+alias		/dev/sd*	/dev/sd
+
+# SCSI CD-ROMs
+probeall	/dev/sr		scsi-hosts sr_mod
+alias		/dev/sr*	/dev/sr
+
+# SCSI tapes
+probeall	/dev/st		scsi-hosts st
+alias		/dev/st*	/dev/sr
+alias		/dev/nst*	/dev/st
+
+# SCSI generic
+probeall	/dev/sg		scsi-hosts sg
+alias		/dev/sg*	/dev/sg
+
+# Floppies
+alias		/dev/floppy	floppy
+alias		/dev/fd*	floppy
+
+# RAMDISCs
+alias		/dev/rd		rd
+alias		/dev/ram*	rd
+
+# Loop devices
+alias		/dev/loop*	loop
+
+# Meta devices
+alias		/dev/md*	gen-md
+
+# Parallel port printers
+alias		/dev/printers	lp
+alias		/dev/lp*	lp
+
+# Soundcard
+alias		/dev/audio	/dev/sound
+alias		/dev/mixer	/dev/sound
+alias		/dev/dsp	/dev/sound
+alias		/dev/dspW	/dev/sound
+alias		/dev/midi	/dev/sound
+
+# Joysticks
+alias		/dev/js*	/dev/joysticks
+
+# Serial ports
+alias		/dev/tts	serial
+alias		/dev/ttyS*	/dev/tts
+alias		/dev/cua*	/dev/tts
+
+# Miscellaneous devices
+alias		/dev/watchdog	gen-watchdog # alias for gen-watchdog needed!
+alias		/dev/atibm	atixlmouse
+alias		/dev/inportbm	msbusmouse
+alias		/dev/logibm	busmouse
+
+# Video capture devices
+alias		/dev/video*	/dev/v4l
+alias		/dev/vbi*	/dev/v4l
diff -u --recursive --new-file v2.3.45/linux/Documentation/filesystems/devfs/rc.devfs linux/Documentation/filesystems/devfs/rc.devfs
--- v2.3.45/linux/Documentation/filesystems/devfs/rc.devfs	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/devfs/rc.devfs	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,104 @@
+#! /bin/sh
+#
+#  /etc/rc.d/rc.devfs
+#
+#  Linux Boot Scripts by  Richard Gooch <rgooch@atnf.csiro.au>
+#  Copyright 1993-1999 under GNU Copyleft version 2.0. See /etc/rc for
+#  copyright notice.
+#
+#  Save and restore devfs ownerships and permissions
+#
+#	Written by	Richard Gooch   11-JAN-1998
+#
+#	Updated by	Richard Gooch	23-JAN-1998: Added "start" and "stop".
+#
+#	Updated by	Richard Gooch	5-AUG-1998: Robustness improvements by
+# Roderich Schupp.
+#
+#	Updated by	Richard Gooch	9-AUG-1998: Took account of change from
+# ".epoch" to ".devfsd".
+#
+#	Updated by	Richard Gooch	19-AUG-1998: Test and tty pattern patch
+# by Roderich Schupp.
+#
+#	Updated by	Richard Gooch	24-MAY-1999: Use sed instead of tr.
+#
+#	Last updated by	Richard Gooch	25-MAY-1999: Don't save /dev/log.
+#
+#
+# Usage:  rc.devfs save|restore [savedir] [devfsdir]
+#
+# Note: "start" is a synonym for "restore" and "stop" is a synonym for "save".
+
+# Set VERBOSE to "no" if you would like a more quiet operation.
+VERBOSE=yes
+
+# Set TAROPTS to "v" or even "vv" to see which files get saved/restored.
+TAROPTS=
+
+option="$1"
+
+case "$option" in
+    save|restore) ;;
+    start)  option=restore ;;
+    stop)   option=save ;;
+    *)      echo "No save or restore option given" ; exit 1 ;;
+esac
+
+if [ "$2" = "" ]; then
+    savedir=/var/state
+else
+    savedir=$2
+fi
+
+if [ ! -d $savedir ]; then
+    echo "Directory: $savedir does not exist"
+    exit 1
+fi
+
+if [ "$3" = "" ]; then
+    if [ -d /devfs ]; then
+	devfs=/devfs
+    else
+	devfs=/dev
+    fi
+else
+    devfs=$3
+fi
+
+grep devfs /proc/filesystems >/dev/null || exit 0
+
+if [ ! -d $devfs ]; then
+    echo "Directory: $devfs does not exist"
+    exit 1
+elif [ ! -c $devfs/.devfsd ]; then
+    echo "Directory: $devfs is not the root of a devfs filesystem"
+    exit 1
+fi
+
+savefile=`echo $devfs | sed 's*/*_*g'`
+tarfile=${savedir}/devfssave.${savefile}.tar.gz
+
+cd $devfs
+
+case "$option" in
+    save)
+	[ "$VERBOSE" != no ] && echo "Saving $devfs permissions..."
+	
+	# You might want to adjust the pattern below to control
+	# which file's permissions will be saved.
+	# The sample pattern exludes all virtual consoles
+	# as well as old and new style pseudo terminals.
+	files=`find * -noleaf -cnewer .devfsd \
+	       ! -regex 'tty[0-9]+\|vc/.*\|vcsa?[0-9]+\|vcc/.*\|[pt]ty[a-z][0-9a-f]\|pt[ms]/.*\|log' -print`
+	rm -f $tarfile
+	[ -n "$files" ] && tar cz${TAROPTS}f $tarfile $files
+	;;
+
+    restore)
+	[ "$VERBOSE" != no ] && echo "Restoring $devfs permissions..."
+	[ -f $tarfile ] && tar xpz${TAROPTS}f $tarfile
+	;;
+esac
+
+exit 0
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX
--- v2.3.45/linux/Documentation/isdn/00-INDEX	Thu Nov 11 20:11:31 1999
+++ linux/Documentation/isdn/00-INDEX	Tue Feb 15 11:40:42 2000
@@ -38,3 +38,6 @@
 	- info on driver for Spellcaster cards.
 README.x25
     _ info for running X.25 over ISDN.
+README.hysdn
+        - info on driver for Hypercope active HYSDN cards
+ 
\ No newline at end of file
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/CREDITS linux/Documentation/isdn/CREDITS
--- v2.3.45/linux/Documentation/isdn/CREDITS	Thu Nov 11 20:11:31 1999
+++ linux/Documentation/isdn/CREDITS	Tue Feb 15 11:40:42 2000
@@ -58,9 +58,13 @@
 Max Riegel (riegel@max.franken.de)
   For making the ICN hardware-documentation and test-equipment available.
 
+Armin Schindler (mac@melware.de)
+  For the eicon active card driver.
+
 Gerhard 'Fido' Schneider (fido@wuff.mayn.de)
   For heavy-duty-beta-testing with his BBS ;)
 
 Thomas Uhl (uhl@think.de)
   For distributing the cards.
   For pushing me to work ;-)
+
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/HiSax.cert linux/Documentation/isdn/HiSax.cert
--- v2.3.45/linux/Documentation/isdn/HiSax.cert	Thu Aug 12 09:42:33 1999
+++ linux/Documentation/isdn/HiSax.cert	Tue Feb 15 11:40:42 2000
@@ -30,7 +30,7 @@
 files are protected by md5 checksums and the md5sum file is pgp signed by
 myself:
 
-KeyID 1024/FF992F6D 1997/01/16 Karsten Keil <keil@temic-ech.spacenet.de>
+KeyID 1024/FF992F6D 1997/01/16 Karsten Keil <kkeil@suse.de>
 Key fingerprint = 92 6B F7 58 EE 86 28 C8  C4 1A E6 DC 39 89 F2 AA
 
 Only if the checksums are OK, and the signature of the file
@@ -70,9 +70,9 @@
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBN6xoKTpxHvX/mS9tAQF4DAP/efRWym6jvNOND1O9eaEFdP5fd2xKB3XD
-Ifh6Iv0DvARcIuxXtEjT+z3FjjQk35eo/wX4C4tpRhYQYdgCxl+iv+5DzhVDpB95
-3QS9E5m0E1eIK3t8XiQTRgb+1JPCMYUThCrakYsX25o3ndGKyDipsCTfkyR38XwC
-bUyTfcOYKAk=
-=VKyL
+iQCVAwUBOFAwqTpxHvX/mS9tAQFI2QP9GLDK2iy/KBhwReE3F7LeO+tVhffTVZ3a
+20q5/z/WcIg/pnH0uTkl2UgDXBFXYl45zJyDGNpAposIFmT+Edd14o7Vj1w/BBdn
+Y+5rBmJf+gyBu61da5d6bv0lpymwRa/um+ri+ilYnZ/XPfg5JKhdjGSBCJuJAElM
+d2jFbTrsMYw=
+=LNf9
 -----END PGP SIGNATURE-----
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.3.45/linux/Documentation/isdn/README	Thu Nov 11 20:11:31 1999
+++ linux/Documentation/isdn/README	Tue Feb 15 11:40:42 2000
@@ -80,8 +80,7 @@
    The functionality is almost the same as that of a serial device
    (the line-discs are handled by the kernel), which lets you run
    SLIP, CSLIP and asynchronous PPP through the devices. We have tested
-   Seyon, minicom, CSLIP (uri-dip) PPP and mgetty (compiled with NO_FAX),
-   XCept.
+   Seyon, minicom, CSLIP (uri-dip) PPP, mgetty, XCept and Hylafax. 
 
    The modem-emulation supports the following:
            1.3.1 Commands:
@@ -124,6 +123,10 @@
                AT&D3    Same as AT&D2 but also resets all registers.
                AT&Ex    Set the EAZ/MSN for this channel to x.
                AT&F     Reset all registers and profile to "factory-defaults"
+               AT&Lx    Set list of phone numbers to listen on.  x is a
+                        list of wildcard patterns separated by semicolon.
+                        If this is set, it has precedence over the MSN set
+                        by AT&E.
                AT&Rx    Select V.110 bitrate adaption.
                         This command enables V.110 protocol with 9600 baud
                         (x=9600), 19200 baud (x=19200) or 38400 baud
@@ -238,7 +241,8 @@
              15   0         Layer-3 protocol:
                                       0 = transparent
                                       1 = transparent with audio features (e.g. DSP)
-                                      2 = Fax G3 (S14 has to be set to 11)
+                                      2 = Fax G3 Class 2 commands (S14 has to be set to 11)
+                                      2 = Fax G3 Class 1 commands (S14 has to be set to 11)
              16   250       Send-Packet-size/16
              17   8         Window-size (not yet implemented)
              18   4         Bit coded register, Service-Octet-1 to accept,
@@ -309,8 +313,6 @@
   If an incoming call matches one network interface, it gets connected to it.
   If another incoming call for the same EAZ arrives, which does not match
   a network interface, the first tty gets a "RING" and so on.
-  As soon as voice gets supported (with the availability of the Diehl-driver),
-  the service-identifier will be evaluated in addition.
 
 2 System prerequisites:
 
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- v2.3.45/linux/Documentation/isdn/README.HiSax	Thu Nov 11 20:11:31 1999
+++ linux/Documentation/isdn/README.HiSax	Tue Feb 15 11:40:42 2000
@@ -64,6 +64,7 @@
 Gazel ISDN cards
 HFC-PCI based cards
 Winbond W6692 based cards
+HFC-S+, HFC-SP/PCMCIA cards
 
 Note: PCF, PCF-Pro: up to now, only the ISDN part is supported
       PCC-8: not tested yet
@@ -72,6 +73,7 @@
       Teles PCI is EXPERIMENTAL
       Teles S0Box is EXPERIMENTAL
       Eicon.Diehl Diva U interface not tested
+      HFC-S+, HFC-SP/PCMCIA are experimental
 
 If you know other passive cards with the Siemens chipset, please let me know.
 To use the PNP cards you need the isapnptools.
@@ -184,6 +186,7 @@
    34	Gazel ISDN cards (PCI)   none
    35	HFC 2BDS0 PCI            none
    36	W6692 based PCI cards    none
+   37   HFC 2BDS0 S+, SP/PCMCIA  irq,io (pcmcia must be set with cardmgr)
 
 
 At the moment IRQ sharing is only possible with PCI cards. Please make sure
@@ -288,6 +291,7 @@
    34	Gazel ISDN cards (PCI)  no parameter
    35	HFC 2BDS0 PCI           no parameter
    36	W6692 based PCI cards   none
+   37   HFC 2BDS0 S+,SP/PCMCIA  pa=irq, pb=io
 
 Running the driver
 ------------------
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/README.eicon linux/Documentation/isdn/README.eicon
--- v2.3.45/linux/Documentation/isdn/README.eicon	Thu Nov 11 20:11:31 1999
+++ linux/Documentation/isdn/README.eicon	Tue Feb 15 11:40:42 2000
@@ -1,9 +1,10 @@
-$Id: README.eicon,v 1.5 1999/10/11 18:13:25 armin Exp $
+$Id: README.eicon,v 1.6 2000/01/27 09:54:44 armin Exp $
 
-(c) 1999 Cytronics & Melware (info@melware.de)
+(c) 1999,2000 Armin Schindler (mac@melware.de)
+(c) 1999,2000 Cytronics & Melware (info@melware.de)
 
 This document describes the eicon driver for the
-Eicon.Diehl active ISDN cards.
+Eicon active ISDN cards.
 
 It is meant to be used with isdn4linux, an ISDN link-level module for Linux.
 
@@ -50,7 +51,8 @@
 
 - ETSI (Euro-DSS1) 
 - 1TR6 (German ISDN) *not testet*
-
+- other protocols exist for the range of DIVA Server cards,
+  but they are not fully testet yet.
 
 
 You can load the module simply by using the insmod or modprobe function :
@@ -58,7 +60,7 @@
   insmod eicon [id=driverid] [membase=<membase>] [irq=<irq>]
 
 
-The module will automatically probe the PCI-cards. If the id-options
+The module will automatically probe the PCI-cards. If the id-option
 is omitted, the driver will assume 'eicon0' for the first pci card and
 increases the digit with each further card. With a given driver-id
 the module appends a number starting with '0'.
@@ -84,6 +86,14 @@
 
 Details about using the eiconctrl utility are in 'man eiconctrl'
 or will be printed by starting eiconctrl without any parameters.
+
+ISDNLOG:
+With eicon driver version 1.77 or newer and the eiconctrl utility
+of version 1.1 or better, you can use the isdnlog user program
+with your DIVA Server BRI card.
+Just use "eiconctrl isdnlog on" and the driver will generate
+the necessary D-Channel traces for isdnlog.
+
 
 Thanks to 
 	Deutsche Mailbox Saar-Lor-Lux GmbH
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/README.hysdn linux/Documentation/isdn/README.hysdn
--- v2.3.45/linux/Documentation/isdn/README.hysdn	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.hysdn	Tue Feb 15 11:40:42 2000
@@ -0,0 +1,177 @@
+$Id: README.hysdn,v 1.1 2000/02/10 19:46:15 werner Exp $
+The hysdn driver has been written by
+by Werner Cornelius (werner@isdn4linux.de or werner@titro.de) 
+for Hypercope GmbH Aachen Germany. Hypercope agreed to publish this driver
+under the GNU Public License.
+
+    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.
+
+Table of contents
+=================
+
+1. About the driver
+
+2. Loading/Unloading the driver
+
+3. Entries in the /proc filesystem
+
+4. The /proc/net/hysdn/cardconfX file
+
+5. The /proc/net/hysdn/cardlogX file
+
+6. Where to get additional info and help
+
+
+1. About the driver
+
+   The drivers/isdn/hysdn subdir contains a driver for HYPERCOPEs active 
+   PCI isdn cards Champ, Ergo and Metro. To enable support for this cards
+   enable ISDN support in the kernel config and support for HYSDN cards in
+   the active cards submenu. The driver may only be compiled and used if 
+   support for loadable modules and the process filesystem have been enabled.
+   No other ISDN options need to be enabled for these cards.
+
+   Up to now these cards do not use or require the standard isdn interface
+   module (isdn.o), but registers itself as an ethernet card. All necessary
+   handlers for various protocols like ppp and others as well as config info
+   and firmware may be fetched from Hypercopes WWW-Site www.hypercope.de.
+   The driver has been included in the i4l tree as a CAPI compliant module
+   is under development and will be connected to the standard i4l modules
+   additionally.
+
+2. Loading/Unloading the driver
+
+   The module has no command line parameters and auto detects up to 10 cards
+   in the id-range 0-9.
+   If a loaded driver shall be unloaded all open files in the /proc/net/hysdn
+   subdir need to be closed and all ethernet interfaces allocated by this 
+   driver must be shut down. Otherwise the module counter will avoid a module
+   unload. 
+
+3. Entries in the /proc filesystem
+
+   When the module has been loaded it adds the directory hysdn in the 
+   /proc/net tree. This directory contains exactly 2 file entries for each 
+   card. One is called cardconfX and the other cardlogX, where X is the
+   card id number from 0 to 9. 
+   The cards are numbered in the order found in the PCI config data.
+
+4. The /proc/net/hysdn/cardconfX file
+
+   This file may be read to get by everyone to get info about the cards type, 
+   actual state, available features and used resources.
+   The first 3 entries (id, bus and slot) are PCI info fields, the following
+   type field gives the information about the cards type:
+
+   4 -> Ergo card (server card with 2 b-chans)
+   5 -> Metro card (server card with 4 or 8 b-chans)
+   6 -> Champ card (client card with 2 b-chans)   
+
+   The following 3 fields show the hardware assignments for irq, iobase and the
+   dual ported memory (dp-mem).
+   The fields b-chans and fax-chans announce the available card resources of
+   this types for the user.
+   The state variable indicates the actual drivers state for this card with the
+   following assignments.
+
+   0 -> card has not been booted since driver load
+   1 -> card booting is actually in progess
+   2 -> card is in an error state due to a previous boot failure
+   3 -> card is booted and active
+
+   And the last field (device) shows the name of the ethernet device assigned
+   to this card. Up to the first successfull boot this field only shows a -
+   to tell that no net device has been allocated up to now. Once a net device
+   has been allocated it remains assigned to this card, even if a card is
+   rebooted and an boot error occurs. 
+
+   Writing to the cardconfX file boots the card or transfers config lines to 
+   the cards firmware. The type of data is automatically detected when the 
+   first data is written. Only root has write access to this file.
+   The firmware boot files are normally called hyclient.pof for client cards
+   and hyserver.pof for server cards.
+   After successfully writing the boot file, complete config files or single
+   config lines may be copied to this file.
+   If an error occurs the return value given to the writing process has the 
+   following additional codes (decimal):
+
+   1000 Another process is currently bootng the card
+   1001 Invalid firmware header
+   1002 Boards dual-port RAM test failed
+   1003 Internal firmware handler error
+   1004 Boot image size invalid
+   1005 First boot stage (bootstrap loader) failed
+   1006 Second boot stage failure
+   1007 Timeout waiting for card ready during boot
+   1008 Operation only allowed in booted state
+   1009 Config line to long 
+   1010 Invalid channel number 
+   1011 Timeout sending config data
+
+   Additional info about error reasons may be fetched from the log output. 
+
+5. The /proc/net/hysdn/cardlogX file
+   	  
+   The cardlogX file entry may be opened multiple for reading by everyone to 
+   get the cards and drivers log data. Card messages always start with the
+   keyword LOG. All other lines are output from the driver. 
+   The driver log data may be redirected to the syslog by selecting the 
+   approriate bitmask. The cards log messages will always be send to this
+   interface but never to the syslog.
+
+   A root user may write a decimal or hex (with 0x) value t this file to select
+   desired output options. As mentioned above the cards log dat is always 
+   written to the cardlog file independant of the following options only used
+   to check and debug the driver itself:
+
+   For example: 
+   echo "0x34560078" > /proc/net/hysdn/cardlog0
+   to output the hex log mask 34560078 for card 0.
+ 
+   The written value is regarded as an unsigned 32-Bit value, bit ored for 
+   desired output. The following bits are already assigned:
+
+   0x80000000   All driver log data is alternatively via syslog 
+   0x00000001   Log memory allocation errors
+   0x00000010   Firmware load start and close are logged
+   0x00000020   Log firmware record parser
+   0x00000040   Log every firmware write actions
+   0x00000080   Log all card related boot messages
+   0x00000100   Output all config data sent for debugging purposes
+   0x00000200   Only non comment config lines are shown wth channel
+   0x00000400   Additional conf log output
+   0x00001000   Log the asynchronous scheduler actions (config and log)
+   0x00100000   Log all open and close actions to /proc/net/hysdn/card files
+   0x00200000   Log all actions from /proc file entries
+   0x00010000   Log network interface init and deinit
+   
+6. Where to get additional info and help
+
+   If you have any problems concerning the driver or configuration contact 
+   the Hypercope support team (www.hypercope.de) and or the author 
+   Werner Cornelius (werner@isdn4linux or cornelius@titro.de)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.3.45/linux/Documentation/isdn/README.x25 linux/Documentation/isdn/README.x25
--- v2.3.45/linux/Documentation/isdn/README.x25	Sun May 23 10:03:41 1999
+++ linux/Documentation/isdn/README.x25	Tue Feb 15 11:40:42 2000
@@ -61,7 +61,21 @@
 
 - You might want to access a public X.25 data network from your Linux box.
   You can use i4l if you were physically connected to the X.25 switch
-  by an ISDN line (leased line as well as dial up connection should work)
+  by an ISDN B-channel (leased line as well as dial up connection should
+  work).
+
+  This corresponds to ITU-T recommendation X.31 Case A (circuit-mode
+  access to PSPDN [packet switched public data network]).
+
+  NOTE: X.31 also covers a Case B (access to PSPDN via virtual
+  circuit / packet mode service). The latter mode (which in theory
+  also allows using the D-channel) is not supported by isdn4linux.
+  It should however be possible to establish such packet mode connections
+  with certain active isdn cards provided that the firmware supports X.31
+  and the driver exports this functionality to the user. Currently, 
+  the AVM B1 driver is the only driver which does so. (It should be
+  possible to access D-channel X.31 with active AVM cards using the
+  CAPI interface of the AVM-B1 driver).
 
 - Or you might want to operate certain ISDN teleservices on your linux
   box. A lot of those teleservices run on top of the ISO-8208
diff -u --recursive --new-file v2.3.45/linux/Documentation/usb/usb-serial.txt linux/Documentation/usb/usb-serial.txt
--- v2.3.45/linux/Documentation/usb/usb-serial.txt	Fri Jan 28 15:09:06 2000
+++ linux/Documentation/usb/usb-serial.txt	Tue Feb 15 11:37:34 2000
@@ -67,20 +67,6 @@
   http://usbvisor.sourceforge.net/
 
 
-Belkin single port serial converter
-Peracom single port serial converter
-
-Current status:
-  The driver can handle enumerating the device, and sending and receiving
-  data from the converter. However, since I do not have a spec for the
-  Belkin, Peracom, and eTek devices, and the raw dumps from the Win98
-  driver are confusing, and eTek refuses to provide documentation on their
-  protocol, no control signals are currently handled, and the data will
-  most likely come through on a baud rate that you are not expecting. So
-  if you have these devices, do not expect the correct data to show up at 
-  either end. 
-  
-
 Generic Serial driver
 
   If your device is not one of the above listed devices, compatible with
diff -u --recursive --new-file v2.3.45/linux/MAINTAINERS linux/MAINTAINERS
--- v2.3.45/linux/MAINTAINERS	Sat Feb 12 11:22:10 2000
+++ linux/MAINTAINERS	Wed Feb 16 15:42:05 2000
@@ -274,6 +274,12 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+DEVICE FILESYSTEM
+P:	Richard Gooch
+M:	rgooch@atnf.csiro.au
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 DIGI INTL. EPCA DRIVER
 P:	 Chad Schwartz
 M:      support@dgii.com
@@ -494,6 +500,11 @@
 INTEL APIC/IOAPIC, LOWLEVEL X86 SMP SUPPORT
 P:	Ingo Molnar
 M:	mingo@redhat.com
+S:	Maintained
+
+INTEL P6 MICROCODE UPDATE SUPPORT
+P:	Tigran Aivazian
+M:	tigran@sco.com
 S:	Maintained
 
 IP MASQUERADING:
diff -u --recursive --new-file v2.3.45/linux/Makefile linux/Makefile
--- v2.3.45/linux/Makefile	Sun Feb 13 19:29:03 2000
+++ linux/Makefile	Tue Feb 15 11:37:33 2000
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 45
+SUBLEVEL = 46
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
@@ -405,6 +405,7 @@
 	if [ -f PCMCIA_MODULES ]; then inst_mod PCMCIA_MODULES pcmcia; fi; \
 	if [ -f PCMCIA_NET_MODULES ]; then inst_mod PCMCIA_NET_MODULES pcmcia; fi; \
 	if [ -f PCMCIA_CHAR_MODULES ]; then inst_mod PCMCIA_CHAR_MODULES pcmcia; fi; \
+	if [ -f PCMCIA_SCSI_MODULES ]; then inst_mod PCMCIA_SCSI_MODULES pcmcia; fi; \
 	\
 	ls -1 -U *.o | sort > $$MODLIB/.allmods; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \
diff -u --recursive --new-file v2.3.45/linux/arch/alpha/kernel/pci_iommu.c linux/arch/alpha/kernel/pci_iommu.c
--- v2.3.45/linux/arch/alpha/kernel/pci_iommu.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/alpha/kernel/pci_iommu.c	Mon Feb 14 15:34:21 2000
@@ -39,20 +39,6 @@
 {
 	return (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
 }
-
-static inline long
-calc_order(long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
 
 struct pci_iommu_arena *
 iommu_arena_new(dma_addr_t base, unsigned long window_size,
@@ -248,7 +234,7 @@
 pci_alloc_consistent(struct pci_dev *pdev, long size, dma_addr_t *dma_addrp)
 {
 	void *cpu_addr;
-	long order = calc_order(size);
+	long order = get_order(size);
 
 	cpu_addr = (void *)__get_free_pages(GFP_ATOMIC, order);
 	if (! cpu_addr) {
@@ -285,7 +271,7 @@
 		    dma_addr_t dma_addr)
 {
 	pci_unmap_single(pdev, dma_addr, size);
-	free_pages((unsigned long)cpu_addr, calc_order(size));
+	free_pages((unsigned long)cpu_addr, get_order(size));
 
 	DBGA2("pci_free_consistent: [%x,%lx] from %p\n",
 	      dma_addr, size, __builtin_return_address(0));
diff -u --recursive --new-file v2.3.45/linux/arch/arm/mm/consistent.c linux/arch/arm/mm/consistent.c
--- v2.3.45/linux/arch/arm/mm/consistent.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/arm/mm/consistent.c	Mon Feb 14 15:34:21 2000
@@ -12,20 +12,6 @@
 #include <asm/io.h>
 #include <asm/pgalloc.h>
 
-/* Pure 2^n version of get_order */
-extern __inline__ int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 /*
  * This allocates one page of cache-coherent memory space and returns
  * both the virtual and a "dma" address to that space.  It is not clear
@@ -43,7 +29,7 @@
 	if (in_interrupt())
 		BUG();
 
-	order = __get_order(size);
+	order = get_order(size);
 
 	page = __get_free_pages(gfp, order);
 	if (!page)
diff -u --recursive --new-file v2.3.45/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.3.45/linux/arch/i386/config.in	Tue Feb  1 01:35:43 2000
+++ linux/arch/i386/config.in	Wed Feb 16 14:36:52 2000
@@ -49,6 +49,10 @@
    define_bool CONFIG_X86_USE_3DNOW y
 fi
 
+if [ "$CONFIG_M686" = "y" -a "$CONFIG_PROC_FS" = "y" ]; then
+   tristate '/proc/driver/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE
+fi
+
 choice 'High Memory Support' \
 	"off    CONFIG_NOHIGHMEM \
 	4GB    CONFIG_HIGHMEM4G \
diff -u --recursive --new-file v2.3.45/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.45/linux/arch/i386/defconfig	Sun Feb 13 19:29:03 2000
+++ linux/arch/i386/defconfig	Wed Feb 16 15:45:47 2000
@@ -27,6 +27,7 @@
 CONFIG_X86_TSC=y
 CONFIG_X86_GOOD_APIC=y
 CONFIG_X86_PGE=y
+# CONFIG_MICROCODE is not set
 CONFIG_NOHIGHMEM=y
 # CONFIG_HIGHMEM4G is not set
 # CONFIG_HIGHMEM64G is not set
@@ -175,7 +176,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
 
@@ -238,6 +238,11 @@
 # CONFIG_SCSI_ULTRASTOR is not set
 
 #
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
 # I2O device support
 #
 # CONFIG_I2O is not set
@@ -324,7 +329,6 @@
 # CONFIG_ARCNET_COM20020_CS is not set
 # CONFIG_PCMCIA_3C575 is not set
 # CONFIG_PCMCIA_TULIP is not set
-# CONFIG_PCMCIA_EPIC100 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.45/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v2.3.45/linux/arch/i386/kernel/Makefile	Thu Feb 10 17:11:03 2000
+++ linux/arch/i386/kernel/Makefile	Wed Feb 16 14:36:52 2000
@@ -40,6 +40,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_PROC_FS),y)
+ifeq ($(CONFIG_MICROCODE),y)
+OX_OBJS += microcode.o
+else
+  ifeq ($(CONFIG_MICROCODE),m)
+  MX_OBJS += microcode.o
+  endif
+endif
+endif
+
 ifeq ($(CONFIG_ACPI),y)
 O_OBJS += acpi.o
 else
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/acpi.c linux/arch/i386/kernel/acpi.c
--- v2.3.45/linux/arch/i386/kernel/acpi.c	Thu Feb 10 17:11:03 2000
+++ linux/arch/i386/kernel/acpi.c	Mon Feb 14 13:37:08 2000
@@ -643,6 +643,7 @@
 	{NULL,},
 	{acpi_init_piix4},
 	{acpi_init_via},
+	{acpi_init_via},
 };
 	
 const static struct pci_device_id acpi_pci_tbl[] =
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- v2.3.45/linux/arch/i386/kernel/apm.c	Thu Feb 10 17:11:03 2000
+++ linux/arch/i386/kernel/apm.c	Wed Feb 16 15:42:05 2000
@@ -1394,7 +1394,7 @@
 
 static struct miscdevice apm_device = {
 	APM_MINOR_DEV,
-	"apm",
+	"apm_bios",
 	&apm_bios_fops
 };
 
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.3.45/linux/arch/i386/kernel/io_apic.c	Sat Feb 12 11:22:10 2000
+++ linux/arch/i386/kernel/io_apic.c	Mon Feb 14 10:31:00 2000
@@ -1368,7 +1368,6 @@
 {
 	if (!smp_found_config)
 		return;
-	phys_cpu_present_map = 0xff;
 	setup_local_APIC();
 	setup_IO_APIC();
 	setup_APIC_clocks();
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/microcode.c linux/arch/i386/kernel/microcode.c
--- v2.3.45/linux/arch/i386/kernel/microcode.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/kernel/microcode.c	Wed Feb 16 14:39:53 2000
@@ -0,0 +1,205 @@
+/*
+ *	CPU Microcode Update interface for Linux
+ *
+ *	Copyright (C) 2000 Tigran Aivazian
+ *
+ *	This driver allows to upgrade microcode on Intel processors
+ *	belonging to P6 family - PentiumPro, Pentium II, Pentium III etc.
+ *
+ *	Reference: Section 8.10 of Volume III, Intel Pentium III Manual, 
+ *	Order Number 243192 or download from:
+ *		
+ *	http://developer.intel.com/design/pentiumii/manuals/243192.htm
+ *
+ *	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.
+ *
+ *	1.0	16 February 2000, Tigran Aivazian <tigran@sco.com>
+ *		Initial release.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+
+#define MICROCODE_VERSION 	"1.0"
+
+MODULE_DESCRIPTION("CPU (P6) microcode update driver");
+MODULE_AUTHOR("Tigran Aivazian <tigran@ocston.org>");
+EXPORT_NO_SYMBOLS;
+
+/* VFS interface */
+static int microcode_open(struct inode *, struct file *);
+static int microcode_release(struct inode *, struct file *);
+static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *);
+
+
+/* internal helpers to do the work */
+static void do_microcode_update(void);
+static void do_update_one(void *);
+
+/*
+ *  Bits in microcode_status. (31 bits of room for future expansion)
+ */
+#define MICROCODE_IS_OPEN	0	/* set if /dev/microcode is in use */
+static unsigned long microcode_status = 0;
+
+/* the actual array of microcode blocks, each 2048 bytes */
+static struct microcode * microcode = NULL;
+static unsigned int microcode_num = 0;
+
+static struct file_operations microcode_fops = {
+	write:		microcode_write,
+	open:		microcode_open,
+	release:	microcode_release,
+};
+
+static struct inode_operations microcode_inops = {
+	default_file_ops:	&microcode_fops,
+};
+
+static struct proc_dir_entry *proc_microcode;
+
+static int __init microcode_init(void)
+{
+	/* write-only /proc/driver/microcode file, one day may become read-write.. */
+	proc_microcode = create_proc_entry("microcode", S_IWUSR, proc_root_driver);
+	if (!proc_microcode) {
+		printk(KERN_ERR "microcode: can't create /proc/driver/microcode entry\n");
+		return -ENOMEM;
+	}
+	proc_microcode->ops = &microcode_inops;
+	printk(KERN_ERR "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION);
+	return 0;
+}
+
+static void __exit microcode_exit(void)
+{
+	remove_proc_entry("microcode", proc_root_driver);
+	printk(KERN_ERR "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION);
+}
+
+module_init(microcode_init);
+module_exit(microcode_exit);
+
+/*
+ * We enforce only one user at a time here with open/close.
+ */
+static int microcode_open(struct inode *inode, struct file *file)
+{
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	/* one at a time, please */
+	if (test_and_set_bit(MICROCODE_IS_OPEN, &microcode_status))
+		return -EBUSY;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int microcode_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+
+	clear_bit(MICROCODE_IS_OPEN, &microcode_status);
+	return 0;
+}
+
+
+static void do_microcode_update(void)
+{
+	if (smp_call_function(do_update_one, NULL, 1, 0) != 0)
+		panic("do_microcode_update(): timed out waiting for other CPUs\n");
+	do_update_one(NULL);
+}
+
+static void do_update_one(void *unused)
+{
+	struct cpuinfo_x86 * c;
+	unsigned int pf = 0, val[2], rev, sig;
+	int i, id;
+
+	id = smp_processor_id();
+	c = cpu_data + id;
+
+
+	if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6)
+		return;
+
+	sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8);
+
+	if (c->x86_model >= 5) {
+		/* get processor flags from BBL_CR_OVRD MSR (0x17) */
+		rdmsr(0x17, val[0], val[1]);
+		pf = 1 << ((val[1] >> 18) & 7);
+	}
+
+	for (i=0; i<microcode_num; i++)
+		if (microcode[i].sig == sig && microcode[i].pf == pf &&
+		    microcode[i].ldrver == 1 && microcode[i].hdrver == 1) {
+
+			rdmsr(0x8B, val[0], rev);
+			if (microcode[i].rev <= rev) {
+				printk(KERN_ERR 
+					"microcode: CPU%d not 'upgrading' to earlier revision"
+					" %d (current=%d)\n", id, microcode[i].rev, rev);
+			} else { 
+				int sum = 0;
+				struct microcode *m = &microcode[i];
+				unsigned int *sump = (unsigned int *)(m+1);
+
+				while (--sump >= (unsigned int *)m)
+					sum += *sump;
+				if (sum != 0) {
+					printk(KERN_ERR "microcode: CPU%d aborting, "
+							"bad checksum\n", id);
+					break;
+				}
+				wrmsr(0x79, (unsigned int)(m->bits), 0);
+				__asm__ __volatile__ ("cpuid");
+				rdmsr(0x8B, val[0], val[1]);
+				printk(KERN_ERR "microcode: CPU%d microcode updated "
+						"from revision %d to %d\n", id, rev, val[1]);
+			}
+			break;
+		}
+}
+
+static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
+{
+	if (len % sizeof(struct microcode) != 0) {
+		printk(KERN_ERR "microcode: can only write in N*%d bytes units\n", 
+			sizeof(struct microcode));
+		return -EINVAL;
+		return -EINVAL;
+	}
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+	lock_kernel();
+	microcode_num = len/sizeof(struct microcode);
+	microcode = vmalloc(len);
+	if (!microcode) {
+		unlock_kernel();
+		return -ENOMEM;
+	}
+	if (copy_from_user(microcode, buf, len)) {
+		vfree(microcode);
+		unlock_kernel();
+		return -EFAULT;
+	}
+	do_microcode_update();
+	vfree(microcode);
+	unlock_kernel();
+	return len;
+}
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.3.45/linux/arch/i386/kernel/mtrr.c	Thu Feb 10 17:11:03 2000
+++ linux/arch/i386/kernel/mtrr.c	Wed Feb 16 15:42:05 2000
@@ -133,7 +133,7 @@
 	       Fixed version numbering and history for v1.23 -> v1.24.
   v1.26
     19990118   Richard Gooch <rgooch@atnf.csiro.au>
-	       PLACEHOLDER.
+	       Added devfs support.
   v1.27
     19990123   Richard Gooch <rgooch@atnf.csiro.au>
 	       Changed locking to spin with reschedule.
@@ -178,7 +178,6 @@
                Moved to linux/arch/i386/kernel/setup.c and
                linux/include/asm-i386/bugs.h
     19990228   Richard Gooch <rgooch@atnf.csiro.au>
-	       Added #ifdef CONFIG_DEVFS_FS
 	       Added MTRRIOC_KILL_ENTRY ioctl(2)
 	       Trap for counter underflow in <mtrr_file_del>.
 	       Trap for 4 MiB aligned regions for PPro, stepping <= 7.
@@ -241,6 +240,7 @@
 #include <linux/fs.h>
 #include <linux/ctype.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #define MTRR_NEED_STRINGS
@@ -305,11 +305,15 @@
 						       TRUE)
 #endif
 
-#ifndef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS)
+# define USERSPACE_INTERFACE
+#endif
+
+#ifndef USERSPACE_INTERFACE
 #  define compute_ascii() while (0)
 #endif
 
-#ifdef CONFIG_PROC_FS
+#ifdef USERSPACE_INTERFACE
 static char *ascii_buffer = NULL;
 static unsigned int ascii_buf_bytes = 0;
 #endif
@@ -317,7 +321,7 @@
 static DECLARE_MUTEX(main_lock);
 
 /*  Private functions  */
-#ifdef CONFIG_PROC_FS
+#ifdef USERSPACE_INTERFACE
 static void compute_ascii (void);
 #endif
 
@@ -1010,7 +1014,7 @@
 	return;
     }
     for (i = 0; i < max; i++) usage_table[i] = 1;
-#ifdef CONFIG_PROC_FS
+#ifdef USERSPACE_INTERFACE
     if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL )
     {
 	printk ("mtrr: could not allocate\n");
@@ -1286,7 +1290,7 @@
     return reg;
 }   /*  End Function mtrr_del  */
 
-#ifdef CONFIG_PROC_FS
+#ifdef USERSPACE_INTERFACE
 
 static int mtrr_file_add (unsigned long base, unsigned long size,
 			  unsigned int type, char increment, struct file *file)
@@ -1475,12 +1479,18 @@
 	 release:	mtrr_close,
 };
 
+#  ifdef CONFIG_PROC_FS
+
 static struct inode_operations proc_mtrr_inode_operations = {
 	&mtrr_fops,             /* default property file-ops */
 };
 
 static struct proc_dir_entry *proc_root_mtrr;
 
+#  endif  /*  CONFIG_PROC_FS  */
+
+static devfs_handle_t devfs_handle = NULL;
+
 static void compute_ascii (void)
 {
     char factor;
@@ -1515,10 +1525,13 @@
 	    ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes);
 	}
     }
+    devfs_set_file_size (devfs_handle, ascii_buf_bytes);
+#  ifdef CONFIG_PROC_FS
     proc_root_mtrr->size = ascii_buf_bytes;
+#  endif  /*  CONFIG_PROC_FS  */
 }   /*  End Function compute_ascii  */
 
-#endif  /*  CONFIG_PROC_FS  */
+#endif  /*  USERSPACE_INTERFACE  */
 
 EXPORT_SYMBOL(mtrr_add);
 EXPORT_SYMBOL(mtrr_del);
@@ -1786,9 +1799,12 @@
 #  endif  /*  !__SMP__  */
 
 #  ifdef CONFIG_PROC_FS
-    proc_root_mtrr = create_proc_entry("mtrr", S_IWUSR|S_IRUGO, &proc_root);
+    proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root);
     proc_root_mtrr->ops = &proc_mtrr_inode_operations;
 #endif    
+    devfs_handle = devfs_register (NULL, "cpu/mtrr", 0, DEVFS_FL_DEFAULT, 0, 0,
+				   S_IFREG | S_IRUGO | S_IWUSR, 0, 0,
+				   &mtrr_fops, NULL);
     init_table ();
     return 0;
 }   /*  End Function mtrr_init  */
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/pci-dma.c linux/arch/i386/kernel/pci-dma.c
--- v2.3.45/linux/arch/i386/kernel/pci-dma.c	Fri Jan 28 15:09:06 2000
+++ linux/arch/i386/kernel/pci-dma.c	Mon Feb 14 15:34:21 2000
@@ -13,20 +13,6 @@
 #include <linux/pci.h>
 #include <asm/io.h>
 
-/* Pure 2^n version of get_order */
-extern __inline__ int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
 			   dma_addr_t *dma_handle)
 {
@@ -35,7 +21,7 @@
 
 	if (hwdev == NULL || hwdev->dma_mask != 0xffffffff)
 		gfp |= GFP_DMA;
-	ret = (void *)__get_free_pages(gfp, __get_order(size));
+	ret = (void *)__get_free_pages(gfp, get_order(size));
 
 	if (ret != NULL) {
 		memset(ret, 0, size);
@@ -47,5 +33,5 @@
 void pci_free_consistent(struct pci_dev *hwdev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle)
 {
-	free_pages((unsigned long)vaddr, __get_order(size));
+	free_pages((unsigned long)vaddr, get_order(size));
 }
diff -u --recursive --new-file v2.3.45/linux/arch/i386/kernel/pci-i386.c linux/arch/i386/kernel/pci-i386.c
--- v2.3.45/linux/arch/i386/kernel/pci-i386.c	Fri Jan 21 18:19:16 2000
+++ linux/arch/i386/kernel/pci-i386.c	Tue Feb 15 08:50:03 2000
@@ -132,7 +132,7 @@
 		/* We need to avoid collisions with `mirrored' VGA ports
 		   and other strange ISA hardware, so we always want the
 		   addresses kilobyte aligned.  */
-		if (size >= 0x100) {
+		if (size > 0x100) {
 			printk(KERN_ERR "PCI: I/O Region %s/%d too large"
 			       " (%ld bytes)\n", dev->slot_name,
 			       dev->resource - res, size);
diff -u --recursive --new-file v2.3.45/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.3.45/linux/arch/i386/mm/init.c	Thu Feb 10 17:11:03 2000
+++ linux/arch/i386/mm/init.c	Mon Feb 14 10:31:00 2000
@@ -307,27 +307,30 @@
 	pmd_t *pmd;
 	pte_t *pte;
 	int i, j, k;
-	unsigned long vaddr;
-	unsigned long end = (unsigned long)__va(max_low_pfn*PAGE_SIZE);
+	unsigned long vaddr, end;
 
-	pgd_base = swapper_pg_dir;
+	end = (unsigned long)__va(max_low_pfn*PAGE_SIZE) - 1;
 
-	vaddr = PAGE_OFFSET;
-	i = __pgd_offset(vaddr);
+	i = __pgd_offset(PAGE_OFFSET);
+	pgd_base = swapper_pg_dir;
 	pgd = pgd_base + i;
 
-	for (; (i < PTRS_PER_PGD) && (vaddr <= end); pgd++, i++) {
+	for (; i < PTRS_PER_PGD; pgd++, i++) {
 		vaddr = i*PGDIR_SIZE;
+		if (vaddr >= end)
+			break;
 #if CONFIG_X86_PAE
-		pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
+		pmd = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE);
 		set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
 #else
 		pmd = (pmd_t *)pgd;
 #endif
 		if (pmd != pmd_offset(pgd, 0))
 			BUG();
-		for (j = 0; (j < PTRS_PER_PMD) && (vaddr <= end); pmd++, j++) {
+		for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {
 			vaddr = i*PGDIR_SIZE + j*PMD_SIZE;
+			if (vaddr >= end)
+				break;
 			if (cpu_has_pse) {
 				unsigned long __pe;
 
@@ -349,10 +352,10 @@
 			if (pte != pte_offset(pmd, 0))
 				BUG();
 
-			for (k = 0;
-				(k < PTRS_PER_PTE) && (vaddr <= end);
-					pte++, k++) {
+			for (k = 0; k < PTRS_PER_PTE; pte++, k++) {
 				vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
+				if (vaddr >= end)
+					break;
 				*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
 			}
 		}
diff -u --recursive --new-file v2.3.45/linux/arch/ia64/kernel/pci-dma.c linux/arch/ia64/kernel/pci-dma.c
--- v2.3.45/linux/arch/ia64/kernel/pci-dma.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/ia64/kernel/pci-dma.c	Mon Feb 14 15:34:21 2000
@@ -17,21 +17,6 @@
 
 #include <asm/io.h>
 
-/* Pure 2^n version of get_order */
-extern __inline__ unsigned long
-get_order (unsigned long size)
-{
-	unsigned long order = ia64_fls(size - 1) + 1;
-
-	printk ("get_order: size=%lu, order=%lu\n", size, order);
-
-	if (order > PAGE_SHIFT)
-		order -= PAGE_SHIFT;
-	else
-		order = 0;
-	return order;
-}
-
 void *
 pci_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle)
 {
diff -u --recursive --new-file v2.3.45/linux/arch/m68k/atari/joystick.c linux/arch/m68k/atari/joystick.c
--- v2.3.45/linux/arch/m68k/atari/joystick.c	Thu Feb 10 17:11:03 2000
+++ linux/arch/m68k/atari/joystick.c	Wed Feb 16 15:42:05 2000
@@ -12,6 +12,7 @@
 #include <linux/major.h>
 #include <linux/poll.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/atarikb.h>
 #include <asm/atari_joystick.h>
@@ -132,8 +133,11 @@
     init_waitqueue_head(&joystick[0].wait);
     init_waitqueue_head(&joystick[1].wait);
 
-    if (register_chrdev(MAJOR_NR, "Joystick", &atari_joystick_fops))
+    if (devfs_register_chrdev(MAJOR_NR, "Joystick", &atari_joystick_fops))
 	printk("unable to get major %d for joystick devices\n", MAJOR_NR);
+    devfs_register_series (NULL, "joysticks/digital%u", 2, DEVFS_FL_DEFAULT,
+			   MAJOR_NR, 128, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			   &atari_joystick_fops, NULL);
 
     return 0;
 }
diff -u --recursive --new-file v2.3.45/linux/arch/m68k/atari/stram.c linux/arch/m68k/atari/stram.c
--- v2.3.45/linux/arch/m68k/atari/stram.c	Fri Jan 28 15:09:07 2000
+++ linux/arch/m68k/atari/stram.c	Wed Feb 16 10:56:44 2000
@@ -1168,7 +1168,7 @@
 {
 	unsigned long start, len;
 
-	while( CURRENT ) {
+	while( !QUEUE_EMPTY ) {
 		if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
 			panic("stram: request list destroyed");
 		if (CURRENT->bh) {
diff -u --recursive --new-file v2.3.45/linux/arch/m68k/mac/baboon.c linux/arch/m68k/mac/baboon.c
--- v2.3.45/linux/arch/m68k/mac/baboon.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/m68k/mac/baboon.c	Tue Feb 15 22:39:01 2000
@@ -6,7 +6,6 @@
  * Nubus slot $C interrupt.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.3.45/linux/arch/m68k/mac/misc.c linux/arch/m68k/mac/misc.c
--- v2.3.45/linux/arch/m68k/mac/misc.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/m68k/mac/misc.c	Tue Feb 15 22:39:01 2000
@@ -3,6 +3,7 @@
  */
 
 #include <stdarg.h>
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/miscdevice.h>
@@ -27,7 +28,6 @@
 #include <asm/mac_oss.h>
 
 #define BOOTINFO_COMPAT_1_0
-#include <asm/setup.h>
 #include <asm/bootinfo.h>
 #include <asm/machdep.h>
 
diff -u --recursive --new-file v2.3.45/linux/arch/mips/jazz/floppy-jazz.c linux/arch/mips/jazz/floppy-jazz.c
--- v2.3.45/linux/arch/mips/jazz/floppy-jazz.c	Fri Jun 25 17:40:12 1999
+++ linux/arch/mips/jazz/floppy-jazz.c	Mon Feb 14 15:34:21 2000
@@ -94,26 +94,11 @@
 	return JAZZ_FDC_BASE;
 }
 
-/* Pure 2^n version of get_order */
-extern inline int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 static unsigned long jazz_fd_dma_mem_alloc(unsigned long size)
 {
-	int order = __get_order(size);
 	unsigned long mem;
 
-	mem = __get_dma_pages(GFP_KERNEL, order);
+	mem = __get_dma_pages(GFP_KERNEL, get_order(size));
 	if(!mem)
 		return 0;
 	vdma_alloc(PHYSADDR(mem), size);	/* XXX error checking */
@@ -125,7 +110,7 @@
                                         unsigned long size)
 {       
 	vdma_free(vdma_phys2log(PHYSADDR(addr)));
-	free_pages(addr, __get_order(size));	
+	free_pages(addr, get_order(size));	
 }
 
 static unsigned long jazz_fd_drive_type(unsigned long n)
diff -u --recursive --new-file v2.3.45/linux/arch/mips/lib/floppy-std.c linux/arch/mips/lib/floppy-std.c
--- v2.3.45/linux/arch/mips/lib/floppy-std.c	Fri Jun 25 17:40:12 1999
+++ linux/arch/mips/lib/floppy-std.c	Mon Feb 14 15:34:21 2000
@@ -102,33 +102,18 @@
 	return 0x3f0;
 }
 
-/* Pure 2^n version of get_order */
-static int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 static unsigned long std_fd_dma_mem_alloc(unsigned long size)
 {
-	int order = __get_order(size);
 	unsigned long mem;
 
-	mem = __get_dma_pages(GFP_KERNEL,order);
+	mem = __get_dma_pages(GFP_KERNEL,get_order(size));
 
 	return mem;
 }
 
 static void std_fd_dma_mem_free(unsigned long addr, unsigned long size)
 {       
-	free_pages(addr, __get_order(size));	
+	free_pages(addr, get_order(size));	
 }
 
 static unsigned long std_fd_drive_type(unsigned long n)
diff -u --recursive --new-file v2.3.45/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.3.45/linux/arch/ppc/config.in	Thu Feb 10 17:11:04 2000
+++ linux/arch/ppc/config.in	Mon Feb 14 13:37:25 2000
@@ -97,7 +97,7 @@
 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
+if [ "$CONFIG_PREP" = "y" -o "$CONFIG_PMAC" = "y" -o "$CONFIG_CHRP" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
     define_bool CONFIG_PCI y
 fi
 
diff -u --recursive --new-file v2.3.45/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.3.45/linux/arch/ppc/kernel/misc.S	Sun Feb 13 19:29:03 2000
+++ linux/arch/ppc/kernel/misc.S	Mon Feb 14 13:37:25 2000
@@ -229,7 +229,7 @@
        blr
 
 /*
- * Flush a particular page from the DATA cache
+ * Flush a particular page from the data cache to RAM.
  * Note: this is necessary because the instruction cache does *not*
  * snoop from the data cache.
  * This is a no-op on the 601 which has a unified cache.
@@ -241,18 +241,31 @@
 	rlwinm	r5,r5,16,16,31
 	cmpi	0,r5,1
 	beqlr				/* for 601, do nothing */
-	li	r4,0x0FFF
-	andc	r3,r3,r4		/* Get page base address */
 	li	r4,4096/CACHE_LINE_SIZE	/* Number of lines in a page */
 	mtctr	r4
-	mr	r6,r3
 0:	dcbst	0,r3			/* Write line to ram */
 	addi	r3,r3,CACHE_LINE_SIZE
 	bdnz	0b
 	sync
+	blr
+
+/*
+ * Flush a particular page from the instruction cache.
+ * Note: this is necessary because the instruction cache does *not*
+ * snoop from the data cache.
+ * This is a no-op on the 601 which has a unified cache.
+ *
+ *	void __flush_icache_page(void *page)
+ */
+_GLOBAL(__flush_icache_page)
+	mfspr	r5,PVR
+	rlwinm	r5,r5,16,16,31
+	cmpi	0,r5,1
+	beqlr				/* for 601, do nothing */
+	li	r4,4096/CACHE_LINE_SIZE	/* Number of lines in a page */
 	mtctr	r4
-1:	icbi	0,r6
-	addi	r6,r6,CACHE_LINE_SIZE
+1:	icbi	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
 	bdnz	1b
 	sync
 	isync
diff -u --recursive --new-file v2.3.45/linux/arch/ppc/kernel/mk_defs.c linux/arch/ppc/kernel/mk_defs.c
--- v2.3.45/linux/arch/ppc/kernel/mk_defs.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/ppc/kernel/mk_defs.c	Tue Feb 15 22:39:01 2000
@@ -9,6 +9,7 @@
  */
 
 #include <stddef.h>
+#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.3.45/linux/arch/ppc/kernel/pci-dma.c linux/arch/ppc/kernel/pci-dma.c
--- v2.3.45/linux/arch/ppc/kernel/pci-dma.c	Thu Feb 10 17:11:04 2000
+++ linux/arch/ppc/kernel/pci-dma.c	Mon Feb 14 15:34:21 2000
@@ -14,20 +14,6 @@
 #include <linux/pci.h>
 #include <asm/io.h>
 
-/* Pure 2^n version of get_order */
-extern __inline__ int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
 			   dma_addr_t *dma_handle)
 {
@@ -36,7 +22,7 @@
 
 	if (hwdev == NULL || hwdev->dma_mask != 0xffffffff)
 		gfp |= GFP_DMA;
-	ret = (void *)__get_free_pages(gfp, __get_order(size));
+	ret = (void *)__get_free_pages(gfp, get_order(size));
 
 	if (ret != NULL) {
 		memset(ret, 0, size);
@@ -48,5 +34,5 @@
 void pci_free_consistent(struct pci_dev *hwdev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle)
 {
-	free_pages((unsigned long)vaddr, __get_order(size));
+	free_pages((unsigned long)vaddr, get_order(size));
 }
diff -u --recursive --new-file v2.3.45/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- v2.3.45/linux/arch/ppc/kernel/ppc_ksyms.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Mon Feb 14 13:37:25 2000
@@ -11,6 +11,7 @@
 #include <linux/spinlock.h>
 #include <linux/console.h>
 #include <linux/irq.h>
+#include <linux/pci.h>
 
 #include <asm/page.h>
 #include <asm/semaphore.h>
@@ -170,6 +171,11 @@
 EXPORT_SYMBOL(chrp_ide_regbase);
 EXPORT_SYMBOL(chrp_ide_probe);
 #endif
+
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(pci_alloc_consistent);
+EXPORT_SYMBOL(pci_free_consistent);
+#endif /* CONFIG_PCI */
 
 EXPORT_SYMBOL(start_thread);
 EXPORT_SYMBOL(kernel_thread);
diff -u --recursive --new-file v2.3.45/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.3.45/linux/arch/ppc/kernel/process.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/ppc/kernel/process.c	Tue Feb 15 22:39:01 2000
@@ -19,6 +19,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/kernel/ioport.c linux/arch/sparc/kernel/ioport.c
--- v2.3.45/linux/arch/sparc/kernel/ioport.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/sparc/kernel/ioport.c	Mon Feb 14 15:34:21 2000
@@ -44,7 +44,6 @@
 #include <asm/pgtable.h>
 
 struct resource *_sparc_find_resource(struct resource *r, unsigned long);
-int _sparc_len2order(unsigned long len);
 
 static void *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz);
 static void *_sparc_alloc_io(unsigned int busno, unsigned long phys,
@@ -280,7 +279,7 @@
 		return NULL;
 	}
 
-	order = _sparc_len2order(len_total);
+	order = get_order(len_total);
 	va = __get_free_pages(GFP_KERNEL, order);
 	if (va == 0) {
 		/*
@@ -341,7 +340,7 @@
 	pgp = (unsigned long) phys_to_virt(mmu_translate_dvma(ba));
 	mmu_unmap_dma_area(ba, n);
 
-	free_pages(pgp, _sparc_len2order(n));
+	free_pages(pgp, get_order(n));
 }
 
 /*
@@ -482,7 +481,7 @@
 		return NULL;
 	}
 
-	order = _sparc_len2order(len_total);
+	order = get_order(len_total);
 	va = __get_free_pages(GFP_KERNEL, order);
 	if (va == 0) {
 		printk("pci_alloc_consistent: no %ld pages\n", len_total>>PAGE_SHIFT);
@@ -569,7 +568,7 @@
 	release_resource(res);
 	kfree(res);
 
-	free_pages(pgp, _sparc_len2order(n));
+	free_pages(pgp, get_order(n));
 }
 
 /* Map a single buffer of the indicated size for DMA in streaming mode.
@@ -737,19 +736,6 @@
 			return tmp;
 	}
 	return NULL;
-}
-
-int
-_sparc_len2order(unsigned long len)
-{
-	int order;
-
-	for (order = 0; order < 7; order++)	/* 2^6 pages == 256K */
-		if ((1 << (order + PAGE_SHIFT)) >= len)
-			return order;
-	printk("len2order: from %p: len %lu(0x%lx) yields order >=7.\n",
-	    __builtin_return_address(0), len, len);
-	return 1;
 }
 
 /*
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.3.45/linux/arch/sparc/kernel/sparc_ksyms.c	Thu Feb 10 17:11:05 2000
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.89 2000/02/09 11:15:03 davem Exp $
+/* $Id: sparc_ksyms.c,v 1.90 2000/02/13 09:52:54 anton Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -145,11 +145,8 @@
 EXPORT_SYMBOL(global_irq_holder);
 EXPORT_SYMBOL(global_irq_lock);
 EXPORT_SYMBOL(global_bh_lock);
-EXPORT_SYMBOL(global_bh_count);
-EXPORT_SYMBOL(sparc_bh_lock);
 EXPORT_SYMBOL(global_irq_count);
 EXPORT_SYMBOL(synchronize_irq);
-EXPORT_SYMBOL(synchronize_bh);
 #endif
 EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL(local_bh_count);
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c
--- v2.3.45/linux/arch/sparc/kernel/sys_sparc.c	Thu Feb 10 17:11:05 2000
+++ linux/arch/sparc/kernel/sys_sparc.c	Mon Feb 14 15:31:14 2000
@@ -34,12 +34,39 @@
 	return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */
 }
 
+unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
+{
+	struct vm_area_struct * vmm;
+
+	/* See asm-sparc/uaccess.h */
+	if (len > TASK_SIZE - PAGE_SIZE)
+		return 0;
+	if (ARCH_SUN4C_SUN4 && len > 0x20000000)
+		return 0;
+	if (!addr)
+		addr = TASK_UNMAPPED_BASE;
+	addr = PAGE_ALIGN(addr);
+
+	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
+		/* At this point:  (!vmm || addr < vmm->vm_end). */
+		if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) {
+			addr = PAGE_OFFSET;
+			vmm = find_vma(current->mm, PAGE_OFFSET);
+		}
+		if (TASK_SIZE - PAGE_SIZE - len < addr)
+			return 0;
+		if (!vmm || addr + len <= vmm->vm_start)
+			return addr;
+		addr = vmm->vm_end;
+	}
+}
+
 extern asmlinkage unsigned long sys_brk(unsigned long brk);
 
 asmlinkage unsigned long sparc_brk(unsigned long brk)
 {
 	if(ARCH_SUN4C_SUN4) {
-		if(brk >= 0x20000000 && brk < 0xe0000000)
+		if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000))
 			return current->mm->brk;
 	}
 	return sys_brk(brk);
@@ -190,24 +217,16 @@
 
 	down(&current->mm->mmap_sem);
 	lock_kernel();
-	retval = -ENOMEM;
+	retval = -EINVAL;
 	len = PAGE_ALIGN(len);
-	if(!(flags & MAP_FIXED) &&
-	   (!addr || (ARCH_SUN4C_SUN4 &&
-		      (addr >= 0x20000000 && addr < 0xe0000000)))) {
-		addr = get_unmapped_area(0, len);
-		if(!addr)
-			goto out_putf;
-		if (ARCH_SUN4C_SUN4 &&
-		    (addr >= 0x20000000 && addr < 0xe0000000)) {
-			retval = -EINVAL;
-			goto out_putf;
-		}
-	}
+	if (ARCH_SUN4C_SUN4 &&
+	    (len > 0x20000000 ||
+	     ((flags & MAP_FIXED) &&
+	      addr < 0xe0000000 && addr + len > 0x20000000)))
+		goto out_putf;
 
 	/* See asm-sparc/uaccess.h */
-	retval = -EINVAL;
-	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
+	if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
 		goto out_putf;
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
@@ -236,6 +255,50 @@
 	unsigned long off)
 {
 	return do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+}
+
+extern unsigned long do_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr);
+                
+asmlinkage unsigned long sparc_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr)
+{
+	unsigned long ret = -EINVAL;
+	if (ARCH_SUN4C_SUN4) {
+		if (old_len > 0x20000000 || new_len > 0x20000000)
+			goto out;
+		if (addr < 0xe0000000 && addr + old_len > 0x20000000)
+			goto out;
+	}
+	if (old_len > TASK_SIZE - PAGE_SIZE ||
+	    new_len > TASK_SIZE - PAGE_SIZE)
+		goto out;
+	down(&current->mm->mmap_sem);
+	if (flags & MREMAP_FIXED) {
+		if (ARCH_SUN4C_SUN4 &&
+		    new_addr < 0xe0000000 &&
+		    new_addr + new_len > 0x20000000)
+			goto out_sem;
+		if (new_addr + new_len > TASK_SIZE - PAGE_SIZE)
+			goto out_sem;
+	} else if ((ARCH_SUN4C_SUN4 && addr < 0xe0000000 &&
+		    addr + new_len > 0x20000000) ||
+		   addr + new_len > TASK_SIZE - PAGE_SIZE) {
+		ret = -ENOMEM;
+		if (!(flags & MREMAP_MAYMOVE))
+			goto out_sem;
+		new_addr = get_unmapped_area (addr, new_len);
+		if (!new_addr)
+			goto out_sem;
+		flags |= MREMAP_FIXED;
+	}
+	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
+out_sem:
+	up(&current->mm->mmap_sem);
+out:
+	return ret;       
 }
 
 /* we come to here via sys_nis_syscall so it can setup the regs argument */
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.3.45/linux/arch/sparc/kernel/sys_sunos.c	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc/kernel/sys_sunos.c	Mon Feb 14 15:31:14 2000
@@ -71,8 +71,10 @@
 	lock_kernel();
 	current->personality |= PER_BSD;
 	if(flags & MAP_NORESERVE) {
-		printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n",
-		       current->comm);
+		static int cnt;
+		if (cnt++ < 10)
+			printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n",
+			       current->comm);
 		flags &= ~MAP_NORESERVE;
 	}
 	retval = -EBADF;
@@ -84,19 +86,7 @@
 			goto out;
 	}
 
-	retval = -ENOMEM;
-	if(!(flags & MAP_FIXED) &&
-	   (!addr || (ARCH_SUN4C_SUN4 &&
-		      (addr >= 0x20000000 && addr < 0xe0000000)))) {
-		addr = get_unmapped_area(0, len);
-		if(!addr)
-			goto out_putf;
-		if (ARCH_SUN4C_SUN4 &&
-		    (addr >= 0x20000000 && addr < 0xe0000000)) {
-			retval = -EINVAL;
-			goto out_putf;
-		}
-	}
+	retval = -EINVAL;
 	/* If this is ld.so or a shared library doing an mmap
 	 * of /dev/zero, transform it into an anonymous mapping.
 	 * SunOS is so stupid some times... hmph!
@@ -105,18 +95,27 @@
 		if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR &&
 		   MINOR(file->f_dentry->d_inode->i_rdev) == 5) {
 			flags |= MAP_ANONYMOUS;
+			fput(file);
 			file = 0;
 		}
 	}
-	if(!(flags & MAP_FIXED))
-		addr = 0;
 	ret_type = flags & _MAP_NEW;
 	flags &= ~_MAP_NEW;
 
-	/* See asm-sparc/uaccess.h */
-	retval = -EINVAL;
-	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
-		goto out_putf;
+	if(!(flags & MAP_FIXED))
+		addr = 0;
+	else {
+		if (ARCH_SUN4C_SUN4 &&
+		    (len > 0x20000000 ||
+		     ((flags & MAP_FIXED) &&
+		      addr < 0xe0000000 && addr + len > 0x20000000)))
+			goto out_putf;
+
+		/* See asm-sparc/uaccess.h */
+		if (len > TASK_SIZE - PAGE_SIZE ||
+		    addr + len > TASK_SIZE - PAGE_SIZE)
+			goto out_putf;
+	}
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	retval = do_mmap(file, addr, len, prot, flags, off);
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.3.45/linux/arch/sparc/kernel/systbls.S	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc/kernel/systbls.S	Mon Feb 14 15:31:14 2000
@@ -69,7 +69,7 @@
 /*235*/	.long sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/	.long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 /*245*/	.long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
-/*250*/	.long sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
+/*250*/	.long sparc_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
 /*255*/	.long sys_nis_syscall, sys_nis_syscall
 
 #ifdef CONFIG_SUNOS_EMUL
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/lib/rwsem.S linux/arch/sparc/lib/rwsem.S
--- v2.3.45/linux/arch/sparc/lib/rwsem.S	Fri Jan  7 19:13:21 2000
+++ linux/arch/sparc/lib/rwsem.S	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: rwsem.S,v 1.2 2000/01/05 01:00:38 davem Exp $
+/* $Id: rwsem.S,v 1.4 2000/02/13 07:59:39 anton Exp $
  * Assembly part of rw semaphores.
  *
  * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
@@ -26,16 +26,19 @@
 	tst		%g7
 	bne		1b
 	 ld		[%g1], %g7
-	subcc		%g7, 1, %g7
+	sub		%g7, 1, %g7
 	st		%g7, [%g1]
 	stb		%g0, [%g1 + 4]
 #else
 	ld		[%g1], %g7
-	subcc		%g7, 1, %g7
+	sub		%g7, 1, %g7
 	st		%g7, [%g1]
 #endif
 	wr		%g3, 0, %psr
+	add		%g7, 1, %g7
 	nop
+	nop
+	subcc		%g7, 1, %g7
 	bneg		3f
 	 nop
 2:	jmpl		%o7, %g0
@@ -74,16 +77,19 @@
 	tst		%g7
 	bne		1b
 	 ld		[%g1], %g7
-	subcc		%g7, %g2, %g7
+	sub		%g7, %g2, %g7
 	st		%g7, [%g1]
 	stb		%g0, [%g1 + 4]
 #else
 	ld		[%g1], %g7
-	subcc		%g7, %g2, %g7
+	sub		%g7, %g2, %g7
 	st		%g7, [%g1]
 #endif
 	wr		%g3, 0, %psr
+	add		%g7, %g2, %g7
+	nop
 	nop
+	subcc		%g7, %g2, %g7
 	bne		3f
 	 nop
 2:	jmpl		%o7, %g0
@@ -122,16 +128,19 @@
 	tst		%g7
 	bne		1b
 	 ld		[%g1], %g7
-	addcc		%g7, 1, %g7
+	add		%g7, 1, %g7
 	st		%g7, [%g1]
 	stb		%g0, [%g1 + 4]
 #else
 	ld		[%g1], %g7
-	addcc		%g7, 1, %g7
+	add		%g7, 1, %g7
 	st		%g7, [%g1]
 #endif
 	wr		%g3, 0, %psr
 	nop
+	nop
+	nop
+	cmp		%g7, 0
 	be		3f
 	 nop
 2:	jmpl		%o7, %g0
@@ -164,16 +173,19 @@
 	tst		%g7
 	bne		1b
 	 ld		[%g1], %g7
-	addcc		%g7, %g2, %g7
+	add		%g7, %g2, %g7
 	st		%g7, [%g1]
 	stb		%g0, [%g1 + 4]
 #else
 	ld		[%g1], %g7
-	addcc		%g7, %g2, %g7
+	add		%g7, %g2, %g7
 	st		%g7, [%g1]
 #endif
 	wr		%g3, 0, %psr
+	sub		%g7, %g2, %g7
+	nop
 	nop
+	addcc		%g7, %g2, %g7
 	bcs		3f
 	 nop
 2:	jmpl		%o7, %g0
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.3.45/linux/arch/sparc/mm/srmmu.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/sparc/mm/srmmu.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.206 2000/02/08 07:45:59 davem Exp $
+/* $Id: srmmu.c,v 1.207 2000/02/14 02:51:53 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.3.45/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c
--- v2.3.45/linux/arch/sparc/mm/sun4c.c	Sun Feb 13 19:29:03 2000
+++ linux/arch/sparc/mm/sun4c.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: sun4c.c,v 1.188 2000/02/12 03:07:35 zaitcev Exp $
+/* $Id: sun4c.c,v 1.189 2000/02/14 02:51:55 davem Exp $
  * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/pci_iommu.c linux/arch/sparc64/kernel/pci_iommu.c
--- v2.3.45/linux/arch/sparc64/kernel/pci_iommu.c	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc64/kernel/pci_iommu.c	Mon Feb 14 15:34:21 2000
@@ -123,11 +123,8 @@
 	int npages;
 
 	size = PAGE_ALIGN(size);
-	for (order = 0; order < 10; order++) {
-		if ((PAGE_SIZE << order) >= size)
-			break;
-	}
-	if (order == 10)
+	order = get_order(size);
+	if (order >= 10)
 		return NULL;
 
 	/* We still don't support devices which don't recognize at least 30 bits
@@ -210,10 +207,7 @@
 
 	spin_unlock_irqrestore(&iommu->lock, flags);
 
-	for (order = 0; order < 10; order++) {
-		if ((PAGE_SIZE << order) >= size)
-			break;
-	}
+	order = get_order(size);
 	if (order < 10)
 		free_pages((unsigned long)cpu, order);
 }
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/pci_sabre.c linux/arch/sparc64/kernel/pci_sabre.c
--- v2.3.45/linux/arch/sparc64/kernel/pci_sabre.c	Thu Feb 10 17:11:06 2000
+++ linux/arch/sparc64/kernel/pci_sabre.c	Mon Feb 14 15:34:21 2000
@@ -1133,10 +1133,7 @@
 	control &= ~(SABRE_IOMMUCTRL_DENAB);
 	sabre_write(p->controller_regs + SABRE_IOMMU_CONTROL, control);
 
-	for(order = 0;; order++)
-		if((PAGE_SIZE << order) >= ((tsbsize * 1024) * 8))
-			break;
-	tsbbase = __get_free_pages(GFP_KERNEL, order);
+	tsbbase = __get_free_pages(GFP_KERNEL, order = get_order(tsbsize * 1024 * 8));
 	if (!tsbbase) {
 		prom_printf("SABRE_IOMMU: Error, gfp(tsb) failed.\n");
 		prom_halt();
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/sbus.c linux/arch/sparc64/kernel/sbus.c
--- v2.3.45/linux/arch/sparc64/kernel/sbus.c	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc64/kernel/sbus.c	Mon Feb 14 15:34:21 2000
@@ -255,11 +255,8 @@
 		return NULL;
 
 	size = PAGE_ALIGN(size);
-	for (order = 0; order < 10; order++) {
-		if ((PAGE_SIZE << order) >= size)
-			break;
-	}
-	if (order == 10)
+	order = get_order(size);
+	if (order >= 10)
 		return NULL;
 	first_page = __get_free_pages(GFP_KERNEL, order);
 	if (first_page == 0UL)
@@ -306,10 +303,7 @@
 	free_consistent_cluster(iommu, dvma, npages);
 	spin_unlock_irq(&iommu->lock);
 
-	for (order = 0; order < 10; order++) {
-		if ((PAGE_SIZE << order) >= size)
-			break;
-	}
+	order = get_order(size);
 	if (order < 10)
 		free_pages((unsigned long)cpu, order);
 }
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.3.45/linux/arch/sparc64/kernel/smp.c	Fri Jan 28 15:09:07 2000
+++ linux/arch/sparc64/kernel/smp.c	Mon Feb 14 15:34:21 2000
@@ -753,9 +753,7 @@
 					 "ecache-size", (512 * 1024));
 	if (ecache_size > (4 * 1024 * 1024))
 		ecache_size = (4 * 1024 * 1024);
-	for (order = 0UL; (PAGE_SIZE << order) < ecache_size; order++)
-		;
-	flush_base = __get_free_pages(GFP_KERNEL, order);
+	flush_base = __get_free_pages(GFP_KERNEL, order = get_order(ecache_size));
 
 	if (flush_base != 0UL) {
 		__save_and_cli(flags);
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- v2.3.45/linux/arch/sparc64/kernel/sys_sparc.c	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc64/kernel/sys_sparc.c	Mon Feb 14 15:31:14 2000
@@ -39,17 +39,49 @@
 	return PAGE_SIZE;
 }
 
+unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
+{
+	struct vm_area_struct * vmm;
+	unsigned long task_size = TASK_SIZE;
+
+	if (current->thread.flags & SPARC_FLAG_32BIT)
+		task_size = 0xf0000000UL;
+	if (len > task_size || len > -PAGE_OFFSET)
+		return 0;
+	if (!addr)
+		addr = TASK_UNMAPPED_BASE;
+	addr = PAGE_ALIGN(addr);
+
+	task_size -= len;
+
+	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
+		/* At this point:  (!vmm || addr < vmm->vm_end). */
+		if (addr < PAGE_OFFSET && -PAGE_OFFSET - len < addr) {
+			addr = PAGE_OFFSET;
+			vmm = find_vma(current->mm, PAGE_OFFSET);
+		}
+		if (task_size < addr)
+			return 0;
+		if (!vmm || addr + len <= vmm->vm_start)
+			return addr;
+		addr = vmm->vm_end;
+	}
+}
+
 extern asmlinkage unsigned long sys_brk(unsigned long brk);
 
 asmlinkage unsigned long sparc_brk(unsigned long brk)
 {
-	if((brk >= 0x80000000000UL && brk < PAGE_OFFSET) ||
-	   (brk - current->mm->brk > 0x80000000000UL &&
-	    brk - current->mm->brk < PAGE_OFFSET)) /* VM hole */
+	/* People could try to be nasty and use ta 0x6d in 32bit programs */
+	if ((current->thread.flags & SPARC_FLAG_32BIT) &&
+	    brk >= 0xf0000000UL)
+		return current->mm->brk;
+
+	if ((current->mm->brk & PAGE_OFFSET) != (brk & PAGE_OFFSET))
 		return current->mm->brk;
 	return sys_brk(brk);
 }
-
+                                                                
 /*
  * sys_pipe() is the normal C calling standard for creating
  * a pipe. It's not the way unix traditionally does this, though.
@@ -164,30 +196,21 @@
 			goto out;
 	}
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	retval = -ENOMEM;
 	len = PAGE_ALIGN(len);
+	retval = -EINVAL;
+
 	down(&current->mm->mmap_sem);
 	lock_kernel();
-	if(!(flags & MAP_FIXED) && !addr) {
-		addr = get_unmapped_area(addr, len);
-		if(!addr)
-			goto out_putf;
-	}
 
-	retval = -EINVAL;
 	if (current->thread.flags & SPARC_FLAG_32BIT) {
-		if (len > 0xf0000000UL || addr > 0xf0000000UL - len)
+		if (len > 0xf0000000UL ||
+		    ((flags & MAP_FIXED) && addr > 0xf0000000UL - len))
 			goto out_putf;
 	} else {
-		if (len >= 0x80000000000UL || 
-		    (addr < 0x80000000000UL &&
-		     addr > 0x80000000000UL-len))
+		if (len > -PAGE_OFFSET ||
+		    ((flags & MAP_FIXED) &&
+		     addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
 			goto out_putf;
-		if (addr >= 0x80000000000UL && addr < PAGE_OFFSET) {
-			/* VM hole */
-			retval = current->mm->brk;
-			goto out_putf;
-		}
 	}
 
 	retval = do_mmap(file, addr, len, prot, flags, off);
@@ -199,6 +222,55 @@
 		fput(file);
 out:
 	return retval;
+}
+
+asmlinkage long sys64_munmap(unsigned long addr, size_t len)
+{
+	long ret;
+
+	if (len > -PAGE_OFFSET ||
+	    (addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
+		return -EINVAL;
+	down(&current->mm->mmap_sem);
+	ret = do_munmap(addr, len);
+	up(&current->mm->mmap_sem);
+	return ret;
+}
+
+extern unsigned long do_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr);
+                
+asmlinkage unsigned long sys64_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr)
+{
+	unsigned long ret = -EINVAL;
+	if (current->thread.flags & SPARC_FLAG_32BIT)
+		goto out;
+	if (old_len > -PAGE_OFFSET || new_len > -PAGE_OFFSET)
+		goto out;
+	if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET)
+		goto out;
+	down(&current->mm->mmap_sem);
+	if (flags & MREMAP_FIXED) {
+		if (new_addr < PAGE_OFFSET &&
+		    new_addr + new_len > -PAGE_OFFSET)
+			goto out_sem;
+	} else if (addr < PAGE_OFFSET && addr + new_len > -PAGE_OFFSET) {
+		ret = -ENOMEM;
+		if (!(flags & MREMAP_MAYMOVE))
+			goto out_sem;
+		new_addr = get_unmapped_area (addr, new_len);
+		if (!new_addr)
+			goto out_sem;
+		flags |= MREMAP_FIXED;
+	}
+	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
+out_sem:
+	up(&current->mm->mmap_sem);
+out:
+	return ret;       
 }
 
 /* we come to here via sys_nis_syscall so it can setup the regs argument */
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.3.45/linux/arch/sparc64/kernel/sys_sparc32.c	Fri Jan 28 15:09:07 2000
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Feb 14 15:31:14 2000
@@ -44,6 +44,7 @@
 #include <linux/filter.h>
 #include <linux/highmem.h>
 #include <linux/highuid.h>
+#include <linux/mman.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -4189,4 +4190,39 @@
 	put_unused_fd(fd);
 	fd = error;
 	goto out;
+}
+
+extern unsigned long do_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr);
+                
+asmlinkage unsigned long sys32_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, u32 __new_addr)
+{
+	unsigned long ret = -EINVAL;
+	unsigned long new_addr = AA(__new_addr);
+
+	if (old_len > 0xf0000000UL || new_len > 0xf0000000UL)
+		goto out;
+	if (addr > 0xf0000000UL - old_len)
+		goto out;
+	down(&current->mm->mmap_sem);
+	if (flags & MREMAP_FIXED) {
+		if (new_addr > 0xf0000000UL - new_len)
+			goto out_sem;
+	} else if (addr > 0xf0000000UL - new_len) {
+		ret = -ENOMEM;
+		if (!(flags & MREMAP_MAYMOVE))
+			goto out_sem;
+		new_addr = get_unmapped_area (addr, new_len);
+		if (!new_addr)
+			goto out_sem;
+		flags |= MREMAP_FIXED;
+	}
+	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
+out_sem:
+	up(&current->mm->mmap_sem);
+out:
+	return ret;       
 }
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.3.45/linux/arch/sparc64/kernel/sys_sunos32.c	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Mon Feb 14 15:31:14 2000
@@ -71,8 +71,10 @@
 	lock_kernel();
 	current->personality |= PER_BSD;
 	if(flags & MAP_NORESERVE) {
-		printk("%s:  unimplemented SunOS MAP_NORESERVE mmap() flag\n",
-		       current->comm);
+		static int cnt;
+		if (cnt++ < 10)
+			printk("%s:  unimplemented SunOS MAP_NORESERVE mmap() flag\n",
+			       current->comm);
 		flags &= ~MAP_NORESERVE;
 	}
 	retval = -EBADF;
@@ -93,15 +95,11 @@
 		}
 	}
 
-	retval = -ENOMEM;
-	if(!(flags & MAP_FIXED) && !addr) {
-		unsigned long attempt = get_unmapped_area(addr, len);
-		if(!attempt || (attempt >= 0xf0000000UL))
-			goto out_putf;
-		addr = (u32) attempt;
-	}
+	retval = -EINVAL;
 	if(!(flags & MAP_FIXED))
 		addr = 0;
+	else if (len > 0xf0000000 || addr > 0xf0000000 - len)
+		goto out_putf;
 	ret_type = flags & _MAP_NEW;
 	flags &= ~_MAP_NEW;
 
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.3.45/linux/arch/sparc64/kernel/systbls.S	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc64/kernel/systbls.S	Mon Feb 14 15:31:14 2000
@@ -69,7 +69,7 @@
 	.word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 	.word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep
-/*250*/	.word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl
+/*250*/	.word sys32_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl
 	.word sys_aplib
 
 	/* Now the 64-bit native Linux syscall table. */
@@ -92,7 +92,7 @@
 	.word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve
 /*60*/	.word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
 	.word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
-/*70*/	.word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect
+/*70*/	.word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
 	.word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
 /*80*/	.word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
 	.word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
@@ -128,7 +128,7 @@
 	.word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 	.word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
-/*250*/	.word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
+/*250*/	.word sys64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
 	.word sys_aplib
 
 #if defined(CONFIG_SUNOS_EMUL) || defined(CONFIG_SOLARIS_EMUL) || \
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/mm/ultra.S linux/arch/sparc64/mm/ultra.S
--- v2.3.45/linux/arch/sparc64/mm/ultra.S	Wed Dec 29 13:13:14 1999
+++ linux/arch/sparc64/mm/ultra.S	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: ultra.S,v 1.36 1999/12/15 15:45:18 davem Exp $
+/* $Id: ultra.S,v 1.37 2000/02/14 02:52:04 davem Exp $
  * ultra.S: Don't expand these all over the place...
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -132,8 +132,8 @@
 	 wrpr		%g1, 0x0, %pstate
 
 	.align		32
-	.globl		flush_icache_page
-flush_icache_page:	/* %o0 = phys_page */
+	.globl		__flush_icache_page
+__flush_icache_page:	/* %o0 = phys_page */
 	sethi		%hi(1 << 13), %o2	! IC_set bit
 	mov		1, %g1
 	srlx		%o0, 5, %o0
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/solaris/misc.c linux/arch/sparc64/solaris/misc.c
--- v2.3.45/linux/arch/sparc64/solaris/misc.c	Tue Feb  1 01:35:43 2000
+++ linux/arch/sparc64/solaris/misc.c	Mon Feb 14 15:31:14 2000
@@ -83,27 +83,25 @@
 		}
 	}
 
-	down(&current->mm->mmap_sem);
-	retval = -ENOMEM;
-	if(!(flags & MAP_FIXED) && !addr) {
-		unsigned long attempt = get_unmapped_area(addr, len);
-		if(!attempt || (attempt >= 0xf0000000UL))
-			goto out_putf;
-		addr = (u32) attempt;
-	}
+	retval = -EINVAL;
+	len = PAGE_ALIGN(len);
 	if(!(flags & MAP_FIXED))
 		addr = 0;
+	else if (len > 0xf0000000UL || addr > 0xf0000000UL - len)
+		goto out_putf;
 	ret_type = flags & _MAP_NEW;
 	flags &= ~_MAP_NEW;
 
+	down(&current->mm->mmap_sem);
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	retval = do_mmap(file,
 			 (unsigned long) addr, (unsigned long) len,
 			 (unsigned long) prot, (unsigned long) flags, off);
+	up(&current->mm->mmap_sem);
 	if(!ret_type)
 		retval = ((retval < 0xf0000000) ? 0 : retval);
+	                        
 out_putf:
-	up(&current->mm->mmap_sem);
 	if (file)
 		fput(file);
 out:
diff -u --recursive --new-file v2.3.45/linux/arch/sparc64/solaris/socksys.c linux/arch/sparc64/solaris/socksys.c
--- v2.3.45/linux/arch/sparc64/solaris/socksys.c	Thu Feb 10 17:11:06 2000
+++ linux/arch/sparc64/solaris/socksys.c	Wed Feb 16 15:42:05 2000
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/malloc.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/uaccess.h>
 #include <asm/termios.h>
@@ -157,6 +158,8 @@
 	release:	socksys_release,
 };
 
+static devfs_handle_t devfs_handle = NULL;
+
 int __init
 init_socksys(void)
 {
@@ -167,7 +170,7 @@
 	int (*sys_close)(unsigned int) = 
 		(int (*)(unsigned int))SYS(close);
 	
-	ret = register_chrdev (30, "socksys", &socksys_fops);
+	ret = devfs_register_chrdev (30, "socksys", &socksys_fops);
 	if (ret < 0) {
 		printk ("Couldn't register socksys character device\n");
 		return ret;
@@ -177,6 +180,10 @@
 		printk ("Couldn't create socket\n");
 		return ret;
 	}
+	devfs_handle = devfs_register (NULL, "socksys", 0, DEVFS_FL_NONE,
+				       30, 0,
+				       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				       &socksys_fops, NULL);
 	file = fcheck(ret);
 	/* N.B. Is this valid? Suppose the f_ops are in a module ... */
 	socksys_file_ops = *file->f_op;
@@ -190,6 +197,7 @@
 void
 cleanup_socksys(void)
 {
-	if (unregister_chrdev (30, "socksys"))
+	if (devfs_unregister_chrdev(30, "socksys"))
 		printk ("Couldn't unregister socksys character device\n");
+	devfs_unregister (devfs_handle);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/acorn/block/fd1772.c linux/drivers/acorn/block/fd1772.c
--- v2.3.45/linux/drivers/acorn/block/fd1772.c	Tue Jan 11 22:31:38 2000
+++ linux/drivers/acorn/block/fd1772.c	Wed Feb 16 10:56:44 2000
@@ -591,7 +591,7 @@
 {
 	printk("FDC1772: fd_error\n");
 	/*panic("fd1772: fd_error"); *//* DAG tmp */
-	if (!CURRENT)
+	if (QUEUE_EMPTY)
 		return;
 	CURRENT->errors++;
 	if (CURRENT->errors >= MAX_ERRORS) {
@@ -1230,14 +1230,14 @@
 
 	DPRINT(("redo_fd_request: CURRENT=%08lx CURRENT->rq_dev=%04x CURRENT->sector=%ld\n",
 		(unsigned long) CURRENT, CURRENT ? CURRENT->rq_dev : 0,
-		CURRENT ? CURRENT->sector : 0));
+		!QUEUE_EMPTY ? CURRENT->sector : 0));
 
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE)
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE)
 		goto the_end;
 
       repeat:
 
-	if (!CURRENT)
+	if (QUEUE_EMPTY)
 		goto the_end;
 
 	if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
diff -u --recursive --new-file v2.3.45/linux/drivers/acorn/block/mfmhd.c linux/drivers/acorn/block/mfmhd.c
--- v2.3.45/linux/drivers/acorn/block/mfmhd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/acorn/block/mfmhd.c	Wed Feb 16 10:56:44 2000
@@ -758,7 +758,7 @@
 		/* No - its the end of the line */
 		/* end_request's should have happened at the end of sector DMAs */
 		/* Turns Drive LEDs off - may slow it down? */
-		if (!CURRENT)
+		if (QUEUE_EMPTY)
 			issue_command(CMD_CKV, block, 2);
 
 		Busy = 0;
@@ -891,7 +891,7 @@
 {
 	DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy);
 
-	if (!CURRENT) {
+	if (QUEUE_EMPTY) {
 		DBG("mfm_request: Exited due to NULL Current 1\n");
 		return;
 	}
@@ -918,7 +918,7 @@
 
 		DBG("mfm_request: before INIT_REQUEST\n");
 
-		if (!CURRENT) {
+		if (QUEUE_EMPTY) {
 			printk("mfm_request: Exiting due to !CURRENT (pre)\n");
 			CLEAR_INTR;
 			Busy = 0;
diff -u --recursive --new-file v2.3.45/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.3.45/linux/drivers/block/Config.in	Thu Feb 10 17:11:06 2000
+++ linux/drivers/block/Config.in	Mon Feb 14 15:27:01 2000
@@ -206,8 +206,8 @@
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
    tristate '  Linear (append) mode' CONFIG_MD_LINEAR
    tristate '  RAID-0 (striping) mode' CONFIG_MD_STRIPED
-   tristate '  RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
-   tristate '  RAID-4/RAID-5 mode' CONFIG_MD_RAID5
+#   tristate '  RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+#   tristate '  RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
    bool '    Boot support (linear, striped)' CONFIG_MD_BOOT
diff -u --recursive --new-file v2.3.45/linux/drivers/block/DAC960.c linux/drivers/block/DAC960.c
--- v2.3.45/linux/drivers/block/DAC960.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/block/DAC960.c	Wed Feb 16 15:42:05 2000
@@ -1009,37 +1009,58 @@
 }
 
 
-static int DAC_merge_fn(request_queue_t *q, struct request *req, 
-			struct buffer_head *bh) 
+static inline int DAC_new_segment(request_queue_t *q, struct request *req,
+				  int __max_segments)
 {
 	int max_segments;
 	DAC960_Controller_T * Controller = q->queuedata;
 
 	max_segments = Controller->MaxSegmentsPerRequest[MINOR(req->rq_dev)];
+	if (__max_segments < max_segments)
+		max_segments = __max_segments;
 
-	if (req->bhtail->b_data + req->bhtail->b_size != bh->b_data) {
-		if (req->nr_segments < max_segments) {
-			req->nr_segments++;
-			return 1;
-		}
-		return 0;
+	if (req->nr_segments < max_segments) {
+		req->nr_segments++;
+		q->nr_segments++;
+		return 1;
 	}
+	return 0;
+}
 
-	return 1;
+static int DAC_back_merge_fn(request_queue_t *q, struct request *req, 
+			     struct buffer_head *bh, int __max_segments)
+{
+	if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data)
+		return 1;
+	return DAC_new_segment(q, req, __max_segments);
+}
+
+static int DAC_front_merge_fn(request_queue_t *q, struct request *req, 
+			      struct buffer_head *bh, int __max_segments)
+{
+	if (bh->b_data + bh->b_size == req->bh->b_data)
+		return 1;
+	return DAC_new_segment(q, req, __max_segments);
 }
 
 static int DAC_merge_requests_fn(request_queue_t *q,
 				 struct request *req,
-				 struct request *next)
+				 struct request *next,
+				 int __max_segments)
 {
 	int max_segments;
 	DAC960_Controller_T * Controller = q->queuedata;
 	int total_segments = req->nr_segments + next->nr_segments;
 
 	max_segments = Controller->MaxSegmentsPerRequest[MINOR(req->rq_dev)];
+	if (__max_segments < max_segments)
+		max_segments = __max_segments;
 
 	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
+	{
 		total_segments--;
+		q->nr_segments--;
+	}
     
 	if (total_segments > max_segments)
 		return 0;
@@ -1068,7 +1089,7 @@
   /*
     Register the Block Device Major Number for this DAC960 Controller.
   */
-  if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0)
+  if (devfs_register_blkdev(MajorNumber, "dac960", &DAC960_FileOperations) < 0)
     {
       DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n",
 		   Controller, MajorNumber);
@@ -1080,7 +1101,8 @@
   q = BLK_DEFAULT_QUEUE(MajorNumber);
   blk_init_queue(q, RequestFunctions[Controller->ControllerNumber]);
   blk_queue_headactive(q, 0);
-  q->merge_fn = DAC_merge_fn;
+  q->back_merge_fn = DAC_back_merge_fn;
+  q->front_merge_fn = DAC_front_merge_fn;
   q->merge_requests_fn = DAC_merge_requests_fn;
   q->queuedata = (void *) Controller;
 
@@ -1108,12 +1130,13 @@
     Complete initialization of the Generic Disk Information structure.
   */
   Controller->GenericDiskInfo.major = MajorNumber;
-  Controller->GenericDiskInfo.major_name = "rd";
+  Controller->GenericDiskInfo.major_name = "dac960";
   Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
   Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
   Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount;
   Controller->GenericDiskInfo.real_devices = Controller;
   Controller->GenericDiskInfo.next = NULL;
+  Controller->GenericDiskInfo.fops = &DAC960_FileOperations;
   /*
     Install the Generic Disk Information structure at the end of the list.
   */
@@ -1142,7 +1165,7 @@
   /*
     Unregister the Block Device Major Number for this DAC960 Controller.
   */
-  unregister_blkdev(MajorNumber, "rd");
+  devfs_unregister_blkdev(MajorNumber, "dac960");
   /*
     Remove the I/O Request Function.
   */
@@ -1156,7 +1179,6 @@
   blk_size[MajorNumber] = NULL;
   blksize_size[MajorNumber] = NULL;
   max_sectors[MajorNumber] = NULL;
-  max_segments[MajorNumber] = NULL;
   /*
     Remove the Generic Disk Information structure from the list.
   */
@@ -1305,15 +1327,17 @@
 static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller,
 				     boolean WaitForCommand)
 {
-  IO_Request_T **RequestQueuePointer =
-    &blk_dev[DAC960_MAJOR + Controller->ControllerNumber].request_queue.current_request;
+  struct list_head * queue_head;
   IO_Request_T *Request;
   DAC960_Command_T *Command;
   char *RequestBuffer;
+
+  queue_head = &blk_dev[DAC960_MAJOR + Controller->ControllerNumber].request_queue.queue_head;
   while (true)
     {
-      Request = *RequestQueuePointer;
-      if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false;
+      if (list_empty(queue_head)) return false;
+      Request = blkdev_entry_next_request(queue_head);
+      if (Request->rq_status == RQ_INACTIVE) return false;
       Command = DAC960_AllocateCommand(Controller);
       if (Command != NULL) break;
       if (!WaitForCommand) return false;
@@ -1335,7 +1359,7 @@
   Command->BufferHeader = Request->bh;
   RequestBuffer = Request->buffer;
   Request->rq_status = RQ_INACTIVE;
-  *RequestQueuePointer = Request->next;
+  blkdev_dequeue_request(Request);
   wake_up(&wait_for_request);
   if (Command->SegmentCount == 1)
     {
diff -u --recursive --new-file v2.3.45/linux/drivers/block/acsi.c linux/drivers/block/acsi.c
--- v2.3.45/linux/drivers/block/acsi.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/acsi.c	Wed Feb 16 15:42:05 2000
@@ -54,6 +54,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/genhd.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/major.h>
@@ -769,7 +770,7 @@
 static void bad_rw_intr( void )
 
 {
-	if (!CURRENT)
+	if (QUEUE_EMPTY)
 		return;
 
 	if (++CURRENT->errors >= MAX_ERRORS)
@@ -843,7 +844,7 @@
 
 	DEVICE_INTR = NULL;
 	printk( KERN_ERR "ACSI timeout\n" );
-	if (!CURRENT) return;
+	if (QUEUE_EMPTY) return;
 	if (++CURRENT->errors >= MAX_ERRORS) {
 #ifdef DEBUG
 		printk( KERN_ERR "ACSI: too many errors.\n" );
@@ -953,7 +954,7 @@
 	unsigned long		pbuffer;
 	struct buffer_head	*bh;
 	
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) {
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) {
 		if (!DEVICE_INTR) {
 			ENABLE_IRQ();
 			stdma_release();
@@ -969,7 +970,7 @@
 	/* Another check here: An interrupt or timer event could have
 	 * happened since the last check!
 	 */
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) {
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) {
 		if (!DEVICE_INTR) {
 			ENABLE_IRQ();
 			stdma_release();
@@ -979,7 +980,7 @@
 	if (DEVICE_INTR)
 		return;
 
-	if (!CURRENT) {
+	if (QUEUE_EMPTY) {
 		CLEAR_INTR;
 		ENABLE_IRQ();
 		stdma_release();
@@ -1385,6 +1386,8 @@
  ********************************************************************/
 
 
+extern struct block_device_operations acsi_fops;
+
 static struct gendisk acsi_gendisk = {
 	MAJOR_NR,		/* Major number */	
 	"ad",			/* Major name */
@@ -1394,7 +1397,8 @@
 	acsi_sizes,		/* block sizes */
 	0,			/* number */
 	(void *)acsi_info,	/* internal */
-	NULL			/* next */
+	NULL,			/* next */
+	&acsi_fops,		/* file operations */
 };
 	
 #define MAX_SCSI_DEVICE_CODE 10
@@ -1776,16 +1780,14 @@
 	int err = 0;
 	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI))
 		return 0;
-
-	if (register_blkdev( MAJOR_NR, "ad", &acsi_fops )) {
+	if (devfs_register_blkdev( MAJOR_NR, "ad", &acsi_fops )) {
 		printk( KERN_ERR "Unable to get major %d for ACSI\n", MAJOR_NR );
 		return -EBUSY;
 	}
-
 	if (!(acsi_buffer =
 		  (char *)atari_stram_alloc( ACSI_BUFFER_SIZE, NULL, "acsi" ))) {
 		printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
-		unregister_blkdev( MAJOR_NR, "ad" );
+		devfs_unregister_blkdev( MAJOR_NR, "ad" );
 		return -ENOMEM;
 	}
 	phys_acsi_buffer = virt_to_phys( acsi_buffer );
@@ -1824,7 +1826,7 @@
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 	atari_stram_free( acsi_buffer );
 
-	if (unregister_blkdev( MAJOR_NR, "ad" ) != 0)
+	if (devfs_unregister_blkdev( MAJOR_NR, "ad" ) != 0)
 		printk( KERN_ERR "acsi: cleanup_module failed\n");
 
 	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
diff -u --recursive --new-file v2.3.45/linux/drivers/block/acsi_slm.c linux/drivers/block/acsi_slm.c
--- v2.3.45/linux/drivers/block/acsi_slm.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/block/acsi_slm.c	Wed Feb 16 15:42:05 2000
@@ -65,6 +65,7 @@
 #include <linux/time.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -987,23 +988,28 @@
 	return( 1 );
 }
 
+static devfs_handle_t devfs_handle = NULL;
 
 int slm_init( void )
 
 {
-	if (register_chrdev( MAJOR_NR, "slm", &slm_fops )) {
+	if (devfs_register_chrdev( MAJOR_NR, "slm", &slm_fops )) {
 		printk( KERN_ERR "Unable to get major %d for ACSI SLM\n", MAJOR_NR );
 		return -EBUSY;
 	}
 	
 	if (!(SLMBuffer = atari_stram_alloc( SLM_BUFFER_SIZE, NULL, "SLM" ))) {
 		printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" );
-		unregister_chrdev( MAJOR_NR, "slm" );
+		devfs_unregister_chrdev( MAJOR_NR, "slm" );
 		return -ENOMEM;
 	}
 	BufferP = SLMBuffer;
 	SLMState = IDLE;
 	
+	devfs_handle = devfs_mk_dir (NULL, "slm", 3, NULL);
+	devfs_register_series (devfs_handle, "%u", MAX_SLM, DEVFS_FL_DEFAULT,
+			       MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &slm_fops, NULL);
 	return 0;
 }
 
@@ -1026,7 +1032,8 @@
 
 void cleanup_module(void)
 {
-	if (unregister_chrdev( MAJOR_NR, "slm" ) != 0)
+	devfs_unregister (devfs_handle);
+	if (devfs_unregister_chrdev( MAJOR_NR, "slm" ) != 0)
 		printk( KERN_ERR "acsi_slm: cleanup_module failed\n");
 	atari_stram_free( SLMBuffer );
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/block/amiflop.c linux/drivers/block/amiflop.c
--- v2.3.45/linux/drivers/block/amiflop.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/block/amiflop.c	Wed Feb 16 10:56:44 2000
@@ -1385,12 +1385,12 @@
 	char *data;
 	unsigned long flags;
 
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE){
 		return;
 	}
 
  repeat:
-	if (!CURRENT) {
+	if (QUEUE_EMPTY) {
 		/* Nothing left to do */
 		return;
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ataflop.c linux/drivers/block/ataflop.c
--- v2.3.45/linux/drivers/block/ataflop.c	Tue Jan 11 22:31:39 2000
+++ linux/drivers/block/ataflop.c	Wed Feb 16 10:56:44 2000
@@ -624,7 +624,7 @@
 		return;
 	}
 		
-	if (!CURRENT) return;
+	if (QUEUE_EMPTY) return;
 	CURRENT->errors++;
 	if (CURRENT->errors >= MAX_ERRORS) {
 		printk(KERN_ERR "fd%d: too many errors.\n", SelectedDrive );
@@ -1450,18 +1450,18 @@
 	int device, drive, type;
   
 	DPRINT(("redo_fd_request: CURRENT=%08lx CURRENT->dev=%04x CURRENT->sector=%ld\n",
-		(unsigned long)CURRENT, CURRENT ? CURRENT->rq_dev : 0,
-		CURRENT ? CURRENT->sector : 0 ));
+		(unsigned long)CURRENT, !QUEUE_EMPTY ? CURRENT->rq_dev : 0,
+		!QUEUE_EMPTY ? CURRENT->sector : 0 ));
 
 	IsFormatting = 0;
 
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE){
 		return;
 	}
 
 repeat:
     
-	if (!CURRENT)
+	if (QUEUE_EMPTY)
 		goto the_end;
 
 	if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
diff -u --recursive --new-file v2.3.45/linux/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c
--- v2.3.45/linux/drivers/block/cpqarray.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/cpqarray.c	Wed Feb 16 10:56:44 2000
@@ -880,14 +880,16 @@
 	cmdlist_t *c;
 	int seg, sect;
 	char *lastdataend;
-	request_queue_t * q;
+	struct list_head * queue_head;
 	struct buffer_head *bh;
 	struct request *creq;
 
-	q = &blk_dev[MAJOR_NR+ctlr].request_queue;
+	queue_head = &blk_dev[MAJOR_NR+ctlr].request_queue.queue_head;
 
-	creq = q->current_request;
-	if (creq == NULL || creq->rq_status == RQ_INACTIVE)
+	if (list_empty(queue_head))
+		goto doreq_done;
+	creq = blkdev_entry_next_request(queue_head);
+	if (creq->rq_status == RQ_INACTIVE)
 		goto doreq_done;
 
 	if (ctlr != MAJOR(creq->rq_dev)-MAJOR_NR ||
@@ -961,10 +963,9 @@
 		bh->b_reqnext = NULL;
 DBGPX(		printk("More to do on same request %p\n", creq); );
 	} else {
-DBGPX(		printk("Done with %p, queueing %p\n", creq, creq->next); );
-		creq->rq_status = RQ_INACTIVE;
-		q->current_request = creq->next;
-		wake_up(&wait_for_request);
+DBGPX(		printk("Done with %p\n", creq); );
+		blkdev_dequeue_request(creq);
+		end_that_request_last(creq);
 	}
 
 	c->req.hdr.cmd = (creq->cmd == READ) ? IDA_READ : IDA_WRITE;
diff -u --recursive --new-file v2.3.45/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.3.45/linux/drivers/block/floppy.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/block/floppy.c	Wed Feb 16 15:42:05 2000
@@ -144,6 +144,10 @@
 #define FDPATCHES
 #include <linux/fdreg.h>
 
+/*
+ * 1998/1/21 -- Richard Gooch <rgooch@atnf.csiro.au> -- devfs support
+ */
+
 
 #include <linux/fd.h>
 #include <linux/hdreg.h>
@@ -158,6 +162,7 @@
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 /*
  * PS/2 floppies have much slower step rates than regular floppies.
@@ -196,7 +201,9 @@
 static unsigned short virtual_dma_port=0x3f0;
 void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
 static int set_dor(int fdc, char mask, char data);
-static inline int __get_order(unsigned long size);
+static void register_devfs_entries (int drive);
+static devfs_handle_t devfs_handle = NULL;
+
 #define K_64	0x10000		/* 64KB */
 #include <asm/floppy.h>
 
@@ -213,26 +220,12 @@
 
 /* Dma Memory related stuff */
 
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 #ifndef fd_dma_mem_free
-#define fd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
+#define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
 #endif
 
 #ifndef fd_dma_mem_alloc
-#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
+#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,get_order(size))
 #endif
 
 static inline void fallback_on_nodma_alloc(char **addr, size_t l)
@@ -2274,7 +2267,7 @@
 	probing = 0;
 	reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
 
-	if (!CURRENT){
+	if (QUEUE_EMPTY){
 		DPRINT("request list destroyed in floppy request done\n");
 		return;
 	}
@@ -2288,14 +2281,14 @@
 			DRS->maxtrack = 1;
 
 		/* unlock chained buffers */
-		while (current_count_sectors && CURRENT &&
+		while (current_count_sectors && !QUEUE_EMPTY &&
 		       current_count_sectors >= CURRENT->current_nr_sectors){
 			current_count_sectors -= CURRENT->current_nr_sectors;
 			CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
 			CURRENT->sector += CURRENT->current_nr_sectors;
 			end_request(1);
 		}
-		if (current_count_sectors && CURRENT){
+		if (current_count_sectors && !QUEUE_EMPTY){
 			/* "unlock" last subsector */
 			CURRENT->buffer += current_count_sectors <<9;
 			CURRENT->current_nr_sectors -= current_count_sectors;
@@ -2304,7 +2297,7 @@
 			return;
 		}
 
-		if (current_count_sectors && !CURRENT)
+		if (current_count_sectors && QUEUE_EMPTY)
 			DPRINT("request list destroyed in floppy request done\n");
 
 	} else {
@@ -2867,14 +2860,14 @@
 	if (current_drive < N_DRIVE)
 		floppy_off(current_drive);
 
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE){
 		CLEAR_INTR;
 		unlock_fdc();
 		return;
 	}
 
 	while(1){
-		if (!CURRENT) {
+		if (QUEUE_EMPTY) {
 			CLEAR_INTR;
 			unlock_fdc();
 			return;
@@ -3629,6 +3622,7 @@
 				first = 0;
 			}
 			printk("%s fd%d is %s", prepend, drive, name);
+			register_devfs_entries (drive);
 		}
 		*UDP = *params;
 	}
@@ -3842,6 +3836,37 @@
 	revalidate:		floppy_revalidate,
 };
 
+static void register_devfs_entries (int drive)
+{
+    int base_minor, i;
+    static char *table[] =
+    {"", "d360", "h1200", "u360", "u720", "h360", "h720",
+     "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410",
+     "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743",
+     "h880", "u1040", "u1120", "h1600", "u1760", "u1920",
+     "u3200", "u3520", "u3840", "u1840", "u800", "u1600",
+     NULL
+    };
+    static int t360[] = {1,0}, t1200[] = {2,5,6,10,12,14,16,18,20,23,0},
+      t3in[] = {8,9,26,27,28, 7,11,15,19,24,25,29,31, 3,4,13,17,21,22,30,0};
+    static int *table_sup[] = 
+    {NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in};
+
+    base_minor = (drive < 4) ? drive : (124 + drive);
+    if (UDP->cmos <= NUMBER(default_drive_params)) {
+	i = 0;
+	do {
+	    char name[16];
+
+	    sprintf (name, "%d%s", drive, table[table_sup[UDP->cmos][i]]);
+	    devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, MAJOR_NR,
+			    base_minor + (table_sup[UDP->cmos][i] << 2),
+			    S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP,
+			    0, 0, &floppy_fops, NULL);
+	} while (table_sup[UDP->cmos][i++]);
+    }
+}
+
 /*
  * Floppy Driver initialization
  * =============================
@@ -4064,7 +4089,8 @@
 
 	raw_cmd = 0;
 
-	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+	devfs_handle = devfs_mk_dir (NULL, "floppy", 0, NULL);
+	if (devfs_register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
 		printk("Unable to get major %d for floppy\n",MAJOR_NR);
 		return -EBUSY;
 	}
@@ -4095,7 +4121,7 @@
 	use_virtual_dma = can_use_virtual_dma & 1;
 	fdc_state[0].address = FDC1;
 	if (fdc_state[0].address == -1) {
-		unregister_blkdev(MAJOR_NR,"fd");
+		devfs_unregister_blkdev(MAJOR_NR,"fd");
 		del_timer(&fd_timeout);
 		return -ENODEV;
 	}
@@ -4107,7 +4133,7 @@
 	if (floppy_grab_irq_and_dma()){
 		del_timer(&fd_timeout);
 		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-		unregister_blkdev(MAJOR_NR,"fd");
+		devfs_unregister_blkdev(MAJOR_NR,"fd");
 		del_timer(&fd_timeout);
 		return -EBUSY;
 	}
@@ -4173,7 +4199,7 @@
  		if (usage_count)
  			floppy_release_irq_and_dma();
 		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-		unregister_blkdev(MAJOR_NR,"fd");		
+		devfs_unregister_blkdev(MAJOR_NR,"fd");		
 	}
 	
 	for (drive = 0; drive < N_DRIVE; drive++) {
@@ -4410,7 +4436,8 @@
 {
 	int dummy;
 		
-	unregister_blkdev(MAJOR_NR, "fd");
+	devfs_unregister (devfs_handle);
+	devfs_unregister_blkdev(MAJOR_NR, "fd");
 
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 	/* eject disk, if any */
diff -u --recursive --new-file v2.3.45/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.3.45/linux/drivers/block/genhd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/genhd.c	Wed Feb 16 15:42:05 2000
@@ -4,6 +4,8 @@
  *
  *  Copyright (C) 1991-1998  Linus Torvalds
  *
+ *  devfs support - jj, rgooch, 980122
+ *
  *  Moved partition checking code to fs/partitions* - Russell King
  *  (linux@arm.uk.linux.org)
  */
diff -u --recursive --new-file v2.3.45/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v2.3.45/linux/drivers/block/hd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/hd.c	Wed Feb 16 15:42:05 2000
@@ -32,6 +32,7 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/kernel.h>
 #include <linux/hdreg.h>
 #include <linux/genhd.h>
@@ -145,7 +146,7 @@
 	unsigned long flags;
 	char devc;
 
-	devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
+	devc = !QUEUE_EMPTY ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
 	save_flags (flags);
 	sti();
 #ifdef VERBOSE_ERRORS
@@ -174,7 +175,7 @@
 		if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
 			printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
 				inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
-			if (CURRENT)
+			if (!QUEUE_EMPTY)
 				printk(", sector=%ld", CURRENT->sector);
 		}
 		printk("\n");
@@ -351,7 +352,7 @@
 {
 	int dev;
 
-	if (!CURRENT)
+	if (QUEUE_EMPTY)
 		return;
 	dev = DEVICE_NR(CURRENT->rq_dev);
 	if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
@@ -414,7 +415,7 @@
 #if (HD_DELAY > 0)
 	last_req = read_timer();
 #endif
-	if (CURRENT)
+	if (!QUEUE_EMPTY)
 		hd_request();
 	return;
 }
@@ -475,7 +476,7 @@
 	unsigned int dev;
 
 	DEVICE_INTR = NULL;
-	if (!CURRENT)
+	if (QUEUE_EMPTY)
 		return;
 	disable_irq(HD_IRQ);
 	sti();
@@ -522,7 +523,7 @@
 {
 	unsigned int dev, block, nsect, sec, track, head, cyl;
 
-	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) return;
 	if (DEVICE_INTR)
 		return;
 repeat:
@@ -662,6 +663,8 @@
 	return 0;
 }
 
+extern struct block_device_operations hd_fops;
+
 static struct gendisk hd_gendisk = {
 	MAJOR_NR,	/* Major number */	
 	"hd",		/* Major name */
@@ -671,7 +674,8 @@
 	hd_sizes,	/* block sizes */
 	0,		/* number */
 	NULL,		/* internal use, not presently used */
-	NULL		/* next */
+	NULL,		/* next */
+	&hd_fops,       /* file operations */
 };
 	
 static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -800,7 +804,7 @@
 
 int __init hd_init(void)
 {
-	if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+	if (devfs_register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
 		printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
 		return -1;
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.3.45/linux/drivers/block/ide-cd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/ide-cd.c	Wed Feb 16 15:42:05 2000
@@ -299,7 +299,6 @@
  * Generic packet command support and error handling routines.
  */
 
-
 /* Mark that we've seen a media change, and invalidate our internal
    buffers. */
 static void cdrom_saw_media_change (ide_drive_t *drive)
@@ -2270,7 +2269,12 @@
 		devinfo->mask |= CDC_PLAY_AUDIO;
 	if (!CDROM_CONFIG_FLAGS (drive)->close_tray)
 		devinfo->mask |= CDC_CLOSE_TRAY;
-		
+
+	devinfo->de = devfs_register (drive->de, "cd", 2, DEVFS_FL_DEFAULT,
+				      HWIF(drive)->major, minor,
+				      S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
+				      ide_fops, NULL);
+
 	return register_cdrom (devinfo);
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.3.45/linux/drivers/block/ide-disk.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/block/ide-disk.c	Wed Feb 16 15:42:05 2000
@@ -744,6 +744,8 @@
 
 static void idedisk_setup (ide_drive_t *drive)
 {
+	int i;
+	
 	struct hd_driveid *id = drive->id;
 	unsigned long capacity;
 	
@@ -763,6 +765,15 @@
 		if (id->model[0] != 'W' || id->model[1] != 'D') {
 			drive->doorlocking = 1;
 		}
+	}
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		ide_hwif_t *hwif = HWIF(drive);
+
+		if (drive != &hwif->drives[i]) continue;
+		hwif->gd->de_arr[i] = drive->de;
+		if (drive->removable)
+			hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
+		break;
 	}
 
 	/* Extract geometry if we did not already have one for the drive */
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.3.45/linux/drivers/block/ide-floppy.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/block/ide-floppy.c	Wed Feb 16 15:42:05 2000
@@ -1549,6 +1549,15 @@
 
 	(void) idefloppy_get_capacity (drive);
 	idefloppy_add_settings(drive);
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		ide_hwif_t *hwif = HWIF(drive);
+
+		if (drive != &hwif->drives[i]) continue;
+		hwif->gd->de_arr[i] = drive->de;
+		if (drive->removable)
+			hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
+		break;
+	}
 }
 
 static int idefloppy_cleanup (ide_drive_t *drive)
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.3.45/linux/drivers/block/ide-probe.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/ide-probe.c	Wed Feb 16 15:42:05 2000
@@ -699,13 +699,28 @@
 	gd->nr_real	= units;		/* current num real drives */
 	gd->real_devices= hwif;			/* ptr to internal data */
 	gd->next	= NULL;			/* linked list of major devs */
+	gd->fops        = ide_fops;             /* file operations */
+	gd->de_arr	= kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL);
+	gd->flags	= kmalloc (sizeof *gd->flags * units, GFP_KERNEL);
+	if (gd->de_arr)
+		memset (gd->de_arr, 0, sizeof *gd->de_arr * units);
+	if (gd->flags)
+		memset (gd->flags, 0, sizeof *gd->flags * units);
 
 	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
 	hwif->gd = *gdp = gd;			/* link onto tail of list */
 
 	for (unit = 0; unit < units; ++unit) {
-		if (hwif->drives[unit].present)
+		if (hwif->drives[unit].present) {
+			char name[64];
+
 			ide_add_generic_settings(hwif->drives + unit);
+			sprintf (name, "ide/host%d/bus%d/target%d/lun%d",
+				 hwif->channel ? hwif->mate->index : hwif->index,
+				 hwif->channel, unit, 0);
+			hwif->drives[unit].de =
+				devfs_mk_dir (NULL, name, 0, NULL);
+		}
 	}
 }
 
@@ -764,7 +779,7 @@
 		printk("%s: request_fn NOT DEFINED\n", hwif->name);
 		return (hwif->present = 0);
 	}
-	if (register_blkdev (hwif->major, hwif->name, ide_fops)) {
+	if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) {
 		printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
 		return (hwif->present = 0);
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.3.45/linux/drivers/block/ide-tape.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/ide-tape.c	Wed Feb 16 15:42:05 2000
@@ -396,6 +396,7 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/major.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/errno.h>
 #include <linux/genhd.h>
 #include <linux/malloc.h>
@@ -794,6 +795,7 @@
  */
 typedef struct {
 	ide_drive_t *drive;
+	devfs_handle_t de_r, de_n;
 
 	/*
 	 *	Since a typical character device operation requires more
@@ -5770,11 +5772,13 @@
 	DRIVER(drive)->busy = 0;
 	(void) ide_unregister_subdriver (drive);
 	drive->driver_data = NULL;
+	devfs_unregister (tape->de_r);
+	devfs_unregister (tape->de_n);
 	kfree (tape);
 	for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++)
 		if (idetape_chrdevs[minor].drive != NULL)
 			return 0;
-	unregister_chrdev (IDETAPE_MAJOR, "ht");
+	devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
 	idetape_chrdev_present = 0;
 	return 0;
 }
@@ -5871,7 +5875,8 @@
 #endif
 		return 0;
 	}
-	if (!idetape_chrdev_present && register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) {
+	if (!idetape_chrdev_present &&
+	    devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) {
 		printk (KERN_ERR "ide-tape: Failed to register character device interface\n");
 		MOD_DEC_USE_COUNT;
 #if ONSTREAM_DEBUG
@@ -5905,10 +5910,21 @@
 		for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++);
 		idetape_setup (drive, tape, minor);
 		idetape_chrdevs[minor].drive = drive;
+		tape->de_r =
+		    devfs_register (drive->de, "mt", 2, DEVFS_FL_DEFAULT,
+				    HWIF(drive)->major, minor,
+				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+				    &idetape_fops, NULL);
+		tape->de_n =
+		    devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT,
+				    HWIF(drive)->major, minor + 128,
+				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+				    &idetape_fops, NULL);
+		devfs_register_tape (tape->de_r);
 		supported++; failed--;
 	} while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL);
 	if (!idetape_chrdev_present && !supported) {
-		unregister_chrdev (IDETAPE_MAJOR, "ht");
+		devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
 	} else
 		idetape_chrdev_present = 1;
 	ide_register_module (&idetape_module);
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.3.45/linux/drivers/block/ide.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/ide.c	Wed Feb 16 15:42:05 2000
@@ -501,8 +501,7 @@
 
 	if (!end_that_request_first(rq, uptodate, hwgroup->drive->name)) {
 		add_blkdev_randomness(MAJOR(rq->rq_dev));
-		hwgroup->drive->queue.current_request = rq->next;
-		blk_dev[MAJOR(rq->rq_dev)].request_queue.current_request = NULL;
+		blkdev_dequeue_request(rq);
         	hwgroup->rq = NULL;
 		end_that_request_last(rq);
 	}
@@ -772,8 +771,7 @@
 		}
 	}
 	spin_lock_irqsave(&io_request_lock, flags);
-	drive->queue.current_request = rq->next;
-	blk_dev[MAJOR(rq->rq_dev)].request_queue.current_request = NULL;
+	blkdev_dequeue_request(rq);
 	HWGROUP(drive)->rq = NULL;
 	rq->rq_status = RQ_INACTIVE;
 	spin_unlock_irqrestore(&io_request_lock, flags);
@@ -1076,7 +1074,7 @@
 {
 	ide_startstop_t startstop;
 	unsigned long block, blockend;
-	struct request *rq = drive->queue.current_request;
+	struct request *rq = blkdev_entry_next_request(&drive->queue.queue_head);
 	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
 	ide_hwif_t *hwif = HWIF(drive);
 
@@ -1159,13 +1157,12 @@
 	best = NULL;
 	drive = hwgroup->drive;
 	do {
-		if (drive->queue.current_request && (!drive->sleep || 0 <= (signed long)(jiffies - drive->sleep))) {
+		if (!list_empty(&drive->queue.queue_head) && (!drive->sleep || 0 <= (signed long)(jiffies - drive->sleep))) {
 			if (!best
 			 || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
 			 || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
 			{
-				struct blk_dev_struct *bdev = &blk_dev[HWIF(drive)->major];
-				if( !bdev->request_queue.plugged )
+				if( !drive->queue.plugged )
 					best = drive;
 			}
 		}
@@ -1229,7 +1226,6 @@
  */
 static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
 {
-	struct blk_dev_struct *bdev;
 	ide_drive_t	*drive;
 	ide_hwif_t	*hwif;
 	ide_startstop_t	startstop;
@@ -1246,9 +1242,6 @@
 			hwgroup->rq = NULL;
 			drive = hwgroup->drive;
 			do {
-				bdev = &blk_dev[HWIF(drive)->major];
-				if( !bdev->request_queue.plugged )
-					bdev->request_queue.current_request = NULL;		/* (broken since patch-2.1.15) */
 				if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
 					sleep = drive->sleep;
 			} while ((drive = drive->next) != hwgroup->drive);
@@ -1285,10 +1278,9 @@
 		drive->sleep = 0;
 		drive->service_start = jiffies;
 
-		bdev = &blk_dev[hwif->major];
-		if ( bdev->request_queue.plugged )	/* FIXME: paranoia */
+		if ( drive->queue.plugged )	/* paranoia */
 			printk("%s: Huh? nuking plugged queue\n", drive->name);
-		bdev->request_queue.current_request = hwgroup->rq = drive->queue.current_request;
+		hwgroup->rq = blkdev_entry_next_request(&drive->queue.queue_head);
 		/*
 		 * Some systems have trouble with IDE IRQs arriving while
 		 * the driver is still setting things up.  So, here we disable
@@ -1670,7 +1662,7 @@
 	rq->sem = NULL;
 	rq->bh = NULL;
 	rq->bhtail = NULL;
-	rq->next = NULL;
+	rq->q = NULL;
 }
 
 /*
@@ -1703,7 +1695,7 @@
 	unsigned long flags;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	unsigned int major = HWIF(drive)->major;
-	struct request *cur_rq;
+	struct list_head * queue_head;
 	DECLARE_MUTEX_LOCKED(sem);
 
 #ifdef CONFIG_BLK_DEV_PDC4030
@@ -1716,20 +1708,17 @@
 	if (action == ide_wait)
 		rq->sem = &sem;
 	spin_lock_irqsave(&io_request_lock, flags);
-	cur_rq = drive->queue.current_request;
-	if (cur_rq == NULL || action == ide_preempt) {
-		rq->next = cur_rq;
-		drive->queue.current_request = rq;
+	queue_head = &drive->queue.queue_head;
+	if (list_empty(queue_head) || action == ide_preempt) {
 		if (action == ide_preempt)
 			hwgroup->rq = NULL;
 	} else {
 		if (action == ide_wait || action == ide_end) {
-			while (cur_rq->next != NULL)	/* find end of list */
-				cur_rq = cur_rq->next;
-		}
-		rq->next = cur_rq->next;
-		cur_rq->next = rq;
+			queue_head = queue_head->prev;
+		} else
+			queue_head = queue_head->next;
 	}
+	list_add(&rq->queue, queue_head);
 	ide_do_request(hwgroup, 0);
 	spin_unlock_irqrestore(&io_request_lock, flags);
 	if (action == ide_wait) {
@@ -1989,6 +1978,10 @@
 	d = hwgroup->drive;
 	for (i = 0; i < MAX_DRIVES; ++i) {
 		drive = &hwif->drives[i];
+		if (drive->de) {
+			devfs_unregister (drive->de);
+			drive->de = NULL;
+		}
 		if (!drive->present)
 			continue;
 		while (hwgroup->drive->next != drive)
@@ -2037,6 +2030,10 @@
 		gd = *gdp; *gdp = gd->next;
 		kfree(gd->sizes);
 		kfree(gd->part);
+		if (gd->de_arr)
+			kfree (gd->de_arr);
+		if (gd->flags)
+			kfree (gd->flags);
 		kfree(gd);
 	}
 	old_hwif = *hwif;
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.3.45/linux/drivers/block/ll_rw_blk.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/ll_rw_blk.c	Wed Feb 16 14:36:45 2000
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 1991, 1992 Linus Torvalds
  * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics
+ * Elevator latency, (C) 2000  Andrea Arcangeli <andrea@suse.de> SuSE
  */
 
 /*
@@ -27,6 +28,8 @@
 
 #include <linux/module.h>
 
+#define DEBUG_ELEVATOR
+
 /*
  * MAC Floppy IWM hooks
  */
@@ -147,6 +150,18 @@
 	return ret;
 }
 
+static inline int get_request_latency(elevator_t * elevator, int rw)
+{
+	int latency;
+
+	if (rw != READ)
+		latency = elevator->write_latency;
+	else
+		latency = elevator->read_latency;
+
+	return latency;
+}
+
 void blk_cleanup_queue(request_queue_t * q)
 {
 	memset(q, 0, sizeof(*q));
@@ -167,55 +182,49 @@
 	q->make_request_fn = mfn;
 }
 
-static int ll_merge_fn(request_queue_t *q, struct request *req, 
-		       struct buffer_head *bh) 
+static inline int ll_new_segment(request_queue_t *q, struct request *req, int max_segments)
 {
-	if (req->bhtail->b_data + req->bhtail->b_size != bh->b_data) {
-		if (req->nr_segments < MAX_SEGMENTS) {
-			req->nr_segments++;
-			return 1;
-		}
-		return 0;
+	if (req->nr_segments < max_segments) {
+		req->nr_segments++;
+		q->nr_segments++;
+		return 1;
 	}
-	return 1;
+	return 0;
+}
+
+static int ll_back_merge_fn(request_queue_t *q, struct request *req, 
+			    struct buffer_head *bh, int max_segments)
+{
+	if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data)
+		return 1;
+	return ll_new_segment(q, req, max_segments);
+}
+
+static int ll_front_merge_fn(request_queue_t *q, struct request *req, 
+			     struct buffer_head *bh, int max_segments)
+{
+	if (bh->b_data + bh->b_size == req->bh->b_data)
+		return 1;
+	return ll_new_segment(q, req, max_segments);
 }
 
 static int ll_merge_requests_fn(request_queue_t *q, struct request *req,
-				struct request *next)
+				struct request *next, int max_segments)
 {
 	int total_segments = req->nr_segments + next->nr_segments;
 
-	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
+	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
 		total_segments--;
+		q->nr_segments--;
+	}
     
-	if (total_segments > MAX_SEGMENTS)
+	if (total_segments > max_segments)
 		return 0;
 
 	req->nr_segments = total_segments;
 	return 1;
 }
 
-void blk_init_queue(request_queue_t * q, request_fn_proc * rfn)
-{
-	q->request_fn     	= rfn;
-	q->current_request	= NULL;
-	q->merge_fn       	= ll_merge_fn;
-	q->merge_requests_fn	= ll_merge_requests_fn;
-	q->make_request_fn	= NULL;
-	q->plug_tq.sync   	= 0;
-	q->plug_tq.routine	= &generic_unplug_device;
-	q->plug_tq.data   	= q;
-	q->plugged        	= 0;
-	/*
-	 * These booleans describe the queue properties.  We set the
-	 * default (and most common) values here.  Other drivers can
-	 * use the appropriate functions to alter the queue properties.
-	 * as appropriate.
-	 */
-	q->plug_device_fn 	= NULL;
-	q->head_active    	= 1;
-}
-
 /*
  * "plug" the device if there are no outstanding requests: this will
  * force the transfer to start only after we have put all the requests
@@ -224,19 +233,44 @@
  * This is called with interrupts off and no requests on the queue.
  * (and with the request spinlock aquired)
  */
-inline void generic_plug_device (request_queue_t *q, kdev_t dev)
+static void generic_plug_device (request_queue_t *q, kdev_t dev)
 {
+#ifdef CONFIG_BLK_DEV_MD
 	if (MAJOR(dev) == MD_MAJOR) {
 		spin_unlock_irq(&io_request_lock);
 		BUG();
 	}
-	if (q->current_request)
+#endif
+	if (!list_empty(&q->queue_head))
 		return;
 
 	q->plugged = 1;
 	queue_task(&q->plug_tq, &tq_disk);
 }
 
+void blk_init_queue(request_queue_t * q, request_fn_proc * rfn)
+{
+	INIT_LIST_HEAD(&q->queue_head);
+	q->elevator = ELEVATOR_DEFAULTS;
+	q->request_fn     	= rfn;
+	q->back_merges_fn       	= ll_back_merge_fn;
+	q->front_merge_fn      	= ll_front_merge_fn;
+	q->merge_requests_fn	= ll_merge_requests_fn;
+	q->make_request_fn	= NULL;
+	q->plug_tq.sync   	= 0;
+	q->plug_tq.routine	= &generic_unplug_device;
+	q->plug_tq.data   	= q;
+	q->plugged        	= 0;
+	/*
+	 * These booleans describe the queue properties.  We set the
+	 * default (and most common) values here.  Other drivers can
+	 * use the appropriate functions to alter the queue properties.
+	 * as appropriate.
+	 */
+	q->plug_device_fn 	= generic_plug_device;
+	q->head_active    	= 1;
+}
+
 /*
  * remove the plug and let it rip..
  */
@@ -248,7 +282,7 @@
 	spin_lock_irqsave(&io_request_lock,flags);
 	if (q->plugged) {
 		q->plugged = 0;
-		if (q->current_request)
+		if (!list_empty(&q->queue_head))
 			(q->request_fn)(q);
 	}
 	spin_unlock_irqrestore(&io_request_lock,flags);
@@ -388,6 +422,124 @@
 		printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
 }
 
+/* elevator */
+
+#define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0)
+#define elevator_sequence_before(a,b) elevator_sequence_after(b,a)
+#define elevator_sequence_after_eq(a,b) ((int)((b)-(a)) <= 0)
+#define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a)
+
+static inline struct list_head * seek_to_not_starving_chunk(request_queue_t * q,
+							    int * lat, int * starving)
+{
+	int sequence = q->elevator.sequence;
+	struct list_head * entry = q->queue_head.prev;
+	int pos = 0;
+
+	do {
+		struct request * req = blkdev_entry_to_request(entry);
+		if (elevator_sequence_before(req->elevator_sequence, sequence)) {
+			*lat -= q->nr_segments - pos;
+			*starving = 1;
+			return entry;
+		}
+		pos += req->nr_segments;
+	} while ((entry = entry->prev) != &q->queue_head);
+
+	*starving = 0;
+
+	return entry->next;
+}
+
+static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next)
+{
+	if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence))
+		req->elevator_sequence = next->elevator_sequence;
+	if (req->cmd == READ)
+		e->read_pendings--;
+
+}
+
+static inline int elevator_sequence(elevator_t * e, int latency)
+{
+	return latency + e->sequence;
+}
+
+#define elevator_merge_before(q, req, lat)	__elevator_merge((q), (req), (lat), 0)
+#define elevator_merge_after(q, req, lat)	__elevator_merge((q), (req), (lat), 1)
+static inline void __elevator_merge(request_queue_t * q, struct request * req, int latency, int after)
+{
+#ifdef DEBUG_ELEVATOR
+	int sequence = elevator_sequence(&q->elevator, latency);
+	if (after)
+		sequence -= req->nr_segments;
+	if (elevator_sequence_before(sequence, req->elevator_sequence)) {
+		static int warned = 0;
+		if (!warned) {
+			printk(KERN_WARNING __FUNCTION__
+			       ": req latency %d req latency %d\n",
+			       req->elevator_sequence - q->elevator.sequence,
+			       sequence - q->elevator.sequence);
+			warned = 1;
+		}
+		req->elevator_sequence = sequence;
+	}
+#endif
+}
+
+static inline void elevator_queue(request_queue_t * q,
+				  struct request * req,
+				  struct list_head * entry,
+				  int latency, int starving)
+{
+	struct request * tmp, * __tmp;
+	int __latency = latency;
+
+	__tmp = tmp = blkdev_entry_to_request(entry);
+
+	for (;; tmp = blkdev_next_request(tmp))
+	{
+		if ((latency -= tmp->nr_segments) <= 0)
+		{
+			tmp = __tmp;
+			latency = __latency;
+
+			if (starving)
+				break;
+
+			if (q->head_active && !q->plugged)
+			{
+				latency -= tmp->nr_segments;
+				break;
+			}
+
+			list_add(&req->queue, &q->queue_head);
+			goto after_link;
+		}
+
+		if (tmp->queue.next == &q->queue_head)
+			break;
+
+		{
+			const int after_current = IN_ORDER(tmp,req);
+			const int before_next = IN_ORDER(req,blkdev_next_request(tmp));
+
+			if (!IN_ORDER(tmp,blkdev_next_request(tmp))) {
+				if (after_current || before_next)
+					break;
+			} else {
+				if (after_current && before_next)
+					break;
+			}
+		}
+	}
+
+	list_add(&req->queue, &tmp->queue);
+
+ after_link:
+	req->elevator_sequence = elevator_sequence(&q->elevator, latency);
+}
+
 /*
  * add-request adds a request to the linked list.
  * It disables interrupts (aquires the request spinlock) so that it can muck
@@ -398,32 +550,20 @@
  * which is important for drive_stat_acct() above.
  */
 
-static inline void __add_request(request_queue_t * q, struct request * req)
+static inline void __add_request(request_queue_t * q, struct request * req,
+				 int empty, struct list_head * entry,
+				 int latency, int starving)
 {
-	int major = MAJOR(req->rq_dev);
-	struct request * tmp;
+	int major;
 
 	drive_stat_acct(req, req->nr_sectors, 1);
-	req->next = NULL;
 
-	if (!(tmp = q->current_request)) {
-		q->current_request = req;
+	if (empty) {
+		req->elevator_sequence = elevator_sequence(&q->elevator, latency);
+		list_add(&req->queue, &q->queue_head);
 		return;
 	}
-	for ( ; tmp->next ; tmp = tmp->next) {
-		const int after_current = IN_ORDER(tmp,req);
-		const int before_next = IN_ORDER(req,tmp->next);
-
-		if (!IN_ORDER(tmp,tmp->next)) {
-			if (after_current || before_next)
-				break;
-		} else {
-			if (after_current && before_next)
-				break;
-		}
-	}
-	req->next = tmp->next;
-	tmp->next = req;
+	elevator_queue(q, req, entry, latency, starving);
 
 	/*
 	 * FIXME(eric) I don't understand why there is a need for this
@@ -432,6 +572,7 @@
 	 * I am leaving this in here until I hear back from the COMPAQ
 	 * people.
 	 */
+	major = MAJOR(req->rq_dev);
 	if (major >= COMPAQ_SMART2_MAJOR+0 && major <= COMPAQ_SMART2_MAJOR+7)
 	{
 		(q->request_fn)(q);
@@ -448,12 +589,14 @@
  */
 static inline void attempt_merge (request_queue_t * q,
 				  struct request *req,
-				  int max_sectors)
+				  int max_sectors,
+				  int max_segments)
 {
-	struct request *next = req->next;
-
-	if (!next)
+	struct request *next;
+  
+	if (req->queue.next == &q->queue_head)
 		return;
+	next = blkdev_next_request(req);
 	if (req->sector + req->nr_sectors != next->sector)
 		return;
 	if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors)
@@ -464,25 +607,79 @@
 	 * will have been updated to the appropriate number,
 	 * and we shouldn't do it here too.
 	 */
-	if(!(q->merge_requests_fn)(q, req, next))
+	if(!(q->merge_requests_fn)(q, req, next, max_segments))
 		return;
 
+	elevator_merge_requests(&q->elevator, req, next);
 	req->bhtail->b_reqnext = next->bh;
 	req->bhtail = next->bhtail;
 	req->nr_sectors += next->nr_sectors;
 	next->rq_status = RQ_INACTIVE;
-	req->next = next->next;
+	list_del(&next->queue);
 	wake_up (&wait_for_request);
 }
 
+static inline void elevator_debug(request_queue_t * q, kdev_t dev)
+{
+#ifdef DEBUG_ELEVATOR
+	int read_pendings = 0, nr_segments = 0;
+	elevator_t * elevator = &q->elevator;
+	struct list_head * entry = &q->queue_head;
+	static int counter;
+
+	if (counter++ % 100)
+		return;
+
+	while ((entry = entry->next) != &q->queue_head)
+	{
+		struct request * req;
+
+		req = blkdev_entry_to_request(entry);
+		if (!req->q)
+			continue;
+		if (req->cmd == READ)
+			read_pendings++;
+		nr_segments += req->nr_segments;
+	}
+
+	if (read_pendings != elevator->read_pendings)
+	{
+		printk(KERN_WARNING
+		       "%s: elevator read_pendings %d should be %d\n",
+		       kdevname(dev), elevator->read_pendings,
+		       read_pendings);
+		elevator->read_pendings = read_pendings;
+	}
+	if (nr_segments != q->nr_segments)
+	{
+		printk(KERN_WARNING
+		       "%s: elevator nr_segments %d should be %d\n",
+		       kdevname(dev), q->nr_segments,
+		       nr_segments);
+		q->nr_segments = nr_segments;
+	}
+#endif
+}
+
+static inline void elevator_account_request(request_queue_t * q, struct request * req)
+{
+	q->elevator.sequence++;
+	if (req->cmd == READ)
+		q->elevator.read_pendings++;
+	q->nr_segments++;
+}
+
 static inline void __make_request(request_queue_t * q, int rw,
 			   struct buffer_head * bh)
 {
 	int major = MAJOR(bh->b_rdev);
 	unsigned int sector, count;
-	struct request * req;
+	int max_segments = MAX_SEGMENTS;
+	struct request * req, * prev;
 	int rw_ahead, max_req, max_sectors;
 	unsigned long flags;
+	int orig_latency, latency, __latency, starving, __starving, empty;
+	struct list_head * entry, * __entry;
 
 	count = bh->b_size >> 9;
 	sector = bh->b_rsector;
@@ -569,21 +766,33 @@
 	 */
 	max_sectors = get_max_sectors(bh->b_rdev);
 
+	__latency = orig_latency = get_request_latency(&q->elevator, rw);
+
 	/*
 	 * Now we acquire the request spinlock, we have to be mega careful
 	 * not to schedule or do something nonatomic
 	 */
 	spin_lock_irqsave(&io_request_lock,flags);
-	req = q->current_request;
-	if (!req) {
-		/* MD and loop can't handle plugging without deadlocking */
-		if (q->plug_device_fn)
-			q->plug_device_fn(q, bh->b_rdev); /* is atomic */
-		else
-			generic_plug_device(q, bh->b_rdev); /* is atomic */
+	elevator_debug(q, bh->b_rdev);
+
+	empty = 0;
+	if (list_empty(&q->queue_head)) {
+		empty = 1;
+		q->plug_device_fn(q, bh->b_rdev); /* is atomic */
 		goto get_rq;
 	}
 
+	/* avoid write-bombs to not hurt iteractiveness of reads */
+	if (rw != READ && q->elevator.read_pendings)
+		max_segments = q->elevator.max_bomb_segments;
+
+	entry = seek_to_not_starving_chunk(q, &__latency, &starving);
+
+	__entry = entry;
+	__starving = starving;
+
+	latency = __latency;
+
 	if (q->head_active && !q->plugged) {
 		/*
 		 * The scsi disk and cdrom drivers completely remove the request
@@ -595,11 +804,18 @@
 		 * entry may be busy being processed and we thus can't change
 		 * it.
 		 */
-		if ((req = req->next) == NULL)
-			goto get_rq;
+		if (entry == q->queue_head.next) {
+			latency -= blkdev_entry_to_request(entry)->nr_segments;
+			if ((entry = entry->next) == &q->queue_head)
+				goto get_rq;
+			starving = 0;
+		}
 	}
 
+	prev = NULL;
 	do {
+		req = blkdev_entry_to_request(entry);
+
 		if (req->sem)
 			continue;
 		if (req->cmd != rw)
@@ -610,6 +826,8 @@
 			continue;
 		/* Can we add it to the end of this request? */
 		if (req->sector + req->nr_sectors == sector) {
+			if (latency - req->nr_segments < 0)
+				break;
 			/*
 			 * The merge_fn is a more advanced way
 			 * of accomplishing the same task.  Instead
@@ -622,16 +840,21 @@
 			 * may suggest that we shouldn't merge
 			 * this 
 			 */
-			if(!(q->merge_fn)(q, req, bh))
+			if(!(q->back_merge_fn)(q, req, bh, max_segments))
 				continue;
 			req->bhtail->b_reqnext = bh;
 			req->bhtail = bh;
 		    	req->nr_sectors += count;
 			drive_stat_acct(req, count, 0);
+
+			elevator_merge_after(q, req, latency);
+
 			/* Can we now merge this req with the next? */
-			attempt_merge(q, req, max_sectors);
+			attempt_merge(q, req, max_sectors, max_segments);
 		/* or to the beginning? */
 		} else if (req->sector - count == sector) {
+			if (!prev && starving)
+				continue;
 			/*
 			 * The merge_fn is a more advanced way
 			 * of accomplishing the same task.  Instead
@@ -644,7 +867,7 @@
 			 * may suggest that we shouldn't merge
 			 * this 
 			 */
-			if(!(q->merge_fn)(q, req, bh))
+			if(!(q->front_merge_fn)(q, req, bh, max_segments))
 				continue;
 		    	bh->b_reqnext = req->bh;
 		    	req->bh = bh;
@@ -653,13 +876,21 @@
 		    	req->sector = sector;
 		    	req->nr_sectors += count;
 			drive_stat_acct(req, count, 0);
+
+			elevator_merge_before(q, req, latency);
+
+			if (prev)
+				attempt_merge(q, prev, max_sectors, max_segments);
 		} else
 			continue;
 
+		q->elevator.sequence++;
 		spin_unlock_irqrestore(&io_request_lock,flags);
 	    	return;
 
-	} while ((req = req->next) != NULL);
+	} while (prev = req,
+		 (latency -= req->nr_segments) >= 0 &&
+		 (entry = entry->next) != &q->queue_head);
 
 /* find an unused request. */
 get_rq:
@@ -675,6 +906,14 @@
 			goto end_io;
 		req = __get_request_wait(max_req, bh->b_rdev);
 		spin_lock_irqsave(&io_request_lock,flags);
+
+		/* lock got dropped so revalidate elevator */
+		empty = 1;
+		if (!list_empty(&q->queue_head)) {
+			empty = 0;
+			__latency = orig_latency;
+			__entry = seek_to_not_starving_chunk(q, &__latency, &__starving);
+		}
 	}
 	/*
 	 * Dont start the IO if the buffer has been
@@ -707,8 +946,10 @@
 	req->sem = NULL;
 	req->bh = bh;
 	req->bhtail = bh;
-	req->next = NULL;
-	__add_request(q, req);
+	req->q = q;
+	__add_request(q, req, empty, __entry, __latency, __starving);
+	elevator_account_request(q, req);
+
 	spin_unlock_irqrestore(&io_request_lock, flags);
 	return;
 
@@ -867,6 +1108,8 @@
 
 void end_that_request_last(struct request *req)
 {
+	if (req->q)
+		BUG();
 	if (req->sem != NULL)
 		up(req->sem);
 	req->rq_status = RQ_INACTIVE;
@@ -886,7 +1129,6 @@
 	req = all_requests + NR_REQUEST;
 	while (--req >= all_requests) {
 		req->rq_status = RQ_INACTIVE;
-		req->next = NULL;
 	}
 	memset(ro_bits,0,sizeof(ro_bits));
 	memset(max_readahead, 0, sizeof(max_readahead));
diff -u --recursive --new-file v2.3.45/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.3.45/linux/drivers/block/loop.c	Sun Feb 13 19:29:03 2000
+++ linux/drivers/block/loop.c	Wed Feb 16 15:42:05 2000
@@ -14,6 +14,8 @@
  *
  * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997
  *
+ * Added devfs support - Richard Gooch <rgooch@atnf.csiro.au> 16-Jan-1998
+ *
  * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998
  *
  * Loadable modules and other fixes by AK, 1998
@@ -56,6 +58,7 @@
 #include <linux/major.h>
 
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/uaccess.h>
 
@@ -77,6 +80,7 @@
 static struct loop_device *loop_dev;
 static int *loop_sizes;
 static int *loop_blksizes;
+static devfs_handle_t devfs_handle = NULL;      /*  For the directory        */
 
 #define FALSE 0
 #define TRUE (!FALSE)
@@ -198,8 +202,6 @@
 		offset = 0;
 		index++;
 		pos += size;
-		if (pos > lo->lo_dentry->d_inode->i_size)
-			lo->lo_dentry->d_inode->i_size = pos;
 		UnlockPage(page);
 		page_cache_release(page);
 	}
@@ -277,7 +279,7 @@
 repeat:
 	INIT_REQUEST;
 	current_request=CURRENT;
-	CURRENT=current_request->next;
+	blkdev_dequeue_request(current_request);
 	if (MINOR(current_request->rq_dev) >= max_loop)
 		goto error_out;
 	lo = &loop_dev[MINOR(current_request->rq_dev)];
@@ -375,15 +377,13 @@
 	spin_lock_irq(&io_request_lock);
 	current_request->sector += current_request->current_nr_sectors;
 	current_request->nr_sectors -= current_request->current_nr_sectors;
-	current_request->next=CURRENT;
-	CURRENT=current_request;
+	list_add(&current_request->queue, &current_request->q->queue_head);
 	end_request(1);
 	goto repeat;
 error_out_lock:
 	spin_lock_irq(&io_request_lock);
 error_out:
-	current_request->next=CURRENT;
-	CURRENT=current_request;
+	list_add(&current_request->queue, &current_request->q->queue_head);
 	end_request(0);
 	goto repeat;
 }
@@ -453,7 +453,7 @@
 				lo->lo_backing_file = NULL;
 			}
 		}
-		aops = lo->lo_dentry->d_inode->i_mapping->a_ops;
+		aops = inode->i_mapping->a_ops;
 		/*
 		 * If we can't read - sorry. If we only can't write - well,
 		 * it's going to be read-only.
@@ -754,11 +754,16 @@
 {
 	int	i;
 
-	if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
+	if (devfs_register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
 		printk(KERN_WARNING "Unable to get major number %d for loop device\n",
 		       MAJOR_NR);
 		return -EIO;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "loop", 0, NULL);
+	devfs_register_series (devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT,
+			       MAJOR_NR, 0,
+			       S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
+			       &lo_fops, NULL);
 
 	if ((max_loop < 1) || (max_loop > 255)) {
 		printk (KERN_WARNING "loop: invalid max_loop (must be between 1 and 255), using default (8)\n");
@@ -790,6 +795,7 @@
 	}		
 
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0);
 	for (i=0; i < max_loop; i++) {
 		memset(&loop_dev[i], 0, sizeof(struct loop_device));
 		loop_dev[i].lo_number = i;
@@ -807,7 +813,8 @@
 #ifdef MODULE
 void cleanup_module(void) 
 {
-	if (unregister_blkdev(MAJOR_NR, "loop") != 0)
+	devfs_unregister (devfs_handle);
+	if (devfs_unregister_blkdev(MAJOR_NR, "loop") != 0)
 		printk(KERN_WARNING "loop: cannot unregister blkdev\n");
 
 	kfree (loop_dev);
diff -u --recursive --new-file v2.3.45/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.3.45/linux/drivers/block/md.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/md.c	Wed Feb 16 15:42:05 2000
@@ -11,6 +11,7 @@
    - kerneld support by Boris Tobotras <boris@xtalk.msk.su>
    - kmod support by: Cyrus Durgin
    - RAID0 bugfixes: Mark Anthony Lisher <markal@iname.com>
+   - Devfs support by Richard Gooch <rgooch@atnf.csiro.au>
 
    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
@@ -24,6 +25,7 @@
 
 #include <linux/config.h>
 #include <linux/raid/md.h>
+#include <linux/devfs_fs_kernel.h>
 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
@@ -68,6 +70,9 @@
 
 int md_size[MAX_MD_DEVS] = {0, };
 
+extern struct block_device_operations md_fops;
+static devfs_handle_t devfs_handle = NULL;
+
 static struct gendisk md_gendisk=
 {
 	MD_MAJOR,
@@ -78,7 +83,8 @@
 	md_size,
 	MAX_MD_DEVS,
 	NULL,
-	NULL
+	NULL,
+	&md_fops,
 };
 
 void md_plug_device (request_queue_t *mdqueue, kdev_t dev)
@@ -3302,11 +3308,15 @@
 			MD_MAJOR_VERSION, MD_MINOR_VERSION,
 			MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MAX_REAL);
 
-	if (register_blkdev (MD_MAJOR, "md", &md_fops))
+	if (devfs_register_blkdev (MD_MAJOR, "md", &md_fops))
 	{
 		printk (KERN_ALERT "Unable to get major %d for md\n", MD_MAJOR);
 		return (-1);
 	}
+	devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL);
+	devfs_register_series (devfs_handle, "%u", MAX_MD_DEV,DEVFS_FL_DEFAULT,
+			       MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, 0, 0,
+			       &md_fops, NULL);
 
 	blk_dev[MD_MAJOR].queue = md_get_queue;
 
diff -u --recursive --new-file v2.3.45/linux/drivers/block/nbd.c linux/drivers/block/nbd.c
--- v2.3.45/linux/drivers/block/nbd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/nbd.c	Wed Feb 16 10:56:45 2000
@@ -184,10 +184,10 @@
 	DEBUG("reading control, ");
 	reply.magic = 0;
 	result = nbd_xmit(0, lo->sock, (char *) &reply, sizeof(reply));
-	req = lo->tail;
 	if (result <= 0)
 		HARDFAIL("Recv control failed.");
 	memcpy(&xreq, reply.handle, sizeof(xreq));
+	req = blkdev_entry_prev_request(&lo->queue_head);
 
 	if (xreq != req)
 		FAIL("Unexpected handle received.\n");
@@ -216,47 +216,42 @@
 {
 	struct request *req;
 
-	while (1) {
+	down (&lo->queue_lock);
+	while (!list_empty(&lo->queue_head)) {
 		req = nbd_read_stat(lo);
 		if (!req)
-			return;
-		down (&lo->queue_lock);
+			goto out;
 #ifdef PARANOIA
-		if (req != lo->tail) {
+		if (req != blkdev_entry_prev_request(&lo->queue_head)) {
 			printk(KERN_ALERT "NBD: I have problem...\n");
 		}
 		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
 			printk(KERN_ALERT "NBD: request corrupted!\n");
-			goto next;
+			continue;
 		}
 		if (lo->magic != LO_MAGIC) {
 			printk(KERN_ALERT "NBD: nbd_dev[] corrupted: Not enough magic\n");
-			up (&lo->queue_lock);
-			return;
+			goto out;
 		}
 #endif
-		nbd_end_request(req);
-		if (lo->tail == lo->head) {
-#ifdef PARANOIA
-			if (lo->tail->next)
-				printk(KERN_ERR "NBD: I did not expect this\n");
-#endif
-			lo->head = NULL;
-		}
-		lo->tail = lo->tail->next;
-	next:
+		list_del(&req->queue);
 		up (&lo->queue_lock);
+		
+		nbd_end_request(req);
+
+		down (&lo->queue_lock);
 	}
+ out:
+	up (&lo->queue_lock);
 }
 
 void nbd_clear_que(struct nbd_device *lo)
 {
 	struct request *req;
+	unsigned long flags;
 
-	while (1) {
-		req = lo->tail;
-		if (!req)
-			return;
+	while (!list_empty(&lo->queue_head)) {
+		req = blkdev_entry_prev_request(&lo->queue_head);
 #ifdef PARANOIA
 		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
 			printk(KERN_ALERT "NBD: request corrupted when clearing!\n");
@@ -268,15 +263,12 @@
 		}
 #endif
 		req->errors++;
+		list_del(&req->queue);
+		up(&lo->queue_lock);
+
 		nbd_end_request(req);
-		if (lo->tail == lo->head) {
-#ifdef PARANOIA
-			if (lo->tail->next)
-				printk(KERN_ERR "NBD: I did not assume this\n");
-#endif
-			lo->head = NULL;
-		}
-		lo->tail = lo->tail->next;
+
+		down(&lo->queue_lock);
 	}
 }
 
@@ -296,7 +288,7 @@
 	int dev;
 	struct nbd_device *lo;
 
-	while (CURRENT) {
+	while (!QUEUE_EMPTY) {
 		req = CURRENT;
 		dev = MINOR(req->rq_dev);
 #ifdef PARANOIA
@@ -314,28 +306,23 @@
 		requests_in++;
 #endif
 		req->errors = 0;
-		CURRENT = CURRENT->next;
-		req->next = NULL;
-
+		blkdev_dequeue_request(req);
 		spin_unlock_irq(&io_request_lock);
-		down (&lo->queue_lock);
-		if (lo->head == NULL) {
-			lo->head = req;
-			lo->tail = req;
-		} else {
-			lo->head->next = req;
-			lo->head = req;
-		}
 
+		down (&lo->queue_lock);
+		list_add(&req->queue, &lo->queue_head);
 		nbd_send_req(lo->sock, req);	/* Why does this block?         */
 		up (&lo->queue_lock);
+
 		spin_lock_irq(&io_request_lock);
 		continue;
 
 	      error_out:
 		req->errors++;
+		blkdev_dequeue_request(req);
+		spin_unlock(&io_request_lock);
 		nbd_end_request(req);
-		CURRENT = CURRENT->next;
+		spin_lock(&io_request_lock);
 	}
 	return;
 }
@@ -359,11 +346,14 @@
 	lo = &nbd_dev[dev];
 	switch (cmd) {
 	case NBD_CLEAR_SOCK:
+		down(&lo->queue_lock);
 		nbd_clear_que(lo);
-		if (lo->head || lo->tail) {
+		if (!list_empty(&lo->queue_head)) {
+			up(&lo->queue_lock);
 			printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n");
 			return -EBUSY;
 		}
+		up(&lo->queue_lock);
 		file = lo->file;
 		if (!file)
 			return -EINVAL;
@@ -415,8 +405,8 @@
 		return 0;
 #ifdef PARANOIA
 	case NBD_PRINT_DEBUG:
-		printk(KERN_INFO "NBD device %d: head = %lx, tail = %lx. Global: in %d, out %d\n",
-		       dev, (long) lo->head, (long) lo->tail, requests_in, requests_out);
+		printk(KERN_INFO "NBD device %d: queue_head = %p. Global: in %d, out %d\n",
+		       dev, lo->queue_head, requests_in, requests_out);
 		return 0;
 #endif
 	case BLKGETSIZE:
@@ -480,6 +470,7 @@
 	blksize_size[MAJOR_NR] = nbd_blksizes;
 	blk_size[MAJOR_NR] = nbd_sizes;
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_nbd_request);
+	blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0);
 	for (i = 0; i < MAX_NBD; i++) {
 		nbd_dev[i].refcnt = 0;
 		nbd_dev[i].file = NULL;
diff -u --recursive --new-file v2.3.45/linux/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- v2.3.45/linux/drivers/block/paride/pcd.c	Tue Dec 14 01:27:23 1999
+++ linux/drivers/block/paride/pcd.c	Wed Feb 16 10:56:45 2000
@@ -756,7 +756,7 @@
 
 	if (pcd_busy) return;
         while (1) {
-	    if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+	    if (QUEUE_EMPTY || (CURRENT->rq_status == RQ_INACTIVE)) return;
 	    INIT_REQUEST;
 	    if (CURRENT->cmd == READ) {
 		unit = MINOR(CURRENT->rq_dev);
diff -u --recursive --new-file v2.3.45/linux/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c
--- v2.3.45/linux/drivers/block/paride/pd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/paride/pd.c	Wed Feb 16 15:42:05 2000
@@ -156,6 +156,7 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/genhd.h>
@@ -339,6 +340,8 @@
 
 /* kernel glue structures */
 
+extern struct block_device_operations pd_fops;
+
 static struct gendisk pd_gendisk = {
         PD_MAJOR,       /* Major number */
         PD_NAME,        /* Major name */
@@ -348,7 +351,8 @@
         pd_sizes,       /* block sizes */
         0,              /* number */
         NULL,           /* internal */
-        NULL            /* next */
+        NULL,           /* next */
+	&pd_fops,       /* block device operations */
 };
 
 static struct block_device_operations pd_fops = {
@@ -386,8 +390,7 @@
 {       int i;
 
 	if (disable) return -1;
-
-        if (register_blkdev(MAJOR_NR,name,&pd_fops)) {
+        if (devfs_register_blkdev(MAJOR_NR,name,&pd_fops)) {
                 printk("%s: unable to get major number %d\n",
                         name,major);
                 return -1;
@@ -592,8 +595,7 @@
 {       struct gendisk **gdp;
 	int unit;
 
-        unregister_blkdev(MAJOR_NR,name);
-
+        devfs_unregister_blkdev(MAJOR_NR,name);
         for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
                 if (*gdp == &pd_gendisk) break;
         if (*gdp) *gdp = (*gdp)->next;
@@ -868,7 +870,7 @@
 
         if (pd_busy) return;
 repeat:
-        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        if (QUEUE_EMPTY || (CURRENT->rq_status == RQ_INACTIVE)) return;
         INIT_REQUEST;
 
         pd_dev = MINOR(CURRENT->rq_dev);
@@ -890,7 +892,7 @@
 	pd_cmd = CURRENT->cmd;
 	pd_run = pd_count;
         while ((pd_run <= cluster) &&
-	       (req = req->next) && 
+	       (req = blkdev_next_request(req)) && 
 	       (pd_block+pd_run == req->sector) &&
 	       (pd_cmd == req->cmd) &&
 	       (pd_dev == MINOR(req->rq_dev)))
@@ -922,7 +924,7 @@
 	
 /* paranoia */
 
-	if ((!CURRENT) ||
+	if (QUEUE_EMPTY ||
 	    (CURRENT->cmd != pd_cmd) ||
 	    (MINOR(CURRENT->rq_dev) != pd_dev) ||
 	    (CURRENT->rq_status == RQ_INACTIVE) ||
diff -u --recursive --new-file v2.3.45/linux/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c
--- v2.3.45/linux/drivers/block/paride/pf.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/paride/pf.c	Wed Feb 16 10:56:45 2000
@@ -854,7 +854,7 @@
 
         if (pf_busy) return;
 repeat:
-        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        if (QUEUE_EMPTY || (CURRENT->rq_status == RQ_INACTIVE)) return;
         INIT_REQUEST;
 
         pf_unit = unit = DEVICE_NR(CURRENT->rq_dev);
@@ -874,7 +874,7 @@
 	pf_cmd = CURRENT->cmd;
 	pf_run = pf_count;
         while ((pf_run <= cluster) &&
-	       (req = req->next) && 
+	       (req = blkdev_next_request(req)) && 
 	       (pf_block+pf_run == req->sector) &&
 	       (pf_cmd == req->cmd) &&
 	       (pf_unit == DEVICE_NR(req->rq_dev)))
@@ -904,7 +904,7 @@
 	
 /* paranoia */
 
-	if ((!CURRENT) ||
+	if (QUEUE_EMPTY ||
 	    (CURRENT->cmd != pf_cmd) ||
 	    (DEVICE_NR(CURRENT->rq_dev) != pf_unit) ||
 	    (CURRENT->rq_status == RQ_INACTIVE) ||
diff -u --recursive --new-file v2.3.45/linux/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c
--- v2.3.45/linux/drivers/block/paride/pg.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/paride/pg.c	Wed Feb 16 15:42:05 2000
@@ -164,6 +164,7 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/malloc.h>
@@ -286,6 +287,8 @@
 	}
 } 
 
+static devfs_handle_t devfs_handle = NULL;
+
 int pg_init (void)      /* preliminary initialisation */
 
 {       int unit;
@@ -296,14 +299,17 @@
 
 	if (pg_detect()) return -1;
 
-	if (register_chrdev(major,name,&pg_fops)) {
+	if (devfs_register_chrdev(major,name,&pg_fops)) {
 		printk("pg_init: unable to get major number %d\n",
 			major);
 		for (unit=0;unit<PG_UNITS;unit++)
 		  if (PG.present) pi_release(PI);
 		return -1;
 	}
-
+	devfs_handle = devfs_mk_dir (NULL, "pg", 2, NULL);
+	devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
+			       major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &pg_fops, NULL);
 	return 0;
 }
 
@@ -332,7 +338,8 @@
 
 {       int unit;
 
-	unregister_chrdev(major,name);
+	devfs_unregister (devfs_handle);
+	devfs_unregister_chrdev(major,name);
 
 	for (unit=0;unit<PG_UNITS;unit++)
 	  if (PG.present) pi_release(PI);
diff -u --recursive --new-file v2.3.45/linux/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c
--- v2.3.45/linux/drivers/block/paride/pt.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/block/paride/pt.c	Wed Feb 16 15:42:05 2000
@@ -143,6 +143,7 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/malloc.h>
@@ -290,6 +291,8 @@
         }
 } 
 
+static devfs_handle_t devfs_handle = NULL;
+
 int pt_init (void)      /* preliminary initialisation */
 
 {       int unit;
@@ -300,7 +303,7 @@
 
 	if (pt_detect()) return -1;
 
-        if (register_chrdev(major,name,&pt_fops)) {
+	if (devfs_register_chrdev(major,name,&pt_fops)) {
                 printk("pt_init: unable to get major number %d\n",
                         major);
 	        for (unit=0;unit<PT_UNITS;unit++)
@@ -308,6 +311,13 @@
                 return -1;
         }
 
+	devfs_handle = devfs_mk_dir (NULL, "pt", 2, NULL);
+	devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
+			       major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &pt_fops, NULL);
+	devfs_register_series (devfs_handle, "%un", 4, DEVFS_FL_DEFAULT,
+			       major, 128, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &pt_fops, NULL);
         return 0;
 }
 
@@ -334,9 +344,10 @@
 
 void    cleanup_module(void)
 
-{       int unit;
+{	int unit;
 
-        unregister_chrdev(major,name);
+	devfs_unregister (devfs_handle);
+	devfs_unregister_chrdev(major,name);
 
 	for (unit=0;unit<PT_UNITS;unit++)
 	  if (PT.present) pi_release(PI);
diff -u --recursive --new-file v2.3.45/linux/drivers/block/ps2esdi.c linux/drivers/block/ps2esdi.c
--- v2.3.45/linux/drivers/block/ps2esdi.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/ps2esdi.c	Wed Feb 16 15:42:05 2000
@@ -41,6 +41,7 @@
 #include <linux/kernel.h>
 #include <linux/genhd.h>
 #include <linux/ps2esdi.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/blk.h>
 #include <linux/blkpg.h>
 #include <linux/mca.h>
@@ -164,7 +165,8 @@
 	ps2esdi_sizes,		/* block sizes */
 	0,			/* number */
 	(void *) ps2esdi_info,	/* internal */
-	NULL			/* next */
+	NULL,			/* next */
+	&ps2esdi_fops,          /* file operations */
 };
 
 /* initialization routine called by ll_rw_blk.c   */
@@ -173,7 +175,7 @@
 
 	/* register the device - pass the name, major number and operations
 	   vector .                                                 */
-	if (register_blkdev(MAJOR_NR, "ed", &ps2esdi_fops)) {
+	if (devfs_register_blkdev(MAJOR_NR, "ed", &ps2esdi_fops)) {
 		printk("%s: Unable to get major number %d\n", DEVICE_NAME, MAJOR_NR);
 		return -1;
 	}
@@ -229,7 +231,7 @@
 	release_region(io_base, 4);
 	free_dma(dma_arb_level);
   	free_irq(PS2ESDI_IRQ, NULL)
-	unregister_blkdev(MAJOR_NR, "ed");
+	devfs_unregister_blkdev(MAJOR_NR, "ed");
 }
 #endif /* MODULE */
 
@@ -476,7 +478,7 @@
 	if (virt_to_bus(CURRENT->buffer + CURRENT->nr_sectors * 512) > 16 * MB) {
 		printk("%s: DMA above 16MB not supported\n", DEVICE_NAME);
 		end_request(FAIL);
-		if (CURRENT)
+		if (!QUEUE_EMPTY)
 			do_ps2esdi_request(q);
 		return;
 	}			/* check for above 16Mb dmas */
@@ -510,7 +512,7 @@
 		default:
 			printk("%s: Unknown command\n", DEVICE_NAME);
 			end_request(FAIL);
-			if (CURRENT)
+			if (!QUEUE_EMPTY)
 				do_ps2esdi_request(q);
 			break;
 		}		/* handle different commands */
@@ -520,7 +522,7 @@
 		printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives,
 		       CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects);
 		end_request(FAIL);
-		if (CURRENT)
+		if (!QUEUE_EMPTY)
 			do_ps2esdi_request(q);
 	}
 
@@ -591,7 +593,7 @@
 			return do_ps2esdi_request(NULL);
 		else {
 			end_request(FAIL);
-			if (CURRENT)
+			if (!QUEUE_EMPTY)
 				do_ps2esdi_request(NULL);
 		}
 	}
@@ -894,7 +896,7 @@
 				do_ps2esdi_request(NULL);
 			else {
 				end_request(FAIL);
-				if (CURRENT)
+				if (!QUEUE_EMPTY)
 					do_ps2esdi_request(NULL);
 			}
 			break;
@@ -940,7 +942,7 @@
 			do_ps2esdi_request(NULL);
 		else {
 			end_request(FAIL);
-			if (CURRENT)
+			if (!QUEUE_EMPTY)
 				do_ps2esdi_request(NULL);
 		}
 		break;
@@ -950,7 +952,7 @@
 		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
 		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
 		end_request(FAIL);
-		if (CURRENT)
+		if (!QUEUE_EMPTY)
 			do_ps2esdi_request(NULL);
 		break;
 
@@ -986,7 +988,7 @@
 		do_ps2esdi_request(NULL);
 	} else {
 		end_request(SUCCES);
-		if (CURRENT)
+		if (!QUEUE_EMPTY)
 			do_ps2esdi_request(NULL);
 	}
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v2.3.45/linux/drivers/block/rd.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/block/rd.c	Wed Feb 16 15:42:05 2000
@@ -58,6 +58,7 @@
 #include <linux/fd.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -97,6 +98,7 @@
 static int rd_hardsec[NUM_RAMDISKS];		/* Size of real blocks in bytes */
 static int rd_blocksizes[NUM_RAMDISKS];		/* Size of 1024 byte blocks :)  */
 static int rd_kbsize[NUM_RAMDISKS];		/* Size in blocks of 1024 bytes */
+static devfs_handle_t devfs_handle = NULL;
 
 /*
  * Parameters for the boot-loading of the RAM disk.  These are set by
@@ -180,6 +182,8 @@
  *  deleted, and make that my Ramdisk.  If the request is outside of the
  *  allocated size, we must get rid of it...
  *
+ * 19-JAN-1998  Richard Gooch <rgooch@atnf.csiro.au>  Added devfs support
+ *
  */
 static void rd_request(request_queue_t * q)
 {
@@ -388,6 +392,7 @@
 	for (i = 0 ; i < NUM_RAMDISKS; i++)
 		destroy_buffers(MKDEV(MAJOR_NR, i));
 
+	devfs_unregister (devfs_handle);
 	unregister_blkdev( MAJOR_NR, "ramdisk" );
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 }
@@ -419,6 +424,11 @@
 		rd_blocksizes[i] = rd_blocksize;
 		rd_kbsize[i] = rd_size;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "rd", 0, NULL);
+	devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS,
+			       DEVFS_FL_DEFAULT, MAJOR_NR, 0,
+			       S_IFBLK | S_IRUSR | S_IWUSR, 0, 0,
+			       &fd_fops, NULL);
 
 	hardsect_size[MAJOR_NR] = rd_hardsec;		/* Size of the RAM disk blocks */
 	blksize_size[MAJOR_NR] = rd_blocksizes;		/* Avoid set_blocksize() check */
@@ -681,6 +691,7 @@
 successful_load:
 	invalidate_buffers(device);
 	ROOT_DEV = MKDEV(MAJOR_NR, unit);
+	if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0");
 
 done:
 	if (infile.f_op->release)
diff -u --recursive --new-file v2.3.45/linux/drivers/block/swim3.c linux/drivers/block/swim3.c
--- v2.3.45/linux/drivers/block/swim3.c	Tue Feb  1 01:35:43 2000
+++ linux/drivers/block/swim3.c	Wed Feb 16 10:56:45 2000
@@ -305,7 +305,7 @@
 		wake_up(&fs->wait);
 		return;
 	}
-	while (CURRENT && fs->state == idle) {
+	while (!QUEUE_EMPTY && fs->state == idle) {
 		if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
 			panic(DEVICE_NAME ": request list destroyed");
 		if (CURRENT->bh && !buffer_locked(CURRENT->bh))
diff -u --recursive --new-file v2.3.45/linux/drivers/block/swim_iop.c linux/drivers/block/swim_iop.c
--- v2.3.45/linux/drivers/block/swim_iop.c	Tue Feb  1 01:35:43 2000
+++ linux/drivers/block/swim_iop.c	Wed Feb 16 10:56:45 2000
@@ -550,7 +550,7 @@
 		wake_up(&fs->wait);
 		return;
 	}
-	while (CURRENT && fs->state == idle) {
+	while (!QUEUE_EMPTY && fs->state == idle) {
 		if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
 			panic(DEVICE_NAME ": request list destroyed");
 		if (CURRENT->bh && !buffer_locked(CURRENT->bh))
diff -u --recursive --new-file v2.3.45/linux/drivers/block/xd.c linux/drivers/block/xd.c
--- v2.3.45/linux/drivers/block/xd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/block/xd.c	Wed Feb 16 15:42:05 2000
@@ -41,6 +41,7 @@
 #include <linux/hdreg.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -87,21 +88,8 @@
    should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */
 
 #include <asm/page.h>
-/* coppied from floppy.c */
-static inline int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
-#define xd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
+#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,get_order(size))
+#define xd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
 static char *xd_dma_buffer = 0;
 
 static XD_SIGNATURE xd_sigs[] __initdata = {
@@ -130,6 +118,9 @@
 static struct hd_struct xd_struct[XD_MAXDRIVES << 6];
 static int xd_sizes[XD_MAXDRIVES << 6], xd_access[XD_MAXDRIVES] = { 0, 0 };
 static int xd_blocksizes[XD_MAXDRIVES << 6];
+
+extern struct block_device_operations xd_fops;
+
 static struct gendisk xd_gendisk = {
 	MAJOR_NR,	/* Major number */
 	"xd",		/* Major name */
@@ -139,7 +130,8 @@
 	xd_sizes,	/* block sizes */
 	0,		/* number */
 	(void *) xd_info,	/* internal */
-	NULL		/* next */
+	NULL,		/* next */
+	&xd_fops,	/* file operations */
 };
 static struct block_device_operations xd_fops = {
 	open:		xd_open,
@@ -164,13 +156,16 @@
 static volatile u_char xd_error;
 static int nodma = XD_DONT_USE_DMA;
 
+static devfs_handle_t devfs_handle = NULL;
+
 /* xd_init: register the block device number and set up pointer tables */
 int __init xd_init (void)
 {
-	if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
+	if (devfs_register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
 		printk("xd: Unable to get major number %d\n",MAJOR_NR);
 		return -1;
 	}
+	devfs_handle = devfs_mk_dir (NULL, xd_gendisk.major_name, 0, NULL);
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
 	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
 	xd_gendisk.next = gendisk_head;
@@ -287,7 +282,7 @@
 	sti();
 	if (xdc_busy)
 		return;
-	while (code = 0, CURRENT) {
+	while (code = 0, !QUEUE_EMPTY) {
 		INIT_REQUEST;	/* do some checking on the request structure */
 
 		if (CURRENT_DEV < xd_drives
@@ -1162,7 +1157,7 @@
 	printk(KERN_INFO "XD: Loaded as a module.\n");
 	if (!xd_drives) {
 		/* no drives detected - unload module */
-		unregister_blkdev(MAJOR_NR, "xd");
+		devfs_unregister_blkdev(MAJOR_NR, "xd");
 		xd_done();
 		return (-1);
 	}
@@ -1174,7 +1169,7 @@
 {
 	int partition,dev,start;
 
-	unregister_blkdev(MAJOR_NR, "xd");
+	devfs_unregister_blkdev(MAJOR_NR, "xd");
 	for (dev = 0; dev < xd_drives; dev++) {
 		start = dev << xd_gendisk.minor_shift; 
 		for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
@@ -1186,6 +1181,7 @@
 		}
 	}
 	xd_done();
+	devfs_unregister (devfs_handle);
 	if (xd_drives) {
 		free_irq(xd_irq, NULL);
 		free_dma(xd_dma);
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v2.3.45/linux/drivers/cdrom/aztcd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/cdrom/aztcd.c	Wed Feb 16 15:42:05 2000
@@ -183,6 +183,7 @@
 #include <linux/ioport.h>
 #include <linux/string.h>
 #include <linux/major.h>
+#include <linux/devfs_fs_kernel.h>
 
 #ifndef AZT_KERNEL_PRIOR_2_1
 #include <linux/init.h>
@@ -234,7 +235,7 @@
 #endif
 
 #define CURRENT_VALID \
-  (CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+  (!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
    && CURRENT -> sector != -1)
 
 #define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
@@ -1785,7 +1786,9 @@
                return -EIO;
 	     }
 	 }
-	if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
+	devfs_register (NULL, "aztcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0,
+			S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &azt_fops, NULL);
+	if (devfs_register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
 	{
 		printk("aztcd: Unable to get major %d for Aztech CD-ROM\n",
 		       MAJOR_NR);
@@ -1811,7 +1814,9 @@
 
 void __exit aztcd_exit(void)
 {
-  if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))    
+  devfs_unregister(devfs_find_handle(NULL, "aztcd", 0, 0, 0, DEVFS_SPECIAL_BLK,
+				     0));
+  if ((devfs_unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))    
     { printk("What's that: can't unregister aztcd\n");
       return;
     }
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.3.45/linux/drivers/cdrom/cdrom.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/cdrom/cdrom.c	Wed Feb 16 15:42:05 2000
@@ -291,6 +291,7 @@
 static void cdrom_sysctl_register(void);
 #endif /* CONFIG_SYSCTL */ 
 static struct cdrom_device_info *topCdromPtr = NULL;
+static devfs_handle_t devfs_handle = NULL;
 
 struct block_device_operations cdrom_fops =
 {
@@ -313,6 +314,8 @@
 	int major = MAJOR(cdi->dev);
         struct cdrom_device_ops *cdo = cdi->ops;
         int *change_capability = (int *)&cdo->capability; /* hack */
+	char vname[16];
+	static unsigned int cdrom_counter = 0;
 
 	cdinfo(CD_OPEN, "entering register_cdrom\n"); 
 
@@ -351,6 +354,31 @@
 	if (check_media_type==1)
 		cdi->options |= (int) CDO_CHECK_TYPE;
 
+	if (!devfs_handle)
+		devfs_handle = devfs_mk_dir (NULL, "cdroms", 6, NULL);
+	sprintf (vname, "cdrom%u", cdrom_counter++);
+	if (cdi->de) {
+		int pos;
+		devfs_handle_t slave;
+		char rname[64];
+
+		pos = devfs_generate_path (cdi->de, rname + 3,
+					   sizeof rname - 3);
+		if (pos >= 0) {
+			strncpy (rname + pos, "../", 3);
+			devfs_mk_symlink (devfs_handle, vname, 0,
+					  DEVFS_FL_DEFAULT,
+					  rname + pos, 0, &slave, NULL);
+			devfs_auto_unregister (cdi->de, slave);
+		}
+	}
+	else {
+		cdi->de =
+		    devfs_register (devfs_handle, vname, 0, DEVFS_FL_DEFAULT,
+				    MAJOR (cdi->dev), MINOR (cdi->dev),
+				    S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
+				    &cdrom_fops, NULL);
+	}
 	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
 	cdi->next = topCdromPtr; 	
 	topCdromPtr = cdi;
@@ -382,6 +410,7 @@
 	else
 		topCdromPtr = cdi->next;
 	cdi->ops->n_minors--;
+	devfs_unregister (cdi->de);
 	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name);
 	return 0;
 }
@@ -2483,6 +2512,7 @@
 #ifdef CONFIG_SYSCTL
 	cdrom_sysctl_register();
 #endif
+	devfs_handle = devfs_mk_dir (NULL, "cdroms", 6, NULL);
 	return 0;
 }
 
@@ -2491,7 +2521,8 @@
 	printk(KERN_INFO "Uniform CD-ROM driver unloaded\n");
 #ifdef CONFIG_SYSCTL
 	cdrom_sysctl_unregister();
-#endif /* CONFIG_SYSCTL */ 
+#endif /* CONFIG_SYSCTL */
+	devfs_unregister (devfs_handle);
 }
 #endif /* endif MODULE */
 
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v2.3.45/linux/drivers/cdrom/cdu31a.c	Tue Dec 14 01:27:23 1999
+++ linux/drivers/cdrom/cdu31a.c	Wed Feb 16 15:42:05 2000
@@ -162,6 +162,7 @@
 #include <linux/hdreg.h>
 #include <linux/genhd.h>
 #include <linux/ioport.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
@@ -1672,7 +1673,7 @@
       if (signal_pending(current))
       {
          restore_flags(flags);
-         if (CURRENT && CURRENT->rq_status != RQ_INACTIVE)
+         if (!QUEUE_EMPTY && CURRENT->rq_status != RQ_INACTIVE)
          {
             end_request(0);
          }
@@ -1705,7 +1706,7 @@
        * The beginning here is stolen from the hard disk driver.  I hope
        * it's right.
        */
-      if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE)
+      if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
       {
          goto end_do_cdu31a_request;
       }
@@ -3441,7 +3442,7 @@
 
       request_region(cdu31a_port, 4,"cdu31a");
       
-      if (register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops))
+      if (devfs_register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops))
       {
 	 printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
          goto errout2;
@@ -3543,7 +3544,7 @@
    }
 errout0:
    printk("Unable to register CDU-31a with Uniform cdrom driver\n");
-   if (unregister_blkdev(MAJOR_NR, "cdu31a"))    
+   if (devfs_unregister_blkdev(MAJOR_NR, "cdu31a"))    
    {
       printk("Can't unregister block device for cdu31a\n");
    }
@@ -3562,7 +3563,7 @@
       printk("Can't unregister cdu31a from Uniform cdrom driver\n");
       return;
    }
-   if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL))    
+   if ((devfs_unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL))    
    {
       printk("Can't unregister cdu31a\n");
       return;
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.3.45/linux/drivers/cdrom/cm206.c	Tue Dec 14 01:27:23 1999
+++ linux/drivers/cdrom/cm206.c	Wed Feb 16 15:42:05 2000
@@ -187,6 +187,7 @@
 #include <linux/interrupt.h>
 #include <linux/timer.h>
 #include <linux/cdrom.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/ioport.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
@@ -816,7 +817,7 @@
   
   while(1) {	 /* repeat until all requests have been satisfied */
     INIT_REQUEST;
-    if (CURRENT == NULL || CURRENT->rq_status == RQ_INACTIVE)
+    if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
       return;
     if (CURRENT->cmd != READ) {
       debug(("Non-read command %d on cdrom\n", CURRENT->cmd));
@@ -1277,7 +1278,7 @@
       printk("Can't unregister cdrom cm206\n");
       return;
     }
-    if (unregister_blkdev(MAJOR_NR, "cm206")) {
+    if (devfs_unregister_blkdev(MAJOR_NR, "cm206")) {
       printk("Can't unregister major cm206\n");
       return;
     }
@@ -1390,7 +1391,7 @@
     return -EIO;
   }
   printk(".\n");
-  if (register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) {
+  if (devfs_register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) {
     printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR);
     cleanup(3);
     return -EIO;
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c
--- v2.3.45/linux/drivers/cdrom/gscd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/cdrom/gscd.c	Wed Feb 16 15:42:05 2000
@@ -63,6 +63,7 @@
 #include <linux/major.h>
 #include <linux/string.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -279,13 +280,13 @@
 unsigned int nsect;
 
 repeat:
-	if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) return;
+	if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) return;
 	INIT_REQUEST;
 	dev = MINOR(CURRENT->rq_dev);
 	block = CURRENT->sector;
 	nsect = CURRENT->nr_sectors;
 
-	if (CURRENT == NULL || CURRENT -> sector == -1)
+	if (QUEUE_EMPTY || CURRENT -> sector == -1)
 		return;
 
 	if (CURRENT -> cmd != READ)
@@ -991,12 +992,13 @@
 void __exit exit_gscd(void)
 {
 
-   if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL))
+   devfs_unregister(devfs_find_handle(NULL, "gscd", 0, 0, 0, DEVFS_SPECIAL_BLK,
+				      0));
+   if ((devfs_unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL))
    {
       printk("What's that: can't unregister GoldStar-module\n" );
       return;
    }
-
    release_region (gscd_port,4);
    printk(KERN_INFO "GoldStar-module released.\n" );
 }
@@ -1067,12 +1069,14 @@
            i++;
         }
 
-	if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0)
+	if (devfs_register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0)
 	{
 		printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n",
 		       MAJOR_NR);
 		return -EIO;
 	}
+	devfs_register (NULL, "gscd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0,
+			S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &gscd_fops, NULL);
 
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
 	blksize_size[MAJOR_NR] = gscd_blocksizes;
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- v2.3.45/linux/drivers/cdrom/mcd.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/cdrom/mcd.c	Wed Feb 16 15:42:05 2000
@@ -86,6 +86,7 @@
 #include <linux/timer.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/cdrom.h>
 #include <linux/ioport.h>
 #include <linux/string.h>
@@ -134,7 +135,7 @@
 /* #define DOUBLE_QUICK_ONLY */
 
 #define CURRENT_VALID \
-(CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+(!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
 && CURRENT -> sector != -1)
 
 #define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
@@ -1156,7 +1157,7 @@
   case 2:
     release_region(mcd_port,4);
   case 1:
-    if (unregister_blkdev(MAJOR_NR, "mcd")) {
+    if (devfs_unregister_blkdev(MAJOR_NR, "mcd")) {
       printk(KERN_WARNING "Can't unregister major mcd\n");
       return;
     }
@@ -1181,7 +1182,7 @@
           return -EIO;
 	}
 
-	if (register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0)
+	if (devfs_register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0)
 	{
 		printk("Unable to get major %d for Mitsumi CD-ROM\n",
 		       MAJOR_NR);
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- v2.3.45/linux/drivers/cdrom/mcdx.c	Tue Dec 14 01:27:23 1999
+++ linux/drivers/cdrom/mcdx.c	Wed Feb 16 15:42:05 2000
@@ -74,6 +74,7 @@
 #include <linux/major.h>
 #define MAJOR_NR MITSUMI_X_CDROM_MAJOR
 #include <linux/blk.h>
+#include <linux/devfs_fs_kernel.h>
 
 /* for compatible parameter passing with "insmod" */
 #define	mcdx_drive_map mcdx
@@ -530,7 +531,7 @@
 
   again:
 
-	if (CURRENT == NULL) {
+	if (QUEUE_EMPTY) {
 		xtrace(REQUEST, "end_request(0): CURRENT == NULL\n");
 		return;
 	}
@@ -995,7 +996,7 @@
 
     for (i = 0; i < MCDX_NDRIVES; i++) {
 		struct s_drive_stuff *stuffp;
-        if (unregister_cdrom(&mcdx_info)) {
+        	if (unregister_cdrom(&mcdx_info)) {
 			printk(KERN_WARNING "Can't unregister cdrom mcdx\n");
 			return;
 		}
@@ -1012,7 +1013,7 @@
 		kfree(stuffp);
     }
 
-    if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) {
+    if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) {
         xwarn("cleanup() unregister_blkdev() failed\n");
     }
 #if !MCDX_QUIET
@@ -1123,7 +1124,7 @@
 	}
 
 	xtrace(INIT, "init() register blkdev\n");
-	if (register_blkdev(MAJOR_NR, "mcdx", &cdrom_fops) != 0) {
+	if (devfs_register_blkdev(MAJOR_NR, "mcdx", &cdrom_fops) != 0) {
 		xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
 		      MCDX,
 		      stuffp->wreg_data, stuffp->irq, MAJOR_NR);
@@ -1181,7 +1182,7 @@
 			       MCDX_IO_SIZE);
 		free_irq(stuffp->irq, NULL);
 		kfree(stuffp);
-		if (unregister_blkdev(MAJOR_NR, "mcdx") != 0)
+		if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0)
         		xwarn("cleanup() unregister_blkdev() failed\n");
 		return 2;
         }
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c
--- v2.3.45/linux/drivers/cdrom/optcd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/cdrom/optcd.c	Wed Feb 16 15:42:05 2000
@@ -71,6 +71,8 @@
 #include <linux/mm.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+
 #include <asm/io.h>
 
 #define MAJOR_NR OPTICS_CDROM_MAJOR
@@ -980,7 +982,7 @@
 
 
 #define CURRENT_VALID \
-	(CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR \
+	(!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR \
 	 && CURRENT -> cmd == READ && CURRENT -> sector != -1)
 
 
@@ -2061,12 +2063,13 @@
 		DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status));
 		return -EIO;
 	}
-	if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
+	if (devfs_register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
 	{
 		printk(KERN_ERR "optcd: unable to get major %d\n", MAJOR_NR);
 		return -EIO;
 	}
-
+	devfs_register (NULL, "optcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0,
+			S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &opt_fops, NULL);
 	hardsect_size[MAJOR_NR] = &hsecsize;
 	blksize_size[MAJOR_NR] = &blksize;
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
@@ -2081,7 +2084,9 @@
 
 void __exit optcd_exit(void)
 {
-	if (unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) {
+	devfs_unregister(devfs_find_handle(NULL, "optcd", 0, 0, 0,
+					   DEVFS_SPECIAL_BLK, 0));
+	if (devfs_unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) {
 		printk(KERN_ERR "optcd: what's that: can't unregister\n");
 		return;
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v2.3.45/linux/drivers/cdrom/sbpcd.c	Tue Dec 14 01:27:23 1999
+++ linux/drivers/cdrom/sbpcd.c	Wed Feb 16 15:42:05 2000
@@ -338,6 +338,7 @@
 #include <linux/kernel.h>
 #include <linux/cdrom.h>
 #include <linux/ioport.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/major.h> 
 #include <linux/string.h>
 #include <linux/vmalloc.h>
@@ -4791,9 +4792,7 @@
  */
 #undef DEBUG_GTL
 static inline void sbpcd_end_request(struct request *req, int uptodate) {
-	req->next=CURRENT;
-	CURRENT=req;
-	up(&ioctl_read_sem);
+	list_add(&req->queue, &req->q->queue_head);
 	end_request(uptodate);
 }
 /*==========================================================================*/
@@ -4815,7 +4814,7 @@
 #ifdef DEBUG_GTL
 	xnr=++xx_nr;
 
-	if(!CURRENT)
+	if(QUEUE_EMPTY)
 	{
 		printk( "do_sbpcd_request[%di](NULL), Pid:%d, Time:%li\n",
 			xnr, current->pid, jiffies);
@@ -4830,15 +4829,15 @@
 #endif
 	INIT_REQUEST;
 	req=CURRENT;		/* take out our request so no other */
-	CURRENT=req->next;	/* task can fuck it up         GTL  */
-	spin_unlock_irq(&io_request_lock);		/* FIXME!!!! */
+	blkdev_dequeue_request(req);	/* task can fuck it up         GTL  */
 	
-	down(&ioctl_read_sem);
 	if (req->rq_status == RQ_INACTIVE)
 		sbpcd_end_request(req, 0);
 	if (req -> sector == -1)
 		sbpcd_end_request(req, 0);
+	spin_unlock_irq(&io_request_lock);
 
+	down(&ioctl_read_sem);
 	if (req->cmd != READ)
 	{
 		msg(DBG_INF, "bad cmd %d\n", req->cmd);
@@ -4875,8 +4874,9 @@
 		printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 2, Time:%li\n",
 			xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
+		up(&ioctl_read_sem);
+		spin_lock_irq(&io_request_lock);
 		sbpcd_end_request(req, 1);
-		spin_lock_irq(&io_request_lock);		/* FIXME!!!! */
 		goto request_loop;
 	}
 
@@ -4915,8 +4915,9 @@
 			printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 3, Time:%li\n",
 				xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
+			up(&ioctl_read_sem);
+			spin_lock_irq(&io_request_lock);
 			sbpcd_end_request(req, 1);
-			spin_lock_irq(&io_request_lock);	/* FIXME!!!! */
 			goto request_loop;
 		}
 	}
@@ -4929,9 +4930,10 @@
 	printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n",
 		xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
-	sbpcd_end_request(req, 0);
+	up(&ioctl_read_sem);
 	sbp_sleep(0);    /* wait a bit, try again */
-	spin_lock_irq(&io_request_lock);		/* FIXME!!!! */
+	spin_lock_irq(&io_request_lock);
+	sbpcd_end_request(req, 0);
 	goto request_loop;
 }
 /*==========================================================================*/
@@ -5583,12 +5585,16 @@
  *  Test for presence of drive and initialize it.
  *  Called once at boot or load time.
  */
+
+static devfs_handle_t devfs_handle = NULL;
+
 #ifdef MODULE
 int __init __SBPCD_INIT(void)
 #else
 int __init SBPCD_INIT(void)
 #endif MODULE
 {
+	char nbuff[16];
 	int i=0, j=0;
 	int addr[2]={1, CDROM_PORT};
 	int port_index;
@@ -5731,7 +5737,7 @@
 	OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
 #endif SOUND_BASE
 	
-	if (register_blkdev(MAJOR_NR, major_name, &cdrom_fops) != 0)
+	if (devfs_register_blkdev(MAJOR_NR, major_name, &cdrom_fops) != 0)
 	{
 		msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
 #ifdef MODULE
@@ -5741,10 +5747,12 @@
 #endif MODULE
 	}
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0);
 	read_ahead[MAJOR_NR] = buffers * (CD_FRAMESIZE / 512);
 	
 	request_region(CDo_command,4,major_name);
 	
+	devfs_handle = devfs_mk_dir (NULL, "sbp", 0, NULL);
 	for (j=0;j<NR_SBPCD;j++)
 	{
 		struct cdrom_device_info * sbpcd_infop;
@@ -5766,7 +5774,7 @@
 		if (D_S[j].sbp_buf==NULL)
 		{
 			msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz);
-			if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
+			if ((devfs_unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
 			{
 				printk("Can't unregister %s\n", major_name);
 			}
@@ -5793,11 +5801,15 @@
 		sbpcd_infop->dev = MKDEV(MAJOR_NR, j);
 		strncpy(sbpcd_infop->name,major_name, sizeof(sbpcd_infop->name)); 
 
+		sprintf (nbuff, "c%dt%d/cd", SBPCD_ISSUE - 1, D_S[j].drv_id);
+		sbpcd_infop->de =
+		    devfs_register (devfs_handle, nbuff, 0, DEVFS_FL_DEFAULT,
+				    MAJOR_NR, j, S_IFBLK | S_IRUGO | S_IWUGO,
+				    0, 0, &cdrom_fops, NULL);
 		if (register_cdrom(sbpcd_infop))
 		{
                 	printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n");
 		}
-
 		/*
 		 * set the block size
 		 */
@@ -5827,13 +5839,14 @@
 {
 	int j;
 	
-	if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
+	if ((devfs_unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
 	{
 		msg(DBG_INF, "What's that: can't unregister %s.\n", major_name);
 		return;
 	}
 	release_region(CDo_command,4);
 	
+	devfs_unregister (devfs_handle);
 	for (j=0;j<NR_SBPCD;j++)
 	{
 		if (D_S[j].drv_id==-1) continue;
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c
--- v2.3.45/linux/drivers/cdrom/sjcd.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/cdrom/sjcd.c	Wed Feb 16 15:42:05 2000
@@ -73,6 +73,7 @@
 #include <linux/string.h>
 #include <linux/major.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -938,7 +939,7 @@
  */
 
 #define CURRENT_IS_VALID                                      \
-    ( CURRENT != NULL && MAJOR( CURRENT->rq_dev ) == MAJOR_NR && \
+    ( !QUEUE_EMPTY && MAJOR( CURRENT->rq_dev ) == MAJOR_NR && \
       CURRENT->cmd == READ && CURRENT->sector != -1 )
 
 static void sjcd_transfer( void ){
@@ -1471,7 +1472,7 @@
 	hardsect_size[MAJOR_NR] = &secsize;
 	blksize_size[MAJOR_NR] = &blksize;
 
-  if( register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){
+  if( devfs_register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){
     printk( "SJCD: Unable to get major %d for Sanyo CD-ROM\n", MAJOR_NR );
     return( -EIO );
   }
@@ -1563,6 +1564,8 @@
   }
 
   printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base);
+  devfs_register (NULL, "sjcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0,
+		  S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &sjcd_fops, NULL);
 
   sjcd_present++;
   return( 0 );
@@ -1571,7 +1574,7 @@
 static int
 sjcd_cleanup(void)
 {
-  if( (unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL) )
+  if( (devfs_unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL) )
     printk( "SJCD: cannot unregister device.\n" );
   else
     release_region( sjcd_base, 4 );
@@ -1582,6 +1585,8 @@
 
 void __exit sjcd_exit(void)
 {
+  devfs_unregister(devfs_find_handle(NULL, "sjcd", 0, 0, 0, DEVFS_SPECIAL_BLK,
+				     0));
   if ( sjcd_cleanup() )
     printk( "SJCD: module: cannot be removed.\n" );
   else
diff -u --recursive --new-file v2.3.45/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v2.3.45/linux/drivers/cdrom/sonycd535.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/cdrom/sonycd535.c	Wed Feb 16 15:42:05 2000
@@ -124,6 +124,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #define REALLY_SLOW_IO
 #include <asm/system.h>
@@ -803,7 +804,7 @@
 		 * The beginning here is stolen from the hard disk driver.  I hope
 		 * it's right.
 		 */
-		if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) {
+		if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) {
 			return;
 		}
 		INIT_REQUEST;
@@ -1586,7 +1587,12 @@
 					printk("IRQ%d, ", tmp_irq);
 				printk("using %d byte buffer\n", sony_buffer_size);
 
-				if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
+				devfs_register (NULL, CDU535_HANDLE, 0,
+						DEVFS_FL_DEFAULT,
+						MAJOR_NR, 0,
+						S_IFBLK | S_IRUGO | S_IWUGO,
+						0, 0, &cdu_fops, NULL);
+				if (devfs_register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
 					printk("Unable to get major %d for %s\n",
 							MAJOR_NR, CDU535_MESSAGE_NAME);
 					return -EIO;
@@ -1684,7 +1690,9 @@
 	kfree_s(sony_buffer, 4 * sony_buffer_sectors);
 	kfree_s(last_sony_subcode, sizeof *last_sony_subcode);
 	kfree_s(sony_toc, sizeof *sony_toc);
-	if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
+	devfs_unregister(devfs_find_handle(NULL, CDU535_HANDLE, 0, 0, 0,
+					   DEVFS_SPECIAL_BLK, 0));
+	if (devfs_unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
 		printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
 	else
 		printk(KERN_INFO CDU535_HANDLE " module released\n");
diff -u --recursive --new-file v2.3.45/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.3.45/linux/drivers/char/Config.in	Fri Jan 28 15:09:07 2000
+++ linux/drivers/char/Config.in	Mon Feb 14 13:37:48 2000
@@ -228,7 +228,7 @@
    if [ "$CONFIG_AGP" != "n" ]; then
       bool '  Intel 440LX/BX/GX support' CONFIG_AGP_INTEL
       bool '  Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810
-      bool '  VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA
+      bool '  VIA chipset support' CONFIG_AGP_VIA
       bool '  AMD Irongate support' CONFIG_AGP_AMD
       bool '  Generic SiS support' CONFIG_AGP_SIS
       bool '  ALI M1541 support' CONFIG_AGP_ALI
diff -u --recursive --new-file v2.3.45/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.3.45/linux/drivers/char/Makefile	Sun Feb 13 19:29:03 2000
+++ linux/drivers/char/Makefile	Wed Feb 16 15:42:05 2000
@@ -37,7 +37,8 @@
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
 export-objs     :=	busmouse.o console.o i2c-old.o keyboard.o \
-			misc.o pty.o random.o selection.o serial.o videodev.o
+			misc.o pty.o random.o selection.o serial.o videodev.o \
+			tty_io.o
 
 KEYMAP   =defkeymap.o
 KEYBD    =pc_keyb.o
diff -u --recursive --new-file v2.3.45/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.3.45/linux/drivers/char/console.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/char/console.c	Wed Feb 16 15:42:05 2000
@@ -81,6 +81,7 @@
 #include <linux/mm.h>
 #include <linux/console.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 #include <linux/console_struct.h>
@@ -120,6 +121,10 @@
 #define DEFAULT_BELL_PITCH	750
 #define DEFAULT_BELL_DURATION	(HZ/8)
 
+extern int tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+			       unsigned int minor);
+extern void vcs_make_devfs (unsigned int index, int unregister);
+
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 #endif
@@ -177,12 +182,13 @@
  * Unfortunately, we need to delay tty echo when we're currently writing to the
  * console since the code is (and always was) not re-entrant, so we insert
  * all filp requests to con_task_queue instead of tq_timer and run it from
- * the console_bh.
+ * the console_tasklet.  The console_tasklet is protected by the IRQ
+ * protected console_lock.
  */
 DECLARE_TASK_QUEUE(con_task_queue);
 
 /*
- * For the same reason, we defer scrollback to the console_bh.
+ * For the same reason, we defer scrollback to the console tasklet.
  */
 static int scrollback_delta = 0;
 
@@ -224,7 +230,7 @@
 static inline void scrolldelta(int lines)
 {
 	scrollback_delta += lines;
-	mark_bh(CONSOLE_BH);
+	tasklet_schedule(&console_tasklet);
 }
 
 static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
@@ -546,16 +552,12 @@
 {
 	int redraw = 1;
 	int currcons, old_console;
-	static int lock = 0;
 
-	if (lock)
-		return;
 	if (!vc_cons_allocated(new_console)) {
 		/* strange ... */
-		printk("redraw_screen: tty %d not allocated ??\n", new_console+1);
+		/* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */
 		return;
 	}
-	lock = 1;
 
 	if (is_switch) {
 		currcons = fg_console;
@@ -591,7 +593,6 @@
 		set_leds();
 		compute_shiftstate();
 	}
-	lock = 0;
 }
 
 /*
@@ -1785,6 +1786,19 @@
 	}
 }
 
+/* This is a temporary buffer used to prepare a tty console write
+ * so that we can easily avoid touching user space while holding the
+ * console spinlock.  It is allocated in con_init and is shared by
+ * this code and the vc_screen read/write tty calls.
+ *
+ * We have to allocate this statically in the kernel data section
+ * since console_init (and thus con_init) are called before any
+ * kernel memory allocation is available.
+ */
+char con_buf[PAGE_SIZE];
+#define CON_BUF_SIZE	PAGE_SIZE
+DECLARE_MUTEX(con_buf_sem);
+
 static int do_con_write(struct tty_struct * tty, int from_user,
 			const unsigned char *buf, int count)
 {
@@ -1814,12 +1828,28 @@
 	    return 0;
 	}
 
+	down(&con_buf_sem);
+
 	if (from_user) {
-		/* just to make sure that noone lurks at places he shouldn't see. */
-		if (verify_area(VERIFY_READ, buf, count))
-			return 0; /* ?? are error codes legal here ?? */
+		if (count > CON_BUF_SIZE)
+			count = CON_BUF_SIZE;
+		if (copy_from_user(con_buf, buf, count)) {
+			n = 0; /* ?? are error codes legal here ?? */
+			goto out;
+		}
+
+		buf = con_buf;
 	}
 
+	/* At this point 'buf' is guarenteed to be a kernel buffer
+	 * and therefore no access to userspace (and therefore sleeping)
+	 * will be needed.  The con_buf_sem serializes all tty based
+	 * console rendering and vcs write/read operations.  We hold
+	 * the console spinlock during the entire write.
+	 */
+
+	spin_lock_irq(&console_lock);
+
 	himask = hi_font_mask;
 	charmask = himask ? 0x1ff : 0xff;
 
@@ -1827,15 +1857,11 @@
 	if (IS_FG)
 		hide_cursor(currcons);
 
-	disable_bh(CONSOLE_BH);
 	while (!tty->stopped && count) {
-		enable_bh(CONSOLE_BH);
-		if (from_user)
-			__get_user(c, buf);
-		else
-			c = *buf;
-		buf++; n++; count--;
-		disable_bh(CONSOLE_BH);
+		c = *buf;
+		buf++;
+		n++;
+		count--;
 
 		if (utf) {
 		    /* Combine UTF-8 into Unicode */
@@ -1940,23 +1966,34 @@
 		do_con_trol(tty, currcons, c);
 	}
 	FLUSH
-	enable_bh(CONSOLE_BH);
+	spin_unlock_irq(&console_lock);
+
+out:
+	up(&con_buf_sem);
+
 	return n;
 #undef FLUSH
 }
 
 /*
- * This is the console switching bottom half handler.
+ * This is the console switching tasklet.
  *
- * Doing console switching in a bottom half handler allows
+ * Doing console switching in a tasklet allows
  * us to do the switches asynchronously (needed when we want
- * to switch due to a keyboard interrupt), while still giving
- * us the option to easily disable it to avoid races when we
- * need to write to the console.
- */
-static void console_bh(void)
-{
+ * to switch due to a keyboard interrupt).  Synchronization
+ * with other console code and prevention of re-entrancy is
+ * ensured with console_lock.
+ */
+static void console_softint(unsigned long ignored)
+{
+	/* Runs the task queue outside of the console lock.  These
+	 * callbacks can come back into the console code and thus
+	 * will perform their own locking.
+	 */
 	run_task_queue(&con_task_queue);
+
+	spin_lock_irq(&console_lock);
+
 	if (want_console >= 0) {
 		if (want_console != fg_console && vc_cons_allocated(want_console)) {
 			hide_cursor(fg_console);
@@ -1978,6 +2015,8 @@
 			sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta);
 		scrollback_delta = 0;
 	}
+
+	spin_unlock_irq(&console_lock);
 }
 
 #ifdef CONFIG_VT_CONSOLE
@@ -1985,10 +2024,7 @@
 /*
  *	Console on virtual terminal
  *
- * NOTE NOTE NOTE! This code can do no global locking. In particular,
- * we can't disable interrupts or bottom half handlers globally, because
- * we can be called from contexts that hold critical spinlocks, and
- * trying do get a global lock at this point will lead to deadlocks.
+ * The console_lock must be held when we get here.
  */
 
 void vt_console_print(struct console *co, const char * b, unsigned count)
@@ -2015,7 +2051,7 @@
 
 	if (!vc_cons_allocated(currcons)) {
 		/* impossible */
-		printk("vt_console_print: tty %d not allocated ??\n", currcons+1);
+		/* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
 		goto quit;
 	}
 
@@ -2263,13 +2299,18 @@
 		tty->winsize.ws_row = video_num_lines;
 		tty->winsize.ws_col = video_num_columns;
 	}
+	if (tty->count == 1)
+		vcs_make_devfs (currcons, 0);
 	return 0;
 }
 
 static void con_close(struct tty_struct *tty, struct file * filp)
 {
-	if (tty->count == 1)
-		tty->driver_data = 0;
+	if (!tty)
+		return;
+	if (tty->count != 1) return;
+	vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1);
+	tty->driver_data = 0;
 }
 
 static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear)
@@ -2305,6 +2346,8 @@
 struct tty_driver console_driver;
 static int console_refcount;
 
+DECLARE_TASKLET_DISABLED(console_tasklet, console_softint, 0);
+
 void __init con_init(void)
 {
 	const char *display_desc = NULL;
@@ -2319,7 +2362,7 @@
 
 	memset(&console_driver, 0, sizeof(struct tty_driver));
 	console_driver.magic = TTY_DRIVER_MAGIC;
-	console_driver.name = "tty";
+	console_driver.name = "vc/%d";
 	console_driver.name_base = 1;
 	console_driver.major = TTY_MAJOR;
 	console_driver.minor_start = 1;
@@ -2327,6 +2370,11 @@
 	console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
 	console_driver.init_termios = tty_std_termios;
 	console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
+	/* Tell tty_register_driver() to skip consoles because they are
+	 * registered before kmalloc() is ready. We'll patch them in later. 
+	 * See comments at console_init(); see also con_init_devfs(). 
+	 */
+	console_driver.flags |= TTY_DRIVER_NO_DEVFS;
 	console_driver.refcount = &console_refcount;
 	console_driver.table = console_table;
 	console_driver.termios = console_termios;
@@ -2393,7 +2441,8 @@
 	register_console(&vt_console_driver);
 #endif
 
-	init_bh(CONSOLE_BH, console_bh);
+	tasklet_enable(&console_tasklet);
+	tasklet_schedule(&console_tasklet);
 }
 
 #ifndef VT_SINGLE_DRIVER
@@ -2486,6 +2535,19 @@
     vesa_blank_mode = (mode < 4) ? mode : 0;
 }
 
+/* We can't register the console with devfs during con_init(), because it
+ * is called before kmalloc() works.  This function is called later to
+ * do the registration.
+ */
+void __init con_init_devfs (void)
+{
+	int i;
+
+	for (i = 0; i < console_driver.num; i++)
+		tty_register_devfs (&console_driver, 0,
+				    console_driver.minor_start + i);
+}
+
 static void vesa_powerdown(void)
 {
     struct vc_data *c = vc_cons[fg_console].d;
@@ -2744,9 +2806,11 @@
 		}
 		op->data = temp;
 	}
-	disable_bh(CONSOLE_BH);
+
+	spin_lock_irq(&console_lock);
 	rc = sw->con_font_op(vc_cons[currcons].d, op);
-	enable_bh(CONSOLE_BH);
+	spin_unlock_irq(&console_lock);
+
 	op->data = old_op.data;
 	if (!rc && !set) {
 		int c = (op->width+7)/8 * 32 * op->charcount;
diff -u --recursive --new-file v2.3.45/linux/drivers/char/dsp56k.c linux/drivers/char/dsp56k.c
--- v2.3.45/linux/drivers/char/dsp56k.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/char/dsp56k.c	Wed Feb 16 15:42:05 2000
@@ -34,6 +34,7 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/segment.h>
 #include <asm/atarihw.h>
@@ -510,6 +511,9 @@
 
 
 /****** Init and module functions ******/
+
+static devfs_handle_t devfs_handle = NULL;
+
 int __init dsp56k_init(void)
 {
 	if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
@@ -517,10 +521,14 @@
 		return -ENODEV;
 	}
 
-	if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
+	if(devfs_register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
 		printk("DSP56k driver: Unable to register driver\n");
 		return -ENODEV;
 	}
+	devfs_handle = devfs_register (NULL, "dsp56k", 0, DEVFS_FL_NONE,
+				       DSP56K_MAJOR, 0,
+				       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				       &dsp56k_fops, NULL);
 
 	dsp56k.in_use = 0;
 
@@ -537,6 +545,7 @@
 
 void cleanup_module(void)
 {
-	unregister_chrdev(DSP56K_MAJOR, "dsp56k");
+	devfs_unregister_chrdev(DSP56K_MAJOR, "dsp56k");
+	devfs_unregister (devfs_handle);
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.3.45/linux/drivers/char/dtlk.c linux/drivers/char/dtlk.c
--- v2.3.45/linux/drivers/char/dtlk.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/char/dtlk.c	Wed Feb 16 15:42:05 2000
@@ -64,6 +64,7 @@
 #include <linux/init.h>		/* for __init, module_{init,exit} */
 #include <linux/poll.h>		/* for POLLIN, etc. */
 #include <linux/dtlk.h>		/* local header file for DoubleTalk values */
+#include <linux/devfs_fs_kernel.h>
 
 #ifdef TRACING
 #define TRACE_TEXT(str) printk(str);
@@ -352,19 +353,25 @@
 	return 0;
 }
 
+static devfs_handle_t devfs_handle;
+
 static int __init dtlk_init(void)
 {
 	dtlk_port_lpc = 0;
 	dtlk_port_tts = 0;
 	dtlk_busy = 0;
 	dtlk_timer_active = 0;
-	dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
+	dtlk_major = devfs_register_chrdev(0, "dtlk", &dtlk_fops);
 	if (dtlk_major == 0) {
 		printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
 		return 0;
 	}
 	if (dtlk_dev_probe() == 0)
 		printk(", MAJOR %d\n", dtlk_major);
+	devfs_handle = devfs_register (NULL, "dtlk", 0, DEVFS_FL_NONE,
+				       dtlk_major, DTLK_MINOR,
+				       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				       &dtlk_fops, NULL);
 
 	init_timer(&dtlk_timer);
 	dtlk_timer.function = dtlk_timer_tick;
@@ -383,7 +390,8 @@
 						   signals... */
 
 	dtlk_write_tts(DTLK_CLEAR);
-	unregister_chrdev(dtlk_major, "dtlk");
+	devfs_unregister_chrdev(dtlk_major, "dtlk");
+	devfs_unregister(devfs_handle);
 	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/char/dz.c linux/drivers/char/dz.c
--- v2.3.45/linux/drivers/char/dz.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/char/dz.c	Mon Feb 14 15:31:14 2000
@@ -1427,6 +1427,7 @@
  * dz_console_print ()
  *
  * dz_console_print is registered for printk.
+ * The console_lock must be held when we get here.
  * ------------------------------------------------------------------- 
  */
 static void dz_console_print (struct console *cons, 
diff -u --recursive --new-file v2.3.45/linux/drivers/char/esp.c linux/drivers/char/esp.c
--- v2.3.45/linux/drivers/char/esp.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/char/esp.c	Mon Feb 14 15:34:21 2000
@@ -203,20 +203,6 @@
 	outb(value, info->port+offset);
 }
 
-static inline int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-
-	return order;
-}
-
 /*
  * ------------------------------------------------------------
  * rs_stop() and rs_start()
@@ -965,14 +951,14 @@
 
 	if (!(info->stat_flags & ESP_STAT_USE_PIO) && !dma_buffer) {
 		dma_buffer = (char *)__get_dma_pages(
-			GFP_KERNEL, __get_order(DMA_BUFFER_SZ));
+			GFP_KERNEL, get_order(DMA_BUFFER_SZ));
 
 		/* use PIO mode if DMA buf/chan cannot be allocated */
 		if (!dma_buffer)
 			info->stat_flags |= ESP_STAT_USE_PIO;
 		else if (request_dma(dma, "esp serial")) {
 			free_pages((unsigned int)dma_buffer,
-				   __get_order(DMA_BUFFER_SZ));
+				   get_order(DMA_BUFFER_SZ));
 			dma_buffer = 0;
 			info->stat_flags |= ESP_STAT_USE_PIO;
 		}
@@ -1076,7 +1062,7 @@
 		if (!current_port) {
 			free_dma(dma);
 			free_pages((unsigned int)dma_buffer,
-				   __get_order(DMA_BUFFER_SZ));
+				   get_order(DMA_BUFFER_SZ));
 			dma_buffer = 0;
 		}		
 	}
@@ -2785,7 +2771,7 @@
 
 	if (dma_buffer)
 		free_pages((unsigned int)dma_buffer,
-			__get_order(DMA_BUFFER_SZ));
+			get_order(DMA_BUFFER_SZ));
 
 	if (tmp_buf)
 		free_page((unsigned long)tmp_buf);
diff -u --recursive --new-file v2.3.45/linux/drivers/char/ftape/lowlevel/ftape-buffer.c linux/drivers/char/ftape/lowlevel/ftape-buffer.c
--- v2.3.45/linux/drivers/char/ftape/lowlevel/ftape-buffer.c	Tue Nov 25 14:45:27 1997
+++ linux/drivers/char/ftape/lowlevel/ftape-buffer.c	Mon Feb 14 15:34:21 2000
@@ -39,20 +39,6 @@
 /*  DMA'able memory allocation stuff.
  */
 
-/* Pure 2^n version of get_order */
-static inline int __get_order(size_t size)
-{
-	unsigned long order;
-
-	size = (size-1) >> (PAGE_SHIFT-1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 static inline void *dmaalloc(size_t size)
 {
 	unsigned long addr;
@@ -60,7 +46,7 @@
 	if (size == 0) {
 		return NULL;
 	}
-	addr = __get_dma_pages(GFP_KERNEL, __get_order(size));
+	addr = __get_dma_pages(GFP_KERNEL, get_order(size));
 	if (addr) {
 		int i;
 
@@ -80,7 +66,7 @@
 		     i < MAP_NR((unsigned long)addr+size); i++) {
 			mem_map_unreserve (i);
 		}
-		free_pages((unsigned long) addr, __get_order(size));
+		free_pages((unsigned long) addr, get_order(size));
 	}
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/char/ftape/zftape/zftape-init.c linux/drivers/char/ftape/zftape/zftape-init.c
--- v2.3.45/linux/drivers/char/ftape/zftape/zftape-init.c	Thu Feb 10 17:11:07 2000
+++ linux/drivers/char/ftape/zftape/zftape-init.c	Wed Feb 16 15:42:05 2000
@@ -35,6 +35,7 @@
 #endif
 #include <linux/fcntl.h>
 #include <linux/wrapper.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <linux/zftape.h>
 #if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16)
@@ -403,6 +404,7 @@
  */
 int __init zft_init(void)
 {
+	int i;
 	TRACE_FUN(ft_t_flow);
 
 #ifdef MODULE
@@ -431,7 +433,43 @@
 	TRACE(ft_t_info, "zft_init @ 0x%p", zft_init);
 	TRACE(ft_t_info,
 	      "installing zftape VFS interface for ftape driver ...");
-	TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+	TRACE_CATCH(devfs_register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+
+	for (i = 0; i < 4; i++) {
+		char devname[8];
+
+		sprintf (devname, "qft%i", i);
+		devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+			        QIC117_TAPE_MAJOR, i,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&zft_cdev, NULL);
+		sprintf (devname, "nqft%i", i);
+		devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+				QIC117_TAPE_MAJOR, i + 4,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&zft_cdev, NULL);
+		sprintf (devname, "zqft%i", i);
+		devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+				QIC117_TAPE_MAJOR, i + 16,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&zft_cdev, NULL);
+		sprintf (devname, "nzqft%i", i);
+		devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+				QIC117_TAPE_MAJOR, i + 20,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&zft_cdev, NULL);
+		sprintf (devname, "rawqft%i", i);
+		devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+				QIC117_TAPE_MAJOR, i + 32,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&zft_cdev, NULL);
+		sprintf (devname, "nrawqft%i", i);
+		devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+				QIC117_TAPE_MAJOR, i + 36,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&zft_cdev, NULL);
+	}
+
 #if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
 	register_symtab(&zft_symbol_table); /* add global zftape symbols */
 #endif
@@ -471,12 +509,29 @@
  */
 void cleanup_module(void)
 {
+	int i;
+	char devname[8];
+
 	TRACE_FUN(ft_t_flow);
 
-	if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
+	if (devfs_unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
 		TRACE(ft_t_warn, "failed");
 	} else {
 		TRACE(ft_t_info, "successful");
+	}
+        for (i = 0; i < 4; i++) {
+		sprintf(devname, "qft%i", i);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i, DEVFS_SPECIAL_CHR, 0));
+		sprintf(devname, "nqft%i", i);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 4, DEVFS_SPECIAL_CHR, 0));
+		sprintf(devname, "zqft%i", i);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 16, DEVFS_SPECIAL_CHR, 0));
+		sprintf(devname, "nzqft%i", i);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 20, DEVFS_SPECIAL_CHR, 0));
+		sprintf(devname, "rawqft%i", i);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 32, DEVFS_SPECIAL_CHR, 0));
+		sprintf(devname, "nrawqft%i", i);
+		devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 36, DEVFS_SPECIAL_CHR, 0));
 	}
 	zft_uninit_mem(); /* release remaining memory, if any */
         printk(KERN_INFO "zftape successfully unloaded.\n");
diff -u --recursive --new-file v2.3.45/linux/drivers/char/isicom.c linux/drivers/char/isicom.c
--- v2.3.45/linux/drivers/char/isicom.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/isicom.c	Mon Feb 14 15:31:14 2000
@@ -2051,7 +2051,8 @@
 	re_schedule = 0;
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(HZ);
-	disable_bh(ISICOM_BH);
+
+	remove_bh(ISICOM_BH);
 	
 #ifdef ISICOM_DEBUG	
 	printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count);
diff -u --recursive --new-file v2.3.45/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.3.45/linux/drivers/char/istallion.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/istallion.c	Wed Feb 16 15:42:05 2000
@@ -39,6 +39,7 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -829,12 +830,14 @@
 
 /*****************************************************************************/
 
+static devfs_handle_t devfs_handle = NULL;
+
 void cleanup_module()
 {
 	stlibrd_t	*brdp;
 	stliport_t	*portp;
 	unsigned long	flags;
-	int		i, j;
+	int		i, j, k;
 
 #if DEBUG
 	printk("cleanup_module()\n");
@@ -863,10 +866,10 @@
 		restore_flags(flags);
 		return;
 	}
-	if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+	devfs_unregister (devfs_handle);
+	if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
 		printk("STALLION: failed to un-register serial memory device, "
 			"errno=%d\n", -i);
-
 	if (stli_tmpwritebuf != (char *) NULL)
 		kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE);
 	if (stli_txcookbuf != (char *) NULL)
@@ -5321,8 +5324,14 @@
  *	Set up a character driver for the shared memory region. We need this
  *	to down load the slave code image. Also it is a useful debugging tool.
  */
-	if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem))
+	if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem))
 		printk("STALLION: failed to register serial memory device\n");
+
+	devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL);
+	devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
+			       STL_SIOMEMMAJOR, 0,
+			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &stli_fsiomem, NULL);
 
 /*
  *	Set up the tty driver structure and register us as a driver.
diff -u --recursive --new-file v2.3.45/linux/drivers/char/joystick/joystick.c linux/drivers/char/joystick/joystick.c
--- v2.3.45/linux/drivers/char/joystick/joystick.c	Wed Dec  8 14:11:26 1999
+++ linux/drivers/char/joystick/joystick.c	Wed Feb 16 15:42:05 2000
@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/joystick.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/malloc.h>
@@ -694,6 +695,10 @@
 	return prev;
 }
 
+extern struct file_operations js_fops;
+
+static devfs_handle_t devfs_handle = NULL;
+
 int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name,
 					js_ops_func open, js_ops_func close)
 {
@@ -702,6 +707,7 @@
 	void *all;
 	int i = 0;
 	unsigned long flags;
+	char devfs_name[8];
 
 	if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) +
 			2 * (((buttons - 1) >> 5) + 1) * sizeof(int) +
@@ -732,6 +738,12 @@
 
 	curd->name = all += axes * sizeof(struct js_corr);
 	strcpy(curd->name, name);
+	sprintf (devfs_name, "analogue%d", number);
+	curd->devfs_handle = devfs_register (devfs_handle, devfs_name, 0,
+					     DEVFS_FL_DEFAULT,
+					     JOYSTICK_MAJOR, number,
+					     S_IFCHR | S_IRUGO | S_IWUSR, 0, 0,
+					     &js_fops, NULL);
 
 	port->devs[number] = curd;
 	port->axes[number] = curd->new.axes;
@@ -760,6 +772,7 @@
 
 	spin_unlock_irqrestore(&js_lock, flags);	
 
+	devfs_unregister (dev->devfs_handle);
 	kfree(dev);
 }
 
@@ -788,10 +801,11 @@
 #endif
 {
 
-	if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
+	if (devfs_register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
 		printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
 		return -EBUSY;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "joysticks", 9, NULL);
 
 	printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n",
 		JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
@@ -871,8 +885,9 @@
 void cleanup_module(void)
 {
 	del_timer(&js_timer);
-	if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
-		printk(KERN_ERR "js: can't unregister device\n");
+	devfs_unregister (devfs_handle);
+	if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js"))
+	printk(KERN_ERR "js: can't unregister device\n");
 }
 #endif
 
diff -u --recursive --new-file v2.3.45/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.3.45/linux/drivers/char/keyboard.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/char/keyboard.c	Mon Feb 14 15:31:14 2000
@@ -208,7 +208,7 @@
 	pm_access(pm_kbd);
 
 	do_poke_blanked_console = 1;
-	mark_bh(CONSOLE_BH);
+	tasklet_schedule(&console_tasklet);
 	add_keyboard_randomness(scancode | up_flag);
 
 	tty = ttytab? ttytab[fg_console]: NULL;
diff -u --recursive --new-file v2.3.45/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.3.45/linux/drivers/char/lp.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/char/lp.c	Wed Feb 16 15:42:05 2000
@@ -16,6 +16,7 @@
  * Parport sharing hacking by Andrea Arcangeli
  * Fixed kernel_(to/from)_user memory copy to check for errors
  * 				by Riccardo Facchetti <fizban@tin.it>
+ * 22-JAN-1998  Added support for devfs  Richard Gooch <rgooch@atnf.csiro.au>
  * Redesigned interrupt handling for handle printers with buggy handshake
  *				by Andrea Arcangeli, 11 May 1998
  * Full efficient handling of printer with buggy irq handshake (now I have
@@ -118,6 +119,7 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/malloc.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
@@ -138,6 +140,8 @@
 /* ROUND_UP macro from fs/select.c */
 #define ROUND_UP(x,y) (((x)+(y)-1)/(y))
 
+static devfs_handle_t devfs_handle = NULL;
+
 struct lp_struct lp_table[LP_NO];
 
 static unsigned int lp_count = 0;
@@ -151,72 +155,6 @@
 
 #undef LP_DEBUG
 
-/* If you want to see if you can get lp_poll working, define this. */
-#undef SUPPORT_POLL
-
-/* --- parport support ----------------------------------------- */
-
-static int lp_preempt(void *handle)
-{
-       struct lp_struct *lps = (struct lp_struct *)handle;
-
-       if (!(lps->flags & LP_PORT_BUSY)) {
-	       /* Let the port go. */
-	       clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
-	       return 0;
-       }
-
-       if (!(lps->flags & LP_PORT_BUSY)) {
-	       /* Let the port go. */
-	       clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
-	       return 0;
-       }
-
-       /* Don't actually release the port now */
-       return 1;
-}
-
-static void lp_check_data (struct lp_struct *lp)
-{
-#if !defined(CONFIG_PARPORT_1284) || !defined (SUPPORT_POLL)
-	return;
-#else
-	struct pardevice *dev = lp->dev;
-	if (!(lp->flags & LP_NO_REVERSE)) {
-		int err = parport_negotiate (dev->port, IEEE1284_MODE_NIBBLE);
-		if (err)
-			lp->flags |= LP_NO_REVERSE;
-		else {
-			unsigned char s = parport_read_status (dev->port);
-			if (s & PARPORT_STATUS_ERROR)
-				lp->flags &= ~LP_DATA_AVAIL;
-			else {
-				lp->flags |= LP_DATA_AVAIL;
-				if (waitqueue_active (&lp->dataq))
-					wake_up_interruptible (&lp->dataq);
-			}
-		}
-	}
-#endif /* IEEE 1284 support */
-}
-
-static void lp_parport_release (int minor)
-{
-	lp_check_data (&lp_table[minor]);
-	if (test_and_clear_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
-		parport_release (lp_table[minor].dev);
-
-	lp_table[minor].flags &= ~LP_PORT_BUSY;
-}
-
-static void lp_parport_claim (int minor)
-{
-	if (!test_and_set_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
-		parport_claim_or_block (lp_table[minor].dev);
-
-	lp_table[minor].flags |= LP_PORT_BUSY;
-}
-
 /* --- low-level port access ----------------------------------- */
 
 #define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))
@@ -227,42 +165,15 @@
 static int lp_reset(int minor)
 {
 	int retval;
-	lp_parport_claim (minor);
+	parport_claim_or_block (lp_table[minor].dev);
 	w_ctr(minor, LP_PSELECP);
 	udelay (LP_DELAY);
 	w_ctr(minor, LP_PSELECP | LP_PINITP);
 	retval = r_str(minor);
-	lp_parport_release (minor);
+	parport_release (lp_table[minor].dev);
 	return retval;
 }
 
-static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
-	if (!(lp_dev->flags & LP_PORT_BUSY))
-		/* We must have the port since we got an interrupt. */
-		lp_check_data (lp_dev);
-	if (waitqueue_active (&lp_dev->waitq))
-		wake_up_interruptible (&lp_dev->waitq);
-}
-
-static void lp_wakeup (void *handle)
-{
-	struct lp_struct *lp_dev = handle;
-
-	if (lp_dev->flags & LP_PORT_BUSY)
-		return;
-
-	/* Grab the port if it can help (i.e. reverse mode is possible). */
-	if (!(lp_dev->flags & LP_NO_REVERSE)) {
-		parport_claim (lp_dev->dev);
-		set_bit (LP_HAVE_PORT_BIT, &lp_dev->flags);
-		lp_check_data (lp_dev);
-		if (waitqueue_active (&lp_dev->waitq))
-			wake_up_interruptible (&lp_dev->waitq);
-	}
-}
-
 static void lp_error (int minor)
 {
 	int polling;
@@ -271,10 +182,10 @@
 		return;
 
 	polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
-	if (polling) lp_parport_release (minor);
+	if (polling) parport_release (lp_table[minor].dev);
 	interruptible_sleep_on_timeout (&lp_table[minor].waitq,
 					LP_TIMEOUT_POLLED);
-	if (polling) lp_parport_claim (minor);
+	if (polling) parport_claim_or_block (lp_table[minor].dev);
 	else parport_yield_blocking (lp_table[minor].dev);
 }
 
@@ -347,7 +258,7 @@
 
  	/* Claim Parport or sleep until it becomes available
  	 */
- 	lp_parport_claim (minor);
+ 	parport_claim_or_block (lp_table[minor].dev);
 
 	/* Go to compatibility mode. */
 	parport_negotiate (port, IEEE1284_MODE_COMPAT);
@@ -402,7 +313,7 @@
 	/* Not really necessary, but polite. */
 	parport_set_timeout (lp_table[minor].dev, old_to);
 
- 	lp_parport_release (minor);
+ 	parport_release (lp_table[minor].dev);
 
 	up (&lp_table[minor].port_mutex);
 
@@ -426,7 +337,7 @@
 	if (down_interruptible (&lp_table[minor].port_mutex))
 		return -EINTR;
 
-	lp_parport_claim (minor);
+	parport_claim_or_block (lp_table[minor].dev);
 
 	for (;;) {
 		retval = parport_read (port, kbuf, count);
@@ -447,7 +358,7 @@
 		}
 	}
 
-	lp_parport_release (minor);
+	parport_release (lp_table[minor].dev);
 
 	if (retval > 0 && copy_to_user (buf, kbuf, retval))
 		retval = -EFAULT;
@@ -479,9 +390,9 @@
 	   should most likely only ever be used by the tunelp application. */
 	if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
 		int status;
-		lp_parport_claim (minor);
+		parport_claim_or_block (lp_table[minor].dev);
 		status = r_str(minor);
-		lp_parport_release (minor);
+		parport_release (lp_table[minor].dev);
 		if (status & LP_POUTPA) {
 			printk(KERN_INFO "lp%d out of paper\n", minor);
 			MOD_DEC_USE_COUNT;
@@ -573,9 +484,9 @@
 				return -EFAULT;
 			break;
 		case LPGETSTATUS:
-			lp_parport_claim(minor);
+			parport_claim_or_block (lp_table[minor].dev);
 			status = r_str(minor);
-			lp_parport_release(minor);
+			parport_release (lp_table[minor].dev);
 
 			if (copy_to_user((int *) arg, &status, sizeof(int)))
 				return -EFAULT;
@@ -624,21 +535,6 @@
 	return retval;
 }
 
-#ifdef CONFIG_PARPORT_1284
-static unsigned int lp_poll (struct file *filp, struct poll_table_struct *wait)
-{
-	unsigned int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
-	unsigned int mask = POLLOUT | POLLWRNORM; /* always writable */
-
-	poll_wait (filp, &lp_table[minor].dataq, wait);
-
-	if (lp_table[minor].flags & LP_DATA_AVAIL)
-		mask |= POLLIN | POLLRDNORM;
-
-	return mask;
-}
-#endif /* IEEE 1284 support */
-
 static struct file_operations lp_fops = {
 	write:		lp_write,
 	ioctl:		lp_ioctl,
@@ -646,7 +542,6 @@
 	release:	lp_release,
 #ifdef CONFIG_PARPORT_1284
 	read:		lp_read,
-	poll:		lp_poll,
 #endif
 };
 
@@ -661,6 +556,8 @@
  * non-zero to get the latter behaviour. */
 #define CONSOLE_LP_STRICT 1
 
+/* The console_lock must be held when we get here. */
+
 static void lp_console_write (struct console *co, const char *s,
 			      unsigned count)
 {
@@ -669,12 +566,9 @@
 	ssize_t written;
 	signed long old_to;
 
-	if (!(lp_table[CONSOLE_LP].flags & (1<<LP_HAVE_PORT_BIT))) {
-		if (parport_claim (dev))
-			/* Nothing we can do. */
-			return;
-		set_bit (LP_HAVE_PORT_BIT, &lp_table[CONSOLE_LP].flags);
-	}
+	if (parport_claim (dev))
+		/* Nothing we can do. */
+		return;
 
 	old_to = parport_set_timeout (dev, 0);
 
@@ -715,6 +609,7 @@
 	} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));
 
 	parport_set_timeout (dev, old_to);
+	parport_release (dev);
 }
 
 static kdev_t lp_console_device (struct console *c)
@@ -786,9 +681,7 @@
 static int lp_register(int nr, struct parport *port)
 {
 	lp_table[nr].dev = parport_register_device(port, "lp", 
-						   lp_preempt, lp_wakeup,
-						   lp_interrupt, 
-						   0,
+						   NULL, NULL, NULL, 0,
 						   (void *) &lp_table[nr]);
 	if (lp_table[nr].dev == NULL)
 		return 1;
@@ -883,7 +776,7 @@
 		lp_table[i].timeout = 10 * HZ;
 	}
 
-	if (register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
+	if (devfs_register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
 		printk ("lp: unable to get major %d\n", LP_MAJOR);
 		return -EIO;
 	}
@@ -893,6 +786,22 @@
 		return -EIO;
 	}
 
+	devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL);
+	if (lp_count) {
+		for (i = 0; i < LP_NO; ++i)
+		{
+		    char name[8];
+
+		    if (!(lp_table[i].flags & LP_EXIST)) 
+			continue; /* skip this entry: it doesn't exist. */
+		    sprintf (name, "%d", i);
+		    devfs_register (devfs_handle, name, 0,
+				    DEVFS_FL_DEFAULT, LP_MAJOR, i,
+				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+				    &lp_fops, NULL);
+		}
+	}
+
 	if (!lp_count) {
 		printk (KERN_INFO "lp: driver loaded but no devices found\n");
 #ifndef CONFIG_PARPORT_12843
@@ -943,12 +852,11 @@
 	unregister_console (&lpcons);
 #endif
 
-	unregister_chrdev(LP_MAJOR, "lp");
+	devfs_unregister (devfs_handle);
+	devfs_unregister_chrdev(LP_MAJOR, "lp");
 	for (offset = 0; offset < LP_NO; offset++) {
 		if (lp_table[offset].dev == NULL)
 			continue;
-		if (lp_table[offset].flags & (1<<LP_HAVE_PORT_BIT))
-			parport_release (lp_table[offset].dev);
 		parport_unregister_device(lp_table[offset].dev);
 	}
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.3.45/linux/drivers/char/mem.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/char/mem.c	Wed Feb 16 15:42:05 2000
@@ -2,6 +2,9 @@
  *  linux/drivers/char/mem.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Added devfs support. 
+ *    Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
  */
 
 #include <linux/config.h>
@@ -570,14 +573,42 @@
 	return 0;
 }
 
+void __init memory_devfs_register (void)
+{
+    /*  These are never unregistered  */
+    static const struct {
+	unsigned short minor;
+	char *name;
+	umode_t mode;
+	struct file_operations *fops;
+    } list[] = { /* list of minor devices */
+	{1, "mem",     S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
+	{2, "kmem",    S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
+	{3, "null",    S_IRUGO | S_IWUGO,           &null_fops},
+	{4, "port",    S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
+	{5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},
+	{7, "full",    S_IRUGO | S_IWUGO,           &full_fops},
+	{8, "random",  S_IRUGO | S_IWUSR,           &random_fops},
+	{9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops}
+    };
+    int i;
+
+    for (i=0; i<(sizeof(list)/sizeof(*list)); i++)
+	devfs_register (NULL, list[i].name, 0, DEVFS_FL_NONE,
+			MEM_MAJOR, list[i].minor,
+			list[i].mode | S_IFCHR, 0, 0,
+			list[i].fops, NULL);
+}
+
 static struct file_operations memory_fops = {
 	open:		memory_open,	/* just a selector for the real open */
 };
 
 int __init chr_dev_init(void)
 {
-	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
+	if (devfs_register_chrdev(MEM_MAJOR,"mem",&memory_fops))
 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+	memory_devfs_register();
 	rand_initialize();
 	raw_init();
 #ifdef CONFIG_I2C
diff -u --recursive --new-file v2.3.45/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.3.45/linux/drivers/char/misc.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/misc.c	Wed Feb 16 15:42:05 2000
@@ -29,6 +29,8 @@
  *
  * Changes for kmod (from kerneld):
  *	Cyrus Durgin <cider@speakeasy.org>
+ *
+ * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au>  10-Jan-1998
  */
 
 #include <linux/module.h>
@@ -41,6 +43,7 @@
 #include <linux/major.h>
 #include <linux/malloc.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/stat.h>
 #include <linux/init.h>
 
@@ -121,6 +124,8 @@
 
 int misc_register(struct miscdevice * misc)
 {
+	static devfs_handle_t devfs_handle = NULL;
+
 	if (misc->next || misc->prev)
 		return -EBUSY;
 	if (misc->minor == MISC_DYNAMIC_MINOR) {
@@ -133,6 +138,13 @@
 	}
 	if (misc->minor < DYNAMIC_MINORS)
 		misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
+	if (!devfs_handle)
+		devfs_handle = devfs_mk_dir (NULL, "misc", 4, NULL);
+	misc->devfs_handle =
+	    devfs_register (devfs_handle, misc->name, 0, DEVFS_FL_NONE,
+			    MISC_MAJOR, misc->minor,
+			    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
+			    misc->fops, NULL);
 
 	/*
 	 * Add it to the front, so that later devices can "override"
@@ -154,6 +166,7 @@
 	misc->next->prev = misc->prev;
 	misc->next = NULL;
 	misc->prev = NULL;
+	devfs_unregister (misc->devfs_handle);
 	if (i < DYNAMIC_MINORS && i>0) {
 		misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
 	}
@@ -217,11 +230,10 @@
 #ifdef CONFIG_SGI
 	streamable_init ();
 #endif
-	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
+	if (devfs_register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
 		printk("unable to get major %d for misc devices\n",
 		       MISC_MAJOR);
 		return -EIO;
 	}
-
 	return 0;
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v2.3.45/linux/drivers/char/pcxx.c	Fri Oct 22 13:21:48 1999
+++ linux/drivers/char/pcxx.c	Mon Feb 14 15:31:14 2000
@@ -1198,7 +1198,6 @@
 	memset(pcxe_termios_locked,0,sizeof(struct termios *)*nbdevs);
 
 	init_bh(DIGI_BH,do_pcxe_bh);
-	enable_bh(DIGI_BH);
 
 	timer_table[DIGI_TIMER].fn = pcxxpoll;
 	timer_table[DIGI_TIMER].expires = 0;
diff -u --recursive --new-file v2.3.45/linux/drivers/char/ppdev.c linux/drivers/char/ppdev.c
--- v2.3.45/linux/drivers/char/ppdev.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/ppdev.c	Wed Feb 16 15:42:05 2000
@@ -45,6 +45,8 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/ioctl.h>
 #include <linux/parport.h>
 #include <linux/ctype.h>
@@ -579,13 +581,20 @@
 	release:	pp_release,
 };
 
+static devfs_handle_t devfs_handle = NULL;
+
 static int __init ppdev_init (void)
 {
-	if (register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) {
+	if (devfs_register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) {
 		printk (KERN_WARNING CHRDEV ": unable to get major %d\n",
 			PP_MAJOR);
 		return -EIO;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL);
+	devfs_register_series (devfs_handle, "%u", PARPORT_MAX,
+			       DEVFS_FL_DEFAULT, PP_MAJOR, 0,
+			       S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+			       &pp_fops, NULL);
 
 	printk (KERN_INFO PP_VERSION "\n");
 	return 0;
@@ -594,7 +603,8 @@
 static void __exit ppdev_cleanup (void)
 {
 	/* Clean up all parport stuff */
-	unregister_chrdev (PP_MAJOR, CHRDEV);
+	devfs_unregister (devfs_handle);
+	devfs_unregister_chrdev (PP_MAJOR, CHRDEV);
 }
 
 module_init(ppdev_init);
diff -u --recursive --new-file v2.3.45/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v2.3.45/linux/drivers/char/pty.c	Thu Aug  5 14:34:01 1999
+++ linux/drivers/char/pty.c	Wed Feb 16 15:42:05 2000
@@ -10,6 +10,7 @@
 #include <linux/config.h>
 #include <linux/module.h>	/* For EXPORT_SYMBOL */
 
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
@@ -20,6 +21,7 @@
 #include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -28,6 +30,10 @@
 #define BUILDING_PTY_C 1
 #include <linux/devpts_fs.h>
 
+extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+				unsigned int minor);
+extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
+
 struct pty_struct {
 	int	magic;
 	wait_queue_head_t open_wait;
@@ -94,6 +100,7 @@
 			}
 		}
 #endif
+		tty_unregister_devfs (&tty->link->driver, MINOR (tty->device));
 		tty_vhangup(tty->link);
 	}
 }
@@ -323,6 +330,11 @@
 	clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
 	wake_up_interruptible(&pty->open_wait);
 	set_bit(TTY_THROTTLED, &tty->flags);
+	/*  register a slave for the master  */
+	if (tty->driver.major == PTY_MASTER_MAJOR)
+		tty_register_devfs(&tty->link->driver, DEVFS_FL_WAIT,
+				   tty->link->driver.minor_start +
+				   MINOR(tty->device)-tty->driver.minor_start);
 	retval = 0;
 out:
 	return retval;
@@ -346,7 +358,7 @@
 	memset(&pty_driver, 0, sizeof(struct tty_driver));
 	pty_driver.magic = TTY_DRIVER_MAGIC;
 	pty_driver.driver_name = "pty_master";
-	pty_driver.name = "pty";
+	pty_driver.name = "pty/m%d";
 	pty_driver.major = PTY_MASTER_MAJOR;
 	pty_driver.minor_start = 0;
 	pty_driver.num = NR_PTYS;
@@ -377,12 +389,16 @@
 	pty_slave_driver = pty_driver;
 	pty_slave_driver.driver_name = "pty_slave";
 	pty_slave_driver.proc_entry = 0;
-	pty_slave_driver.name = "ttyp";
+	pty_slave_driver.name = "pty/s%d";
 	pty_slave_driver.subtype = PTY_TYPE_SLAVE;
 	pty_slave_driver.major = PTY_SLAVE_MAJOR;
 	pty_slave_driver.minor_start = 0;
 	pty_slave_driver.init_termios = tty_std_termios;
 	pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
+	/* Slave ptys are registered when their corresponding master pty
+	 * is opened, and unregistered when the pair is closed.
+	 */
+	pty_slave_driver.flags |= TTY_DRIVER_NO_DEVFS;
 	pty_slave_driver.table = ttyp_table;
 	pty_slave_driver.termios = ttyp_termios;
 	pty_slave_driver.termios_locked = ttyp_termios_locked;
@@ -403,6 +419,7 @@
 
 	/* Unix98 devices */
 #ifdef CONFIG_UNIX98_PTYS
+	devfs_mk_dir (NULL, "pts", 3, NULL);
 	printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS);
 	for ( i = 0 ; i < UNIX98_NR_MAJORS ; i++ ) {
 		int j;
@@ -415,6 +432,7 @@
 		ptm_driver[i].name_base = i*NR_PTYS;
 		ptm_driver[i].num = NR_PTYS;
 		ptm_driver[i].other = &pts_driver[i];
+		ptm_driver[i].flags |= TTY_DRIVER_NO_DEVFS;
 		ptm_driver[i].table = ptm_table[i];
 		ptm_driver[i].termios = ptm_termios[i];
 		ptm_driver[i].termios_locked = ptm_termios_locked[i];
@@ -424,7 +442,7 @@
 			init_waitqueue_head(&ptm_state[i][j].open_wait);
 		
 		pts_driver[i] = pty_slave_driver;
-		pts_driver[i].name = "pts";
+		pts_driver[i].name = "pts/%d";
 		pts_driver[i].proc_entry = 0;
 		pts_driver[i].major = UNIX98_PTY_SLAVE_MAJOR+i;
 		pts_driver[i].minor_start = 0;
diff -u --recursive --new-file v2.3.45/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.3.45/linux/drivers/char/serial.c	Fri Jan 28 15:09:07 2000
+++ linux/drivers/char/serial.c	Wed Feb 16 15:42:05 2000
@@ -215,7 +215,12 @@
 #else
 #define _INLINE_
 #endif
-	
+
+extern void tty_register_devfs  (struct tty_driver *driver, unsigned int flags,
+				 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);
@@ -4394,7 +4399,7 @@
 #if (LINUX_VERSION_CODE > 0x20100)
 	serial_driver.driver_name = "serial";
 #endif
-	serial_driver.name = "ttyS";
+	serial_driver.name = "tts/%d";
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
 	serial_driver.num = NR_PORTS;
@@ -4403,7 +4408,7 @@
 	serial_driver.init_termios = tty_std_termios;
 	serial_driver.init_termios.c_cflag =
 		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
 	serial_driver.refcount = &serial_refcount;
 	serial_driver.table = serial_table;
 	serial_driver.termios = serial_termios;
@@ -4438,7 +4443,7 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
-	callout_driver.name = "cua";
+	callout_driver.name = "cua/%d";
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 #if (LINUX_VERSION_CODE >= 131343)
@@ -4485,6 +4490,10 @@
 		       (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
 		       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,
+				   callout_driver.minor_start + state->line);
 	}
 #ifdef ENABLE_SERIAL_PCI
 	probe_serial_pci();
@@ -4562,6 +4571,10 @@
 	      state->iomem_base ? (unsigned long)state->iomem_base :
 	      (unsigned long)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,
+			   callout_driver.minor_start + state->line);
 	return state->line + SERIAL_DEV_OFFSET;
 }
 
@@ -4576,6 +4589,13 @@
 		tty_hangup(state->info->tty);
 	state->type = PORT_UNKNOWN;
 	printk(KERN_INFO "tty%02d unloaded\n", state->line);
+	/* These will be hidden, because they are devices that will no longer
+	 * be available to the system. (ie, PCMCIA modems, once ejected)
+	 */
+	tty_unregister_devfs(&serial_driver,
+			     serial_driver.minor_start + state->line);
+	tty_unregister_devfs(&callout_driver,
+			     callout_driver.minor_start + state->line);
 	restore_flags(flags);
 }
 
@@ -4664,6 +4684,8 @@
 /*
  *	Print a string to the serial port trying not to disturb
  *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
  */
 static void serial_console_write(struct console *co, const char *s,
 				unsigned count)
diff -u --recursive --new-file v2.3.45/linux/drivers/char/serial167.c linux/drivers/char/serial167.c
--- v2.3.45/linux/drivers/char/serial167.c	Tue Feb  1 01:35:43 2000
+++ linux/drivers/char/serial167.c	Mon Feb 14 15:31:14 2000
@@ -2729,6 +2729,8 @@
  *
  * Of course, once the console has been registered, we had better ensure
  * that serial167_init() doesn't leave the chip non-functional.
+ *
+ * The console_lock must be held when we get here.
  */
 
 void serial167_console_write(struct console *co, const char *str, unsigned count)
diff -u --recursive --new-file v2.3.45/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.3.45/linux/drivers/char/stallion.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/stallion.c	Wed Feb 16 15:42:05 2000
@@ -40,6 +40,7 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -774,13 +775,15 @@
 
 /*****************************************************************************/
 
+static devfs_handle_t devfs_handle = NULL;
+
 void cleanup_module()
 {
 	stlbrd_t	*brdp;
 	stlpanel_t	*panelp;
 	stlport_t	*portp;
 	unsigned long	flags;
-	int		i, j, k;
+	int		i, j, k, l;
 
 #if DEBUG
 	printk("cleanup_module()\n");
@@ -806,7 +809,8 @@
 		restore_flags(flags);
 		return;
 	}
-	if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+	devfs_unregister (devfs_handle);
+	if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
 		printk("STALLION: failed to un-register serial memory device, "
 			"errno=%d\n", -i);
 
@@ -3213,8 +3217,13 @@
  *	Set up a character driver for per board stuff. This is mainly used
  *	to do stats ioctls on the ports.
  */
-	if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
+	if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
 		printk("STALLION: failed to register serial board device\n");
+	devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL);
+	devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
+			       STL_SIOMEMMAJOR, 0,
+			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &stl_fsiomem, NULL);
 
 /*
  *	Set up the tty driver structure and register us as a driver.
diff -u --recursive --new-file v2.3.45/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- v2.3.45/linux/drivers/char/tpqic02.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/tpqic02.c	Wed Feb 16 15:42:05 2000
@@ -89,7 +89,8 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
- 
+#include <linux/devfs_fs_kernel.h> 
+
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
@@ -2774,23 +2775,6 @@
 };
 
 
-/* Why is this not is one place? */
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
-    int order;
-
-    size = (size-1) >> (PAGE_SHIFT-1);
-    order = -1;
-    do
-    {
-	size >>= 1;
-	order++;
-    } while (size);
-    return order;
-}
-
-
 static void qic02_release_resources(void)
 {
     free_irq(QIC02_TAPE_IRQ, NULL);
@@ -2798,7 +2782,7 @@
     release_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE);
     if (buffaddr)
     {
-	free_pages(buffaddr, __get_order(TPQBUF_SIZE));
+	free_pages(buffaddr, get_order(TPQBUF_SIZE));
     }
     buffaddr = 0; /* Better to cause a panic than overwite someone else */
     status_zombie = YES;
@@ -2850,7 +2834,7 @@
     /* Setup the page-address for the dma transfer. */
 
     /*** TODO: does _get_dma_pages() really return the physical address?? ****/
-    buffaddr = __get_dma_pages(GFP_KERNEL,__get_order(TPQBUF_SIZE));
+    buffaddr = __get_dma_pages(GFP_KERNEL,get_order(TPQBUF_SIZE));
     
     if (!buffaddr)
     {
@@ -2919,7 +2903,7 @@
 #endif
     printk(TPQIC02_NAME ": DMA buffers: %u blocks\n", NR_BLK_BUF);
     /* If we got this far, install driver functions */
-    if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops))
+    if (devfs_register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops))
     {
 	printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
 #ifndef CONFIG_QIC02_DYNCONF
@@ -2927,7 +2911,38 @@
 #endif
 	return -ENODEV;
     }
-
+    devfs_register (NULL, "ntpqic11", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 2,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "tpqic11", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 3,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "ntpqic24", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 4,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "tpqic24", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 5,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "ntpqic120", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 6,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "tpqic120", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 7,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "ntpqic150", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 8,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
+    devfs_register (NULL, "tpqic150", 0, DEVFS_FL_NONE,
+		    QIC02_TAPE_MAJOR, 9,
+		    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+		    &qic02_tape_fops, NULL);
     init_waitqueue_head(&qic02_tape_transfer);
     /* prepare timer */
     TIMEROFF;
@@ -2974,7 +2989,15 @@
     {
 	qic02_release_resources();
     }
-    unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+    devfs_unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+    devfs_unregister(devfs_find_handle(NULL, "ntpqic11", 0, QIC02_TAPE_MAJOR, 2, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "tpqic11", 0, QIC02_TAPE_MAJOR, 3, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "ntpqic24", 0, QIC02_TAPE_MAJOR, 4, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "tpqic24", 0, QIC02_TAPE_MAJOR, 5, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "ntpqic120", 0, QIC02_TAPE_MAJOR, 6, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "tpqic120", 0, QIC02_TAPE_MAJOR, 7, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "ntpqic150", 0, QIC02_TAPE_MAJOR, 8, DEVFS_SPECIAL_CHR, 0));
+    devfs_unregister(devfs_find_handle(NULL, "tpqic150", 0, QIC02_TAPE_MAJOR, 9, DEVFS_SPECIAL_CHR, 0));
 }
 
 int init_module(void)
diff -u --recursive --new-file v2.3.45/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.3.45/linux/drivers/char/tty_io.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/tty_io.c	Wed Feb 16 15:42:05 2000
@@ -52,6 +52,9 @@
  * Rewrote init_dev and release_dev to eliminate races.
  *	-- Bill Hawes <whawes@star.net>, June 97
  *
+ * Added devfs support.
+ *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
+ *
  * Added support for a Unix98-style ptmx device.
  *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
  */
@@ -79,6 +82,7 @@
 #include <linux/poll.h>
 #include <linux/proc_fs.h>
 #include <linux/init.h>
+#include <linux/module.h>
 #include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
@@ -88,9 +92,17 @@
 #include <linux/kbd_kern.h>
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <linux/kmod.h>
 
+#ifdef CONFIG_VT
+extern void con_init_devfs (void);
+#endif
+void tty_register_devfs  (struct tty_driver *driver, unsigned int flags,
+			  unsigned minor);
+void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
+
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
 #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
@@ -107,6 +119,7 @@
 
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver ptm_driver[];	/* Unix98 pty masters; for /dev/ptmx */
+extern struct tty_driver pts_driver[];	/* Unix98 pty slaves;  for /dev/ptmx */
 #endif
 
 /*
@@ -149,16 +162,26 @@
 /*
  * This routine returns the name of tty.
  */
+static char *
+_tty_make_name(struct tty_struct *tty, const char *name, char *buf)
+{
+	int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
+
+	if (!tty) /* Hmm.  NULL pointer.  That's fun. */
+		strcpy(buf, "NULL tty");
+	else
+		sprintf(buf, name,
+			idx + tty->driver.name_base);
+		
+	return buf;
+}
+
 #define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \
 			 (tty)->driver.name_base)
-	
+
 char *tty_name(struct tty_struct *tty, char *buf)
 {
-	if (tty)
-		sprintf(buf, "%s%d", tty->driver.name, TTY_NUMBER(tty));
-	else
-		strcpy(buf, "NULL tty");
-	return buf;
+	return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);
 }
 
 inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
@@ -1298,6 +1321,8 @@
 		set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
 		minor -= driver->minor_start;
 		devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start));
+		tty_register_devfs(&pts_driver[major], 0,
+				   pts_driver[major].minor_start + minor);
 		noctty = 1;
 		goto init_dev_done;
 
@@ -1966,16 +1991,86 @@
 }
 
 /*
+ * Register a tty device described by <driver>, with minor number <minor>.
+ */
+void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+			 unsigned int minor)
+{
+#ifdef CONFIG_DEVFS_FS
+	umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
+	uid_t uid = 0;
+	gid_t gid = 0;
+	struct tty_struct tty;
+	char buf[32];
+
+	flags |= DEVFS_FL_DEFAULT;
+	tty.driver = *driver;
+	tty.device = MKDEV (driver->major, minor);
+	switch (tty.device) {
+		case TTY_DEV:
+		case PTMX_DEV:
+			mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+			break;
+		default:
+			flags |= DEVFS_FL_AUTO_OWNER;
+			break;
+	}
+	if ((minor <  driver->minor_start) || 
+	    (minor >= driver->minor_start + driver->num)) {
+		printk(KERN_ERR "Attempt to register invalid minor number "
+		       "with devfs (%d:%d).\n", (int)driver->major,(int)minor);
+		return;
+	}
+	if (driver->type == TTY_DRIVER_TYPE_CONSOLE) {
+		flags |= DEVFS_FL_AOPEN_NOTIFY;
+		flags &= ~DEVFS_FL_AUTO_OWNER;
+	}
+#  ifdef CONFIG_UNIX98_PTYS
+	if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) &&
+	     (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) {
+		flags &= ~DEVFS_FL_AUTO_OWNER;
+		uid = current->uid;
+		gid = current->gid;
+	}
+#  endif
+	devfs_register (NULL, tty_name (&tty, buf), 0, flags,
+			driver->major, minor, mode, uid, gid,
+			&tty_fops, NULL);
+#endif /* CONFIG_DEVFS_FS */
+}
+
+void tty_unregister_devfs (struct tty_driver *driver, unsigned minor)
+{
+#ifdef CONFIG_DEVFS_FS
+	void * handle;
+	struct tty_struct tty;
+	char buf[32];
+
+	tty.driver = *driver;
+	tty.device = MKDEV(driver->major, minor);
+	
+	handle = devfs_find_handle (NULL, tty_name (&tty, buf), 0,
+				    driver->major, minor,
+				    DEVFS_SPECIAL_CHR, 0);
+	devfs_unregister (handle);
+#endif /* CONFIG_DEVFS_FS */
+}
+
+EXPORT_SYMBOL(tty_register_devfs);
+EXPORT_SYMBOL(tty_unregister_devfs);
+
+/*
  * Called by a tty driver to register itself.
  */
 int tty_register_driver(struct tty_driver *driver)
 {
 	int error;
+        int i;
 
 	if (driver->flags & TTY_DRIVER_INSTALLED)
 		return 0;
 
-	error = register_chrdev(driver->major, driver->name, &tty_fops);
+	error = devfs_register_chrdev(driver->major, driver->name, &tty_fops);
 	if (error < 0)
 		return error;
 	else if(driver->major == 0)
@@ -1989,6 +2084,10 @@
 	if (tty_drivers) tty_drivers->prev = driver;
 	tty_drivers = driver;
 	
+	if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
+		for(i = 0; i < driver->num; i++)
+		    tty_register_devfs(driver, 0, driver->minor_start + i);
+	}
 	proc_tty_register_driver(driver);
 	return error;
 }
@@ -2018,11 +2117,11 @@
 		return -ENOENT;
 
 	if (othername == NULL) {
-		retval = unregister_chrdev(driver->major, driver->name);
+		retval = devfs_unregister_chrdev(driver->major, driver->name);
 		if (retval)
 			return retval;
 	} else
-		register_chrdev(driver->major, othername, &tty_fops);
+		devfs_register_chrdev(driver->major, othername, &tty_fops);
 
 	if (driver->prev)
 		driver->prev->next = driver->next;
@@ -2048,6 +2147,7 @@
 			driver->termios_locked[i] = NULL;
 			kfree_s(tp, sizeof(struct termios));
 		}
+		tty_unregister_devfs(driver, driver->minor_start + i);
 	}
 	proc_tty_unregister_driver(driver);
 	return 0;
@@ -2148,6 +2248,13 @@
 	if (tty_register_driver(&dev_syscons_driver))
 		panic("Couldn't register /dev/console driver\n");
 
+	/* console calls tty_register_driver() before kmalloc() works.
+	 * Thus, we can't devfs_register() then.  Do so now, instead. 
+	 */
+#ifdef CONFIG_VT
+	con_init_devfs();
+#endif
+
 #ifdef CONFIG_UNIX98_PTYS
 	dev_ptmx_driver = dev_tty_driver;
 	dev_ptmx_driver.driver_name = "/dev/ptmx";
@@ -2163,7 +2270,7 @@
 	
 #ifdef CONFIG_VT
 	dev_console_driver = dev_tty_driver;
-	dev_console_driver.driver_name = "/dev/tty0";
+	dev_console_driver.driver_name = "/dev/vc/0";
 	dev_console_driver.name = dev_console_driver.driver_name + 5;
 	dev_console_driver.major = TTY_MAJOR;
 	dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
diff -u --recursive --new-file v2.3.45/linux/drivers/char/vc_screen.c linux/drivers/char/vc_screen.c
--- v2.3.45/linux/drivers/char/vc_screen.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/vc_screen.c	Wed Feb 16 15:42:05 2000
@@ -25,6 +25,7 @@
 #include <linux/major.h>
 #include <linux/errno.h>
 #include <linux/tty.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/mm.h>
@@ -32,6 +33,8 @@
 #include <linux/vt_kern.h>
 #include <linux/console_struct.h>
 #include <linux/selection.h>
+#include <linux/kbd_kern.h>
+#include <linux/console.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
@@ -80,21 +83,34 @@
 	return file->f_pos;
 }
 
-#define RETURN(x) { enable_bh(CONSOLE_BH); return x; }
+/* We share this temporary buffer with the console write code
+ * so that we can easily avoid touching user space while holding the
+ * console spinlock.
+ */
+extern char con_buf[PAGE_SIZE];
+#define CON_BUF_SIZE	PAGE_SIZE
+extern struct semaphore con_buf_sem;
+
 static ssize_t
 vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	unsigned int currcons = MINOR(inode->i_rdev);
-	long p = *ppos;
-	long viewed, attr, size, read;
-	char *buf0;
+	long pos = *ppos;
+	long viewed, attr, read;
 	int col, maxcol;
 	unsigned short *org = NULL;
+	ssize_t ret;
+
+	down(&con_buf_sem);
+
+	/* Select the proper current console and verify
+	 * sanity of the situation under the console lock.
+	 */
+	spin_lock_irq(&console_lock);
 
 	attr = (currcons & 128);
 	currcons = (currcons & 127);
-	disable_bh(CONSOLE_BH);
 	if (currcons == 0) {
 		currcons = fg_console;
 		viewed = 1;
@@ -102,78 +118,156 @@
 		currcons--;
 		viewed = 0;
 	}
+	ret = -ENXIO;
 	if (!vc_cons_allocated(currcons))
-		RETURN( -ENXIO );
+		goto unlock_out;
 
-	size = vcs_size(inode);
-	if (p < 0 || p > size)
-		RETURN( -EINVAL );
-	if (count > size - p)
-		count = size - p;
-
-	buf0 = buf;
-	maxcol = video_num_columns;
-	if (!attr) {
-		org = screen_pos(currcons, p, viewed);
-		col = p % maxcol;
-		p += maxcol - col;
-		while (count-- > 0) {
-			put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
-			if (++col == maxcol) {
-				org = screen_pos(currcons, p, viewed);
-				col = 0;
-				p += maxcol;
+	ret = -EINVAL;
+	if (pos < 0)
+		goto unlock_out;
+	read = 0;
+	ret = 0;
+	while (count) {
+		char *con_buf0, *con_buf_start;
+		long this_round, size;
+		ssize_t orig_count;
+		long p = pos;
+
+		/* Check whether we are above size each round,
+		 * as copy_to_user at the end of this loop
+		 * could sleep.
+		 */
+		size = vcs_size(inode);
+		if (pos >= size)
+			break;
+		if (count > size - pos)
+			count = size - pos;
+
+		this_round = count;
+		if (this_round > CON_BUF_SIZE)
+			this_round = CON_BUF_SIZE;
+
+		/* Perform the whole read into the local con_buf.
+		 * Then we can drop the console spinlock and safely
+		 * attempt to move it to userspace.
+		 */
+
+		con_buf_start = con_buf0 = con_buf;
+		orig_count = this_round;
+		maxcol = video_num_columns;
+		if (!attr) {
+			org = screen_pos(currcons, p, viewed);
+			col = p % maxcol;
+			p += maxcol - col;
+			while (this_round-- > 0) {
+				*con_buf0++ = (vcs_scr_readw(currcons, org++) & 0xff);
+				if (++col == maxcol) {
+					org = screen_pos(currcons, p, viewed);
+					col = 0;
+					p += maxcol;
+				}
 			}
-		}
-	} else {
-		if (p < HEADER_SIZE) {
-			char header[HEADER_SIZE];
-			header[0] = (char) video_num_lines;
-			header[1] = (char) video_num_columns;
-			getconsxy(currcons, header+2);
-			while (p < HEADER_SIZE && count > 0)
-			    { count--; put_user(header[p++], buf++); }
-		}
-		p -= HEADER_SIZE;
-		col = (p/2) % maxcol;
-		if (count > 0) {
-			org = screen_pos(currcons, p/2, viewed);
-			if ((p & 1) && count > 0) {
-				count--;   
+		} else {
+			if (p < HEADER_SIZE) {
+				size_t tmp_count;
+
+				con_buf0[0] = (char) video_num_lines;
+				con_buf0[1] = (char) video_num_columns;
+				getconsxy(currcons, con_buf0 + 2);
+
+				tmp_count = HEADER_SIZE - p;
+				if (tmp_count > this_round)
+					tmp_count = this_round;
+
+				/* Advance state pointers and move on. */
+				this_round -= tmp_count;
+				con_buf_start += p;
+				orig_count -= p;
+				p += tmp_count;
+				con_buf0 = con_buf + p;
+			}
+			p -= HEADER_SIZE;
+			col = (p/2) % maxcol;
+			if (this_round > 0) {
+				char tmp_byte;
+
+				org = screen_pos(currcons, p/2, viewed);
+				if ((p & 1) && this_round > 0) {
 #ifdef __BIG_ENDIAN
-				put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
+					tmp_byte = vcs_scr_readw(currcons, org++) & 0xff;
 #else
-				put_user(vcs_scr_readw(currcons, org++) >> 8, buf++);
+					tmp_byte = vcs_scr_readw(currcons, org++) >> 8;
 #endif
-				p++;
-				if (++col == maxcol) {
-					org = screen_pos(currcons, p/2, viewed);
-					col = 0;
+
+					*con_buf0++ = tmp_byte;
+
+					this_round--;   
+					p++;
+					if (++col == maxcol) {
+						org = screen_pos(currcons, p/2, viewed);
+						col = 0;
+					}
 				}
+				p /= 2;
+				p += maxcol - col;
 			}
-			p /= 2;
-			p += maxcol - col;
-		}
-		while (count > 1) {
-			put_user(vcs_scr_readw(currcons, org++), (unsigned short *) buf);
-			buf += 2;
-			count -= 2;
-			if (++col == maxcol) {
-				org = screen_pos(currcons, p, viewed);
-				col = 0;
-				p += maxcol;
+
+			if (this_round > 1) {
+				size_t tmp_count = this_round;
+				unsigned short *tmp_buf = (unsigned short *)con_buf0;
+
+				while (tmp_count > 1) {
+					*tmp_buf++ = vcs_scr_readw(currcons, org++);
+					tmp_count -= 2;
+					if (++col == maxcol) {
+						org = screen_pos(currcons, p, viewed);
+						col = 0;
+						p += maxcol;
+					}
+				}
+
+				/* Advance pointers, and move on. */
+				this_round = tmp_count;
+				con_buf0 = (char*)tmp_buf;
 			}
-		}
-		if (count > 0)
+			if (this_round > 0) {
+				char tmp_byte;
+
 #ifdef __BIG_ENDIAN
-			put_user(vcs_scr_readw(currcons, org) >> 8, buf++);
+				tmp_byte = vcs_scr_readw(currcons, org) >> 8;
 #else
-			put_user(vcs_scr_readw(currcons, org) & 0xff, buf++);
+				tmp_byte = vcs_scr_readw(currcons, org) & 0xff;
 #endif
+
+				*con_buf0++ = tmp_byte;
+			}
+		}
+
+		/* Finally, temporarily drop the console lock and push
+		 * all the data to userspace from our temporary buffer.
+		 */
+
+		spin_unlock_irq(&console_lock);
+		ret = copy_to_user(buf, con_buf_start, orig_count);
+		spin_lock_irq(&console_lock);
+
+		if (ret) {
+			read += (orig_count - ret);
+			ret = -EFAULT;
+			break;
+		}
+		buf += orig_count;
+		pos += orig_count;
+		read += orig_count;
+		count -= orig_count;
 	}
-	read = buf - buf0;
 	*ppos += read;
-	RETURN( read );
+	if (read)
+		ret = read;
+unlock_out:
+	spin_unlock_irq(&console_lock);
+	up(&con_buf_sem);
+	return ret;
 }
 
 static ssize_t
@@ -181,15 +275,23 @@
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	unsigned int currcons = MINOR(inode->i_rdev);
-	long p = *ppos;
+	long pos = *ppos;
 	long viewed, attr, size, written;
-	const char *buf0;
+	char *con_buf0;
 	int col, maxcol;
 	u16 *org0 = NULL, *org = NULL;
+	size_t ret;
+
+	down(&con_buf_sem);
+
+	/* Select the proper current console and verify
+	 * sanity of the situation under the console lock.
+	 */
+	spin_lock_irq(&console_lock);
 
 	attr = (currcons & 128);
 	currcons = (currcons & 127);
-	disable_bh(CONSOLE_BH);
+
 	if (currcons == 0) {
 		currcons = fg_console;
 		viewed = 1;
@@ -197,94 +299,159 @@
 		currcons--;
 		viewed = 0;
 	}
+	ret = -ENXIO;
 	if (!vc_cons_allocated(currcons))
-		RETURN( -ENXIO );
+		goto unlock_out;
 
 	size = vcs_size(inode);
-	if (p < 0 || p > size)
-		RETURN( -EINVAL );
-	if (count > size - p)
-		count = size - p;
-
-	buf0 = buf;
-	maxcol = video_num_columns;
-	if (!attr) {
-		org0 = org = screen_pos(currcons, p, viewed);
-		col = p % maxcol;
-		p += maxcol - col;
-		while (count > 0) {
-			unsigned char c;
-			count--;
-			get_user(c, (const unsigned char*)buf++);
-			vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
-			org++;
-			if (++col == maxcol) {
-				org = screen_pos(currcons, p, viewed);
-				col = 0;
-				p += maxcol;
+	ret = -EINVAL;
+	if (pos < 0 || pos > size)
+		goto unlock_out;
+	if (count > size - pos)
+		count = size - pos;
+	written = 0;
+	while (count) {
+		long this_round = count;
+		size_t orig_count;
+		long p;
+
+		if (this_round > CON_BUF_SIZE)
+			this_round = CON_BUF_SIZE;
+
+		/* Temporarily drop the console lock so that we can read
+		 * in the write data from userspace safely.
+		 */
+		spin_unlock_irq(&console_lock);
+		ret = copy_from_user(con_buf, buf, this_round);
+		spin_lock_irq(&console_lock);
+
+		if (ret) {
+			this_round -= ret;
+			if (!this_round) {
+				/* Abort loop if no data were copied. Otherwise
+				 * fail with -EFAULT.
+				 */
+				if (written)
+					break;
+				ret = -EFAULT;
+				goto unlock_out;
 			}
 		}
-	} else {
-		if (p < HEADER_SIZE) {
-			char header[HEADER_SIZE];
-			getconsxy(currcons, header+2);
-			while (p < HEADER_SIZE && count > 0)
-				{ count--; get_user(header[p++], buf++); }
-			if (!viewed)
-				putconsxy(currcons, header+2);
-		}
-		p -= HEADER_SIZE;
-		col = (p/2) % maxcol;
-		if (count > 0) {
-			org0 = org = screen_pos(currcons, p/2, viewed);
-			if ((p & 1) && count > 0) {
-			    char c;
-				count--;
-				get_user(c,buf++);
+
+		/* The vcs_size might have changed while we slept to grab
+		 * the user buffer, so recheck.
+		 * Return data written up to now on failure.
+		 */
+		size = vcs_size(inode);
+		if (pos >= size)
+			break;
+		if (this_round > size - pos)
+			this_round = size - pos;
+
+		/* OK, now actually push the write to the console
+		 * under the lock using the local kernel buffer.
+		 */
+
+		con_buf0 = con_buf;
+		orig_count = this_round;
+		maxcol = video_num_columns;
+		p = pos;
+		if (!attr) {
+			org0 = org = screen_pos(currcons, p, viewed);
+			col = p % maxcol;
+			p += maxcol - col;
+
+			while (this_round > 0) {
+				unsigned char c = *con_buf0++;
+
+				this_round--;
+				vcs_scr_writew(currcons,
+					       (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
+				org++;
+				if (++col == maxcol) {
+					org = screen_pos(currcons, p, viewed);
+					col = 0;
+					p += maxcol;
+				}
+			}
+		} else {
+			if (p < HEADER_SIZE) {
+				char header[HEADER_SIZE];
+
+				getconsxy(currcons, header + 2);
+				while (p < HEADER_SIZE && this_round > 0) {
+					this_round--;
+					header[p++] = *con_buf0++;
+				}
+				if (!viewed)
+					putconsxy(currcons, header + 2);
+			}
+			p -= HEADER_SIZE;
+			col = (p/2) % maxcol;
+			if (this_round > 0) {
+				org0 = org = screen_pos(currcons, p/2, viewed);
+				if ((p & 1) && this_round > 0) {
+					char c;
+
+					this_round--;
+					c = *con_buf0++;
 #ifdef __BIG_ENDIAN
-				vcs_scr_writew(currcons, c |
-				     (vcs_scr_readw(currcons, org) & 0xff00), org);
+					vcs_scr_writew(currcons, c |
+					     (vcs_scr_readw(currcons, org) & 0xff00), org);
 #else
-				vcs_scr_writew(currcons, (c << 8) |
-				     (vcs_scr_readw(currcons, org) & 0xff), org);
+					vcs_scr_writew(currcons, (c << 8) |
+					     (vcs_scr_readw(currcons, org) & 0xff), org);
 #endif
-				org++;
-				p++;
+					org++;
+					p++;
+					if (++col == maxcol) {
+						org = screen_pos(currcons, p/2, viewed);
+						col = 0;
+					}
+				}
+				p /= 2;
+				p += maxcol - col;
+			}
+			while (this_round > 1) {
+				unsigned short w;
+
+				w = *((const unsigned short *)con_buf0);
+				vcs_scr_writew(currcons, w, org++);
+				con_buf0 += 2;
+				this_round -= 2;
 				if (++col == maxcol) {
-					org = screen_pos(currcons, p/2, viewed);
+					org = screen_pos(currcons, p, viewed);
 					col = 0;
+					p += maxcol;
 				}
 			}
-			p /= 2;
-			p += maxcol - col;
-		}
-		while (count > 1) {
-			unsigned short w;
-			get_user(w, (const unsigned short *) buf);
-			vcs_scr_writew(currcons, w, org++);
-			buf += 2;
-			count -= 2;
-			if (++col == maxcol) {
-				org = screen_pos(currcons, p, viewed);
-				col = 0;
-				p += maxcol;
-			}
-		}
-		if (count > 0) {
-			unsigned char c;
-			get_user(c, (const unsigned char*)buf++);
+			if (this_round > 0) {
+				unsigned char c;
+
+				c = *con_buf0++;
 #ifdef __BIG_ENDIAN
-			vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
+				vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
 #else
-			vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
+				vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
 #endif
+			}
 		}
+		count -= orig_count;
+		written += orig_count;
+		buf += orig_count;
+		pos += orig_count;
+		if (org0)
+			update_region(currcons, (unsigned long)(org0), org-org0);
 	}
-	if (org0)
-		update_region(currcons, (unsigned long)(org0), org-org0);
-	written = buf - buf0;
 	*ppos += written;
-	RETURN( written );
+	ret = written;
+
+unlock_out:
+	spin_unlock_irq(&console_lock);
+
+	up(&con_buf_sem);
+
+	return ret;
 }
 
 static int
@@ -303,12 +470,49 @@
 	open:		vcs_open,
 };
 
+static devfs_handle_t devfs_handle = NULL;
+
+void vcs_make_devfs (unsigned int index, int unregister)
+{
+#ifdef CONFIG_DEVFS_FS
+    char name[8];
+
+    sprintf (name, "a%u", index + 1);
+    if (unregister)
+    {
+	devfs_unregister ( devfs_find_handle (devfs_handle, name + 1, 0, 0, 0,
+					      DEVFS_SPECIAL_CHR, 0) );
+	devfs_unregister ( devfs_find_handle (devfs_handle, name, 0, 0, 0,
+					      DEVFS_SPECIAL_CHR, 0) );
+    }
+    else
+    {
+	devfs_register (devfs_handle, name + 1, 0, DEVFS_FL_DEFAULT,
+			VCS_MAJOR, index + 1,
+			S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+	devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT,
+			VCS_MAJOR, index + 129,
+			S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+    }
+#endif /* CONFIG_DEVFS_FS */
+}
+
 int __init vcs_init(void)
 {
 	int error;
 
-	error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
+	error = devfs_register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
+
 	if (error)
 		printk("unable to get major %d for vcs device", VCS_MAJOR);
+
+	devfs_handle = devfs_mk_dir (NULL, "vcc", 3, NULL);
+	devfs_register (devfs_handle, "0", 1, DEVFS_FL_DEFAULT,
+			VCS_MAJOR, 0,
+			S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+	devfs_register (devfs_handle, "a", 1, DEVFS_FL_DEFAULT,
+			VCS_MAJOR, 128,
+			S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+
 	return error;
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.3.45/linux/drivers/char/videodev.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/videodev.c	Wed Feb 16 15:42:05 2000
@@ -229,6 +229,8 @@
 	return -EINVAL;
 }
 
+extern struct file_operations video_fops;
+
 /*
  *	Video For Linux device drivers request registration here.
  */
@@ -239,24 +241,29 @@
 	int base;
 	int err;
 	int end;
+	char *name_base;
 	
 	switch(type)
 	{
 		case VFL_TYPE_GRABBER:
 			base=0;
 			end=64;
+			name_base = "video";
 			break;
 		case VFL_TYPE_VTX:
 			base=192;
 			end=224;
+			name_base = "vtx";
 			break;
 		case VFL_TYPE_VBI:
 			base=224;
 			end=240;
+			name_base = "vbi";
 			break;
 		case VFL_TYPE_RADIO:
 			base=64;
 			end=128;
+			name_base = "radio";
 			break;
 		default:
 			return -1;
@@ -266,6 +273,8 @@
 	{
 		if(video_device[i]==NULL)
 		{
+			char name[16];
+
 			video_device[i]=vfd;
 			vfd->minor=i;
 			/* The init call may sleep so we book the slot out
@@ -281,6 +290,12 @@
 					return err;
 				}
 			}
+			sprintf (name, "v4l/%s%d", name_base, i - base);
+			vfd->devfs_handle =
+			    devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT,
+					    VIDEO_MAJOR, vfd->minor,
+					    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+					    &video_fops, NULL);
 			return 0;
 		}
 	}
@@ -295,6 +310,7 @@
 {
 	if(video_device[vfd->minor]!=vfd)
 		panic("vfd: bad unregister");
+	devfs_unregister (vfd->devfs_handle);
 	video_device[vfd->minor]=NULL;
 	MOD_DEC_USE_COUNT;
 }
@@ -323,7 +339,7 @@
 	struct video_init *vfli = video_init_list;
 	
 	printk(KERN_INFO "Linux video capture interface: v1.00\n");
-	if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
+	if(devfs_register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
 	{
 		printk("video_dev: unable to get major %d\n", VIDEO_MAJOR);
 		return -EIO;
@@ -349,7 +365,7 @@
 
 void cleanup_module(void)
 {
-	unregister_chrdev(VIDEO_MAJOR, "video_capture");
+	devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
 }
 
 
diff -u --recursive --new-file v2.3.45/linux/drivers/char/vme_scc.c linux/drivers/char/vme_scc.c
--- v2.3.45/linux/drivers/char/vme_scc.c	Tue Feb  1 01:35:43 2000
+++ linux/drivers/char/vme_scc.c	Mon Feb 14 15:31:14 2000
@@ -1045,6 +1045,7 @@
 	*p = ch;
 }
 
+/* The console_lock must be held when we get here. */
 
 static void scc_console_write (struct console *co, const char *str, unsigned count)
 {
diff -u --recursive --new-file v2.3.45/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.3.45/linux/drivers/char/vt.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/vt.c	Mon Feb 14 15:31:14 2000
@@ -22,6 +22,7 @@
 #include <linux/malloc.h>
 #include <linux/major.h>
 #include <linux/fs.h>
+#include <linux/console.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -804,12 +805,10 @@
 				 * When we actually do the console switch,
 				 * make sure we are atomic with respect to
 				 * other console switches..
-				 *
-				 * Damn! Was it difficult to make this clean?
 				 */
-				disable_bh(CONSOLE_BH);
+				spin_lock_irq(&console_lock);
 				complete_change_console(newvt);
-				enable_bh(CONSOLE_BH);
+				spin_unlock_irq(&console_lock);
 			}
 		}
 
diff -u --recursive --new-file v2.3.45/linux/drivers/char/zr36120_mem.c linux/drivers/char/zr36120_mem.c
--- v2.3.45/linux/drivers/char/zr36120_mem.c	Tue Jan  4 13:57:17 2000
+++ linux/drivers/char/zr36120_mem.c	Mon Feb 14 15:34:21 2000
@@ -35,17 +35,6 @@
 /* Memory management functions */
 /*******************************/
 
-inline int __get_order(unsigned long size)
-{
-        int order = 0;
-	size = (size+PAGE_SIZE-1)/PAGE_SIZE;
-	while (size) {
-		size /= 2;
-		order++;
-	}
-	return order;
-}
-			
 void* bmalloc(unsigned long size)
 {
 	void* mem;
@@ -56,7 +45,7 @@
 	 * The following function got a lot of memory at boottime,
 	 * so we know its always there...
 	 */
-	mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,__get_order(size));
+	mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,get_order(size));
 #endif
 	if (mem) {
 		unsigned long adr = (unsigned long)mem;
@@ -82,7 +71,7 @@
 #ifdef CONFIG_BIGPHYS_AREA
 		bigphysarea_free_pages(mem);
 #else
-		free_pages((unsigned long)mem,__get_order(size));
+		free_pages((unsigned long)mem,get_order(size));
 #endif
 	}
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c
--- v2.3.45/linux/drivers/i2o/i2o_block.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/i2o/i2o_block.c	Wed Feb 16 10:56:45 2000
@@ -461,7 +461,7 @@
 	struct i2ob_device *dev;
 	u32 m;
 
-	while (CURRENT) {
+	while (!QUEUE_EMPTY) {
 		/*
 		 *	On an IRQ completion if there is an inactive
 		 *	request on the queue head it means it isnt yet
@@ -515,8 +515,7 @@
 			}
 		}
 		req->errors = 0;
-		CURRENT = CURRENT->next;
-		req->next = NULL;
+		blkdev_dequeue_request(req);
 		req->sem = NULL;
 		
 		ireq = i2ob_qhead;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.3.45/linux/drivers/isdn/Config.in	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/Config.in	Tue Feb 15 11:40:42 2000
@@ -12,68 +12,99 @@
 if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then
    bool '    Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX
 fi
-bool '  Support isdn diversion services' CONFIG_ISDN_DIVERSION
 if [ "$CONFIG_X25" != "n" ]; then
    bool '  X.25 PLP on top of ISDN' CONFIG_ISDN_X25
 fi
-dep_tristate '  ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
-dep_tristate '  isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
-dep_tristate '  PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
-dep_tristate '  HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
+
+mainmenu_option next_comment
+comment 'ISDN feature submodules'
+   dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
+   bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION
+endmenu
+
+comment 'low-level hardware drivers'
+
+mainmenu_option next_comment
+comment 'Passive ISDN cards'
+dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
-   bool '    HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
+   comment '  D-channel protocol features'
+   bool '  HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
    if [ "$CONFIG_HISAX_EURO" != "n" ]; then
-      bool '      Support for german chargeinfo' CONFIG_DE_AOC
-      bool '      Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
-      bool '      Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
+      bool '    Support for german chargeinfo' CONFIG_DE_AOC
+      bool '    Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
+      bool '    Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
+      bool '    Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD
    fi
-   bool '    HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
-   bool '    HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
-   bool '    HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
-   bool '    HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI 
-   bool '    HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX 
-   bool '    HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
-   bool '    HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
-   bool '    HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
-   bool '    HiSax Support for Elsa cards' CONFIG_HISAX_ELSA
-   bool '    HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
-   bool '    HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
-   bool '    HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM
-   bool '    HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT
-   bool '    HiSax Support for HFC-S based cards' CONFIG_HISAX_HFCS
-   bool '    HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
-   bool '    HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
-   bool '    HiSax Support for MIC card' CONFIG_HISAX_MIC
-   bool '    HiSax Support for NETjet card' CONFIG_HISAX_NETJET
-   bool '    HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
-   bool '    HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF
-   bool '    HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR
-   bool '    HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T
-   bool '    HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO
-   bool '    HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL
-   bool '    HiSax Support for HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI
-   bool '    HiSax Support for Winbond W6692 based cards' CONFIG_HISAX_W6692
+   bool '  HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+   comment '  HiSax supported cards'
+   bool '  Teles 16.0/8.0' CONFIG_HISAX_16_0
+   bool '  Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
+   bool '  Teles PCI' CONFIG_HISAX_TELESPCI 
+   bool '  Teles S0Box' CONFIG_HISAX_S0BOX 
+   bool '  AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
+   bool '  AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
+   bool '  AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
+   bool '  Elsa cards' CONFIG_HISAX_ELSA
+   bool '  ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
+   bool '  Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
+   bool '  ASUSCOM ISA cards' CONFIG_HISAX_ASUSCOM
+   bool '  TELEINT cards' CONFIG_HISAX_TELEINT
+   bool '  HFC-S based cards' CONFIG_HISAX_HFCS
+   bool '  Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
+   bool '  USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
+   bool '  MIC card' CONFIG_HISAX_MIC
+   bool '  NETjet card' CONFIG_HISAX_NETJET
+   bool '  Niccy PnP/PCI card' CONFIG_HISAX_NICCY
+   bool '  Siemens I-Surf card' CONFIG_HISAX_ISURF
+   bool '  HST Saphir card' CONFIG_HISAX_HSTSAPHIR
+   bool '  Telekom A4T card' CONFIG_HISAX_BKM_A4T
+   bool '  Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO
+   bool '  Gazel cards' CONFIG_HISAX_GAZEL
+   bool '  HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI
+   bool '  Winbond W6692 based cards' CONFIG_HISAX_W6692
    if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
-#      bool '    HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
+      bool '  HFC-S+, HFC-SP, HFC-PCMCIA cards' CONFIG_HISAX_HFC_SX
+#      bool '  TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
       if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
-	 bool '    HiSax Support for Am7930' CONFIG_HISAX_AMD7930
+	 bool '  Am7930' CONFIG_HISAX_AMD7930
       fi
    fi
 fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Active ISDN cards'
+dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
+dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
 if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
-   dep_tristate '  Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
-   dep_tristate '  IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
+   dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
+   dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
 fi
-dep_tristate '  Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
+dep_tristate 'Eicon active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then
-   bool '    Eicon S, SX, SCOM, Quadro, S2M support' CONFIG_ISDN_DRV_EICON_ISA
+   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
+dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
-   bool '    AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA
-   bool '    AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI
-   bool '    AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA
-   bool '    AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
-   bool '    AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI
-   bool '    Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+   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
+      if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+         bool '  AVM B1 PCI V4 support' CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+      fi
+   fi
+   bool '  AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA
+   bool '  AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
+   bool '  AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI
+   if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+      bool '  AVM C4 support' CONFIG_ISDN_DRV_AVMB1_C4
+   fi
+   bool '  Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+fi
+if [ "$CONFIG_PROC_FS" != "n" ]; then
+if [ "$CONFIG_MODULES" != "n" ]; then
+   bool 'Hypercope HYSDN cards (Champ, Ergo, Metro) support (module)' CONFIG_HYSDN
+fi
 fi
+endmenu
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v2.3.45/linux/drivers/isdn/Makefile	Thu Aug 12 09:42:33 1999
+++ linux/drivers/isdn/Makefile	Tue Feb 15 11:40:42 2000
@@ -1,6 +1,6 @@
 SUB_DIRS     :=
 MOD_SUB_DIRS :=
-ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon divert
+ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon divert hysdn
 
 L_OBJS :=
 LX_OBJS :=
@@ -135,6 +135,10 @@
   ifeq ($(CONFIG_ISDN_DRV_EICON),m)
     MOD_SUB_DIRS += eicon
   endif
+endif
+
+ifeq ($(CONFIG_HYSDN),y)
+  MOD_SUB_DIRS += hysdn
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/Makefile linux/drivers/isdn/avmb1/Makefile
--- v2.3.45/linux/drivers/isdn/avmb1/Makefile	Thu Nov 11 20:11:35 1999
+++ linux/drivers/isdn/avmb1/Makefile	Tue Feb 15 11:40:42 2000
@@ -1,5 +1,5 @@
 #
-# $Id: Makefile,v 1.7 1999/09/15 08:16:03 calle Exp $
+# $Id: Makefile,v 1.8 2000/01/25 14:33:38 calle Exp $
 #
 # Makefile for the CAPI and AVM-B1 device drivers.
 #
@@ -11,6 +11,11 @@
 # parent makes..
 #
 # $Log: Makefile,v $
+# 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
+#   - support for revision register
+#
 # Revision 1.7  1999/09/15 08:16:03  calle
 # Implementation of 64Bit extention complete.
 #
@@ -99,7 +104,7 @@
   ifdef CONFIG_ISDN_DRV_AVMB1_C4
   O_OBJS   += c4.o
   endif
-  OX_OBJS  += capiutil.o capidrv.o b1.o
+  OX_OBJS  += capiutil.o capidrv.o b1.o b1dma.o
 else
   ifeq ($(CONFIG_ISDN_DRV_AVMB1),m)
   O_TARGET += kernelcapi.o
@@ -123,7 +128,7 @@
   ifdef CONFIG_ISDN_DRV_AVMB1_C4
   M_OBJS   += c4.o
   endif
-  MX_OBJS  += capiutil.o capidrv.o b1.o
+  MX_OBJS  += capiutil.o capidrv.o b1.o b1dma.o
   endif
 endif
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/avmcard.h linux/drivers/isdn/avmb1/avmcard.h
--- v2.3.45/linux/drivers/isdn/avmb1/avmcard.h	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/avmcard.h	Tue Feb 15 11:40:42 2000
@@ -1,9 +1,14 @@
 /*
- * $Id: avmcard.h,v 1.6 1999/11/05 16:38:01 calle Exp $
+ * $Id: avmcard.h,v 1.7 2000/01/25 14:33:38 calle Exp $
  *
  * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: avmcard.h,v $
+ * Revision 1.7  2000/01/25 14:33:38  calle
+ * - Added Support AVM B1 PCI V4.0 (tested with prototype)
+ *   - splitted up t1pci.c into b1dma.c for common function with b1pciv4
+ *   - support for revision register
+ *
  * Revision 1.6  1999/11/05 16:38:01  calle
  * Cleanups before kernel 2.4:
  * - Changed all messages to use card->name or driver->name instead of
@@ -92,6 +97,8 @@
 	unsigned irq;
 	unsigned long membase;
 	enum avmcardtype cardtype;
+	unsigned char revision;
+	unsigned char class;
 	int cardnr; /* for t1isa */
 
 	char msgbuf[128];	/* capimsg msg part */
@@ -228,8 +235,9 @@
 #define B1_WRITE		0x01
 #define B1_INSTAT		0x02
 #define B1_OUTSTAT		0x03
-#define B1_RESET		0x10
 #define B1_ANALYSE		0x04
+#define B1_REVISION		0x05
+#define B1_RESET		0x10
 
 
 #define B1_STAT0(cardtype)  ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l)
@@ -561,10 +569,13 @@
 	 }
 }
 
+/* b1.c */
 int b1_detect(unsigned int base, enum avmcardtype cardtype);
+void b1_getrevision(avmcard *card);
 int b1_load_t4file(avmcard *card, capiloaddatapart * t4file);
 int b1_load_config(avmcard *card, capiloaddatapart * config);
 int b1_loaded(avmcard *card);
+
 int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
 void b1_reset_ctr(struct capi_ctr *ctrl);
 void b1_register_appl(struct capi_ctr *ctrl, __u16 appl,
@@ -577,5 +588,21 @@
 int b1ctl_read_proc(char *page, char **start, off_t off,
         		int count, int *eof, struct capi_ctr *ctrl);
 
+/* b1dma.c */
+int b1pciv4_detect(avmcard *card);
+int t1pci_detect(avmcard *card);
+void b1dma_reset(avmcard *card);
+void b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs);
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1dma_reset_ctr(struct capi_ctr *ctrl);
+void b1dma_remove_ctr(struct capi_ctr *ctrl);
+void b1dma_register_appl(struct capi_ctr *ctrl,
+				__u16 appl,
+				capi_register_params *rp);
+void b1dma_release_appl(struct capi_ctr *ctrl, __u16 appl);
+void b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+int b1dmactl_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl);
 
 #endif /* _AVMCARD_H_ */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/b1.c linux/drivers/isdn/avmb1/b1.c
--- v2.3.45/linux/drivers/isdn/avmb1/b1.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/b1.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,16 @@
 /*
- * $Id: b1.c,v 1.12 1999/11/05 16:38:01 calle Exp $
+ * $Id: b1.c,v 1.13 2000/01/25 14:33:38 calle Exp $
  * 
  * Common module for AVM B1 cards.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1.c,v $
+ * Revision 1.13  2000/01/25 14:33:38  calle
+ * - Added Support AVM B1 PCI V4.0 (tested with prototype)
+ *   - splitted up t1pci.c into b1dma.c for common function with b1pciv4
+ *   - support for revision register
+ *
  * Revision 1.12  1999/11/05 16:38:01  calle
  * Cleanups before kernel 2.4:
  * - Changed all messages to use card->name or driver->name instead of
@@ -86,7 +91,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.12 $";
+static char *revision = "$Revision: 1.13 $";
 
 /* ------------------------------------------------------------- */
 
@@ -158,6 +163,12 @@
 	return 0;
 }
 
+void b1_getrevision(avmcard *card)
+{
+    card->class = inb(card->port + B1_ANALYSE);
+    card->revision = inb(card->port + B1_REVISION);
+}
+
 int b1_load_t4file(avmcard *card, capiloaddatapart * t4file)
 {
 	unsigned char buf[256];
@@ -688,6 +699,7 @@
 EXPORT_SYMBOL(b1_irq_table);
 
 EXPORT_SYMBOL(b1_detect);
+EXPORT_SYMBOL(b1_getrevision);
 EXPORT_SYMBOL(b1_load_t4file);
 EXPORT_SYMBOL(b1_load_config);
 EXPORT_SYMBOL(b1_loaded);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/b1dma.c linux/drivers/isdn/avmb1/b1dma.c
--- v2.3.45/linux/drivers/isdn/avmb1/b1dma.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/b1dma.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,984 @@
+/*
+ * $Id: b1dma.c,v 1.2 2000/01/25 14:44:47 calle Exp $
+ * 
+ * Common module for AVM B1 cards that support dma with AMCC
+ * 
+ * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: b1dma.c,v $
+ * Revision 1.2  2000/01/25 14:44:47  calle
+ * typo in b1pciv4_detect().
+ *
+ * Revision 1.1  2000/01/25 14:36:43  calle
+ * common function for  T1 PCI and B1 PCI V4.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "capilli.h"
+#include "avmcard.h"
+#include "capicmd.h"
+#include "capiutil.h"
+
+static char *revision = "$Revision: 1.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+
+int suppress_pollack = 0;
+MODULE_PARM(suppress_pollack, "0-1i");
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+/* S5933 */
+
+#define	AMCC_RXPTR	0x24
+#define	AMCC_RXLEN	0x28
+#define	AMCC_TXPTR	0x2c
+#define	AMCC_TXLEN	0x30
+
+#define	AMCC_INTCSR	0x38
+#	define EN_READ_TC_INT		0x00008000L
+#	define EN_WRITE_TC_INT		0x00004000L
+#	define EN_TX_TC_INT		EN_READ_TC_INT
+#	define EN_RX_TC_INT		EN_WRITE_TC_INT
+#	define AVM_FLAG			0x30000000L
+
+#	define ANY_S5933_INT		0x00800000L
+#	define	READ_TC_INT		0x00080000L
+#	define WRITE_TC_INT		0x00040000L
+#	define	TX_TC_INT		READ_TC_INT
+#	define	RX_TC_INT		WRITE_TC_INT
+#	define MASTER_ABORT_INT		0x00100000L
+#	define TARGET_ABORT_INT		0x00200000L
+#	define BUS_MASTER_INT		0x00200000L
+#	define ALL_INT			0x000C0000L
+
+#define	AMCC_MCSR	0x3c
+#	define A2P_HI_PRIORITY		0x00000100L
+#	define EN_A2P_TRANSFERS		0x00000400L
+#	define P2A_HI_PRIORITY		0x00001000L
+#	define EN_P2A_TRANSFERS		0x00004000L
+#	define RESET_A2P_FLAGS		0x04000000L
+#	define RESET_P2A_FLAGS		0x02000000L
+
+/* ------------------------------------------------------------- */
+
+#define b1dmaoutmeml(addr, value)	writel(value, addr)
+#define b1dmainmeml(addr)	readl(addr)
+#define b1dmaoutmemw(addr, value)	writew(value, addr)
+#define b1dmainmemw(addr)	readw(addr)
+#define b1dmaoutmemb(addr, value)	writeb(value, addr)
+#define b1dmainmemb(addr)	readb(addr)
+
+/* ------------------------------------------------------------- */
+
+static inline int b1dma_tx_empty(unsigned int port)
+{
+	return inb(port + 0x03) & 0x1;
+}
+
+static inline int b1dma_rx_full(unsigned int port)
+{
+	return inb(port + 0x02) & 0x1;
+}
+
+static int b1dma_tolink(avmcard *card, void *buf, unsigned int len)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	unsigned char *s = (unsigned char *)buf;
+	while (len--) {
+		while (   !b1dma_tx_empty(card->port)
+		       && time_before(jiffies, stop));
+		if (!b1dma_tx_empty(card->port)) 
+			return -1;
+	        t1outp(card->port, 0x01, *s++);
+	}
+	return 0;
+}
+
+static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	unsigned char *s = (unsigned char *)buf;
+	while (len--) {
+		while (   !b1dma_rx_full(card->port)
+		       && time_before(jiffies, stop));
+		if (!b1dma_rx_full(card->port)) 
+			return -1;
+	        *s++ = t1inp(card->port, 0x00);
+	}
+	return 0;
+}
+
+static int WriteReg(avmcard *card, __u32 reg, __u8 val)
+{
+	__u8 cmd = 0x00;
+	if (   b1dma_tolink(card, &cmd, 1) == 0
+	    && b1dma_tolink(card, &reg, 4) == 0) {
+		__u32 tmp = val;
+		return b1dma_tolink(card, &tmp, 4);
+	}
+	return -1;
+}
+
+static __u8 ReadReg(avmcard *card, __u32 reg)
+{
+	__u8 cmd = 0x01;
+	if (   b1dma_tolink(card, &cmd, 1) == 0
+	    && b1dma_tolink(card, &reg, 4) == 0) {
+		__u32 tmp;
+		if (b1dma_fromlink(card, &tmp, 4) == 0)
+			return (__u8)tmp;
+	}
+	return 0xff;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, __u8 val)
+{
+	__u8 *s = *pp;
+	*s++ = val;
+	*pp = s;
+}
+
+static inline void _put_word(void **pp, __u32 val)
+{
+	__u8 *s = *pp;
+	*s++ = val & 0xff;
+	*s++ = (val >> 8) & 0xff;
+	*s++ = (val >> 16) & 0xff;
+	*s++ = (val >> 24) & 0xff;
+	*pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	_put_word(pp, i);
+	while (i-- > 0)
+		_put_byte(pp, *dp++);
+}
+
+static inline __u8 _get_byte(void **pp)
+{
+	__u8 *s = *pp;
+	__u8 val;
+	val = *s++;
+	*pp = s;
+	return val;
+}
+
+static inline __u32 _get_word(void **pp)
+{
+	__u8 *s = *pp;
+	__u32 val;
+	val = *s++;
+	val |= (*s++ << 8);
+	val |= (*s++ << 16);
+	val |= (*s++ << 24);
+	*pp = s;
+	return val;
+}
+
+static inline __u32 _get_slice(void **pp, unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = _get_word(pp);
+	while (i-- > 0) *dp++ = _get_byte(pp);
+	return len;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_reset(avmcard *card)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	card->csr = 0x0;
+	b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr);
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
+	b1dmaoutmeml(card->mbase+AMCC_RXLEN, 0);
+	b1dmaoutmeml(card->mbase+AMCC_TXLEN, 0);
+
+	t1outp(card->port, 0x10, 0x00);
+	t1outp(card->port, 0x07, 0x00);
+
+	restore_flags(flags);
+
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(10 * 1000);
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
+	udelay(10 * 1000);
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
+	if (card->cardtype == avm_t1pci)
+		udelay(42 * 1000);
+	else
+		udelay(10 * 1000);
+}
+
+/* ------------------------------------------------------------- */
+
+int b1dma_detect(avmcard *card)
+{
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(10 * 1000);
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
+	udelay(10 * 1000);
+	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(42 * 1000);
+
+	b1dmaoutmeml(card->mbase+AMCC_RXLEN, 0);
+	b1dmaoutmeml(card->mbase+AMCC_TXLEN, 0);
+	card->csr = 0x0;
+	b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr);
+
+	if (b1dmainmeml(card->mbase+AMCC_MCSR) != 0x000000E6)
+		return 1;
+
+	b1dmaoutmeml(card->mbase+AMCC_RXPTR, 0xffffffff);
+	b1dmaoutmeml(card->mbase+AMCC_TXPTR, 0xffffffff);
+	if (   b1dmainmeml(card->mbase+AMCC_RXPTR) != 0xfffffffc
+	    || b1dmainmeml(card->mbase+AMCC_TXPTR) != 0xfffffffc)
+		return 2;
+
+	b1dmaoutmeml(card->mbase+AMCC_RXPTR, 0x0);
+	b1dmaoutmeml(card->mbase+AMCC_TXPTR, 0x0);
+	if (   b1dmainmeml(card->mbase+AMCC_RXPTR) != 0x0
+	    || b1dmainmeml(card->mbase+AMCC_TXPTR) != 0x0)
+		return 3;
+
+	t1outp(card->port, 0x10, 0x00);
+	t1outp(card->port, 0x07, 0x00);
+	
+	t1outp(card->port, 0x02, 0x02);
+	t1outp(card->port, 0x03, 0x02);
+
+	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x02
+	    || t1inp(card->port, 0x3) != 0x03)
+		return 4;
+
+	t1outp(card->port, 0x02, 0x00);
+	t1outp(card->port, 0x03, 0x00);
+
+	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x00
+	    || t1inp(card->port, 0x3) != 0x01)
+		return 5;
+
+	return 0;
+}
+
+int t1pci_detect(avmcard *card)
+{
+	int ret;
+
+	if ((ret = b1dma_detect(card)) != 0)
+		return ret;
+	
+	/* Transputer test */
+	
+	if (   WriteReg(card, 0x80001000, 0x11) != 0
+	    || WriteReg(card, 0x80101000, 0x22) != 0
+	    || WriteReg(card, 0x80201000, 0x33) != 0
+	    || WriteReg(card, 0x80301000, 0x44) != 0)
+		return 6;
+
+	if (   ReadReg(card, 0x80001000) != 0x11
+	    || ReadReg(card, 0x80101000) != 0x22
+	    || ReadReg(card, 0x80201000) != 0x33
+	    || ReadReg(card, 0x80301000) != 0x44)
+		return 7;
+
+	if (   WriteReg(card, 0x80001000, 0x55) != 0
+	    || WriteReg(card, 0x80101000, 0x66) != 0
+	    || WriteReg(card, 0x80201000, 0x77) != 0
+	    || WriteReg(card, 0x80301000, 0x88) != 0)
+		return 8;
+
+	if (   ReadReg(card, 0x80001000) != 0x55
+	    || ReadReg(card, 0x80101000) != 0x66
+	    || ReadReg(card, 0x80201000) != 0x77
+	    || ReadReg(card, 0x80301000) != 0x88)
+		return 9;
+
+	return 0;
+}
+
+int b1pciv4_detect(avmcard *card)
+{
+	int ret, i;
+
+	if ((ret = b1dma_detect(card)) != 0)
+		return ret;
+	
+	for (i=0; i < 5 ; i++) {
+		if (WriteReg(card, 0x80A00000, 0x21) != 0)
+			return 6;
+		if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01)
+			return 7;
+	}
+	for (i=0; i < 5 ; i++) {
+		if (WriteReg(card, 0x80A00000, 0x20) != 0)
+			return 8;
+		if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00)
+			return 9;
+	}
+	
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	unsigned long flags;
+	struct sk_buff *skb;
+	__u8 cmd, subcmd;
+	__u16 len;
+	__u32 txlen;
+	int inint;
+	void *p;
+	
+	save_flags(flags);
+	cli();
+
+	inint = card->interrupt;
+
+	if (card->csr & EN_TX_TC_INT) { /* tx busy */
+	        restore_flags(flags);
+		return;
+	}
+
+	skb = skb_dequeue(&dma->send_queue);
+	if (!skb) {
+#ifdef CONFIG_B1DMA_DEBUG
+		printk(KERN_DEBUG "tx(%d): underrun\n", inint);
+#endif
+	        restore_flags(flags);
+		return;
+	}
+
+	len = CAPIMSG_LEN(skb->data);
+
+	if (len) {
+		cmd = CAPIMSG_COMMAND(skb->data);
+		subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+		p = dma->sendbuf;
+
+		if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+			__u16 dlen = CAPIMSG_DATALEN(skb->data);
+			_put_byte(&p, SEND_DATA_B3_REQ);
+			_put_slice(&p, skb->data, len);
+			_put_slice(&p, skb->data + len, dlen);
+		} else {
+			_put_byte(&p, SEND_MESSAGE);
+			_put_slice(&p, skb->data, len);
+		}
+		txlen = (__u8 *)p - (__u8 *)dma->sendbuf;
+#ifdef CONFIG_B1DMA_DEBUG
+		printk(KERN_DEBUG "tx(%d): put msg len=%d\n",
+				inint, txlen);
+#endif
+	} else {
+		txlen = skb->len-2;
+#ifdef CONFIG_B1DMA_POLLDEBUG
+		if (skb->data[2] == SEND_POLLACK)
+			printk(KERN_INFO "%s: send ack\n", card->name);
+#endif
+#ifdef CONFIG_B1DMA_DEBUG
+		printk(KERN_DEBUG "tx(%d): put 0x%x len=%d\n",
+				inint, skb->data[2], txlen);
+#endif
+		memcpy(dma->sendbuf, skb->data+2, skb->len-2);
+	}
+	txlen = (txlen + 3) & ~3;
+
+	b1dmaoutmeml(card->mbase+AMCC_TXPTR, virt_to_phys(dma->sendbuf));
+	b1dmaoutmeml(card->mbase+AMCC_TXLEN, txlen);
+
+	card->csr |= EN_TX_TC_INT;
+
+	if (!inint)
+		b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr);
+
+	restore_flags(flags);
+	dev_kfree_skb(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_POLLACK);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	b1dma_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_rx(avmcard *card)
+{
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	avmcard_dmainfo *dma = card->dma;
+	struct capi_ctr *ctrl = cinfo->capi_ctrl;
+	struct sk_buff *skb;
+	void *p = dma->recvbuf+4;
+	__u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+	__u8 b1cmd =  _get_byte(&p);
+
+#ifdef CONFIG_B1DMA_DEBUG
+	printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
+#endif
+	
+	switch (b1cmd) {
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		DataB3Len = _get_slice(&p, card->databuf);
+
+		if (MsgLen < 30) { /* not CAPI 64Bit */
+			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+			MsgLen = 30;
+			CAPIMSG_SETLEN(card->msgbuf, 30);
+		}
+		if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			ctrl->handle_capimsg(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			ctrl->handle_capimsg(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+		WindowSize = _get_word(&p);
+
+		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+
+		if (NCCI != 0xffffffff)
+			ctrl->free_ncci(ctrl, ApplId, NCCI);
+		else ctrl->appl_released(ctrl, ApplId);
+		break;
+
+	case RECEIVE_START:
+#ifdef CONFIG_B1DMA_POLLDEBUG
+		printk(KERN_INFO "%s: receive poll\n", card->name);
+#endif
+		if (!suppress_pollack)
+			queue_pollack(card);
+		ctrl->resume_output(ctrl);
+		break;
+
+	case RECEIVE_STOP:
+		ctrl->suspend_output(ctrl);
+		break;
+
+	case RECEIVE_INIT:
+
+		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+		b1_parse_version(cinfo);
+		printk(KERN_INFO "%s: %s-card (%s) now active\n",
+		       card->name,
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
+		ctrl->ready(ctrl);
+		break;
+
+	case RECEIVE_TASK_READY:
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen--] = 0;
+		while (    MsgLen >= 0
+		       && (   card->msgbuf[MsgLen] == '\n'
+			   || card->msgbuf[MsgLen] == '\r'))
+			card->msgbuf[MsgLen--] = 0;
+		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+				card->name, ApplId, card->msgbuf);
+		break;
+
+	case RECEIVE_DEBUGMSG:
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen--] = 0;
+		while (    MsgLen >= 0
+		       && (   card->msgbuf[MsgLen] == '\n'
+			   || card->msgbuf[MsgLen] == '\r'))
+			card->msgbuf[MsgLen--] = 0;
+		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n",
+				card->name, b1cmd);
+		return;
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_interrupt(avmcard *card)
+{
+	__u32 status = b1dmainmeml(card->mbase+AMCC_INTCSR);
+	__u32 newcsr;
+
+	if ((status & ANY_S5933_INT) == 0) 
+		return;
+
+        newcsr = card->csr | (status & ALL_INT);
+	if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
+	if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
+	b1dmaoutmeml(card->mbase+AMCC_INTCSR, newcsr);
+
+	if ((status & RX_TC_INT) != 0) {
+		__u8 *recvbuf = card->dma->recvbuf;
+		__u32 rxlen;
+	   	if (card->dma->recvlen == 0) {
+			card->dma->recvlen = *((__u32 *)recvbuf);
+			rxlen = (card->dma->recvlen + 3) & ~3;
+			b1dmaoutmeml(card->mbase+AMCC_RXPTR,
+					virt_to_phys(recvbuf+4));
+			b1dmaoutmeml(card->mbase+AMCC_RXLEN, rxlen);
+		} else {
+			b1dma_handle_rx(card);
+	   		card->dma->recvlen = 0;
+			b1dmaoutmeml(card->mbase+AMCC_RXPTR, virt_to_phys(recvbuf));
+			b1dmaoutmeml(card->mbase+AMCC_RXLEN, 4);
+		}
+	}
+
+	if ((status & TX_TC_INT) != 0) {
+		card->csr &= ~EN_TX_TC_INT;
+	        b1dma_dispatch_tx(card);
+	} else if (card->csr & EN_TX_TC_INT) {
+		if (b1dmainmeml(card->mbase+AMCC_TXLEN) == 0) {
+			card->csr &= ~EN_TX_TC_INT;
+			b1dma_dispatch_tx(card);
+		}
+	}
+	b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr);
+}
+
+void b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card;
+
+	card = (avmcard *) devptr;
+
+	if (!card) {
+		printk(KERN_WARNING "b1dma: interrupt: wrong device\n");
+		return;
+	}
+	if (card->interrupt) {
+		printk(KERN_ERR "%s: reentering interrupt hander\n", card->name);
+		return;
+	}
+
+	card->interrupt = 1;
+
+	b1dma_handle_interrupt(card);
+
+	card->interrupt = 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_loaded(avmcard *card)
+{
+	unsigned long stop;
+	unsigned char ans;
+	unsigned long tout = 2;
+	unsigned int base = card->port;
+
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_tx_empty(base))
+			break;
+	}
+	if (!b1_tx_empty(base)) {
+		printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n",
+				card->name);
+		return 0;
+	}
+	b1_put_byte(base, SEND_POLLACK);
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_rx_full(base)) {
+			if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
+				return 1;
+			}
+			printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
+			return 0;
+		}
+	}
+	printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name);
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_send_init(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(15, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_INIT);
+	_put_word(&p, AVM_NAPPS);
+	_put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+	_put_word(&p, card->cardnr - 1);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	b1dma_dispatch_tx(card);
+}
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+	int retval;
+
+	b1dma_reset(card);
+
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
+		b1dma_reset(card);
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		return retval;
+	}
+
+	if (data->configuration.len > 0 && data->configuration.data) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
+			b1dma_reset(card);
+			printk(KERN_ERR "%s: failed to load config!!\n",
+					card->name);
+			return retval;
+		}
+	}
+
+	if (!b1dma_loaded(card)) {
+		b1dma_reset(card);
+		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+		return -EIO;
+	}
+
+	save_flags(flags);
+	cli();
+
+	card->csr = AVM_FLAG;
+	b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr);
+	b1dmaoutmeml(card->mbase+AMCC_MCSR,
+		EN_A2P_TRANSFERS|EN_P2A_TRANSFERS
+		|A2P_HI_PRIORITY|P2A_HI_PRIORITY
+		|RESET_A2P_FLAGS|RESET_P2A_FLAGS);
+	t1outp(card->port, 0x07, 0x30);
+	t1outp(card->port, 0x10, 0xF0);
+
+	card->dma->recvlen = 0;
+	b1dmaoutmeml(card->mbase+AMCC_RXPTR, virt_to_phys(card->dma->recvbuf));
+	b1dmaoutmeml(card->mbase+AMCC_RXLEN, 4);
+	card->csr |= EN_RX_TC_INT;
+	b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr);
+	restore_flags(flags);
+
+        b1dma_send_init(card);
+
+	return 0;
+}
+
+void b1dma_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+
+ 	b1dma_reset(card);
+
+	memset(cinfo->version, 0, sizeof(cinfo->version));
+	ctrl->reseted(ctrl);
+}
+
+
+/* ------------------------------------------------------------- */
+
+
+void b1dma_register_appl(struct capi_ctr *ctrl,
+				__u16 appl,
+				capi_register_params *rp)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	int want = rp->level3cnt;
+	int nconn;
+	void *p;
+
+	if (want > 0) nconn = want;
+	else nconn = ctrl->profile.nbchannel * -want;
+	if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+	skb = alloc_skb(23, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_REGISTER);
+	_put_word(&p, appl);
+	_put_word(&p, 1024 * (nconn+1));
+	_put_word(&p, nconn);
+	_put_word(&p, rp->datablkcnt);
+	_put_word(&p, rp->datablklen);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	b1dma_dispatch_tx(card);
+
+	ctrl->appl_registered(ctrl, appl);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(7, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_RELEASE);
+	_put_word(&p, appl);
+
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+	skb_queue_tail(&card->dma->send_queue, skb);
+	b1dma_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	skb_queue_tail(&card->dma->send_queue, skb);
+	b1dma_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+int b1dmactl_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+	__u8 flag;
+	int len = 0;
+	char *s;
+	__u32 txaddr, txlen, rxaddr, rxlen, csr;
+
+	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+	len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+	switch (card->cardtype) {
+	case avm_b1isa: s = "B1 ISA"; break;
+	case avm_b1pci: s = "B1 PCI"; break;
+	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+	case avm_m1: s = "M1"; break;
+	case avm_m2: s = "M2"; break;
+	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
+	default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+	if (card->cardtype != avm_m1) {
+        	flag = ((__u8 *)(ctrl->profile.manu))[3];
+        	if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+			"protocol",
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+	}
+	if (card->cardtype != avm_m1) {
+        	flag = ((__u8 *)(ctrl->profile.manu))[5];
+		if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s\n",
+			"linetype",
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+	}
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+	save_flags(flags);
+	cli();
+
+	txaddr = (__u32)phys_to_virt(b1dmainmeml(card->mbase+0x2c));
+	txaddr -= (__u32)card->dma->sendbuf;
+	txlen  = b1dmainmeml(card->mbase+0x30);
+
+	rxaddr = (__u32)phys_to_virt(b1dmainmeml(card->mbase+0x24));
+	rxaddr -= (__u32)card->dma->recvbuf;
+	rxlen  = b1dmainmeml(card->mbase+0x28);
+
+	csr  = b1dmainmeml(card->mbase+AMCC_INTCSR);
+
+	restore_flags(flags);
+
+        len += sprintf(page+len, "%-16s 0x%lx\n",
+				"csr (cached)", (unsigned long)card->csr);
+        len += sprintf(page+len, "%-16s 0x%lx\n",
+				"csr", (unsigned long)csr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"txoff", (unsigned long)txaddr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"txlen", (unsigned long)txlen);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"rxoff", (unsigned long)rxaddr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"rxlen", (unsigned long)rxlen);
+
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+EXPORT_SYMBOL(b1dma_reset);
+EXPORT_SYMBOL(t1pci_detect);
+EXPORT_SYMBOL(b1pciv4_detect);
+EXPORT_SYMBOL(b1dma_interrupt);
+
+EXPORT_SYMBOL(b1dma_load_firmware);
+EXPORT_SYMBOL(b1dma_reset_ctr);
+EXPORT_SYMBOL(b1dma_register_appl);
+EXPORT_SYMBOL(b1dma_release_appl);
+EXPORT_SYMBOL(b1dma_send_message);
+EXPORT_SYMBOL(b1dmactl_read_proc);
+
+#ifdef MODULE
+#define b1dma_init init_module
+void cleanup_module(void);
+#endif
+
+int b1dma_init(void)
+{
+	char *p;
+	char rev[10];
+
+	if ((p = strchr(revision, ':'))) {
+		strncpy(rev, p + 1, sizeof(rev));
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	printk(KERN_INFO "b1dma: revision %s\n", rev);
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+}
+#endif
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/b1isa.c linux/drivers/isdn/avmb1/b1isa.c
--- v2.3.45/linux/drivers/isdn/avmb1/b1isa.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/b1isa.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,19 @@
 /*
- * $Id: b1isa.c,v 1.5 1999/11/05 16:38:01 calle Exp $
+ * $Id: b1isa.c,v 1.7 2000/02/02 18:36:03 calle Exp $
  * 
  * Module for AVM B1 ISA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1isa.c,v $
+ * Revision 1.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
+ *
+ * Revision 1.6  2000/01/25 14:37:39  calle
+ * new message after successfull detection including card revision and
+ * used resources.
+ *
  * Revision 1.5  1999/11/05 16:38:01  calle
  * Cleanups before kernel 2.4:
  * - Changed all messages to use card->name or driver->name instead of
@@ -61,7 +69,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.7 $";
 
 /* ------------------------------------------------------------- */
 
@@ -69,10 +77,6 @@
 
 /* ------------------------------------------------------------- */
 
-static struct capi_driver_interface *di;
-
-/* ------------------------------------------------------------- */
-
 static void b1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
 {
 	avmcard *card;
@@ -96,6 +100,10 @@
 }
 /* ------------------------------------------------------------- */
 
+static struct capi_driver_interface *di;
+
+/* ------------------------------------------------------------- */
+
 static void b1isa_remove_ctr(struct capi_ctr *ctrl)
 {
 	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
@@ -122,10 +130,13 @@
 	avmcard *card;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
 		printk(KERN_WARNING "b1isa: no memory.\n");
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
@@ -133,6 +144,7 @@
 	if (!cinfo) {
 		printk(KERN_WARNING "b1isa: no memory.\n");
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(cinfo, 0, sizeof(avmctrl_info));
@@ -149,12 +161,14 @@
 		       card->port, card->port + AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 	if (b1_irq_table[card->irq & 0xf] == 0) {
 		printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EINVAL;
 	}
 	if (   card->port != 0x150 && card->port != 0x250
@@ -162,6 +176,7 @@
 		printk(KERN_WARNING "b1isa: illegal port 0x%x.\n", card->port);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EINVAL;
 	}
 	b1_reset(card->port);
@@ -170,9 +185,11 @@
 					card->port, retval);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 	b1_reset(card->port);
+	b1_getrevision(card);
 
 	request_region(p->port, AVMB1_PORTLEN, card->name);
 
@@ -182,6 +199,7 @@
 		release_region(card->port, AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
@@ -192,10 +210,14 @@
 		release_region(card->port, AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
-	MOD_INC_USE_COUNT;
+	printk(KERN_INFO
+		"%s: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
+		driver->name, card->port, card->irq, card->revision);
+
 	return 0;
 }
 
@@ -205,11 +227,12 @@
 
 	if (!cinfo)
 		return "";
-	sprintf(cinfo->infobuf, "%s %s 0x%x %d",
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
 		cinfo->cardname[0] ? cinfo->cardname : "-",
 		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
 		cinfo->card ? cinfo->card->port : 0x0,
-		cinfo->card ? cinfo->card->irq : 0
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->revision : 0
 		);
 	return cinfo->infobuf;
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c
--- v2.3.45/linux/drivers/isdn/avmb1/b1pci.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/b1pci.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,20 @@
 /*
- * $Id: b1pci.c,v 1.18 1999/11/05 16:38:01 calle Exp $
+ * $Id: b1pci.c,v 1.20 2000/02/02 18:36:03 calle Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.20  2000/02/02 18:36:03  calle
+ * - Modules are now locked while init_module is running
+ * - fixed problem with memory mapping if address is not aligned
+ *
+ * Revision 1.19  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
+ *   - support for revision register
+ *
  * Revision 1.18  1999/11/05 16:38:01  calle
  * Cleanups before kernel 2.4:
  * - Changed all messages to use card->name or driver->name instead of
@@ -66,7 +75,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.18 $";
+static char *revision = "$Revision: 1.20 $";
 
 /* ------------------------------------------------------------- */
 
@@ -138,11 +147,12 @@
 
 	if (!cinfo)
 		return "";
-	sprintf(cinfo->infobuf, "%s %s 0x%x %d",
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
 		cinfo->cardname[0] ? cinfo->cardname : "-",
 		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
 		cinfo->card ? cinfo->card->port : 0x0,
-		cinfo->card ? cinfo->card->irq : 0
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->revision : 0
 		);
 	return cinfo->infobuf;
 }
@@ -155,10 +165,13 @@
 	avmctrl_info *cinfo;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
@@ -166,6 +179,7 @@
 	if (!cinfo) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(cinfo, 0, sizeof(avmctrl_info));
@@ -182,6 +196,7 @@
 		       driver->name, card->port, card->port + AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 	b1_reset(card->port);
@@ -190,9 +205,11 @@
 					driver->name, card->port, retval);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 	b1_reset(card->port);
+	b1_getrevision(card);
 
 	request_region(p->port, AVMB1_PORTLEN, card->name);
 
@@ -203,6 +220,7 @@
 		release_region(card->port, AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
@@ -214,10 +232,19 @@
 		release_region(card->port, AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
-	MOD_INC_USE_COUNT;
+	if (card->revision >= 4) {
+		printk(KERN_INFO
+			"%s: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n",
+			driver->name, card->port, card->irq, card->revision);
+	} else {
+		printk(KERN_INFO
+			"%s: AVM B1 PCI at i/o %#x, irq %d, revision %d\n",
+			driver->name, card->port, card->irq, card->revision);
+	}
 
 	return 0;
 }
@@ -241,6 +268,187 @@
     0, /* no add_card function */
 };
 
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+/* ------------------------------------------------------------- */
+
+static struct capi_driver_interface *div4;
+
+/* ------------------------------------------------------------- */
+
+static void b1pciv4_remove_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+
+ 	b1dma_reset(card);
+
+	div4->detach_ctr(ctrl);
+	free_irq(card->irq, card);
+	iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+	release_region(card->port, AVMB1_PORTLEN);
+	ctrl->driverdata = 0;
+	kfree(card->ctrlinfo);
+	kfree(card->dma);
+	kfree(card);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static char *b1pciv4_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0,
+		cinfo->card ? cinfo->card->revision : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pciv4_add_card(struct capi_driver *driver, struct capicardparams *p)
+{
+	unsigned long base, page_offset;
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int retval;
+
+	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
+
+	if (!card) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		return -ENOMEM;
+	}
+	memset(card, 0, sizeof(avmcard));
+	card->dma = (avmcard_dmainfo *) kmalloc(sizeof(avmcard_dmainfo), GFP_ATOMIC);
+	if (!card->dma) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(card->dma, 0, sizeof(avmcard_dmainfo));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card->dma);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info));
+	card->ctrlinfo = cinfo;
+	cinfo->card = card;
+	sprintf(card->name, "b1pciv4-%x", p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->membase = p->membase;
+	card->cardtype = avm_b1pci;
+
+	if (check_region(card->port, AVMB1_PORTLEN)) {
+		printk(KERN_WARNING
+		       "%s: ports 0x%03x-0x%03x in use.\n",
+		       driver->name, card->port, card->port + AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EBUSY;
+	}
+
+	base = card->membase & PAGE_MASK;
+	page_offset = card->membase - base;
+	card->mbase = ioremap_nocache(base, page_offset + 64);
+	if (card->mbase) {
+		card->mbase += page_offset;
+	} else {
+		printk(KERN_NOTICE "%s: can't remap memory at 0x%lx\n",
+					driver->name, card->membase);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EIO;
+	}
+
+	b1dma_reset(card);
+
+	if ((retval = b1pciv4_detect(card)) != 0) {
+		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EIO;
+	}
+	b1dma_reset(card);
+	b1_getrevision(card);
+
+	request_region(p->port, AVMB1_PORTLEN, card->name);
+
+	retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+				driver->name, card->irq);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EBUSY;
+	}
+
+	cinfo->capi_ctrl = div4->attach_ctr(driver, card->name, cinfo);
+	if (!cinfo->capi_ctrl) {
+		printk(KERN_ERR "%s: attach controller failed.\n", driver->name);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+		free_irq(card->irq, card);
+		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EBUSY;
+	}
+	card->cardnr = cinfo->capi_ctrl->cnr;
+
+	skb_queue_head_init(&card->dma->send_queue);
+
+	printk(KERN_INFO
+		"%s: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n",
+		driver->name, card->port, card->irq,
+		card->membase, card->revision);
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+
+static struct capi_driver b1pciv4_driver = {
+    "b1pciv4",
+    "0.0",
+    b1dma_load_firmware,
+    b1dma_reset_ctr,
+    b1pciv4_remove_ctr,
+    b1dma_register_appl,
+    b1dma_release_appl,
+    b1dma_send_message,
+
+    b1pciv4_procinfo,
+    b1dmactl_read_proc,
+    0,	/* use standard driver_read_proc */
+
+    0, /* no add_card function */
+};
+
+#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
+
 #ifdef MODULE
 #define b1pci_init init_module
 void cleanup_module(void);
@@ -248,9 +456,55 @@
 
 static int ncards = 0;
 
+static int add_card(struct pci_dev *dev)
+{
+	struct capi_driver *driver = &b1pci_driver;
+	struct capicardparams param;
+	int retval;
+
+	if (dev->resource[ 2].start & PCI_BASE_ADDRESS_IO_MASK) { /* B1 PCI V4 */
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+		driver = &b1pciv4_driver;
+#endif
+		param.membase = dev->resource[ 0].start & PCI_BASE_ADDRESS_MEM_MASK;
+		param.port = dev->resource[ 2].start & PCI_BASE_ADDRESS_IO_MASK;
+		param.irq = dev->irq;
+		printk(KERN_INFO
+		"%s: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
+		driver->name, param.port, param.irq, param.membase);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+		retval = b1pciv4_add_card(driver, &param);
+#else
+		retval = b1pci_add_card(driver, &param);
+#endif
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n",
+			driver->name, param.port, param.irq, param.membase);
+		}
+	} else {
+		param.membase = 0;
+		param.port = dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK;
+		param.irq = dev->irq;
+		printk(KERN_INFO
+		"%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
+		driver->name, param.port, param.irq);
+		retval = b1pci_add_card(driver, &param);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: no AVM-B1 at i/o %#x, irq %d detected\n",
+			driver->name, param.port, param.irq);
+		}
+	}
+	return retval;
+}
+
 int b1pci_init(void)
 {
 	struct capi_driver *driver = &b1pci_driver;
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+	struct capi_driver *driverv4 = &b1pciv4_driver;
+#endif
 	struct pci_dev *dev = NULL;
 	char *p;
 	int retval;
@@ -271,26 +525,32 @@
 		return -EIO;
 	}
 
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+	printk(KERN_INFO "%s: revision %s\n", driverv4->name, driverv4->revision);
+
+        div4 = attach_capi_driver(driverv4);
+
+	if (!div4) {
+    		detach_capi_driver(driver);
+		printk(KERN_ERR "%s: failed to attach capi_driver\n",
+				driverv4->name);
+		return -EIO;
+	}
+#endif
+
 #ifdef CONFIG_PCI
 	if (!pci_present()) {
 		printk(KERN_ERR "%s: no PCI bus present\n", driver->name);
     		detach_capi_driver(driver);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+    		detach_capi_driver(driverv4);
+#endif
 		return -EIO;
 	}
 
 	while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev))) {
-		struct capicardparams param;
-
-		param.port = dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK;
-		param.irq = dev->irq;
-		printk(KERN_INFO
-			"%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
-			driver->name, param.port, param.irq);
-		retval = b1pci_add_card(driver, &param);
+		retval = add_card(dev);
 		if (retval != 0) {
-		        printk(KERN_ERR
-			"%s: no AVM-B1 at i/o %#x, irq %d detected\n",
-			driver->name, param.port, param.irq);
 #ifdef MODULE
 			cleanup_module();
 #endif
@@ -315,5 +575,8 @@
 void cleanup_module(void)
 {
     detach_capi_driver(&b1pci_driver);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+    detach_capi_driver(&b1pciv4_driver);
+#endif
 }
 #endif
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/b1pcmcia.c linux/drivers/isdn/avmb1/b1pcmcia.c
--- v2.3.45/linux/drivers/isdn/avmb1/b1pcmcia.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/b1pcmcia.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,19 @@
 /*
- * $Id: b1pcmcia.c,v 1.5 1999/11/05 16:38:01 calle Exp $
+ * $Id: b1pcmcia.c,v 1.7 2000/02/02 18:36:03 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.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
+ *
+ * Revision 1.6  2000/01/25 14:37:39  calle
+ * new message after successfull detection including card revision and
+ * used resources.
+ *
  * Revision 1.5  1999/11/05 16:38:01  calle
  * Cleanups before kernel 2.4:
  * - Changed all messages to use card->name or driver->name instead of
@@ -62,7 +70,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.7 $";
 
 /* ------------------------------------------------------------- */
 
@@ -126,12 +134,16 @@
 {
 	avmctrl_info *cinfo;
 	avmcard *card;
+	char *cardname;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
@@ -139,6 +151,7 @@
 	if (!cinfo) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(cinfo, 0, sizeof(avmctrl_info));
@@ -159,9 +172,11 @@
 					driver->name, card->port, retval);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 	b1_reset(card->port);
+	b1_getrevision(card);
 
 	retval = request_irq(card->irq, b1pcmcia_interrupt, 0, card->name, card);
 	if (retval) {
@@ -169,6 +184,7 @@
 				driver->name, card->irq);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
@@ -179,10 +195,19 @@
 		free_irq(card->irq, card);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
+	switch (cardtype) {
+		case avm_m1: cardname = "M1"; break;
+		case avm_m2: cardname = "M2"; break;
+		default    : cardname = "B1 PCMCIA"; break;
+	}
+
+	printk(KERN_INFO
+		"%s: AVM %s at i/o %#x, irq %d, revision %d\n",
+		driver->name, cardname, card->port, card->irq, card->revision);
 
-	MOD_INC_USE_COUNT;
 	return cinfo->capi_ctrl->cnr;
 }
 
@@ -194,11 +219,12 @@
 
 	if (!cinfo)
 		return "";
-	sprintf(cinfo->infobuf, "%s %s 0x%x %d",
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
 		cinfo->cardname[0] ? cinfo->cardname : "-",
 		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
 		cinfo->card ? cinfo->card->port : 0x0,
-		cinfo->card ? cinfo->card->irq : 0
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->revision : 0
 		);
 	return cinfo->infobuf;
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/c4.c linux/drivers/isdn/avmb1/c4.c
--- v2.3.45/linux/drivers/isdn/avmb1/c4.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/c4.c	Tue Feb 15 11:40:42 2000
@@ -0,0 +1,1326 @@
+/*
+ * $Id: c4.c,v 1.4 2000/02/02 18:36:03 calle Exp $
+ * 
+ * Module for AVM C4 card.
+ * 
+ * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: c4.c,v $
+ * 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
+ *
+ * Revision 1.3  2000/01/25 14:37:39  calle
+ * new message after successfull detection including card revision and
+ * used resources.
+ *
+ * Revision 1.2  2000/01/21 20:52:58  keil
+ * pci_find_subsys as local function for 2.2.X kernel
+ *
+ * Revision 1.1  2000/01/20 10:51:37  calle
+ * Added driver for C4.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "capicmd.h"
+#include "capiutil.h"
+#include "capilli.h"
+#include "avmcard.h"
+
+static char *revision = "$Revision: 1.4 $";
+
+#undef CONFIG_C4_DEBUG
+#undef CONFIG_C4_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_DEC
+#define PCI_VENDOR_ID_DEC	0x1011
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_21285
+#define PCI_DEVICE_ID_DEC_21285	0x1065
+#endif
+
+#ifndef PCI_VENDOR_ID_AVM
+#define PCI_VENDOR_ID_AVM	0x1244
+#endif
+
+#ifndef PCI_DEVICE_ID_AVM_C4
+#define PCI_DEVICE_ID_AVM_C4	0x0800
+#endif
+
+/* ------------------------------------------------------------- */
+
+int suppress_pollack = 0;
+
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+
+MODULE_PARM(suppress_pollack, "0-1i");
+
+/* ------------------------------------------------------------- */
+
+static struct capi_driver_interface *di;
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+#define DC21285_DRAM_A0MR	0x40000000
+#define DC21285_DRAM_A1MR	0x40004000
+#define DC21285_DRAM_A2MR	0x40008000
+#define DC21285_DRAM_A3MR	0x4000C000
+
+#define	CAS_OFFSET	0x88
+
+#define DC21285_ARMCSR_BASE	0x42000000
+
+#define	PCI_OUT_INT_STATUS	0x30
+#define	PCI_OUT_INT_MASK	0x34
+#define	MAILBOX_0		0x50
+#define	MAILBOX_1		0x54
+#define	MAILBOX_2		0x58
+#define	MAILBOX_3		0x5C
+#define	DOORBELL		0x60
+#define	DOORBELL_SETUP		0x64
+
+#define CHAN_1_CONTROL		0x90
+#define CHAN_2_CONTROL		0xB0
+#define DRAM_TIMING		0x10C
+#define DRAM_ADDR_SIZE_0	0x110
+#define DRAM_ADDR_SIZE_1	0x114
+#define DRAM_ADDR_SIZE_2	0x118
+#define DRAM_ADDR_SIZE_3	0x11C
+#define	SA_CONTROL		0x13C
+#define	XBUS_CYCLE		0x148
+#define	XBUS_STROBE		0x14C
+#define	DBELL_PCI_MASK		0x150
+#define DBELL_SA_MASK		0x154
+
+#define SDRAM_SIZE		0x1000000
+
+/* ------------------------------------------------------------- */
+
+#define	MBOX_PEEK_POKE		MAILBOX_0
+
+#define DBELL_ADDR		0x01
+#define DBELL_DATA		0x02
+#define DBELL_RNWR		0x40
+#define DBELL_INIT		0x80
+
+/* ------------------------------------------------------------- */
+
+#define	MBOX_UP_ADDR		MAILBOX_0
+#define	MBOX_UP_LEN		MAILBOX_1
+#define	MBOX_DOWN_ADDR		MAILBOX_2
+#define	MBOX_DOWN_LEN		MAILBOX_3
+
+#define	DBELL_UP_HOST		0x00000100
+#define	DBELL_UP_ARM		0x00000200
+#define	DBELL_DOWN_HOST		0x00000400
+#define	DBELL_DOWN_ARM		0x00000800
+#define	DBELL_RESET_HOST	0x40000000
+#define	DBELL_RESET_ARM		0x80000000
+
+/* ------------------------------------------------------------- */
+
+#define	DRAM_TIMING_DEF		0x001A01A5
+#define DRAM_AD_SZ_DEF0		0x00000045
+#define DRAM_AD_SZ_NULL		0x00000000
+
+#define SA_CTL_ALLRIGHT		0x64AA0271
+
+#define	INIT_XBUS_CYCLE		0x100016DB
+#define	INIT_XBUS_STROBE	0xF1F1F1F1
+
+/* ------------------------------------------------------------- */
+
+#define	RESET_TIMEOUT		(15*HZ)	/* 15 sec */
+#define	PEEK_POKE_TIMEOUT	(HZ/10)	/* 0.1 sec */
+
+/* ------------------------------------------------------------- */
+
+#define c4outmeml(addr, value)	writel(value, addr)
+#define c4inmeml(addr)	readl(addr)
+#define c4outmemw(addr, value)	writew(value, addr)
+#define c4inmemw(addr)	readw(addr)
+#define c4outmemb(addr, value)	writeb(value, addr)
+#define c4inmemb(addr)	readb(addr)
+
+/* ------------------------------------------------------------- */
+
+static inline int wait_for_doorbell(avmcard *card, unsigned long t)
+{
+	unsigned long stop;
+
+	stop = jiffies + t;
+	while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+		if (!time_before(jiffies, stop))
+			return -1;
+	}
+	return 0;
+}
+
+static int c4_poke(avmcard *card,  unsigned long off, unsigned long value)
+{
+
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+	
+	c4outmeml(card->mbase+MBOX_PEEK_POKE, off);
+	c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+
+	c4outmeml(card->mbase+MBOX_PEEK_POKE, value);
+	c4outmeml(card->mbase+DOORBELL, DBELL_DATA | DBELL_ADDR);
+
+	return 0;
+}
+
+static int c4_peek(avmcard *card,  unsigned long off, unsigned long *valuep)
+{
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+
+	c4outmeml(card->mbase+MBOX_PEEK_POKE, off);
+	c4outmeml(card->mbase+DOORBELL, DBELL_RNWR | DBELL_ADDR);
+
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+
+	*valuep = c4inmeml(card->mbase+MBOX_PEEK_POKE);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_load_t4file(avmcard *card, capiloaddatapart * t4file)
+{
+	__u32 val;
+	unsigned char *dp;
+	int left, retval;
+	__u32 loadoff = 0;
+
+	dp = t4file->data;
+	left = t4file->len;
+	while (left >= sizeof(__u32)) {
+	        if (t4file->user) {
+			retval = copy_from_user(&val, dp, sizeof(val));
+			if (retval)
+				return -EFAULT;
+		} else {
+			memcpy(&val, dp, sizeof(val));
+		}
+		if (c4_poke(card, loadoff, val)) {
+			printk(KERN_ERR "%s: corrupted firmware file ?\n",
+					card->name);
+			return -EIO;
+		}
+		left -= sizeof(__u32);
+		dp += sizeof(__u32);
+		loadoff += sizeof(__u32);
+	}
+	if (left) {
+		val = 0;
+		if (t4file->user) {
+			retval = copy_from_user(&val, dp, left);
+			if (retval)
+				return -EFAULT;
+		} else {
+			memcpy(&val, dp, left);
+		}
+		if (c4_poke(card, loadoff, val)) {
+			printk(KERN_ERR "%s: corrupted firmware file ?\n",
+					card->name);
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, __u8 val)
+{
+	__u8 *s = *pp;
+	*s++ = val;
+	*pp = s;
+}
+
+static inline void _put_word(void **pp, __u32 val)
+{
+	__u8 *s = *pp;
+	*s++ = val & 0xff;
+	*s++ = (val >> 8) & 0xff;
+	*s++ = (val >> 16) & 0xff;
+	*s++ = (val >> 24) & 0xff;
+	*pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	_put_word(pp, i);
+	while (i-- > 0)
+		_put_byte(pp, *dp++);
+}
+
+static inline __u8 _get_byte(void **pp)
+{
+	__u8 *s = *pp;
+	__u8 val;
+	val = *s++;
+	*pp = s;
+	return val;
+}
+
+static inline __u32 _get_word(void **pp)
+{
+	__u8 *s = *pp;
+	__u32 val;
+	val = *s++;
+	val |= (*s++ << 8);
+	val |= (*s++ << 16);
+	val |= (*s++ << 24);
+	*pp = s;
+	return val;
+}
+
+static inline __u32 _get_slice(void **pp, unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = _get_word(pp);
+	while (i-- > 0) *dp++ = _get_byte(pp);
+	return len;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_reset(avmcard *card)
+{
+	unsigned long stop;
+
+	c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM);
+
+	stop = jiffies + HZ*10;
+	while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+		if (!time_before(jiffies, stop))
+			return;
+		c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+	}
+
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_detect(avmcard *card)
+{
+	unsigned long stop, dummy;
+
+	c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+	if (c4inmeml(card->mbase+PCI_OUT_INT_MASK) != 0x0c)
+		return	1;
+
+	c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM);
+
+	stop = jiffies + HZ*10;
+	while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+		if (!time_before(jiffies, stop))
+			return 2;
+		c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+	}
+
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+
+	c4outmeml(card->mbase+MAILBOX_0, 0x55aa55aa);
+	if (c4inmeml(card->mbase+MAILBOX_0) != 0x55aa55aa) return 3;
+
+	c4outmeml(card->mbase+MAILBOX_0, 0xaa55aa55);
+	if (c4inmeml(card->mbase+MAILBOX_0) != 0xaa55aa55) return 4;
+
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_SA_MASK, 0)) return 5;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_PCI_MASK, 0)) return 6;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+SA_CONTROL, SA_CTL_ALLRIGHT))
+		return 7;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_CYCLE, INIT_XBUS_CYCLE))
+		return 8;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_STROBE, INIT_XBUS_STROBE))
+		return 8;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, 0)) return 9;
+
+        udelay(1000);
+
+	if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10;
+	if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11;
+	if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12;
+	if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13;
+
+	if (c4_poke(card, DC21285_DRAM_A0MR+CAS_OFFSET, 0)) return 14;
+	if (c4_poke(card, DC21285_DRAM_A1MR+CAS_OFFSET, 0)) return 15;
+	if (c4_poke(card, DC21285_DRAM_A2MR+CAS_OFFSET, 0)) return 16;
+	if (c4_poke(card, DC21285_DRAM_A3MR+CAS_OFFSET, 0)) return 17;
+
+        udelay(1000);
+
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, DRAM_TIMING_DEF))
+		return 18;
+
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_0,DRAM_AD_SZ_DEF0))
+		return 19;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_1,DRAM_AD_SZ_NULL))
+		return 20;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_2,DRAM_AD_SZ_NULL))
+		return 21;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_3,DRAM_AD_SZ_NULL))
+		return 22;
+
+	/* Transputer test */
+	
+	if (   c4_poke(card, 0x000000, 0x11111111)
+	    || c4_poke(card, 0x400000, 0x22222222)
+	    || c4_poke(card, 0x800000, 0x33333333)
+	    || c4_poke(card, 0xC00000, 0x44444444))
+		return 23;
+
+	if (   c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111
+	    || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222
+	    || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333
+	    || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444)
+		return 24;
+
+	if (   c4_poke(card, 0x000000, 0x55555555)
+	    || c4_poke(card, 0x400000, 0x66666666)
+	    || c4_poke(card, 0x800000, 0x77777777)
+	    || c4_poke(card, 0xC00000, 0x88888888))
+		return 25;
+
+	if (   c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555
+	    || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666
+	    || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777
+	    || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888)
+		return 26;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	unsigned long flags;
+	struct sk_buff *skb;
+	__u8 cmd, subcmd;
+	__u16 len;
+	__u32 txlen;
+	void *p;
+	
+	save_flags(flags);
+	cli();
+
+	if (card->csr & DBELL_DOWN_ARM) { /* tx busy */
+	        restore_flags(flags);
+		return;
+	}
+
+	skb = skb_dequeue(&dma->send_queue);
+	if (!skb) {
+#ifdef CONFIG_C4_DEBUG
+		printk(KERN_DEBUG "%s: tx underrun\n", card->name);
+#endif
+	        restore_flags(flags);
+		return;
+	}
+
+	len = CAPIMSG_LEN(skb->data);
+
+	if (len) {
+		cmd = CAPIMSG_COMMAND(skb->data);
+		subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+		p = dma->sendbuf;
+
+		if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+			__u16 dlen = CAPIMSG_DATALEN(skb->data);
+			_put_byte(&p, SEND_DATA_B3_REQ);
+			_put_slice(&p, skb->data, len);
+			_put_slice(&p, skb->data + len, dlen);
+		} else {
+			_put_byte(&p, SEND_MESSAGE);
+			_put_slice(&p, skb->data, len);
+		}
+		txlen = (__u8 *)p - (__u8 *)dma->sendbuf;
+#ifdef CONFIG_C4_DEBUG
+		printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen);
+#endif
+	} else {
+		txlen = skb->len-2;
+#ifdef CONFIG_C4_POLLDEBUG
+		if (skb->data[2] == SEND_POLLACK)
+			printk(KERN_INFO "%s: ack to c4\n", card->name);
+#endif
+#ifdef CONFIG_C4_DEBUG
+		printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
+				card->name, skb->data[2], txlen);
+#endif
+		memcpy(dma->sendbuf, skb->data+2, skb->len-2);
+	}
+	txlen = (txlen + 3) & ~3;
+
+	c4outmeml(card->mbase+MBOX_DOWN_ADDR, virt_to_phys(dma->sendbuf));
+	c4outmeml(card->mbase+MBOX_DOWN_LEN, txlen);
+
+	card->csr |= DBELL_DOWN_ARM;
+
+	c4outmeml(card->mbase+DOORBELL, DBELL_DOWN_ARM);
+
+	restore_flags(flags);
+	dev_kfree_skb(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_POLLACK);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_handle_rx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	struct capi_ctr *ctrl;
+	avmctrl_info *cinfo;
+	struct sk_buff *skb;
+	void *p = dma->recvbuf;
+	__u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+	__u8 b1cmd =  _get_byte(&p);
+	__u32 cidx;
+
+
+#ifdef CONFIG_C4_DEBUG
+	printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name,
+				b1cmd, (unsigned long)dma->recvlen);
+#endif
+	
+	switch (b1cmd) {
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		DataB3Len = _get_slice(&p, card->databuf);
+		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+		if (cidx > 3) cidx = 0;
+		ctrl = card->ctrlinfo[cidx].capi_ctrl;
+
+		if (MsgLen < 30) { /* not CAPI 64Bit */
+			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+			MsgLen = 30;
+			CAPIMSG_SETLEN(card->msgbuf, 30);
+		}
+		if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			ctrl->handle_capimsg(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+		if (cidx > 3) cidx = 0;
+		ctrl = card->ctrlinfo[cidx].capi_ctrl;
+
+		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			ctrl->handle_capimsg(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+		WindowSize = _get_word(&p);
+		cidx = (NCCI&0x7f) - card->cardnr;
+		if (cidx > 3) cidx = 0;
+		ctrl = card->ctrlinfo[cidx].capi_ctrl;
+
+		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+
+		if (NCCI != 0xffffffff) {
+			cidx = (NCCI&0x7f) - card->cardnr;
+			if (cidx > 3) cidx = 0;
+			ctrl = card->ctrlinfo[cidx].capi_ctrl;
+			ctrl->free_ncci(ctrl, ApplId, NCCI);
+		} else {
+			for (cidx=0; cidx < 4; cidx++) {
+				ctrl = card->ctrlinfo[cidx].capi_ctrl;
+				ctrl->appl_released(ctrl, ApplId);
+			}
+		}
+		break;
+
+	case RECEIVE_START:
+#ifdef CONFIG_C4_POLLDEBUG
+		printk(KERN_INFO "%s: poll from c4\n", card->name);
+#endif
+		if (!suppress_pollack)
+			queue_pollack(card);
+		for (cidx=0; cidx < 4; cidx++) {
+			ctrl = card->ctrlinfo[cidx].capi_ctrl;
+			ctrl->resume_output(ctrl);
+		}
+		break;
+
+	case RECEIVE_STOP:
+		for (cidx=0; cidx < 4; cidx++) {
+			ctrl = card->ctrlinfo[cidx].capi_ctrl;
+			ctrl->suspend_output(ctrl);
+		}
+		break;
+
+	case RECEIVE_INIT:
+
+	        cidx = card->nlogcontr++;
+	        cinfo = &card->ctrlinfo[cidx];
+		ctrl = cinfo->capi_ctrl;
+		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+		b1_parse_version(cinfo);
+		printk(KERN_INFO "%s: %s-card (%s) now active\n",
+		       card->name,
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
+		ctrl->ready(cinfo->capi_ctrl);
+		break;
+
+	case RECEIVE_TASK_READY:
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen--] = 0;
+		while (    MsgLen >= 0
+		       && (   card->msgbuf[MsgLen] == '\n'
+			   || card->msgbuf[MsgLen] == '\r'))
+			card->msgbuf[MsgLen--] = 0;
+		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+				card->name, ApplId, card->msgbuf);
+		break;
+
+	case RECEIVE_DEBUGMSG:
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen--] = 0;
+		while (    MsgLen >= 0
+		       && (   card->msgbuf[MsgLen] == '\n'
+			   || card->msgbuf[MsgLen] == '\r'))
+			card->msgbuf[MsgLen--] = 0;
+		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n",
+				card->name, b1cmd);
+		return;
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_handle_interrupt(avmcard *card)
+{
+	__u32 status = c4inmeml(card->mbase+DOORBELL);
+
+	if (status & DBELL_RESET_HOST) {
+		int i;
+		c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+		printk(KERN_ERR "%s: unexpected reset\n", card->name);
+                for (i=0; i < 4; i++) {
+			avmctrl_info *cinfo = &card->ctrlinfo[i];
+			memset(cinfo->version, 0, sizeof(cinfo->version));
+			if (cinfo->capi_ctrl)
+				cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
+		}
+		return;
+	}
+
+	status &= (DBELL_UP_HOST | DBELL_DOWN_HOST);
+	if (!status)
+		return;
+	c4outmeml(card->mbase+DOORBELL, status);
+
+	if ((status & DBELL_UP_HOST) != 0) {
+		card->dma->recvlen = c4inmeml(card->mbase+MBOX_UP_LEN);
+		c4outmeml(card->mbase+MBOX_UP_LEN, 0);
+		c4_handle_rx(card);
+		card->dma->recvlen = 0;
+		c4outmeml(card->mbase+MBOX_UP_LEN, sizeof(card->dma->recvbuf));
+		c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
+	}
+
+	if ((status & DBELL_DOWN_HOST) != 0) {
+		card->csr &= ~DBELL_DOWN_ARM;
+	        c4_dispatch_tx(card);
+	} else if (card->csr & DBELL_DOWN_HOST) {
+		if (c4inmeml(card->mbase+MBOX_DOWN_LEN) == 0) {
+		        card->csr &= ~DBELL_DOWN_ARM;
+			c4_dispatch_tx(card);
+		}
+	}
+}
+
+static void c4_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card;
+
+	card = (avmcard *) devptr;
+
+	if (!card) {
+		printk(KERN_WARNING "%s: interrupt: wrong device\n", card->name);
+		return;
+	}
+	if (card->interrupt) {
+		printk(KERN_ERR "%s: reentering interrupt hander\n",
+				card->name);
+		return;
+	}
+
+	card->interrupt = 1;
+
+	c4_handle_interrupt(card);
+
+	card->interrupt = 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_send_init(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(15, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_INIT);
+	_put_word(&p, AVM_NAPPS);
+	_put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+	_put_word(&p, card->cardnr - 1);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+}
+
+static int c4_send_config(avmcard *card, capiloaddatapart * config)
+{
+	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);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, can't 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, 1);
+	_put_byte(&p, SEND_CONFIG);
+	_put_word(&p, config->len);	/* 12 */
+
+	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);
+				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]);
+		left -= sizeof(val);
+		dp += sizeof(val);
+	}
+	if (left) {
+		memset(val, 0, sizeof(val));
+		if (config->user) {
+			retval = copy_from_user(&val, dp, left);
+			if (retval) {
+				dev_kfree_skb(skb);
+				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]);
+	}
+
+	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_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+	int retval;
+
+	if ((retval = c4_load_t4file(card, &data->firmware))) {
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		c4_reset(card);
+		return retval;
+	}
+
+	save_flags(flags);
+	cli();
+
+	card->csr = 0;
+	c4outmeml(card->mbase+MBOX_UP_LEN, 0);
+	c4outmeml(card->mbase+MBOX_DOWN_LEN, 0);
+	c4outmeml(card->mbase+DOORBELL, DBELL_INIT);
+	udelay(1000);
+	c4outmeml(card->mbase+DOORBELL,
+			DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST);
+
+	c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x08);
+
+	card->dma->recvlen = 0;
+	c4outmeml(card->mbase+MBOX_UP_ADDR, virt_to_phys(card->dma->recvbuf));
+	c4outmeml(card->mbase+MBOX_UP_LEN, sizeof(card->dma->recvbuf));
+	c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
+	restore_flags(flags);
+
+	if (data->configuration.len > 0 && data->configuration.data)
+		c4_send_config(card, &data->configuration);
+
+        c4_send_init(card);
+
+	return 0;
+}
+
+
+void c4_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
+	avmctrl_info *cinfo;
+	int i;
+
+ 	c4_reset(card);
+
+        for (i=0; i < 4; i++) {
+		cinfo = &card->ctrlinfo[i];
+		memset(cinfo->version, 0, sizeof(cinfo->version));
+		if (cinfo->capi_ctrl)
+			cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
+	}
+}
+
+static void c4_remove_ctr(struct capi_ctr *ctrl)
+{
+	avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
+	avmctrl_info *cinfo;
+	int i;
+
+ 	c4_reset(card);
+
+        for (i=0; i <= 4; i++) {
+		cinfo = &card->ctrlinfo[i];
+		if (cinfo->capi_ctrl)
+			di->detach_ctr(cinfo->capi_ctrl);
+	}
+
+	free_irq(card->irq, card);
+	iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+	release_region(card->port, AVMB1_PORTLEN);
+	ctrl->driverdata = 0;
+	kfree(card->ctrlinfo);
+	kfree(card->dma);
+	kfree(card);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------------------------------------------------------------- */
+
+
+void c4_register_appl(struct capi_ctr *ctrl,
+				__u16 appl,
+				capi_register_params *rp)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	int want = rp->level3cnt;
+	int nconn;
+	void *p;
+
+	if (ctrl->cnr == card->cardnr) {
+
+		if (want > 0) nconn = want;
+		else nconn = ctrl->profile.nbchannel * 4 * -want;
+		if (nconn == 0) nconn = ctrl->profile.nbchannel * 4;
+
+		skb = alloc_skb(23, GFP_ATOMIC);
+		if (!skb) {
+			printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+						card->name);
+			return;
+		}
+		p = skb->data;
+		_put_byte(&p, 0);
+		_put_byte(&p, 0);
+		_put_byte(&p, SEND_REGISTER);
+		_put_word(&p, appl);
+		_put_word(&p, 1024 * (nconn+1));
+		_put_word(&p, nconn);
+		_put_word(&p, rp->datablkcnt);
+		_put_word(&p, rp->datablklen);
+		skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+		skb_queue_tail(&card->dma->send_queue, skb);
+		c4_dispatch_tx(card);
+	}
+
+	ctrl->appl_registered(ctrl, appl);
+}
+
+/* ------------------------------------------------------------- */
+
+void c4_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	void *p;
+
+	if (ctrl->cnr == card->cardnr) {
+		skb = alloc_skb(7, GFP_ATOMIC);
+		if (!skb) {
+			printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+						card->name);
+			return;
+		}
+		p = skb->data;
+		_put_byte(&p, 0);
+		_put_byte(&p, 0);
+		_put_byte(&p, SEND_RELEASE);
+		_put_word(&p, appl);
+
+		skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+		skb_queue_tail(&card->dma->send_queue, skb);
+		c4_dispatch_tx(card);
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+
+static void c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *c4_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0
+		);
+	return cinfo->infobuf;
+}
+
+static int c4_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	__u8 flag;
+	int len = 0;
+	char *s;
+
+	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+	len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+	switch (card->cardtype) {
+	case avm_b1isa: s = "B1 ISA"; break;
+	case avm_b1pci: s = "B1 PCI"; break;
+	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+	case avm_m1: s = "M1"; break;
+	case avm_m2: s = "M2"; break;
+	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
+	default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+	if (card->cardtype != avm_m1) {
+        	flag = ((__u8 *)(ctrl->profile.manu))[3];
+        	if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+			"protocol",
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+	}
+	if (card->cardtype != avm_m1) {
+        	flag = ((__u8 *)(ctrl->profile.manu))[5];
+		if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s\n",
+			"linetype",
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+	}
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_add_card(struct capi_driver *driver, struct capicardparams *p)
+{
+	unsigned long base, page_offset;
+	avmctrl_info *cinfo;
+	avmcard *card;
+	int retval;
+	int i;
+
+	MOD_INC_USE_COUNT;
+
+	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
+
+	if (!card) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+	        MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
+	memset(card, 0, sizeof(avmcard));
+	card->dma = (avmcard_dmainfo *) kmalloc(sizeof(avmcard_dmainfo), GFP_ATOMIC);
+	if (!card->dma) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
+	memset(card->dma, 0, sizeof(avmcard_dmainfo));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info)*4, GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info)*4);
+	card->ctrlinfo = cinfo;
+	for (i=0; i < 4; i++) {
+		cinfo = &card->ctrlinfo[i];
+		cinfo->card = card;
+	}
+	sprintf(card->name, "c4-%x", p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->membase = p->membase;
+	card->cardtype = avm_c4;
+
+	if (check_region(card->port, AVMB1_PORTLEN)) {
+		printk(KERN_WARNING
+		       "%s: ports 0x%03x-0x%03x in use.\n",
+		       driver->name, card->port, card->port + AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -EBUSY;
+	}
+
+	base = card->membase & PAGE_MASK;
+	page_offset = card->membase - base;
+	card->mbase = ioremap_nocache(base, page_offset + 128);
+	if (card->mbase) {
+		card->mbase += page_offset;
+	} else {
+		printk(KERN_NOTICE "%s: can't remap memory at 0x%lx\n",
+					driver->name, card->membase);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	if ((retval = c4_detect(card)) != 0) {
+		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	c4_reset(card);
+
+	request_region(p->port, AVMB1_PORTLEN, card->name);
+
+	retval = request_irq(card->irq, c4_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+				driver->name, card->irq);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -EBUSY;
+	}
+
+	for (i=0; i < 4; i++) {
+		cinfo = &card->ctrlinfo[i];
+		cinfo->card = card;
+		cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
+		if (!cinfo->capi_ctrl) {
+			printk(KERN_ERR "%s: attach controller failed (%d).\n",
+					driver->name, i);
+			for (i--; i >= 0; i--) {
+				cinfo = &card->ctrlinfo[i];
+				di->detach_ctr(cinfo->capi_ctrl);
+			}
+                	iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+			free_irq(card->irq, card);
+			release_region(card->port, AVMB1_PORTLEN);
+	        	kfree(card->dma);
+	        	kfree(card->ctrlinfo);
+			kfree(card);
+	        	MOD_DEC_USE_COUNT;
+			return -EBUSY;
+		}
+		if (i == 0)
+			card->cardnr = cinfo->capi_ctrl->cnr;
+	}
+
+	skb_queue_head_init(&card->dma->send_queue);
+
+	printk(KERN_INFO
+		"%s: AVM C4 at i/o %#x, irq %d, mem %#lx\n",
+		driver->name, card->port, card->irq, card->membase);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static struct capi_driver c4_driver = {
+    "c4",
+    "0.0",
+    c4_load_firmware,
+    c4_reset_ctr,
+    c4_remove_ctr,
+    c4_register_appl,
+    c4_release_appl,
+    c4_send_message,
+
+    c4_procinfo,
+    c4_read_proc,
+    0,	/* use standard driver_read_proc */
+
+    0, /* no add_card function */
+};
+
+#ifdef MODULE
+#define c4_init init_module
+void cleanup_module(void);
+#endif
+
+
+static int ncards = 0;
+
+int c4_init(void)
+{
+	struct capi_driver *driver = &c4_driver;
+	struct pci_dev *dev = NULL;
+	char *p;
+	int retval;
+
+	if ((p = strchr(revision, ':'))) {
+		strncpy(driver->revision, p + 1, sizeof(driver->revision));
+		p = strchr(driver->revision, '$');
+		*p = 0;
+	}
+
+	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
+
+        di = attach_capi_driver(driver);
+
+	if (!di) {
+		printk(KERN_ERR "%s: failed to attach capi_driver\n",
+				driver->name);
+		return -EIO;
+	}
+
+#ifdef CONFIG_PCI
+	if (!pci_present()) {
+		printk(KERN_ERR "%s: no PCI bus present\n", driver->name);
+    		detach_capi_driver(driver);
+		return -EIO;
+	}
+
+	while ((dev = pci_find_subsys(
+			PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285,
+			PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, dev))) {
+		struct capicardparams param;
+
+		param.port = dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK;
+		param.irq = dev->irq;
+		param.membase = dev->resource[ 0].start & PCI_BASE_ADDRESS_MEM_MASK;
+
+		printk(KERN_INFO
+			"%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n",
+			driver->name, param.port, param.irq, param.membase);
+		retval = c4_add_card(driver, &param);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: no AVM-C4 at i/o %#x, irq %d detected, mem %#x\n",
+			driver->name, param.port, param.irq, param.membase);
+#ifdef MODULE
+			cleanup_module();
+#endif
+			return retval;
+		}
+		ncards++;
+	}
+	if (ncards) {
+		printk(KERN_INFO "%s: %d C4 card(s) detected\n",
+				driver->name, ncards);
+		return 0;
+	}
+	printk(KERN_ERR "%s: NO C4 card detected\n", driver->name);
+	return -ESRCH;
+#else
+	printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name);
+	return -EIO;
+#endif
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+    detach_capi_driver(&c4_driver);
+}
+#endif
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.3.45/linux/drivers/isdn/avmb1/capi.c	Thu Feb 10 17:11:09 2000
+++ linux/drivers/isdn/avmb1/capi.c	Wed Feb 16 15:42:05 2000
@@ -128,6 +128,7 @@
 #include <linux/poll.h>
 #include <linux/capi.h>
 #include <linux/kernelcapi.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include "capiutil.h"
 #include "capicmd.h"
@@ -511,13 +512,18 @@
 
 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,
+	capi_llseek,
+	capi_read,
+	capi_write,
+	NULL,			/* capi_readdir */
+	capi_poll,
+	capi_ioctl,
+	NULL,			/* capi_mmap */
+	capi_open,
+        NULL,                   /* capi_flush */
+	capi_release,
+	NULL,			/* capi_fsync */
+	NULL,			/* capi_fasync */
 };
 
 /* -------- /proc functions ----------------------------------- */
@@ -616,14 +622,36 @@
 		init_waitqueue_head(&capidevs[j].recv_wait);
 	}
 
-	if (register_chrdev(capi_major, "capi20", &capi_fops)) {
+	if (devfs_register_chrdev(capi_major, "capi20", &capi_fops)) {
 		printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
 		return -EIO;
 	}
+	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) {
-		unregister_chrdev(capi_major, "capi20");
+		devfs_unregister_chrdev(capi_major, "capi20");
+		devfs_unregister(devfs_find_handle(NULL, "capi20", 0,
+						   capi_major, 0,
+						   DEVFS_SPECIAL_CHR, 0));
+		for (j = 0; j < 10; j++) {
+			char devname[32];
+
+			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));
+		}
 		return -EIO;
 	}
 	(void)proc_init();
@@ -633,8 +661,18 @@
 #ifdef MODULE
 void cleanup_module(void)
 {
+	int i;
+	char devname[32];
+
 	(void)proc_exit();
-	unregister_chrdev(capi_major, "capi20");
+	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));
+	}
 	(void) detach_capi_interface(&cuser);
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c
--- v2.3.45/linux/drivers/isdn/avmb1/capidrv.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/capidrv.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,14 @@
 /*
- * $Id: capidrv.c,v 1.28 1999/11/05 16:22:37 calle Exp $
+ * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.29  1999/12/06 17:13:06  calle
+ * Added controller watchdog.
+ *
  * Revision 1.28  1999/11/05 16:22:37  calle
  * Bugfix: Missing break in switch on ISDN_CMD_HANGUP.
  *
@@ -168,7 +171,7 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.28 $";
+static char *revision = "$Revision: 1.29 $";
 int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
@@ -196,6 +199,7 @@
 	int state;
 	__u32 cipmask;
 	__u32 cipmask2;
+        struct timer_list listentimer;
 
 	/*
 	 * ID of capi message sent
@@ -2188,6 +2192,29 @@
 	send_message(card, &cmdcmsg);
 }
 
+static void send_listen(capidrv_contr *card)
+{
+	capi_fill_LISTEN_REQ(&cmdcmsg, global.appid,
+			     card->msgid++,
+			     card->contrnr, /* controller */
+			     1 << 6,	/* Infomask */
+			     card->cipmask,
+			     card->cipmask2,
+			     0, 0);
+	send_message(card, &cmdcmsg);
+	listen_change_state(card, EV_LISTEN_REQ);
+}
+
+static void listentimerfunc(unsigned long x)
+{
+	capidrv_contr *card = (capidrv_contr *)x;
+	if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE)
+		printk(KERN_ERR "%s: controller dead ??\n", card->name);
+        send_listen(card);
+	mod_timer(&card->listentimer, jiffies + 60*HZ);
+}
+
+
 static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
 {
 	capidrv_contr *card;
@@ -2202,6 +2229,7 @@
 		return -1;
 	}
 	memset(card, 0, sizeof(capidrv_contr));
+	init_timer(&card->listentimer);
 	strcpy(card->name, id);
 	card->contrnr = contr;
 	card->nbchan = profp->nbchannel;
@@ -2258,15 +2286,11 @@
 	card->cipmask = 0x1FFF03FF;	/* any */
 	card->cipmask2 = 0;
 
-	capi_fill_LISTEN_REQ(&cmdcmsg, global.appid,
-			     card->msgid++,
-			     contr,	/* controller */
-			     1 << 6,	/* Infomask */
-			     card->cipmask,
-			     card->cipmask2,
-			     0, 0);
-	send_message(card, &cmdcmsg);
-	listen_change_state(card, EV_LISTEN_REQ);
+	send_listen(card);
+
+	card->listentimer.data = (unsigned long)card;
+	card->listentimer.function = listentimerfunc;
+	mod_timer(&card->listentimer, jiffies + 60*HZ);
 
 	printk(KERN_INFO "%s: now up (%d B channels)\n",
 		card->name, card->nbchan);
@@ -2312,6 +2336,7 @@
 			printk(KERN_ERR "capidrv: bug in free_plci()\n");
 	}
 	kfree(card->bchans);
+	del_timer(&card->listentimer);
 
 	printk(KERN_INFO "%s: now down.\n", card->name);
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/kcapi.c linux/drivers/isdn/avmb1/kcapi.c
--- v2.3.45/linux/drivers/isdn/avmb1/kcapi.c	Tue Dec  7 09:32:43 1999
+++ linux/drivers/isdn/avmb1/kcapi.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,18 @@
 /*
- * $Id: kcapi.c,v 1.10 1999/10/26 15:30:32 calle Exp $
+ * $Id: kcapi.c,v 1.12 2000/01/28 16:45:39 calle Exp $
  * 
  * Kernel CAPI 2.0 Module
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: kcapi.c,v $
+ * 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.
+ *
+ * Revision 1.11  1999/11/23 13:29:29  calle
+ * Bugfix: incoming capi message were never traced.
+ *
  * Revision 1.10  1999/10/26 15:30:32  calle
  * Generate error message if user want to add card, but driver module is
  * not loaded.
@@ -79,7 +86,7 @@
 #include <linux/b1lli.h>
 #endif
 
-static char *revision = "$Revision: 1.10 $";
+static char *revision = "$Revision: 1.12 $";
 
 /* ------------------------------------------------------------- */
 
@@ -154,10 +161,6 @@
 static struct sk_buff_head recv_queue;
 static struct capi_interface_user *capi_users = 0;
 static struct capi_driver *drivers;
-#ifdef CONFIG_AVMB1_COMPAT
-static struct capi_driver *b1isa_driver;
-static struct capi_driver *t1isa_driver;
-#endif
 static long notify_up_set = 0;
 static long notify_down_set = 0;
 
@@ -703,9 +706,9 @@
 	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
 		card->nrecvdatapkt++;
 	        if (card->traceflag > 2) showctl |= 2;
-	        if (card->traceflag) showctl |= 2;
 	} else {
 		card->nrecvctlpkt++;
+	        if (card->traceflag) showctl |= 2;
 	}
 	showctl |= (card->traceflag & 1);
 	if (showctl & 2) {
@@ -877,8 +880,14 @@
 	*pp = card;
 	driver->ncontroller++;
 	sprintf(card->procfn, "capi/controllers/%d", card->cnr);
-	card->procent = create_proc_read_entry(card->procfn, 0, 0,
-					driver->ctr_read_proc, card);
+	card->procent = create_proc_entry(card->procfn, 0, 0);
+	if (card->procent) {
+	   card->procent->read_proc = 
+		(int (*)(char *,char **,off_t,int,int *,void *))
+			driver->ctr_read_proc;
+	   card->procent->data = card;
+	}
+
 	ncards++;
 	printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n",
 			card->cnr, card->name);
@@ -947,18 +956,18 @@
 	driver->next = 0;
 	*pp = driver;
 	printk(KERN_NOTICE "kcapi: driver %s attached\n", driver->name);
-#ifdef CONFIG_AVMB1_COMPAT
-        if (strcmp(driver->name, "b1isa") == 0 && driver->add_card)
-		b1isa_driver = driver;
-        if (strcmp(driver->name, "t1isa") == 0 && driver->add_card)
-		t1isa_driver = driver;
-#endif
 	sprintf(driver->procfn, "capi/drivers/%s", driver->name);
-	driver->procent = create_proc_read_entry(driver->procfn, 0, 0,
-						driver->driver_read_proc
-						? driver->driver_read_proc
-						: driver_read_proc,
-					        driver);
+	driver->procent = create_proc_entry(driver->procfn, 0, 0);
+	if (driver->procent) {
+	   if (driver->driver_read_proc) {
+		   driver->procent->read_proc = 
+	       		(int (*)(char *,char **,off_t,int,int *,void *))
+					driver->driver_read_proc;
+	   } else {
+		   driver->procent->read_proc = driver_read_proc;
+	   }
+	   driver->procent->data = driver;
+	}
 	return &di;
 }
 
@@ -968,10 +977,6 @@
 	for (pp = &drivers; *pp && *pp != driver; pp = &(*pp)->next) ;
 	if (*pp) {
 		*pp = (*pp)->next;
-#ifdef CONFIG_AVMB1_COMPAT
-		if (driver == b1isa_driver) b1isa_driver = 0;
-		if (driver == t1isa_driver) t1isa_driver = 0;
-#endif
 		printk(KERN_NOTICE "kcapi: driver %s detached\n", driver->name);
 	} else {
 		printk(KERN_ERR "kcapi: driver %s double detach ?\n", driver->name);
@@ -1186,6 +1191,15 @@
 	return CAPI_NOERROR;
 }
 
+static struct capi_driver *find_driver(char *name)
+{
+	struct capi_driver *dp;
+	for (dp = drivers; dp; dp = dp->next)
+		if (strcmp(dp->name, name) == 0)
+			return dp;
+	return 0;
+}
+
 #ifdef CONFIG_AVMB1_COMPAT
 static int old_capi_manufacturer(unsigned int cmd, void *data)
 {
@@ -1217,9 +1231,15 @@
 		cparams.cardnr = cdef.cardnr;
 
                 switch (cdef.cardtype) {
-			case AVM_CARDTYPE_B1: driver = b1isa_driver; break;
-			case AVM_CARDTYPE_T1: driver = t1isa_driver; break;
-			default: driver = 0;
+			case AVM_CARDTYPE_B1:
+				driver = find_driver("b1isa");
+				break;
+			case AVM_CARDTYPE_T1:
+				driver = find_driver("t1isa");
+				break;
+			default:
+				driver = 0;
+				break;
 		}
 		if (!driver) {
 			printk(KERN_ERR "kcapi: driver not loaded.\n");
@@ -1331,9 +1351,7 @@
 			return -ESRCH;
 
 		gdef.cardstate = card->cardstate;
-		if (card->driver == b1isa_driver)
-			gdef.cardtype = AVM_CARDTYPE_B1;
-		else if (card->driver == t1isa_driver)
+		if (card->driver == find_driver("t1isa"))
 			gdef.cardtype = AVM_CARDTYPE_T1;
 		else gdef.cardtype = AVM_CARDTYPE_B1;
 
@@ -1377,7 +1395,6 @@
 static int capi_manufacturer(unsigned int cmd, void *data)
 {
         struct capi_ctr *card;
-	kcapi_flagdef fdef;
 	int retval;
 
 	switch (cmd) {
@@ -1392,6 +1409,9 @@
 		return old_capi_manufacturer(cmd, data);
 #endif
 	case KCAPI_CMD_TRACE:
+	{
+		kcapi_flagdef fdef;
+
 		if ((retval = copy_from_user((void *) &fdef, data,
 					 sizeof(kcapi_flagdef))))
 			return retval;
@@ -1405,6 +1425,44 @@
 		printk(KERN_INFO "kcapi: contr %d set trace=%d\n",
 			card->cnr, card->traceflag);
 		return 0;
+	}
+
+	case KCAPI_CMD_ADDCARD:
+	{
+		struct capi_driver *driver;
+		capicardparams cparams;
+		kcapi_carddef cdef;
+
+		if ((retval = copy_from_user((void *) &cdef, data,
+							sizeof(cdef))))
+			return retval;
+
+		cparams.port = cdef.port;
+		cparams.irq = cdef.irq;
+		cparams.membase = cdef.membase;
+		cparams.cardnr = cdef.cardnr;
+		cparams.cardtype = 0;
+		cdef.driver[sizeof(cdef.driver)-1] = 0;
+
+		if ((driver = find_driver(cdef.driver)) == 0) {
+			printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n",
+					cdef.driver);
+			return -ESRCH;
+		}
+
+		if (!driver->add_card) {
+			printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver);
+			return -EIO;
+		}
+
+		return driver->add_card(driver, &cparams);
+	}
+
+	default:
+		printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n",
+					cmd);
+		break;
+
 	}
 	return -EINVAL;
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/t1isa.c linux/drivers/isdn/avmb1/t1isa.c
--- v2.3.45/linux/drivers/isdn/avmb1/t1isa.c	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/avmb1/t1isa.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,19 @@
 /*
- * $Id: t1isa.c,v 1.8 1999/11/05 16:38:01 calle Exp $
+ * $Id: t1isa.c,v 1.10 2000/02/02 18:36:04 calle Exp $
  * 
  * Module for AVM T1 HEMA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: t1isa.c,v $
+ * Revision 1.10  2000/02/02 18:36:04  calle
+ * - Modules are now locked while init_module is running
+ * - fixed problem with memory mapping if address is not aligned
+ *
+ * Revision 1.9  2000/01/25 14:37:39  calle
+ * new message after successfull detection including card revision and
+ * used resources.
+ *
  * Revision 1.8  1999/11/05 16:38:01  calle
  * Cleanups before kernel 2.4:
  * - Changed all messages to use card->name or driver->name instead of
@@ -73,7 +81,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.8 $";
+static char *revision = "$Revision: 1.10 $";
 
 /* ------------------------------------------------------------- */
 
@@ -413,10 +421,13 @@
 	avmcard *card;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
@@ -424,6 +435,7 @@
 	if (!cinfo) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(cinfo, 0, sizeof(avmctrl_info));
@@ -440,6 +452,7 @@
 				driver->name, card->port);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EINVAL;
         }
 
@@ -449,6 +462,7 @@
 		       driver->name, card->port, card->port + AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 	if (hema_irq_table[card->irq & 0xf] == 0) {
@@ -456,6 +470,7 @@
 				driver->name, card->irq);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EINVAL;
 	}
 	for (ctrl = driver->controller; ctrl; ctrl = ctrl->next) {
@@ -465,6 +480,7 @@
 					driver->name, card->cardnr, cardp->port);
 	                kfree(card->ctrlinfo);
 			kfree(card);
+	        	MOD_DEC_USE_COUNT;
 			return -EBUSY;
 		}
 	}
@@ -473,6 +489,7 @@
 					driver->name, card->port, retval);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 	t1_disable_irq(card->port);
@@ -487,6 +504,7 @@
 		release_region(card->port, AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+		MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
@@ -498,10 +516,14 @@
 		release_region(card->port, AVMB1_PORTLEN);
 	        kfree(card->ctrlinfo);
 		kfree(card);
+		MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
-	MOD_INC_USE_COUNT;
+	printk(KERN_INFO
+		"%s: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
+		driver->name, card->port, card->irq, card->cardnr);
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/avmb1/t1pci.c linux/drivers/isdn/avmb1/t1pci.c
--- v2.3.45/linux/drivers/isdn/avmb1/t1pci.c	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/avmb1/t1pci.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,20 @@
 /*
- * $Id: t1pci.c,v 1.3 1999/11/13 21:27:16 keil Exp $
+ * $Id: t1pci.c,v 1.5 2000/02/02 18:36:04 calle Exp $
  * 
  * Module for AVM T1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: t1pci.c,v $
+ * Revision 1.5  2000/02/02 18:36:04  calle
+ * - Modules are now locked while init_module is running
+ * - fixed problem with memory mapping if address is not aligned
+ *
+ * Revision 1.4  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
+ *   - support for revision register
+ *
  * Revision 1.3  1999/11/13 21:27:16  keil
  * remove KERNELVERSION
  *
@@ -38,7 +47,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.3 $";
+static char *revision = "$Revision: 1.5 $";
 
 #undef CONFIG_T1PCI_DEBUG
 #undef CONFIG_T1PCI_POLLDEBUG
@@ -55,712 +64,20 @@
 
 /* ------------------------------------------------------------- */
 
-int suppress_pollack = 0;
-
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 
-MODULE_PARM(suppress_pollack, "0-1i");
-
-
 /* ------------------------------------------------------------- */
 
 static struct capi_driver_interface *di;
 
 /* ------------------------------------------------------------- */
 
-static void t1pci_dispatch_tx(avmcard *card);
-
-/* ------------------------------------------------------------- */
-
-/* S5933 */
-
-#define	AMCC_RXPTR	0x24
-#define	AMCC_RXLEN	0x28
-#define	AMCC_TXPTR	0x2c
-#define	AMCC_TXLEN	0x30
-
-#define	AMCC_INTCSR	0x38
-#	define EN_READ_TC_INT		0x00008000L
-#	define EN_WRITE_TC_INT		0x00004000L
-#	define EN_TX_TC_INT		EN_READ_TC_INT
-#	define EN_RX_TC_INT		EN_WRITE_TC_INT
-#	define AVM_FLAG			0x30000000L
-
-#	define ANY_S5933_INT		0x00800000L
-#	define	READ_TC_INT		0x00080000L
-#	define WRITE_TC_INT		0x00040000L
-#	define	TX_TC_INT		READ_TC_INT
-#	define	RX_TC_INT		WRITE_TC_INT
-#	define MASTER_ABORT_INT		0x00100000L
-#	define TARGET_ABORT_INT		0x00200000L
-#	define BUS_MASTER_INT		0x00200000L
-#	define ALL_INT			0x000C0000L
-
-#define	AMCC_MCSR	0x3c
-#	define A2P_HI_PRIORITY		0x00000100L
-#	define EN_A2P_TRANSFERS		0x00000400L
-#	define P2A_HI_PRIORITY		0x00001000L
-#	define EN_P2A_TRANSFERS		0x00004000L
-#	define RESET_A2P_FLAGS		0x04000000L
-#	define RESET_P2A_FLAGS		0x02000000L
-
-/* ------------------------------------------------------------- */
-
-#define t1outmeml(addr, value)	writel(value, addr)
-#define t1inmeml(addr)	readl(addr)
-#define t1outmemw(addr, value)	writew(value, addr)
-#define t1inmemw(addr)	readw(addr)
-#define t1outmemb(addr, value)	writeb(value, addr)
-#define t1inmemb(addr)	readb(addr)
-
-/* ------------------------------------------------------------- */
-
-static inline int t1pci_tx_empty(unsigned int port)
-{
-	return inb(port + 0x03) & 0x1;
-}
-
-static inline int t1pci_rx_full(unsigned int port)
-{
-	return inb(port + 0x02) & 0x1;
-}
-
-static int t1pci_tolink(avmcard *card, void *buf, unsigned int len)
-{
-	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
-	unsigned char *s = (unsigned char *)buf;
-	while (len--) {
-		while (   !t1pci_tx_empty(card->port)
-		       && time_before(jiffies, stop));
-		if (!t1pci_tx_empty(card->port)) 
-			return -1;
-	        t1outp(card->port, 0x01, *s++);
-	}
-	return 0;
-}
-
-static int t1pci_fromlink(avmcard *card, void *buf, unsigned int len)
-{
-	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
-	unsigned char *s = (unsigned char *)buf;
-	while (len--) {
-		while (   !t1pci_rx_full(card->port)
-		       && time_before(jiffies, stop));
-		if (!t1pci_rx_full(card->port)) 
-			return -1;
-	        *s++ = t1inp(card->port, 0x00);
-	}
-	return 0;
-}
-
-static int WriteReg(avmcard *card, __u32 reg, __u8 val)
-{
-	__u8 cmd = 0x00;
-	if (   t1pci_tolink(card, &cmd, 1) == 0
-	    && t1pci_tolink(card, &reg, 4) == 0) {
-		__u32 tmp = val;
-		return t1pci_tolink(card, &tmp, 4);
-	}
-	return -1;
-}
-
-static __u8 ReadReg(avmcard *card, __u32 reg)
-{
-	__u8 cmd = 0x01;
-	if (   t1pci_tolink(card, &cmd, 1) == 0
-	    && t1pci_tolink(card, &reg, 4) == 0) {
-		__u32 tmp;
-		if (t1pci_fromlink(card, &tmp, 4) == 0)
-			return (__u8)tmp;
-	}
-	return 0xff;
-}
-
-/* ------------------------------------------------------------- */
-
-static inline void _put_byte(void **pp, __u8 val)
-{
-	__u8 *s = *pp;
-	*s++ = val;
-	*pp = s;
-}
-
-static inline void _put_word(void **pp, __u32 val)
-{
-	__u8 *s = *pp;
-	*s++ = val & 0xff;
-	*s++ = (val >> 8) & 0xff;
-	*s++ = (val >> 16) & 0xff;
-	*s++ = (val >> 24) & 0xff;
-	*pp = s;
-}
-
-static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
-{
-	unsigned i = len;
-	_put_word(pp, i);
-	while (i-- > 0)
-		_put_byte(pp, *dp++);
-}
-
-static inline __u8 _get_byte(void **pp)
-{
-	__u8 *s = *pp;
-	__u8 val;
-	val = *s++;
-	*pp = s;
-	return val;
-}
-
-static inline __u32 _get_word(void **pp)
-{
-	__u8 *s = *pp;
-	__u32 val;
-	val = *s++;
-	val |= (*s++ << 8);
-	val |= (*s++ << 16);
-	val |= (*s++ << 24);
-	*pp = s;
-	return val;
-}
-
-static inline __u32 _get_slice(void **pp, unsigned char *dp)
-{
-	unsigned int len, i;
-
-	len = i = _get_word(pp);
-	while (i-- > 0) *dp++ = _get_byte(pp);
-	return len;
-}
-
-/* ------------------------------------------------------------- */
-
-static void t1pci_reset(avmcard *card)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	card->csr = 0x0;
-	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
-	t1outmeml(card->mbase+AMCC_MCSR, 0);
-	t1outmeml(card->mbase+AMCC_RXLEN, 0);
-	t1outmeml(card->mbase+AMCC_TXLEN, 0);
-
-	t1outp(card->port, T1_RESETLINK, 0x00);
-	t1outp(card->port, 0x07, 0x00);
-
-	restore_flags(flags);
-
-	t1outmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(10 * 1000);
-	t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
-	udelay(10 * 1000);
-	t1outmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(42 * 1000);
-
-}
-
-/* ------------------------------------------------------------- */
-
-static int t1pci_detect(avmcard *card)
-{
-	t1outmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(10 * 1000);
-	t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
-	udelay(10 * 1000);
-	t1outmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(42 * 1000);
-
-	t1outmeml(card->mbase+AMCC_RXLEN, 0);
-	t1outmeml(card->mbase+AMCC_TXLEN, 0);
-	card->csr = 0x0;
-	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
-
-	if (t1inmeml(card->mbase+AMCC_MCSR) != 0x000000E6)
-		return 1;
-
-	t1outmeml(card->mbase+AMCC_RXPTR, 0xffffffff);
-	t1outmeml(card->mbase+AMCC_TXPTR, 0xffffffff);
-	if (   t1inmeml(card->mbase+AMCC_RXPTR) != 0xfffffffc
-	    || t1inmeml(card->mbase+AMCC_TXPTR) != 0xfffffffc)
-		return 2;
-
-	t1outmeml(card->mbase+AMCC_RXPTR, 0x0);
-	t1outmeml(card->mbase+AMCC_TXPTR, 0x0);
-	if (   t1inmeml(card->mbase+AMCC_RXPTR) != 0x0
-	    || t1inmeml(card->mbase+AMCC_TXPTR) != 0x0)
-		return 3;
-
-	t1outp(card->port, T1_RESETLINK, 0x00);
-	t1outp(card->port, 0x07, 0x00);
-	
-	t1outp(card->port, 0x02, 0x02);
-	t1outp(card->port, 0x03, 0x02);
-
-	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x02
-	    || t1inp(card->port, 0x3) != 0x03)
-		return 4;
-
-	t1outp(card->port, 0x02, 0x00);
-	t1outp(card->port, 0x03, 0x00);
-
-	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x00
-	    || t1inp(card->port, 0x3) != 0x01)
-		return 5;
-
-	/* Transputer test */
-	
-	if (   WriteReg(card, 0x80001000, 0x11) != 0
-	    || WriteReg(card, 0x80101000, 0x22) != 0
-	    || WriteReg(card, 0x80201000, 0x33) != 0
-	    || WriteReg(card, 0x80301000, 0x44) != 0)
-		return 6;
-
-	if (   ReadReg(card, 0x80001000) != 0x11
-	    || ReadReg(card, 0x80101000) != 0x22
-	    || ReadReg(card, 0x80201000) != 0x33
-	    || ReadReg(card, 0x80301000) != 0x44)
-		return 7;
-
-	if (   WriteReg(card, 0x80001000, 0x55) != 0
-	    || WriteReg(card, 0x80101000, 0x66) != 0
-	    || WriteReg(card, 0x80201000, 0x77) != 0
-	    || WriteReg(card, 0x80301000, 0x88) != 0)
-		return 8;
-
-	if (   ReadReg(card, 0x80001000) != 0x55
-	    || ReadReg(card, 0x80101000) != 0x66
-	    || ReadReg(card, 0x80201000) != 0x77
-	    || ReadReg(card, 0x80301000) != 0x88)
-		return 9;
-
-	return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static void t1pci_dispatch_tx(avmcard *card)
-{
-	avmcard_dmainfo *dma = card->dma;
-	unsigned long flags;
-	struct sk_buff *skb;
-	__u8 cmd, subcmd;
-	__u16 len;
-	__u32 txlen;
-	int inint;
-	void *p;
-	
-	save_flags(flags);
-	cli();
-
-	inint = card->interrupt;
-
-	if (card->csr & EN_TX_TC_INT) { /* tx busy */
-	        restore_flags(flags);
-		return;
-	}
-
-	skb = skb_dequeue(&dma->send_queue);
-	if (!skb) {
-#ifdef CONFIG_T1PCI_DEBUG
-		printk(KERN_DEBUG "tx(%d): underrun\n", inint);
-#endif
-	        restore_flags(flags);
-		return;
-	}
-
-	len = CAPIMSG_LEN(skb->data);
-
-	if (len) {
-		cmd = CAPIMSG_COMMAND(skb->data);
-		subcmd = CAPIMSG_SUBCOMMAND(skb->data);
-
-		p = dma->sendbuf;
-
-		if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-			__u16 dlen = CAPIMSG_DATALEN(skb->data);
-			_put_byte(&p, SEND_DATA_B3_REQ);
-			_put_slice(&p, skb->data, len);
-			_put_slice(&p, skb->data + len, dlen);
-		} else {
-			_put_byte(&p, SEND_MESSAGE);
-			_put_slice(&p, skb->data, len);
-		}
-		txlen = (__u8 *)p - (__u8 *)dma->sendbuf;
-#ifdef CONFIG_T1PCI_DEBUG
-		printk(KERN_DEBUG "tx(%d): put msg len=%d\n",
-				inint, txlen);
-#endif
-	} else {
-		txlen = skb->len-2;
-#ifdef CONFIG_T1PCI_POLLDEBUG
-		if (skb->data[2] == SEND_POLLACK)
-			printk(KERN_INFO "%s: ack to t1\n", card->name);
-#endif
-#ifdef CONFIG_T1PCI_DEBUG
-		printk(KERN_DEBUG "tx(%d): put 0x%x len=%d\n",
-				inint, skb->data[2], txlen);
-#endif
-		memcpy(dma->sendbuf, skb->data+2, skb->len-2);
-	}
-	txlen = (txlen + 3) & ~3;
-
-	t1outmeml(card->mbase+AMCC_TXPTR, virt_to_phys(dma->sendbuf));
-	t1outmeml(card->mbase+AMCC_TXLEN, txlen);
-
-	card->csr |= EN_TX_TC_INT;
-
-	if (!inint)
-		t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
-
-	restore_flags(flags);
-	dev_kfree_skb(skb);
-}
-
-/* ------------------------------------------------------------- */
-
-static void queue_pollack(avmcard *card)
-{
-	struct sk_buff *skb;
-	void *p;
-
-	skb = alloc_skb(3, GFP_ATOMIC);
-	if (!skb) {
-		printk(KERN_CRIT "%s: no memory, lost poll ack\n",
-					card->name);
-		return;
-	}
-	p = skb->data;
-	_put_byte(&p, 0);
-	_put_byte(&p, 0);
-	_put_byte(&p, SEND_POLLACK);
-	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
-
-	skb_queue_tail(&card->dma->send_queue, skb);
-	t1pci_dispatch_tx(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static void t1pci_handle_rx(avmcard *card)
-{
-	avmctrl_info *cinfo = &card->ctrlinfo[0];
-	avmcard_dmainfo *dma = card->dma;
-	struct capi_ctr *ctrl = cinfo->capi_ctrl;
-	struct sk_buff *skb;
-	void *p = dma->recvbuf+4;
-	__u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
-	__u8 b1cmd =  _get_byte(&p);
-
-#ifdef CONFIG_T1PCI_DEBUG
-	printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
-#endif
-	
-	switch (b1cmd) {
-	case RECEIVE_DATA_B3_IND:
-
-		ApplId = (unsigned) _get_word(&p);
-		MsgLen = _get_slice(&p, card->msgbuf);
-		DataB3Len = _get_slice(&p, card->databuf);
-
-		if (MsgLen < 30) { /* not CAPI 64Bit */
-			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
-			MsgLen = 30;
-			CAPIMSG_SETLEN(card->msgbuf, 30);
-		}
-		if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
-			printk(KERN_ERR "%s: incoming packet dropped\n",
-					card->name);
-		} else {
-			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
-			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
-			ctrl->handle_capimsg(ctrl, ApplId, skb);
-		}
-		break;
-
-	case RECEIVE_MESSAGE:
-
-		ApplId = (unsigned) _get_word(&p);
-		MsgLen = _get_slice(&p, card->msgbuf);
-		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
-			printk(KERN_ERR "%s: incoming packet dropped\n",
-					card->name);
-		} else {
-			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
-			ctrl->handle_capimsg(ctrl, ApplId, skb);
-		}
-		break;
-
-	case RECEIVE_NEW_NCCI:
-
-		ApplId = _get_word(&p);
-		NCCI = _get_word(&p);
-		WindowSize = _get_word(&p);
-
-		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
-
-		break;
-
-	case RECEIVE_FREE_NCCI:
-
-		ApplId = _get_word(&p);
-		NCCI = _get_word(&p);
-
-		if (NCCI != 0xffffffff)
-			ctrl->free_ncci(ctrl, ApplId, NCCI);
-		else ctrl->appl_released(ctrl, ApplId);
-		break;
-
-	case RECEIVE_START:
-#ifdef CONFIG_T1PCI_POLLDEBUG
-		printk(KERN_INFO "%s: poll from t1\n", card->name);
-#endif
-		if (!suppress_pollack)
-			queue_pollack(card);
-		ctrl->resume_output(ctrl);
-		break;
-
-	case RECEIVE_STOP:
-		ctrl->suspend_output(ctrl);
-		break;
-
-	case RECEIVE_INIT:
-
-		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
-		b1_parse_version(cinfo);
-		printk(KERN_INFO "%s: %s-card (%s) now active\n",
-		       card->name,
-		       cinfo->version[VER_CARDTYPE],
-		       cinfo->version[VER_DRIVER]);
-		ctrl->ready(ctrl);
-		break;
-
-	case RECEIVE_TASK_READY:
-		ApplId = (unsigned) _get_word(&p);
-		MsgLen = _get_slice(&p, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
-		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
-				card->name, ApplId, card->msgbuf);
-		break;
-
-	case RECEIVE_DEBUGMSG:
-		MsgLen = _get_slice(&p, card->msgbuf);
-		card->msgbuf[MsgLen--] = 0;
-		while (    MsgLen >= 0
-		       && (   card->msgbuf[MsgLen] == '\n'
-			   || card->msgbuf[MsgLen] == '\r'))
-			card->msgbuf[MsgLen--] = 0;
-		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
-		break;
-
-	default:
-		printk(KERN_ERR "%s: t1pci_interrupt: 0x%x ???\n",
-				card->name, b1cmd);
-		return;
-	}
-}
-
-/* ------------------------------------------------------------- */
-
-static void t1pci_handle_interrupt(avmcard *card)
-{
-	__u32 status = t1inmeml(card->mbase+AMCC_INTCSR);
-	__u32 newcsr;
-
-	if ((status & ANY_S5933_INT) == 0) 
-		return;
-
-        newcsr = card->csr | (status & ALL_INT);
-	if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
-	if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
-	t1outmeml(card->mbase+AMCC_INTCSR, newcsr);
-
-	if ((status & RX_TC_INT) != 0) {
-		__u8 *recvbuf = card->dma->recvbuf;
-		__u32 rxlen;
-	   	if (card->dma->recvlen == 0) {
-			card->dma->recvlen = *((__u32 *)recvbuf);
-			rxlen = (card->dma->recvlen + 3) & ~3;
-			t1outmeml(card->mbase+AMCC_RXPTR,
-					virt_to_phys(recvbuf+4));
-			t1outmeml(card->mbase+AMCC_RXLEN, rxlen);
-		} else {
-			t1pci_handle_rx(card);
-	   		card->dma->recvlen = 0;
-			t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(recvbuf));
-			t1outmeml(card->mbase+AMCC_RXLEN, 4);
-		}
-	}
-
-	if ((status & TX_TC_INT) != 0) {
-		card->csr &= ~EN_TX_TC_INT;
-	        t1pci_dispatch_tx(card);
-	} else if (card->csr & EN_TX_TC_INT) {
-		if (t1inmeml(card->mbase+AMCC_TXLEN) == 0) {
-			card->csr &= ~EN_TX_TC_INT;
-			t1pci_dispatch_tx(card);
-		}
-	}
-	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
-}
-
-static void t1pci_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
-{
-	avmcard *card;
-
-	card = (avmcard *) devptr;
-
-	if (!card) {
-		printk(KERN_WARNING "t1pci: interrupt: wrong device\n");
-		return;
-	}
-	if (card->interrupt) {
-		printk(KERN_ERR "%s: reentering interrupt hander\n", card->name);
-		return;
-	}
-
-	card->interrupt = 1;
-
-	t1pci_handle_interrupt(card);
-
-	card->interrupt = 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static int t1pci_loaded(avmcard *card)
-{
-	unsigned long stop;
-	unsigned char ans;
-	unsigned long tout = 2;
-	unsigned int base = card->port;
-
-	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
-		if (b1_tx_empty(base))
-			break;
-	}
-	if (!b1_tx_empty(base)) {
-		printk(KERN_ERR "%s: t1pci_loaded: tx err, corrupted t4 file ?\n",
-				card->name);
-		return 0;
-	}
-	b1_put_byte(base, SEND_POLLACK);
-	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
-		if (b1_rx_full(base)) {
-			if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
-				return 1;
-			}
-			printk(KERN_ERR "%s: t1pci_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
-			return 0;
-		}
-	}
-	printk(KERN_ERR "%s: t1pci_loaded: firmware not running\n", card->name);
-	return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static void t1pci_send_init(avmcard *card)
-{
-	struct sk_buff *skb;
-	void *p;
-
-	skb = alloc_skb(15, GFP_ATOMIC);
-	if (!skb) {
-		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
-					card->name);
-		return;
-	}
-	p = skb->data;
-	_put_byte(&p, 0);
-	_put_byte(&p, 0);
-	_put_byte(&p, SEND_INIT);
-	_put_word(&p, AVM_NAPPS);
-	_put_word(&p, AVM_NCCI_PER_CHANNEL*30);
-	_put_word(&p, card->cardnr - 1);
-	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
-
-	skb_queue_tail(&card->dma->send_queue, skb);
-	t1pci_dispatch_tx(card);
-}
-
-static int t1pci_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
-	unsigned long flags;
-	int retval;
-
-	t1pci_reset(card);
-
-	if ((retval = b1_load_t4file(card, &data->firmware))) {
-		t1pci_reset(card);
-		printk(KERN_ERR "%s: failed to load t4file!!\n",
-					card->name);
-		return retval;
-	}
-
-	if (data->configuration.len > 0 && data->configuration.data) {
-		if ((retval = b1_load_config(card, &data->configuration))) {
-			t1pci_reset(card);
-			printk(KERN_ERR "%s: failed to load config!!\n",
-					card->name);
-			return retval;
-		}
-	}
-
-	if (!t1pci_loaded(card)) {
-		t1pci_reset(card);
-		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
-		return -EIO;
-	}
-
-	save_flags(flags);
-	cli();
-
-	card->csr = AVM_FLAG;
-	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
-	t1outmeml(card->mbase+AMCC_MCSR,
-		EN_A2P_TRANSFERS|EN_P2A_TRANSFERS
-		|A2P_HI_PRIORITY|P2A_HI_PRIORITY
-		|RESET_A2P_FLAGS|RESET_P2A_FLAGS);
-	t1outp(card->port, 0x07, 0x30);
-	t1outp(card->port, 0x10, 0xF0);
-
-	card->dma->recvlen = 0;
-	t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(card->dma->recvbuf));
-	t1outmeml(card->mbase+AMCC_RXLEN, 4);
-	card->csr |= EN_RX_TC_INT;
-	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
-	restore_flags(flags);
-
-        t1pci_send_init(card);
-
-	return 0;
-}
-
-void t1pci_reset_ctr(struct capi_ctr *ctrl)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
-
- 	t1pci_reset(card);
-
-	memset(cinfo->version, 0, sizeof(cinfo->version));
-	ctrl->reseted(ctrl);
-}
-
 static void t1pci_remove_ctr(struct capi_ctr *ctrl)
 {
 	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
 	avmcard *card = cinfo->card;
 
- 	t1pci_reset(card);
+ 	b1dma_reset(card);
 
 	di->detach_ctr(ctrl);
 	free_irq(card->irq, card);
@@ -776,210 +93,20 @@
 
 /* ------------------------------------------------------------- */
 
-
-void t1pci_register_appl(struct capi_ctr *ctrl,
-				__u16 appl,
-				capi_register_params *rp)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
-	struct sk_buff *skb;
-	int want = rp->level3cnt;
-	int nconn;
-	void *p;
-
-	if (want > 0) nconn = want;
-	else nconn = ctrl->profile.nbchannel * -want;
-	if (nconn == 0) nconn = ctrl->profile.nbchannel;
-
-	skb = alloc_skb(23, GFP_ATOMIC);
-	if (!skb) {
-		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
-					card->name);
-		return;
-	}
-	p = skb->data;
-	_put_byte(&p, 0);
-	_put_byte(&p, 0);
-	_put_byte(&p, SEND_REGISTER);
-	_put_word(&p, appl);
-	_put_word(&p, 1024 * (nconn+1));
-	_put_word(&p, nconn);
-	_put_word(&p, rp->datablkcnt);
-	_put_word(&p, rp->datablklen);
-	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
-
-	skb_queue_tail(&card->dma->send_queue, skb);
-	t1pci_dispatch_tx(card);
-
-	ctrl->appl_registered(ctrl, appl);
-}
-
-/* ------------------------------------------------------------- */
-
-void t1pci_release_appl(struct capi_ctr *ctrl, __u16 appl)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
-	struct sk_buff *skb;
-	void *p;
-
-	skb = alloc_skb(7, GFP_ATOMIC);
-	if (!skb) {
-		printk(KERN_CRIT "%s: no memory, lost release appl.\n",
-					card->name);
-		return;
-	}
-	p = skb->data;
-	_put_byte(&p, 0);
-	_put_byte(&p, 0);
-	_put_byte(&p, SEND_RELEASE);
-	_put_word(&p, appl);
-
-	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
-	skb_queue_tail(&card->dma->send_queue, skb);
-	t1pci_dispatch_tx(card);
-}
-
-/* ------------------------------------------------------------- */
-
-
-static void t1pci_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
-	skb_queue_tail(&card->dma->send_queue, skb);
-	t1pci_dispatch_tx(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static char *t1pci_procinfo(struct capi_ctr *ctrl)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-	if (!cinfo)
-		return "";
-	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
-		cinfo->cardname[0] ? cinfo->cardname : "-",
-		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-		cinfo->card ? cinfo->card->port : 0x0,
-		cinfo->card ? cinfo->card->irq : 0,
-		cinfo->card ? cinfo->card->membase : 0
-		);
-	return cinfo->infobuf;
-}
-
-static int t1pci_read_proc(char *page, char **start, off_t off,
-        		int count, int *eof, struct capi_ctr *ctrl)
-{
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
-	unsigned long flags;
-	__u8 flag;
-	int len = 0;
-	char *s;
-	__u32 txaddr, txlen, rxaddr, rxlen, csr;
-
-	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
-	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
-	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
-	len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
-	switch (card->cardtype) {
-	case avm_b1isa: s = "B1 ISA"; break;
-	case avm_b1pci: s = "B1 PCI"; break;
-	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
-	case avm_m1: s = "M1"; break;
-	case avm_m2: s = "M2"; break;
-	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
-	case avm_t1pci: s = "T1 PCI"; break;
-	case avm_c4: s = "C4"; break;
-	default: s = "???"; break;
-	}
-	len += sprintf(page+len, "%-16s %s\n", "type", s);
-	if ((s = cinfo->version[VER_DRIVER]) != 0)
-	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
-	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
-	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
-	if ((s = cinfo->version[VER_SERIAL]) != 0)
-	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
-
-	if (card->cardtype != avm_m1) {
-        	flag = ((__u8 *)(ctrl->profile.manu))[3];
-        	if (flag)
-			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
-			"protocol",
-			(flag & 0x01) ? " DSS1" : "",
-			(flag & 0x02) ? " CT1" : "",
-			(flag & 0x04) ? " VN3" : "",
-			(flag & 0x08) ? " NI1" : "",
-			(flag & 0x10) ? " AUSTEL" : "",
-			(flag & 0x20) ? " ESS" : "",
-			(flag & 0x40) ? " 1TR6" : ""
-			);
-	}
-	if (card->cardtype != avm_m1) {
-        	flag = ((__u8 *)(ctrl->profile.manu))[5];
-		if (flag)
-			len += sprintf(page+len, "%-16s%s%s%s%s\n",
-			"linetype",
-			(flag & 0x01) ? " point to point" : "",
-			(flag & 0x02) ? " point to multipoint" : "",
-			(flag & 0x08) ? " leased line without D-channel" : "",
-			(flag & 0x04) ? " leased line with D-channel" : ""
-			);
-	}
-	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
-
-	save_flags(flags);
-	cli();
-
-	txaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x2c));
-	txaddr -= (__u32)card->dma->sendbuf;
-	txlen  = t1inmeml(card->mbase+0x30);
-
-	rxaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x24));
-	rxaddr -= (__u32)card->dma->recvbuf;
-	rxlen  = t1inmeml(card->mbase+0x28);
-
-	csr  = t1inmeml(card->mbase+AMCC_INTCSR);
-
-	restore_flags(flags);
-
-        len += sprintf(page+len, "%-16s 0x%lx\n",
-				"csr (cached)", (unsigned long)card->csr);
-        len += sprintf(page+len, "%-16s 0x%lx\n",
-				"csr", (unsigned long)csr);
-        len += sprintf(page+len, "%-16s %lu\n",
-				"txoff", (unsigned long)txaddr);
-        len += sprintf(page+len, "%-16s %lu\n",
-				"txlen", (unsigned long)txlen);
-        len += sprintf(page+len, "%-16s %lu\n",
-				"rxoff", (unsigned long)rxaddr);
-        len += sprintf(page+len, "%-16s %lu\n",
-				"rxlen", (unsigned long)rxlen);
-
-	if (off+count >= len)
-	   *eof = 1;
-	if (len < off)
-           return 0;
-	*start = page + off;
-	return ((count < len-off) ? count : len-off);
-}
-
-/* ------------------------------------------------------------- */
-
 static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p)
 {
-	unsigned long page_offset, base;
+	unsigned long base, page_offset;
 	avmcard *card;
 	avmctrl_info *cinfo;
 	int retval;
 
+	MOD_INC_USE_COUNT;
+
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
@@ -987,6 +114,7 @@
 	if (!card->dma) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(card->dma, 0, sizeof(avmcard_dmainfo));
@@ -995,6 +123,7 @@
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		kfree(card->dma);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	memset(cinfo, 0, sizeof(avmctrl_info));
@@ -1013,14 +142,26 @@
 	        kfree(card->ctrlinfo);
 		kfree(card->dma);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
-        base = card->membase & PAGE_MASK;
+	base = card->membase & PAGE_MASK;
 	page_offset = card->membase - base;
 	card->mbase = ioremap_nocache(base, page_offset + 64);
+	if (card->mbase) {
+		card->mbase += page_offset;
+	} else {
+		printk(KERN_NOTICE "%s: can't remap memory at 0x%lx\n",
+					driver->name, card->membase);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+	        MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
 
-	t1pci_reset(card);
+	b1dma_reset(card);
 
 	if ((retval = t1pci_detect(card)) != 0) {
 		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
@@ -1029,13 +170,14 @@
 	        kfree(card->ctrlinfo);
 		kfree(card->dma);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
-	t1pci_reset(card);
+	b1dma_reset(card);
 
 	request_region(p->port, AVMB1_PORTLEN, card->name);
 
-	retval = request_irq(card->irq, t1pci_interrupt, SA_SHIRQ, card->name, card);
+	retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card);
 	if (retval) {
 		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
 				driver->name, card->irq);
@@ -1044,6 +186,7 @@
 	        kfree(card->ctrlinfo);
 		kfree(card->dma);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 
@@ -1056,31 +199,52 @@
 	        kfree(card->ctrlinfo);
 		kfree(card->dma);
 		kfree(card);
+	        MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
 	card->cardnr = cinfo->capi_ctrl->cnr;
 
 	skb_queue_head_init(&card->dma->send_queue);
 
-	MOD_INC_USE_COUNT;
+	printk(KERN_INFO
+		"%s: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
+		driver->name, card->port, card->irq, card->membase);
 
 	return 0;
 }
 
 /* ------------------------------------------------------------- */
 
+static char *t1pci_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
 static struct capi_driver t1pci_driver = {
     "t1pci",
     "0.0",
-    t1pci_load_firmware,
-    t1pci_reset_ctr,
+    b1dma_load_firmware,
+    b1dma_reset_ctr,
     t1pci_remove_ctr,
-    t1pci_register_appl,
-    t1pci_release_appl,
-    t1pci_send_message,
+    b1dma_register_appl,
+    b1dma_release_appl,
+    b1dma_send_message,
 
     t1pci_procinfo,
-    t1pci_read_proc,
+    b1dmactl_read_proc,
     0,	/* use standard driver_read_proc */
 
     0, /* no add_card function */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/divert/divert_procfs.c linux/drivers/isdn/divert/divert_procfs.c
--- v2.3.45/linux/drivers/isdn/divert/divert_procfs.c	Thu Feb 10 17:11:09 2000
+++ linux/drivers/isdn/divert/divert_procfs.c	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,10 @@
-/* 
- * $Id: divert_procfs.c,v 1.5 1999/09/14 20:31:01 werner Exp $
+/*
+ * $Id: divert_procfs.c,v 1.6 2000/02/14 19:23:03 werner Exp $
  *
  * Filesystem handling for the diversion supplementary services.
  *
  * Copyright 1998       by Werner Cornelius (werner@isdn4linux.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, or (at your option)
@@ -17,9 +17,13 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: divert_procfs.c,v $
+ * Revision 1.6  2000/02/14 19:23:03  werner
+ *
+ * Changed handling of proc filesystem tables to a more portable version
+ *
  * Revision 1.5  1999/09/14 20:31:01  werner
  *
  * Removed obsoleted functions for proc fs and synced with new ones.
@@ -44,9 +48,9 @@
 #include <linux/version.h>
 #include <linux/poll.h>
 #ifdef CONFIG_PROC_FS
-  #include <linux/proc_fs.h>
+#include <linux/proc_fs.h>
 #else
-  #include <linux/fs.h>
+#include <linux/fs.h>
 #endif
 #include <linux/isdnif.h>
 #include "isdn_divert.h"
@@ -54,220 +58,239 @@
 /*********************************/
 /* Variables for interface queue */
 /*********************************/
-ulong if_used = 0; /* number of interface users */
-static struct divert_info *divert_info_head = NULL; /* head of queue */
-static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */
+ulong if_used = 0;		/* number of interface users */
+static struct divert_info *divert_info_head = NULL;	/* head of queue */
+static struct divert_info *divert_info_tail = NULL;	/* pointer to last entry */
 static wait_queue_head_t rd_queue;
 
 /*********************************/
 /* put an info buffer into queue */
 /*********************************/
-void put_info_buffer(char *cp)
-{ struct divert_info *ib;
-  int flags; 
-  
-  if (if_used <= 0) return;
-  if (!cp) return;
-  if (!*cp) return; 
-  if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info)+strlen(cp), GFP_ATOMIC))) return; /* no memory */
-  strcpy(ib->info_start,cp); /* set output string */
-  ib->next = NULL;
-  save_flags(flags);
-  cli();
-  ib->usage_cnt = if_used; 
-  if (!divert_info_head) 
-    divert_info_head = ib; /* new head */
-  else
-    divert_info_tail->next = ib; /* follows existing messages */
-  divert_info_tail = ib; /* new tail */   
-  restore_flags(flags);
-
-  /* delete old entrys */
-  while (divert_info_head->next)
-   { if ((divert_info_head->usage_cnt <= 0) &&
-         (divert_info_head->next->usage_cnt <= 0))
-       { ib = divert_info_head;
-         divert_info_head = divert_info_head->next;
-         kfree(ib);
-       }
-     else break;
-   } /* divert_info_head->next */  
-  wake_up_interruptible(&(rd_queue));
-} /* put_info_buffer */
+void
+put_info_buffer(char *cp)
+{
+	struct divert_info *ib;
+	int flags;
+
+	if (if_used <= 0)
+		return;
+	if (!cp)
+		return;
+	if (!*cp)
+		return;
+	if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
+		 return;	/* no memory */
+	strcpy(ib->info_start, cp);	/* set output string */
+	ib->next = NULL;
+	save_flags(flags);
+	cli();
+	ib->usage_cnt = if_used;
+	if (!divert_info_head)
+		divert_info_head = ib;	/* new head */
+	else
+		divert_info_tail->next = ib;	/* follows existing messages */
+	divert_info_tail = ib;	/* new tail */
+	restore_flags(flags);
+
+	/* delete old entrys */
+	while (divert_info_head->next) {
+		if ((divert_info_head->usage_cnt <= 0) &&
+		    (divert_info_head->next->usage_cnt <= 0)) {
+			ib = divert_info_head;
+			divert_info_head = divert_info_head->next;
+			kfree(ib);
+		} else
+			break;
+	}			/* divert_info_head->next */
+	wake_up_interruptible(&(rd_queue));
+}				/* put_info_buffer */
 
 /**********************************/
 /* deflection device read routine */
 /**********************************/
-static ssize_t isdn_divert_read(struct file *file, char *buf, size_t count, loff_t *off)
-{ struct divert_info *inf;
-  int len;
-	
-  if (!*((struct divert_info **)file->private_data))
-    { if (file->f_flags & O_NONBLOCK)
-	return -EAGAIN;
-      interruptible_sleep_on(&(rd_queue));
-    }
-  if (!(inf = *((struct divert_info **)file->private_data))) return(0);
-  
-  inf->usage_cnt--; /* new usage count */
-  (struct divert_info **)file->private_data = &inf->next; /* next structure */
-  if ((len = strlen(inf->info_start)) <= count) 
-    { if (copy_to_user(buf, inf->info_start, len))
-	return -EFAULT;
-      file->f_pos += len;
-      return(len);
-    }
-  return(0);
-} /* isdn_divert_read */
+static ssize_t
+isdn_divert_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+	struct divert_info *inf;
+	int len;
+
+	if (!*((struct divert_info **) file->private_data)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		interruptible_sleep_on(&(rd_queue));
+	}
+	if (!(inf = *((struct divert_info **) file->private_data)))
+		return (0);
+
+	inf->usage_cnt--;	/* new usage count */
+	(struct divert_info **) file->private_data = &inf->next;	/* next structure */
+	if ((len = strlen(inf->info_start)) <= count) {
+		if (copy_to_user(buf, inf->info_start, len))
+			return -EFAULT;
+		file->f_pos += len;
+		return (len);
+	}
+	return (0);
+}				/* isdn_divert_read */
 
 /**********************************/
 /* deflection device write routine */
 /**********************************/
-static ssize_t isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t *off)
+static ssize_t
+isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t * off)
 {
-  return(-ENODEV);
-} /* isdn_divert_write */
+	return (-ENODEV);
+}				/* isdn_divert_write */
 
 
 /***************************************/
 /* select routines for various kernels */
 /***************************************/
-static unsigned int isdn_divert_poll(struct file *file, poll_table * wait)
-{ unsigned int mask = 0;
+static unsigned int
+isdn_divert_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
 
-  poll_wait(file, &(rd_queue), wait);
-  /* mask = POLLOUT | POLLWRNORM; */
-  if (*((struct divert_info **)file->private_data)) 
-    { mask |= POLLIN | POLLRDNORM;
-    }
-  return mask;
-} /* isdn_divert_poll */
+	poll_wait(file, &(rd_queue), wait);
+	/* mask = POLLOUT | POLLWRNORM; */
+	if (*((struct divert_info **) file->private_data)) {
+		mask |= POLLIN | POLLRDNORM;
+	}
+	return mask;
+}				/* isdn_divert_poll */
 
 /****************/
 /* Open routine */
 /****************/
-static int isdn_divert_open(struct inode *ino, struct file *filep)
-{ int flags;
+static int
+isdn_divert_open(struct inode *ino, struct file *filep)
+{
+	int flags;
 
-  MOD_INC_USE_COUNT;
-  save_flags(flags);
-  cli();
-  if_used++;
-  if (divert_info_head)
-    (struct divert_info **)filep->private_data = &(divert_info_tail->next);
-   else
-    (struct divert_info **)filep->private_data = &divert_info_head; 
-  restore_flags(flags);
-  /*  start_divert(); */
-  return(0);
-} /* isdn_divert_open */
+	MOD_INC_USE_COUNT;
+	save_flags(flags);
+	cli();
+	if_used++;
+	if (divert_info_head)
+		(struct divert_info **) filep->private_data = &(divert_info_tail->next);
+	else
+		(struct divert_info **) filep->private_data = &divert_info_head;
+	restore_flags(flags);
+	/*  start_divert(); */
+	return (0);
+}				/* isdn_divert_open */
 
 /*******************/
 /* close routine   */
 /*******************/
-static int isdn_divert_close(struct inode *ino, struct file *filep)
-{ struct divert_info *inf; 
-  int flags;
-
-  save_flags(flags);
-  cli();
-  if_used--;
-  inf = *((struct divert_info **)filep->private_data);
-  while (inf)
-   { inf->usage_cnt--;
-     inf = inf->next;
-   } 
-  restore_flags(flags);
-  if (if_used <= 0)
-   while (divert_info_head)
-    { inf = divert_info_head;
-      divert_info_head = divert_info_head->next;
-      kfree(inf);
-    }
-  MOD_DEC_USE_COUNT;
-  return(0);
-} /* isdn_divert_close */
+static int
+isdn_divert_close(struct inode *ino, struct file *filep)
+{
+	struct divert_info *inf;
+	int flags;
+
+	save_flags(flags);
+	cli();
+	if_used--;
+	inf = *((struct divert_info **) filep->private_data);
+	while (inf) {
+		inf->usage_cnt--;
+		inf = inf->next;
+	}
+	restore_flags(flags);
+	if (if_used <= 0)
+		while (divert_info_head) {
+			inf = divert_info_head;
+			divert_info_head = divert_info_head->next;
+			kfree(inf);
+		}
+	MOD_DEC_USE_COUNT;
+	return (0);
+}				/* isdn_divert_close */
 
 /*********/
 /* IOCTL */
 /*********/
-static int isdn_divert_ioctl(struct inode *inode, struct file *file, 
-                             uint cmd, ulong arg)
-{ divert_ioctl dioctl;
-  int i, flags;
-  divert_rule *rulep;
-  char *cp;
-
-  if ((i = copy_from_user(&dioctl,(char *) arg, sizeof(dioctl))))
-    return(i); 
- 
-  switch (cmd)
-   { 
-     case IIOCGETVER:
-       dioctl.drv_version = DIVERT_IIOC_VERSION ; /* set version */
-       break;
-
-     case IIOCGETDRV:
-       if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
-         return(-EINVAL);  
-       break;
-
-     case IIOCGETNAM:
-       cp = divert_if.drv_to_name(dioctl.getid.drvid);
-       if (!cp) return(-EINVAL);
-       if (!*cp) return(-EINVAL);
-       strcpy(dioctl.getid.drvnam,cp);  
-       break;
-
-     case IIOCGETRULE:
-       if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
-         return(-EINVAL);  
-       dioctl.getsetrule.rule = *rulep; /* copy data */
-       break;
-
-     case IIOCMODRULE:
-       if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
-         return(-EINVAL);  
-       save_flags(flags);
-       cli();
-       *rulep = dioctl.getsetrule.rule; /* copy data */
-       restore_flags(flags);
-       return(0); /* no copy required */
-       break;
-
-     case IIOCINSRULE:
-       return(insertrule(dioctl.getsetrule.ruleidx,&dioctl.getsetrule.rule));
-       break;
-
-     case IIOCDELRULE:
-       return(deleterule(dioctl.getsetrule.ruleidx));
-       break;
-
-     case IIOCDODFACT:
-       return(deflect_extern_action(dioctl.fwd_ctrl.subcmd,
-                                    dioctl.fwd_ctrl.callid,
-                                    dioctl.fwd_ctrl.to_nr)); 
-
-     case  IIOCDOCFACT:
-     case  IIOCDOCFDIS:
-     case  IIOCDOCFINT:
-       if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid)) 
-         return(-EINVAL); /* invalid driver */
-       if ((i = cf_command(dioctl.cf_ctrl.drvid,
-                          (cmd == IIOCDOCFACT) ? 1: (cmd == IIOCDOCFDIS) ? 0:2,
-                          dioctl.cf_ctrl.cfproc,
-                          dioctl.cf_ctrl.msn,  
-                          dioctl.cf_ctrl.service,
-                          dioctl.cf_ctrl.fwd_nr,
-                          &dioctl.cf_ctrl.procid)))
-         return(i);    
-       break;
-   
-     default: 
-       return(-EINVAL);
-   } /* switch cmd */
-  return(copy_to_user((char *) arg, &dioctl, sizeof(dioctl))); /* success */
-} /* isdn_divert_ioctl */
+static int
+isdn_divert_ioctl(struct inode *inode, struct file *file,
+		  uint cmd, ulong arg)
+{
+	divert_ioctl dioctl;
+	int i, flags;
+	divert_rule *rulep;
+	char *cp;
+
+	if ((i = copy_from_user(&dioctl, (char *) arg, sizeof(dioctl))))
+		return (i);
+
+	switch (cmd) {
+		case IIOCGETVER:
+			dioctl.drv_version = DIVERT_IIOC_VERSION;	/* set version */
+			break;
+
+		case IIOCGETDRV:
+			if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
+				return (-EINVAL);
+			break;
+
+		case IIOCGETNAM:
+			cp = divert_if.drv_to_name(dioctl.getid.drvid);
+			if (!cp)
+				return (-EINVAL);
+			if (!*cp)
+				return (-EINVAL);
+			strcpy(dioctl.getid.drvnam, cp);
+			break;
+
+		case IIOCGETRULE:
+			if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
+				return (-EINVAL);
+			dioctl.getsetrule.rule = *rulep;	/* copy data */
+			break;
+
+		case IIOCMODRULE:
+			if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
+				return (-EINVAL);
+			save_flags(flags);
+			cli();
+			*rulep = dioctl.getsetrule.rule;	/* copy data */
+			restore_flags(flags);
+			return (0);	/* no copy required */
+			break;
+
+		case IIOCINSRULE:
+			return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule));
+			break;
+
+		case IIOCDELRULE:
+			return (deleterule(dioctl.getsetrule.ruleidx));
+			break;
+
+		case IIOCDODFACT:
+			return (deflect_extern_action(dioctl.fwd_ctrl.subcmd,
+						  dioctl.fwd_ctrl.callid,
+						 dioctl.fwd_ctrl.to_nr));
+
+		case IIOCDOCFACT:
+		case IIOCDOCFDIS:
+		case IIOCDOCFINT:
+			if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))
+				return (-EINVAL);	/* invalid driver */
+			if ((i = cf_command(dioctl.cf_ctrl.drvid,
+					    (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2,
+					    dioctl.cf_ctrl.cfproc,
+					    dioctl.cf_ctrl.msn,
+					    dioctl.cf_ctrl.service,
+					    dioctl.cf_ctrl.fwd_nr,
+					    &dioctl.cf_ctrl.procid)))
+				return (i);
+			break;
+
+		default:
+			return (-EINVAL);
+	}			/* switch cmd */
+	return (copy_to_user((char *) arg, &dioctl, sizeof(dioctl)));	/* success */
+}				/* isdn_divert_ioctl */
 
 
 #ifdef CONFIG_PROC_FS
@@ -279,63 +302,66 @@
 
 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,
-};
-
-struct inode_operations divert_file_inode_operations = {
-    &isdn_fops,  /* default proc file-ops */
+	isdn_divert_lseek,
+	isdn_divert_read,
+	isdn_divert_write,
+	NULL,			/* isdn_readdir */
+	isdn_divert_poll,	/* isdn_poll */
+	isdn_divert_ioctl,	/* isdn_ioctl */
+	NULL,			/* isdn_mmap */
+	isdn_divert_open,
+	NULL,			/* flush */
+	isdn_divert_close,
+	NULL			/* fsync */
 };
 
+struct inode_operations divert_file_inode_operations;
 
 /****************************/
 /* isdn subdir in /proc/net */
 /****************************/
 static struct proc_dir_entry *isdn_proc_entry = NULL;
 static struct proc_dir_entry *isdn_divert_entry = NULL;
-#endif CONFIG_PROC_FS
+#endif	/* CONFIG_PROC_FS */
 
 /***************************************************************************/
 /* divert_dev_init must be called before the proc filesystem may be used   */
 /***************************************************************************/
-int divert_dev_init(void)
-{ int i;
+int
+divert_dev_init(void)
+{
 
 	init_waitqueue_head(&rd_queue);
 
 #ifdef CONFIG_PROC_FS
-  isdn_proc_entry = proc_mkdir("isdn", proc_net);
-  if (!isdn_proc_entry) 
-    return(-1);
-  isdn_divert_entry = create_proc_entry("divert",0,isdn_proc_entry); 
-  if (!isdn_divert_entry) 
-   {
-     remove_proc_entry("isdn",proc_net);
-     return(-1);
-   }
-  isdn_divert_entry->ops = &divert_file_inode_operations;
-#endif CONFIG_PROC_FS
+	isdn_proc_entry = create_proc_entry("isdn", S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+	if (!isdn_proc_entry)
+		return (-1);
+	isdn_divert_entry = create_proc_entry("divert", S_IFREG | S_IRUGO, isdn_proc_entry);
+	if (!isdn_divert_entry) {
+		remove_proc_entry("isdn", proc_net);
+		return (-1);
+	}
+	memset(&divert_file_inode_operations, 0, sizeof(struct inode_operations));
+	divert_file_inode_operations.default_file_ops = &isdn_fops;
+	isdn_divert_entry->ops = &divert_file_inode_operations;
+#endif	/* CONFIG_PROC_FS */
 
-  return(0);
-} /* divert_dev_init */
+	return (0);
+}				/* divert_dev_init */
 
 /***************************************************************************/
 /* divert_dev_deinit must be called before leaving isdn when included as   */
 /* a module.                                                               */
 /***************************************************************************/
-int divert_dev_deinit(void)
-{ int i;
+int
+divert_dev_deinit(void)
+{
 
 #ifdef CONFIG_PROC_FS
-  remove_proc_entry("divert",isdn_proc_entry);
-  remove_proc_entry("isdn",proc_net);
-#endif CONFIG_PROC_FS
-
-  return(0);
-} /* divert_dev_deinit */
+	remove_proc_entry("divert", isdn_proc_entry);
+	remove_proc_entry("isdn", proc_net);
+#endif	/* CONFIG_PROC_FS */
 
+	return (0);
+}				/* divert_dev_deinit */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon.h linux/drivers/isdn/eicon/eicon.h
--- v2.3.45/linux/drivers/isdn/eicon/eicon.h	Thu Nov 11 20:11:36 1999
+++ linux/drivers/isdn/eicon/eicon.h	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,10 @@
-/* $Id: eicon.h,v 1.17 1999/10/26 21:15:33 armin Exp $
+/* $Id: eicon.h,v 1.19 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * ISDN low-level module for Eicon active ISDN-Cards.
  *
  * Copyright 1998    by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1998,99 by Armin Schindler (mac@melware.de) 
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998-2000  by Armin Schindler (mac@melware.de) 
+ * Copyright 1999,2000  Cytronics & Melware (info@melware.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
@@ -21,6 +21,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon.h,v $
+ * Revision 1.19  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.18  1999/11/25 11:43:27  armin
+ * Fixed statectrl and connect message.
+ * X.75 fix and HDLC/transparent with autoconnect.
+ * Minor cleanup.
+ *
  * Revision 1.17  1999/10/26 21:15:33  armin
  * using define for checking phone number len to avoid buffer overflow.
  *
@@ -258,7 +267,7 @@
 
 /* Macro for delay via schedule() */
 #define SLEEP(j) {                     \
-  set_current_state(TASK_INTERRUPTIBLE); \
+  set_current_state(TASK_UNINTERRUPTIBLE); \
   schedule_timeout(j);                 \
 }
 
@@ -277,6 +286,8 @@
 #define XLOG_ERR_UNKNOWN        (18)
 #define XLOG_OK                  (0)
 
+#define TRACE_OK                 (1)
+
 typedef struct {
   __u8 Id	__attribute__ ((packed));
   __u8 uX	__attribute__ ((packed));
@@ -494,12 +505,13 @@
 typedef struct {
 	int	       No;		 /* Channel Number	        */
 	unsigned short fsm_state;        /* Current D-Channel state     */
+	unsigned short statectrl;	 /* State controling bits	*/
 	unsigned short eazmask;          /* EAZ-Mask for this Channel   */
 	int		queued;          /* User-Data Bytes in TX queue */
 	int		waitq;           /* User-Data Bytes in wait queue */
 	int		waitpq;          /* User-Data Bytes in packet queue */
-	unsigned short plci;
-	unsigned short ncci;
+	struct sk_buff *tskb1;           /* temp skb 1			*/
+	struct sk_buff *tskb2;           /* temp skb 2			*/
 	unsigned char  l2prot;           /* Layer 2 protocol            */
 	unsigned char  l3prot;           /* Layer 3 protocol            */
 #ifdef CONFIG_ISDN_TTY_FAX
@@ -600,21 +612,16 @@
         struct eicon_card *next;	 /* Pointer to next device struct    */
         int myid;                        /* Driver-Nr. assigned by linklevel */
         unsigned long flags;             /* Statusflags                      */
-        unsigned long ilock;             /* Semaphores for IRQ-Routines      */
 	struct sk_buff_head rcvq;        /* Receive-Message queue            */
 	struct sk_buff_head sndq;        /* Send-Message queue               */
 	struct sk_buff_head rackq;       /* Req-Ack-Message queue            */
 	struct sk_buff_head sackq;       /* Data-Ack-Message queue           */
 	struct sk_buff_head statq;       /* Status-Message queue             */
 	int statq_entries;
-	u_char *ack_msg;                 /* Ptr to User Data in User skb     */
-	__u16 need_b3ack;                /* Flag: Need ACK for current skb   */
-	struct sk_buff *sbuf;            /* skb which is currently sent      */
 	struct tq_struct snd_tq;         /* Task struct for xmit bh          */
 	struct tq_struct rcv_tq;         /* Task struct for rcv bh           */
 	struct tq_struct ack_tq;         /* Task struct for ack bh           */
 	msn_entry *msn_list;
-	unsigned short msgnum;           /* Message number for sending       */
 	eicon_chan*	IdTable[256];	 /* Table to find entity   */
 	__u16  ref_in;
 	__u16  ref_out;
@@ -696,6 +703,7 @@
 
 extern ulong DebugVar;
 extern void eicon_log(eicon_card * card, int level, const char *fmt, ...);
+extern void eicon_putstatus(eicon_card * card, char * buf);
 
 #endif  /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_dsp.h linux/drivers/isdn/eicon/eicon_dsp.h
--- v2.3.45/linux/drivers/isdn/eicon/eicon_dsp.h	Thu Aug 12 09:42:33 1999
+++ linux/drivers/isdn/eicon/eicon_dsp.h	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,10 @@
-/* $Id: eicon_dsp.h,v 1.4 1999/07/25 15:12:02 armin Exp $
+/* $Id: eicon_dsp.h,v 1.5 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * ISDN lowlevel-module for Eicon active cards.
  *        DSP definitions
  *
- * Copyright 1999    by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1999,2000  by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000  Cytronics & Melware (info@melware.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
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: eicon_dsp.h,v $
+ * Revision 1.5  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
  * Revision 1.4  1999/07/25 15:12:02  armin
  * fix of some debug logs.
  * enabled ISA-cards option.
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_idi.c linux/drivers/isdn/eicon/eicon_idi.c
--- v2.3.45/linux/drivers/isdn/eicon/eicon_idi.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/eicon/eicon_idi.c	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,10 @@
-/* $Id: eicon_idi.c,v 1.24 1999/10/26 21:15:33 armin Exp $
+/* $Id: eicon_idi.c,v 1.29 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * ISDN lowlevel-module for Eicon active cards.
  *        IDI interface 
  *
- * Copyright 1998,99 by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998-2000  by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000  Cytronics & Melware (info@melware.de)
  *
  * Thanks to	Deutsche Mailbox Saar-Lor-Lux GmbH
  *		for sponsoring and testing fax
@@ -26,6 +26,25 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_idi.c,v $
+ * Revision 1.29  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.28  2000/01/20 19:55:34  keil
+ * Add FAX Class 1 support
+ *
+ * Revision 1.27  1999/11/29 13:12:03  armin
+ * Autoconnect on L2_TRANS doesn't work with link_level correctly,
+ * changed back to former mode.
+ *
+ * Revision 1.26  1999/11/25 11:43:27  armin
+ * Fixed statectrl and connect message.
+ * X.75 fix and HDLC/transparent with autoconnect.
+ * Minor cleanup.
+ *
+ * Revision 1.25  1999/11/18 20:30:55  armin
+ * removed old workaround for ISA cards.
+ *
  * Revision 1.24  1999/10/26 21:15:33  armin
  * using define for checking phone number len to avoid buffer overflow.
  *
@@ -130,7 +149,7 @@
 
 #undef EICON_FULL_SERVICE_OKTETT
 
-char *eicon_idi_revision = "$Revision: 1.24 $";
+char *eicon_idi_revision = "$Revision: 1.29 $";
 
 eicon_manifbuf *manbuf;
 
@@ -187,16 +206,13 @@
 	reqbuf->XBuffer.P[l++] = LLC;
 	reqbuf->XBuffer.P[l++] = 2;
 	switch(chan->l2prot) {
-		case ISDN_PROTO_L2_HDLC:
-			reqbuf->XBuffer.P[l++] = 2;
+		case ISDN_PROTO_L2_TRANS:
+			reqbuf->XBuffer.P[l++] = 2; /* transparent */
 			break;
 		case ISDN_PROTO_L2_X75I:
 		case ISDN_PROTO_L2_X75UI:
 		case ISDN_PROTO_L2_X75BUI:
-			reqbuf->XBuffer.P[l++] = 5; 
-			break;
-		case ISDN_PROTO_L2_TRANS:
-			reqbuf->XBuffer.P[l++] = 2;
+			reqbuf->XBuffer.P[l++] = 5; /* X.75 */ 
 			break;
 		case ISDN_PROTO_L2_MODEM:
   			if (chan->fsm_state == EICON_STATE_IWAIT)
@@ -204,17 +220,18 @@
 			else
 				reqbuf->XBuffer.P[l++] = 10; /* V.42 */
 			break;
+		case ISDN_PROTO_L2_HDLC:
 		case ISDN_PROTO_L2_FAX:
   			if (chan->fsm_state == EICON_STATE_IWAIT)
 				reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */
 			else
-				reqbuf->XBuffer.P[l++] = 2;
+				reqbuf->XBuffer.P[l++] = 2; /* transparent */
 			break;
 		default:
 			reqbuf->XBuffer.P[l++] = 1;
 	}
 	switch(chan->l3prot) {
-		case ISDN_PROTO_L3_FAX:
+		case ISDN_PROTO_L3_FCLASS2:
 #ifdef CONFIG_ISDN_TTY_FAX
 			reqbuf->XBuffer.P[l++] = 6;
 			reqbuf->XBuffer.P[l++] = NLC;
@@ -404,8 +421,10 @@
 		idi_do_req(card, chan, ASSIGN, 0); 
 	}
 	if (chan->fsm_state == EICON_STATE_NULL) {
-		idi_do_req(card, chan, INDICATE_REQ, 0);
-		chan->fsm_state = EICON_STATE_LISTEN;
+		if (!(chan->statectrl & HAVE_CONN_REQ)) {
+			idi_do_req(card, chan, INDICATE_REQ, 0);
+			chan->fsm_state = EICON_STATE_LISTEN;
+		}
 	}
   return(0);
 }
@@ -462,6 +481,7 @@
 	}
 	if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
 	if (chan->fsm_state != EICON_STATE_NULL) {
+		chan->statectrl |= WAITING_FOR_HANGUP;
 		idi_do_req(card, chan, HANGUP, 0);
 		chan->fsm_state = EICON_STATE_NULL;
 	}
@@ -479,7 +499,6 @@
 		return 1;
 
 	chan->fsm_state = EICON_STATE_IWAIT;
-	idi_do_req(card, chan, CALL_RES, 0);
 	
 	/* check if old NetID has been removed */
 	if (chan->e.B2Id) {
@@ -489,6 +508,7 @@
 	}
 
 	idi_do_req(card, chan, ASSIGN, 1);
+	idi_do_req(card, chan, CALL_RES, 0);
 	return(0);
 }
 
@@ -656,9 +676,18 @@
 	reqbuf->XBuffer.length = l;
 	reqbuf->Reference = 0; /* Sig Entity */
 
-	skb_queue_tail(&chan->e.X, skb);
-	skb_queue_tail(&card->sndq, skb2); 
-	eicon_schedule_tx(card);
+	if (chan->statectrl & WAITING_FOR_HANGUP) {
+		/*	If the line did not disconnect yet,
+			we have to delay this command		*/
+		eicon_log(card, 32, "idi_req: Ch%d: delaying conn_req\n", chan->No);
+		chan->statectrl |= HAVE_CONN_REQ;
+		chan->tskb1 = skb;
+		chan->tskb2 = skb2;
+	} else {
+		skb_queue_tail(&chan->e.X, skb);
+		skb_queue_tail(&card->sndq, skb2); 
+		eicon_schedule_tx(card);
+	}
 
 	eicon_log(card, 8, "idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
    return(0);
@@ -1433,6 +1462,7 @@
 			cmd.driver = ccard->myid;
 			cmd.command = ISDN_STAT_BCONN;
 			cmd.arg = chan->No;
+			strcpy(cmd.parm.num, "");
 			ccard->interface.statcallb(&cmd);
 
 			cmd.driver = ccard->myid;
@@ -1489,6 +1519,8 @@
 					break;
 				case 2:	/* session end */
 				default:
+					/* send_edata produces error on some */
+					/* fax-machines here, so we don't */
 					/* idi_send_edata(ccard, chan); */
 					break;
 			}
@@ -1505,6 +1537,7 @@
 			cmd.driver = ccard->myid;
 			cmd.command = ISDN_STAT_BCONN;
 			cmd.arg = chan->No;
+			strcpy(cmd.parm.num, "");
 			ccard->interface.statcallb(&cmd);
 
 			cmd.driver = ccard->myid;
@@ -2277,6 +2310,51 @@
 }
 
 void
+eicon_parse_trace(eicon_card *ccard, unsigned char *buffer, int len)
+{
+	int i,j,n;
+	int buflen = len * 3 + 30;
+	char *p;
+	struct trace_s {
+		unsigned long time;
+		unsigned short size;
+		unsigned short code;
+		unsigned char data[1];
+	} *q;
+
+	if (!(p = kmalloc(buflen, GFP_KERNEL))) {
+		eicon_log(ccard, 1, "idi_err: Ch??: could not allocate trace buffer\n");
+		return;
+	}
+	memset(p, 0, buflen);
+	q = (struct trace_s *)buffer;
+
+	if (DebugVar & 512) {
+		if ((q->code == 3) || (q->code == 4)) {
+			n = (short) *(q->data);
+			if (n) {
+				j = sprintf(p, "DTRC:");
+				for (i = 0; i < n; i++) {
+					j += sprintf(p + j, "%02x ", q->data[i+2]);
+				}
+				j += sprintf(p + j, "\n");
+			}
+		}
+	} else {
+		j = sprintf(p, "XLOG: %lx %04x %04x ",
+			q->time, q->size, q->code);
+
+		for (i = 0; i < q->size; i++) {
+			j += sprintf(p + j, "%02x ", q->data[i]);
+		}
+		j += sprintf(p + j, "\n");
+	}
+	if (strlen(p))
+		eicon_putstatus(ccard, p);
+	kfree(p);
+}
+
+void
 idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
 {
 	int tmp;
@@ -2307,7 +2385,7 @@
 	else
 		dlev = 128;
 
-       	eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
+       	eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%x Id=%x Ch=%x MInd=%x MLen=%x Len=%x\n", chan->No,
 	        ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
 
 	free_buff = 1;
@@ -2347,12 +2425,25 @@
 				} else {
 					if (chan->e.B2Id)
 						idi_do_req(ccard, chan, REMOVE, 1);
-					chan->fsm_state = EICON_STATE_NULL;
-					cmd.driver = ccard->myid;
-					cmd.arg = chan->No;
-					cmd.command = ISDN_STAT_DHUP;
-					ccard->interface.statcallb(&cmd);
-					eicon_idi_listen_req(ccard, chan);
+					chan->statectrl &= ~WAITING_FOR_HANGUP;
+					if (chan->statectrl & HAVE_CONN_REQ) {
+						eicon_log(ccard, 32, "idi_req: Ch%d: queueing delayed conn_req\n", chan->No);
+						chan->statectrl &= ~HAVE_CONN_REQ;
+						if ((chan->tskb1) && (chan->tskb2)) {
+							skb_queue_tail(&chan->e.X, chan->tskb1);
+							skb_queue_tail(&ccard->sndq, chan->tskb2); 
+							eicon_schedule_tx(ccard);
+						}
+						chan->tskb1 = NULL;
+						chan->tskb2 = NULL;
+					} else {
+						chan->fsm_state = EICON_STATE_NULL;
+						cmd.driver = ccard->myid;
+						cmd.arg = chan->No;
+						cmd.command = ISDN_STAT_DHUP;
+						ccard->interface.statcallb(&cmd);
+						eicon_idi_listen_req(ccard, chan);
+					}
 				}
 				break;
 			case INDICATE_IND:
@@ -2450,8 +2541,12 @@
 						case ISDN_PROTO_L2_MODEM:
 							/* do nothing, wait for connect */
 							break;
-						default:
+						case ISDN_PROTO_L2_TRANS:
 							idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+							break;
+						default:
+							/* On most incoming calls we use automatic connect */
+							/* idi_do_req(ccard, chan, IDI_N_CONNECT, 1); */
 					}
 				} else
 					idi_hangup(ccard, chan);
@@ -2495,8 +2590,12 @@
 
 		if (chan->No == ccard->nchannels) {
 			/* Management Indication */
-			idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
-			chan->fsm_state = 1;
+			if (ind->Ind == 0x04) { /* Trace_Ind */
+				eicon_parse_trace(ccard, ind->RBuffer.P, ind->RBuffer.length);
+			} else {
+				idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+				chan->fsm_state = 1;
+			}
 		} 
 		else
 		switch(ind->Ind) {
@@ -2530,6 +2629,7 @@
 				cmd.driver = ccard->myid;
 				cmd.command = ISDN_STAT_BCONN;
 				cmd.arg = chan->No;
+				strcpy(cmd.parm.num, "64000");
 				ccard->interface.statcallb(&cmd);
 				break; 
 			case IDI_N_CONNECT:
@@ -2546,6 +2646,7 @@
 				cmd.driver = ccard->myid;
 				cmd.command = ISDN_STAT_BCONN;
 				cmd.arg = chan->No;
+				strcpy(cmd.parm.num, "64000");
 				ccard->interface.statcallb(&cmd);
 				break; 
 			case IDI_N_DISC:
@@ -2576,6 +2677,7 @@
 					cmd.arg = chan->No;
 					ccard->interface.statcallb(&cmd);
 					chan->fsm_state = EICON_STATE_NULL;
+					chan->statectrl |= WAITING_FOR_HANGUP;
 				}
 #ifdef CONFIG_ISDN_TTY_FAX
 				chan->fax = 0;
@@ -2631,7 +2733,8 @@
 	isdn_ctrl cmd;
 
 	if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
-		/* I dont know why this happens, just ignoring this RC */
+		/* I dont know why this happens, should not ! */
+		/* just ignoring this RC */
 		eicon_log(ccard, 16, "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, 
 			ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id);
 		return 1;
@@ -2640,16 +2743,16 @@
 	/* Management Interface */	
 	if (chan->No == ccard->nchannels) {
 		/* Managementinterface: changing state */
-		if (chan->e.Req == 0x04)
+		if (chan->e.Req != 0x02)
 			chan->fsm_state = 1;
 	}
 
 	/* Remove an Id */
 	if (chan->e.Req == REMOVE) {
 		if (ack->Reference != chan->e.ref) {
+			/* This should not happen anymore */
 			eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
 				ack->Reference, chan->e.ref);
-			return 0;
 		}
 		save_flags(flags);
 		cli();
@@ -2807,11 +2910,14 @@
 						dCh, ack->Rc, ack->RcId, ack->RcCh);
 					break;
 				default:
-					eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
-						dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
+					if (dCh != ccard->nchannels)
+						eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
+							dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
 			}
 			if (dCh == ccard->nchannels) { /* Management */
 				chan->fsm_state = 2;
+				eicon_log(ccard, 8, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
+					dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
 			} else if (dCh >= 0) {
 					/* any other channel */
 					/* card reports error: we hangup */
@@ -3011,38 +3117,36 @@
 
         chan = &(card->bch[card->nchannels]);
 
-	if (chan->e.D3Id)
-		return -EBUSY;
-	chan->e.D3Id = 1;
-	while((skb2 = skb_dequeue(&chan->e.X)))
-		dev_kfree_skb(skb2);
-	chan->e.busy = 0;
+	if (!(chan->e.D3Id)) {
+		chan->e.D3Id = 1;
+		while((skb2 = skb_dequeue(&chan->e.X)))
+			dev_kfree_skb(skb2);
+		chan->e.busy = 0;
  
-	if ((ret = eicon_idi_manage_assign(card))) {
-		chan->e.D3Id = 0;
-		return(ret); 
-	}
+		if ((ret = eicon_idi_manage_assign(card))) {
+			chan->e.D3Id = 0;
+			return(ret); 
+		}
 
-        timeout = jiffies + 50;
-        while (timeout > jiffies) {
-                if (chan->e.B2Id) break;
-                SLEEP(10);
-        }
-        if (!chan->e.B2Id) {
-		chan->e.D3Id = 0;
-		return -EIO;
+	        timeout = jiffies + 50;
+        	while (timeout > jiffies) {
+	                if (chan->e.B2Id) break;
+        	        SLEEP(10);
+	        }
+	        if (!chan->e.B2Id) {
+			chan->e.D3Id = 0;
+			return -EIO;
+		}
 	}
 
 	chan->fsm_state = 0;
 
 	if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
                	eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n");
-		chan->e.D3Id = 0;
 		return -ENOMEM;
 	}
 	if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
 		kfree(manbuf);
-		chan->e.D3Id = 0;
 		return -EFAULT;
 	}
 
@@ -3056,7 +3160,6 @@
 		if (skb2) 
 			dev_kfree_skb(skb2);
 		kfree(manbuf);
-		chan->e.D3Id = 0;
                 return -ENOMEM;
         }
 
@@ -3093,25 +3196,14 @@
                 SLEEP(10);
         }
         if ((!chan->fsm_state) || (chan->fsm_state == 2)) {
-		eicon_idi_manage_remove(card);
 		kfree(manbuf);
-		chan->e.D3Id = 0;
 		return -EIO;
 	}
-
-	if ((ret = eicon_idi_manage_remove(card))) {
-		kfree(manbuf);
-		chan->e.D3Id = 0;
-		return(ret);
-	}
-
 	if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
 		kfree(manbuf);
-		chan->e.D3Id = 0;
 		return -EFAULT;
 	}
 
 	kfree(manbuf);
-	chan->e.D3Id = 0;
   return(0);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_idi.h linux/drivers/isdn/eicon/eicon_idi.h
--- v2.3.45/linux/drivers/isdn/eicon/eicon_idi.h	Thu Aug 26 13:05:35 1999
+++ linux/drivers/isdn/eicon/eicon_idi.h	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,10 @@
-/* $Id: eicon_idi.h,v 1.7 1999/08/22 20:26:46 calle Exp $
+/* $Id: eicon_idi.h,v 1.9 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN lowlevel-module for the Eicon.Diehl active cards.
+ * ISDN lowlevel-module for the Eicon active cards.
  * IDI-Interface
  *
- * Copyright 1998,99 by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998-2000  by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000  Cytronics & Melware (info@melware.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
@@ -21,6 +21,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_idi.h,v $
+ * Revision 1.9  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.8  1999/11/25 11:43:27  armin
+ * Fixed statectrl and connect message.
+ * X.75 fix and HDLC/transparent with autoconnect.
+ * Minor cleanup.
+ *
  * Revision 1.7  1999/08/22 20:26:46  calle
  * backported changes from kernel 2.3.14:
  * - several #include "config.h" gone, others come.
@@ -169,6 +178,10 @@
 #define OK                      0xff    /* command accepted         */
 
 /*------------------------------------------------------------------*/
+
+/* defines for statectrl */
+#define WAITING_FOR_HANGUP	0x01
+#define HAVE_CONN_REQ		0x02
 
 typedef struct {
 	char cpn[32];
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_io.c linux/drivers/isdn/eicon/eicon_io.c
--- v2.3.45/linux/drivers/isdn/eicon/eicon_io.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/eicon/eicon_io.c	Tue Feb 15 11:40:42 2000
@@ -1,12 +1,12 @@
-/* $Id: eicon_io.c,v 1.8 1999/10/08 22:09:34 armin Exp $
+/* $Id: eicon_io.c,v 1.10 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * ISDN low-level module for Eicon active ISDN-Cards.
  * Code for communicating with hardware.
  *
- * Copyright 1999    by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1999,2000  by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000  Cytronics & Melware (info@melware.de)
  *
- * Thanks to	Eicon Technology Diehl GmbH & Co. oHG for 
+ * Thanks to	Eicon Technology GmbH & Co. oHG for 
  *		documents, informations and hardware. 
  *
  * This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,13 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_io.c,v $
+ * Revision 1.10  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.9  1999/11/18 20:55:25  armin
+ * Ready_Int fix of ISA cards.
+ *
  * Revision 1.8  1999/10/08 22:09:34  armin
  * Some fixes of cards interface handling.
  * Bugfix of NULL pointer occurence.
@@ -470,7 +477,7 @@
                 save_flags(flags);
                 cli();
 		if (scom) {
-			if (ram_inb(ccard, &com->Req)) {
+			if ((ram_inb(ccard, &com->Req)) || (ccard->ReadyInt)) {
 				if (!ccard->ReadyInt) {
 					tmp = ram_inb(ccard, &com->ReadyInt) + 1;
 					ram_outb(ccard, &com->ReadyInt, tmp);
@@ -566,7 +573,8 @@
 			chan->e.busy = 1;
 	               	eicon_log(ccard, dlev, "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", 
 					reqbuf->Req, 
-					ram_inb(ccard, &ReqOut->ReqId),
+					(scom) ? ram_inb(ccard, &com->ReqId) :
+						ram_inb(ccard, &ReqOut->ReqId),
 					reqbuf->ReqCh, reqbuf->XBuffer.length,
 					chan->e.ref); 
 		  }
@@ -754,6 +762,7 @@
 			if (ccard->ReadyInt) {
 				ccard->ReadyInt--;
 				ram_outb(ccard, &com->Rc, 0);
+				eicon_schedule_tx(ccard);
 			}
 		} else {
 			skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_isa.c linux/drivers/isdn/eicon/eicon_isa.c
--- v2.3.45/linux/drivers/isdn/eicon/eicon_isa.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/eicon/eicon_isa.c	Tue Feb 15 11:40:42 2000
@@ -1,11 +1,11 @@
-/* $Id: eicon_isa.c,v 1.9 1999/09/08 20:17:31 armin Exp $
+/* $Id: eicon_isa.c,v 1.13 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * ISDN low-level module for Eicon active ISDN-Cards.
  * Hardware-specific code for old ISA cards.
  *
- * Copyright 1998    by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1998,99 by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998      by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1998-2000 by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000 Cytronics & Melware (info@melware.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
@@ -22,8 +22,21 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_isa.c,v $
+ * Revision 1.13  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.12  1999/11/27 12:56:19  armin
+ * Forgot some iomem changes for last ioremap compat.
+ *
+ * Revision 1.11  1999/11/25 11:33:09  armin
+ * Microchannel fix from Erik Weber (exrz73@ibm.net).
+ *
+ * Revision 1.10  1999/11/18 21:14:30  armin
+ * New ISA memory mapped IO
+ *
  * Revision 1.9  1999/09/08 20:17:31  armin
- * Added microchannel patch from Erik Weber.
+ * Added microchannel patch from Erik Weber (exrz73@ibm.net).
  *
  * Revision 1.8  1999/09/06 07:29:35  fritz
  * Changed my mail-address.
@@ -70,7 +83,7 @@
 #define release_shmem release_region
 #define request_shmem request_region
 
-char *eicon_isa_revision = "$Revision: 1.9 $";
+char *eicon_isa_revision = "$Revision: 1.13 $";
 
 #undef EICON_MCA_DEBUG
 
@@ -87,8 +100,10 @@
 
 static void
 eicon_isa_release_shmem(eicon_isa_card *card) {
-	if (card->mvalid)
-		release_shmem((unsigned long)card->shmem, card->ramsize);
+	if (card->mvalid) {
+		iounmap(card->shmem);
+		release_mem_region(card->physmem, card->ramsize);
+	}
 	card->mvalid = 0;
 }
 
@@ -117,7 +132,7 @@
 		case EICON_CTYPE_S2M:
 			printk(KERN_INFO "Eicon %s at 0x%lx, irq %d.\n",
 			       eicon_ctype_name[card->type],
-			       (unsigned long)card->shmem,
+			       card->physmem,
 			       card->irq);
 	}
 }
@@ -126,6 +141,7 @@
 eicon_isa_find_card(int Mem, int Irq, char * Id)
 {
 	int primary = 1;
+	unsigned long amem;
 
 	if (!strlen(Id))
 		return -1;
@@ -138,24 +154,27 @@
 			 Mem, Id);
 		return -1;
 	}
-	if (check_shmem(Mem, RAMSIZE)) {
+	if (check_mem_region(Mem, RAMSIZE)) {
 		printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem);
 		return -1;
 	}
 
-        writew(0x55aa, Mem + 0x402);
-        if (readw(Mem + 0x402) != 0x55aa) primary = 0;
-	writew(0, Mem + 0x402);
-	if (readw(Mem + 0x402) != 0) primary = 0;
+	amem = (unsigned long) ioremap(Mem, RAMSIZE);
+        writew(0x55aa, amem + 0x402);
+        if (readw(amem + 0x402) != 0x55aa) primary = 0;
+	writew(0, amem + 0x402);
+	if (readw(amem + 0x402) != 0) primary = 0;
 
 	printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id);
 	if (primary) {
 		printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem);
-		writeb(0, Mem + 0x3ffe);
+		writeb(0, amem + 0x3ffe);
+		iounmap((unsigned char *)amem);
 		return EICON_CTYPE_ISAPRI;
 	} else {
 		printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem);
-		writeb(0, Mem + 0x400);
+		writeb(0, amem + 0x400);
+		iounmap((unsigned char *)amem);
 		return EICON_CTYPE_ISABRI;
 	}
 	return -1;
@@ -187,57 +206,65 @@
 		return -EFAULT;
 	}
 
+	if (card->type == EICON_CTYPE_ISAPRI)
+		card->ramsize  = RAMSIZE_P;
+	else
+		card->ramsize  = RAMSIZE;
+
+	if (check_mem_region(card->physmem, card->ramsize)) {
+		printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n",
+			card->physmem);
+		kfree(code);
+		return -EBUSY;
+	}
+	request_mem_region(card->physmem, card->ramsize, "Eicon ISA ISDN");
+	card->shmem = (eicon_isa_shmem *) ioremap(card->physmem, card->ramsize);
+#ifdef EICON_MCA_DEBUG
+	printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize);
+#endif
+	card->mvalid = 1;
+
 	switch(card->type) {
 		case EICON_CTYPE_S:
 		case EICON_CTYPE_SX:
 		case EICON_CTYPE_SCOM:
 		case EICON_CTYPE_QUADRO:
 		case EICON_CTYPE_ISABRI:
-			card->ramsize  = RAMSIZE;
 			card->intack   = (__u8 *)card->shmem + INTACK;
 			card->startcpu = (__u8 *)card->shmem + STARTCPU;
 			card->stopcpu  = (__u8 *)card->shmem + STOPCPU;
 			break;
 		case EICON_CTYPE_S2M:
 		case EICON_CTYPE_ISAPRI:
-			card->ramsize  = RAMSIZE_P;
 			card->intack   = (__u8 *)card->shmem + INTACK_P;
 			card->startcpu = (__u8 *)card->shmem + STARTCPU_P;
 			card->stopcpu  = (__u8 *)card->shmem + STOPCPU_P;
 			break;
 		default:
 			printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type);
+			eicon_isa_release_shmem(card);
+			kfree(code);
 			return -EINVAL;
 	}
 
-	/* Register shmem */
-	if (check_shmem((unsigned long)card->shmem, card->ramsize)) {
-		printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n",
-			(unsigned long)card->shmem);
-		kfree(code);
-		return -EBUSY;
-	}
-	request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN");
-#ifdef EICON_MCA_DEBUG
-	printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize);
-#endif
-	card->mvalid = 1;
-
 	/* clear any pending irq's */
 	readb(card->intack);
 #ifdef CONFIG_MCA
-	if (card->type == EICON_CTYPE_SCOM) {
-		outb_p(0,card->io+1);
-	}
-	else {
-		printk(KERN_WARNING "eicon_isa_boot: Card type yet not supported.\n");
-		return -EINVAL;
-	};
+	if (MCA_bus) {
+		if (card->type == EICON_CTYPE_SCOM) {
+			outb_p(0,card->io+1);
+		}
+		else {
+			printk(KERN_WARNING "eicon_isa_boot: Card type not supported yet.\n");
+			eicon_isa_release_shmem(card);
+			return -EINVAL;
+		};
 
 #ifdef EICON_MCA_DEBUG
 	printk(KERN_INFO "eicon_isa_boot: card->io      = %x.\n", card->io);
 	printk(KERN_INFO "eicon_isa_boot: card->irq     = %d.\n", (int)card->irq);
 #endif
+	}
 #else
 	/* set reset-line active */
 	writeb(0, card->stopcpu); 
@@ -269,7 +296,9 @@
 	/* Start CPU */
 	writeb(cbuf.boot_opt, &boot->ctrl);
 #ifdef CONFIG_MCA
-	outb_p(0, card->io);
+	if (MCA_bus) {
+		outb_p(0, card->io);
+	}
 #else 
 	writeb(0, card->startcpu); 
 #endif /* CONFIG_MCA */
@@ -320,7 +349,7 @@
         }
 	printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]); 
 	if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) {
-		tmp = eicon_addcard(card->type, (unsigned long)card->shmem, card->irq, 
+		tmp = eicon_addcard(card->type, card->physmem, card->irq, 
 					((eicon_card *)card->card)->regname);
 		printk(KERN_INFO "Eicon: %d adapters added\n", tmp);
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_isa.h linux/drivers/isdn/eicon/eicon_isa.h
--- v2.3.45/linux/drivers/isdn/eicon/eicon_isa.h	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/eicon/eicon_isa.h	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,10 @@
-/* $Id: eicon_isa.h,v 1.6 1999/11/15 19:37:04 keil Exp $
+/* $Id: eicon_isa.h,v 1.8 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * ISDN low-level module for Eicon active ISDN-Cards.
  *
- * Copyright 1998    by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1998,99 by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998      by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1998-2000 by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000 Cytronics & Melware (info@melware.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
@@ -21,6 +21,13 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_isa.h,v $
+ * Revision 1.8  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.7  1999/11/18 21:14:30  armin
+ * New ISA memory mapped IO
+ *
  * Revision 1.6  1999/11/15 19:37:04  keil
  * need config.h
  *
@@ -116,6 +123,7 @@
 typedef struct {
 	int               ramsize;
 	int               irq;	    /* IRQ                        */
+	unsigned long	  physmem;  /* physical memory address	  */
 #ifdef CONFIG_MCA
 	int		  io;	    /* IO-port for MCA brand      */
 #endif /* CONFIG_MCA */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_mod.c linux/drivers/isdn/eicon/eicon_mod.c
--- v2.3.45/linux/drivers/isdn/eicon/eicon_mod.c	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/eicon/eicon_mod.c	Tue Feb 15 11:40:42 2000
@@ -1,12 +1,12 @@
-/* $Id: eicon_mod.c,v 1.19 1999/11/12 13:21:44 armin Exp $
+/* $Id: eicon_mod.c,v 1.24 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * ISDN lowlevel-module for Eicon active cards.
  * 
- * Copyright 1997    by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1998,99 by Armin Schindler (mac@melware.de) 
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1997      by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1998-2000 by Armin Schindler (mac@melware.de) 
+ * Copyright 1999,2000 Cytronics & Melware (info@melware.de)
  * 
- * Thanks to    Eicon Technology Diehl GmbH & Co. oHG for
+ * Thanks to    Eicon Technology GmbH & Co. oHG for
  *              documents, informations and hardware.
  *
  *              Deutsche Telekom AG for S2M support.
@@ -31,6 +31,23 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_mod.c,v $
+ * Revision 1.24  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
+ * Revision 1.23  2000/01/20 19:55:34  keil
+ * Add FAX Class 1 support
+ *
+ * Revision 1.22  1999/11/27 12:56:19  armin
+ * Forgot some iomem changes for last ioremap compat.
+ *
+ * Revision 1.21  1999/11/25 11:35:10  armin
+ * Microchannel fix from Erik Weber (exrz73@ibm.net).
+ * Minor cleanup.
+ *
+ * Revision 1.20  1999/11/18 21:14:30  armin
+ * New ISA memory mapped IO
+ *
  * Revision 1.19  1999/11/12 13:21:44  armin
  * Bugfix of undefined reference with CONFIG_MCA
  *
@@ -46,7 +63,7 @@
  * Improved debug and log via readstat()
  *
  * Revision 1.15  1999/09/08 20:17:31  armin
- * Added microchannel patch from Erik Weber.
+ * Added microchannel patch from Erik Weber (exrz73@ibm.net).
  *
  * Revision 1.14  1999/09/06 07:29:35  fritz
  * Changed my mail-address.
@@ -123,7 +140,7 @@
 static eicon_card *cards = (eicon_card *) NULL;   /* glob. var , contains
                                                      start of card-list   */
 
-static char *eicon_revision = "$Revision: 1.19 $";
+static char *eicon_revision = "$Revision: 1.24 $";
 
 extern char *eicon_pci_revision;
 extern char *eicon_isa_revision;
@@ -144,7 +161,7 @@
 #endif
 static char *id           = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
 
-MODULE_DESCRIPTION(             "Driver for Eicon.Diehl active ISDN cards");
+MODULE_DESCRIPTION(             "Driver for Eicon active ISDN cards");
 MODULE_AUTHOR(                  "Armin Schindler");
 MODULE_SUPPORTED_DEVICE(        "ISDN subsystem");
 MODULE_PARM_DESC(id,   		"ID-String of first card");
@@ -659,7 +676,7 @@
 				break;
 			chan->l3prot = (c->arg >> 8);
 #ifdef CONFIG_ISDN_TTY_FAX
-			if (chan->l3prot == ISDN_PROTO_L3_FAX)
+			if (chan->l3prot == ISDN_PROTO_L3_FCLASS2)
 				chan->fax = c->parm.fax;
 #endif
 			return 0;
@@ -839,8 +856,7 @@
 }
 
 /* jiftime() copied from HiSax */
-inline int
-jiftime(char *s, long mark)
+static inline int jiftime(char *s, long mark)
 {
         s += 8;
 
@@ -1000,19 +1016,28 @@
                         case EICON_CTYPE_S:
                         case EICON_CTYPE_SX:
                         case EICON_CTYPE_SCOM:
-                                if (membase == -1)
-                                        membase = EICON_ISA_MEMBASE;
-                                if (irq == -1)
-                                        irq = EICON_ISA_IRQ;
-                                card->bus = EICON_BUS_MCA;
-                                card->hwif.isa.card = (void *)card;
-                                card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
-                                card->hwif.isa.master = 1;
-
-                                card->hwif.isa.irq = irq;
-                                card->hwif.isa.type = Type;
-                                card->nchannels = 2;
-                                card->interface.channels = 1;
+				if (MCA_bus) {
+	                                if (membase == -1)
+        	                                membase = EICON_ISA_MEMBASE;
+                	                if (irq == -1)
+                        	                irq = EICON_ISA_IRQ;
+	                                card->bus = EICON_BUS_MCA;
+        	                        card->hwif.isa.card = (void *)card;
+                	                card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+					card->hwif.isa.physmem = (unsigned long)membase;
+                        	        card->hwif.isa.master = 1;
+
+	                                card->hwif.isa.irq = irq;
+        	                        card->hwif.isa.type = Type;
+                	                card->nchannels = 2;
+                        	        card->interface.channels = 1;
+				} else {
+					printk(KERN_WARNING
+						"eicon (%s): no MCA bus detected.\n",
+						card->interface.id);
+					kfree(card);
+					return;
+				}
                                 break;
 #endif /* CONFIG_MCA */
 			case EICON_CTYPE_QUADRO:
@@ -1023,6 +1048,7 @@
                                 card->bus = EICON_BUS_ISA;
 				card->hwif.isa.card = (void *)card;
 				card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET);
+				card->hwif.isa.physmem = (unsigned long)(membase + (i+1) * EICON_ISA_QOFFSET);
 				card->hwif.isa.master = 0;
 				strcpy(card->interface.id, id);
 				if (id[strlen(id) - 1] == 'a') {
@@ -1067,7 +1093,7 @@
 					ISDN_FEATURE_L2_MODEM |
 					ISDN_FEATURE_L2_FAX | 
 					ISDN_FEATURE_L3_TRANSDSP |
-					ISDN_FEATURE_L3_FAX;
+					ISDN_FEATURE_L3_FCLASS2;
                                 card->hwif.pci.card = (void *)card;
 				card->hwif.pci.PCIreg = pcic->PCIreg;
 				card->hwif.pci.PCIcfg = pcic->PCIcfg;
@@ -1091,7 +1117,7 @@
 					ISDN_FEATURE_L2_MODEM |
 					ISDN_FEATURE_L2_FAX |
 					ISDN_FEATURE_L3_TRANSDSP |
-					ISDN_FEATURE_L3_FAX;
+					ISDN_FEATURE_L3_FCLASS2;
                                 card->hwif.pci.card = (void *)card;
                                 card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem;
 				card->hwif.pci.PCIreg = pcic->PCIreg;
@@ -1116,6 +1142,7 @@
 				card->bus = EICON_BUS_ISA;
 				card->hwif.isa.card = (void *)card;
 				card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+				card->hwif.isa.physmem = (unsigned long)membase;
 				card->hwif.isa.master = 1;
 				card->hwif.isa.irq = irq;
 				card->hwif.isa.type = Type;
@@ -1130,6 +1157,7 @@
                                 card->bus = EICON_BUS_ISA;
 				card->hwif.isa.card = (void *)card;
 				card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+				card->hwif.isa.physmem = (unsigned long)membase;
 				card->hwif.isa.master = 1;
 				card->hwif.isa.irq = irq;
 				card->hwif.isa.type = Type;
@@ -1151,14 +1179,15 @@
 		}
 		for (j=0; j< (card->nchannels + 1); j++) {
 			memset((char *)&card->bch[j], 0, sizeof(eicon_chan));
-			card->bch[j].plci = 0x8000;
-			card->bch[j].ncci = 0x8000;
+			card->bch[j].statectrl = 0;
 			card->bch[j].l2prot = ISDN_PROTO_L2_X75I;
 			card->bch[j].l3prot = ISDN_PROTO_L3_TRANS;
 			card->bch[j].e.D3Id = 0;
 			card->bch[j].e.B2Id = 0;
 			card->bch[j].e.Req = 0;
 			card->bch[j].No = j;
+			card->bch[j].tskb1 = NULL;
+			card->bch[j].tskb2 = NULL;
 			skb_queue_head_init(&card->bch[j].e.X);
 			skb_queue_head_init(&card->bch[j].e.R);
 		}
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_pci.c linux/drivers/isdn/eicon/eicon_pci.c
--- v2.3.45/linux/drivers/isdn/eicon/eicon_pci.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/eicon/eicon_pci.c	Tue Feb 15 11:40:42 2000
@@ -1,12 +1,12 @@
-/* $Id: eicon_pci.c,v 1.10 1999/08/22 20:26:49 calle Exp $
+/* $Id: eicon_pci.c,v 1.11 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * ISDN low-level module for Eicon active ISDN-Cards.
  * Hardware-specific code for PCI cards.
  *
- * Copyright 1998,99 by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998-2000 by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000 Cytronics & Melware (info@melware.de)
  *
- * Thanks to	Eicon Technology Diehl GmbH & Co. oHG for 
+ * Thanks to	Eicon Technology GmbH & Co. oHG for 
  *		documents, informations and hardware. 
  *
  *		Deutsche Telekom AG for S2M support.
@@ -26,6 +26,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_pci.c,v $
+ * Revision 1.11  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
  * Revision 1.10  1999/08/22 20:26:49  calle
  * backported changes from kernel 2.3.14:
  * - several #include "config.h" gone, others come.
@@ -77,7 +81,7 @@
 #include "eicon_pci.h"
 
 
-char *eicon_pci_revision = "$Revision: 1.10 $";
+char *eicon_pci_revision = "$Revision: 1.11 $";
 
 #if CONFIG_PCI	         /* intire stuff is only for PCI */
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/eicon/eicon_pci.h linux/drivers/isdn/eicon/eicon_pci.h
--- v2.3.45/linux/drivers/isdn/eicon/eicon_pci.h	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/eicon/eicon_pci.h	Tue Feb 15 11:40:42 2000
@@ -1,9 +1,9 @@
-/* $Id: eicon_pci.h,v 1.3 1999/03/29 11:19:51 armin Exp $
+/* $Id: eicon_pci.h,v 1.4 2000/01/23 21:21:23 armin Exp $
  *
- * ISDN low-level module for Eicon.Diehl active ISDN-Cards (PCI part).
+ * ISDN low-level module for Eicon active ISDN-Cards (PCI part).
  *
- * Copyright 1998,99 by Armin Schindler (mac@melware.de)
- * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * Copyright 1998-2000 by Armin Schindler (mac@melware.de)
+ * Copyright 1999,2000 Cytronics & Melware (info@melware.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
@@ -20,6 +20,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_pci.h,v $
+ * Revision 1.4  2000/01/23 21:21:23  armin
+ * Added new trace capability and some updates.
+ * DIVA Server BRI now supports data for ISDNLOG.
+ *
  * Revision 1.3  1999/03/29 11:19:51  armin
  * I/O stuff now in seperate file (eicon_io.c)
  * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.3.45/linux/drivers/isdn/hisax/Makefile	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/Makefile	Tue Feb 15 11:40:42 2000
@@ -137,6 +137,10 @@
 	 HFC_2BDS0 += hfc_pci.o
 endif
 
+ifeq ($(CONFIG_HISAX_HFC_SX),y)
+	 HFC_2BDS0 += hfc_sx.o
+endif
+
 ifeq ($(CONFIG_HISAX_NICCY),y)
 	 O_OBJS += niccy.o
 	 ISAC_OBJ := isac.o
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/arcofi.c linux/drivers/isdn/hisax/arcofi.c
--- v2.3.45/linux/drivers/isdn/hisax/arcofi.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/arcofi.c	Tue Feb 15 11:40:42 2000
@@ -1,12 +1,19 @@
-/* $Id: arcofi.c,v 1.8 1999/08/25 16:50:51 keil Exp $
+/* $Id: arcofi.c,v 1.10 1999/12/23 15:09:32 keil Exp $
 
  * arcofi.c   Ansteuerung ARCOFI 2165
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  *
  *
  * $Log: arcofi.c,v $
+ * Revision 1.10  1999/12/23 15:09:32  keil
+ * change email
+ *
+ * Revision 1.9  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.8  1999/08/25 16:50:51  keil
  * Fix bugs which cause 2.3.14 hangs (waitqueue init)
  *
@@ -83,7 +90,7 @@
 	if (event == ARCOFI_TIMEOUT) {
 		cs->dc.isac.arcofi_state = ARCOFI_NOP;
 		test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags);
-		wake_up_interruptible(&cs->dc.isac.arcofi_wait);
+		wake_up(&cs->dc.isac.arcofi_wait);
  		return(1);
 	}
 	switch (cs->dc.isac.arcofi_state) {
@@ -109,7 +116,7 @@
 							del_timer(&cs->dc.isac.arcofitimer);
 						}
 						cs->dc.isac.arcofi_state = ARCOFI_NOP;
-						wake_up_interruptible(&cs->dc.isac.arcofi_wait);
+						wake_up(&cs->dc.isac.arcofi_wait);
 					}
 				}
 			}
@@ -126,7 +133,7 @@
 						del_timer(&cs->dc.isac.arcofitimer);
 					}
 					cs->dc.isac.arcofi_state = ARCOFI_NOP;
-					wake_up_interruptible(&cs->dc.isac.arcofi_wait);
+					wake_up(&cs->dc.isac.arcofi_wait);
 				}
 			}
 			break;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/arcofi.h linux/drivers/isdn/hisax/arcofi.h
--- v2.3.45/linux/drivers/isdn/hisax/arcofi.h	Thu Aug 12 09:42:33 1999
+++ linux/drivers/isdn/hisax/arcofi.h	Tue Feb 15 11:40:42 2000
@@ -1,12 +1,15 @@
-/* $Id: arcofi.h,v 1.4 1999/07/01 08:11:18 keil Exp $
+/* $Id: arcofi.h,v 1.5 1999/12/23 15:09:32 keil Exp $
 
  * arcofi.h   Ansteuerung ARCOFI 2165
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  *
  *
  * $Log: arcofi.h,v $
+ * Revision 1.5  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.4  1999/07/01 08:11:18  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/asuscom.c linux/drivers/isdn/hisax/asuscom.c
--- v2.3.45/linux/drivers/isdn/hisax/asuscom.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/asuscom.c	Tue Feb 15 11:40:42 2000
@@ -1,4 +1,4 @@
-/* $Id: asuscom.c,v 1.8 1999/09/04 06:20:05 keil Exp $
+/* $Id: asuscom.c,v 1.9 1999/12/19 13:09:41 keil Exp $
 
  * asuscom.c     low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
  *
@@ -8,6 +8,10 @@
  *
  *
  * $Log: asuscom.c,v $
+ * Revision 1.9  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.8  1999/09/04 06:20:05  keil
  * Changes from kernel set_current_state()
  *
@@ -42,7 +46,7 @@
 
 extern const char *CardType[];
 
-const char *Asuscom_revision = "$Revision: 1.8 $";
+const char *Asuscom_revision = "$Revision: 1.9 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -291,13 +295,13 @@
 		byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
 	save_flags(flags);
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	if (cs->subtyp == ASUS_IPAC)
 		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
 	else
 		byteout(cs->hw.asus.adr, 0);	/* Reset Off */
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	if (cs->subtyp == ASUS_IPAC) {
 		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c
--- v2.3.45/linux/drivers/isdn/hisax/avm_pci.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/avm_pci.c	Tue Feb 15 11:40:42 2000
@@ -1,4 +1,4 @@
-/* $Id: avm_pci.c,v 1.12 1999/09/04 06:20:05 keil Exp $
+/* $Id: avm_pci.c,v 1.14 1999/12/19 13:09:41 keil Exp $
 
  * avm_pci.c    low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
  *              Thanks to AVM, Berlin for informations
@@ -7,6 +7,13 @@
  *
  *
  * $Log: avm_pci.c,v $
+ * Revision 1.14  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
+ * Revision 1.13  1999/12/03 12:10:14  keil
+ * Bugfix: Wrong channel use on hangup of channel 2
+ *
  * Revision 1.12  1999/09/04 06:20:05  keil
  * Changes from kernel set_current_state()
  *
@@ -56,7 +63,7 @@
 #include <linux/interrupt.h>
 
 extern const char *CardType[];
-static const char *avm_pci_rev = "$Revision: 1.12 $";
+static const char *avm_pci_rev = "$Revision: 1.14 $";
 
 #define  AVM_FRITZ_PCI		1
 #define  AVM_FRITZ_PNP		2
@@ -269,18 +276,26 @@
 	int hdlc = bcs->channel;
 
 	if (cs->debug & L1_DEB_HSCX)
-		debugl1(cs, "hdlc %c mode %d ichan %d",
-			'A' + hdlc, mode, bc);
-	bcs->mode = mode;
-	bcs->channel = bc;
+		debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d",
+			'A' + hdlc, bcs->mode, mode, hdlc, bc);
 	bcs->hw.hdlc.ctrl.ctrl = 0;
 	switch (mode) {
+		case (-1): /* used for init */
+			bcs->mode = 1;
+			bcs->channel = bc;
+			bc = 0;
 		case (L1_MODE_NULL):
+			if (bcs->mode == L1_MODE_NULL)
+				return;
 			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
 			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
 			write_ctrl(bcs, 5);
+			bcs->mode = L1_MODE_NULL;
+			bcs->channel = bc;
 			break;
 		case (L1_MODE_TRANS):
+			bcs->mode = mode;
+			bcs->channel = bc;
 			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
 			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
 			write_ctrl(bcs, 5);
@@ -290,6 +305,8 @@
 			hdlc_sched_event(bcs, B_XMTBUFREADY);
 			break;
 		case (L1_MODE_HDLC):
+			bcs->mode = mode;
+			bcs->channel = bc;
 			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
 			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
 			write_ctrl(bcs, 5);
@@ -695,8 +712,8 @@
 	cs->bcs[1].BC_SetStack = setstack_hdlc;
 	cs->bcs[0].BC_Close = close_hdlcstate;
 	cs->bcs[1].BC_Close = close_hdlcstate;
-	modehdlc(cs->bcs, 0, 0);
-	modehdlc(cs->bcs + 1, 0, 0);
+	modehdlc(cs->bcs, -1, 0);
+	modehdlc(cs->bcs + 1, -1, 1);
 }
 
 static void
@@ -734,11 +751,11 @@
 	save_flags(flags);
 	sti();
 	outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
 	outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 	printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/bkm_a4t.c linux/drivers/isdn/hisax/bkm_a4t.c
--- v2.3.45/linux/drivers/isdn/hisax/bkm_a4t.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/bkm_a4t.c	Tue Feb 15 11:40:42 2000
@@ -1,4 +1,4 @@
-/* $Id: bkm_a4t.c,v 1.8 1999/09/04 06:20:05 keil Exp $
+/* $Id: bkm_a4t.c,v 1.9 1999/12/19 13:09:41 keil Exp $
  * bkm_a4t.c    low level stuff for T-Berkom A4T
  *              derived from the original file sedlbauer.c
  *              derived from the original file niccy.c
@@ -7,6 +7,10 @@
  * Author       Roland Klabunde (R.Klabunde@Berkom.de)
  *
  * $Log: bkm_a4t.c,v $
+ * Revision 1.9  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.8  1999/09/04 06:20:05  keil
  * Changes from kernel set_current_state()
  *
@@ -48,7 +52,7 @@
 
 extern const char *CardType[];
 
-const char *bkm_a4t_revision = "$Revision: 1.8 $";
+const char *bkm_a4t_revision = "$Revision: 1.9 $";
 
 
 static inline u_char
@@ -231,11 +235,11 @@
 		sti();
 		/* Issue the I20 soft reset     */
 		pI20_Regs->i20SysControl = 0xFF;	/* all in */
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 		/* Remove the soft reset */
 		pI20_Regs->i20SysControl = sysRESET | 0xFF;
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 		/* Set our configuration */
 		pI20_Regs->i20SysControl = sysRESET | sysCFG;
@@ -246,14 +250,14 @@
 		    g_A4T_ISAC_RES |
 		    g_A4T_JADE_BOOTR |
 		    g_A4T_ISAR_BOOTR;
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 
 		/* Remove RESET state from ISDN */
 		pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES |
 						g_A4T_JADE_RES |
 						g_A4T_ISAR_RES);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10 * HZ) / 1000);
 		restore_flags(flags);
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/bkm_a8.c linux/drivers/isdn/hisax/bkm_a8.c
--- v2.3.45/linux/drivers/isdn/hisax/bkm_a8.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/bkm_a8.c	Tue Feb 15 11:40:42 2000
@@ -1,4 +1,4 @@
-/* $Id: bkm_a8.c,v 1.8 1999/09/04 06:20:05 keil Exp $
+/* $Id: bkm_a8.c,v 1.9 1999/12/19 13:09:41 keil Exp $
  * bkm_a8.c     low level stuff for Scitel Quadro (4*S0, passive)
  *              derived from the original file sedlbauer.c
  *              derived from the original file niccy.c
@@ -7,6 +7,10 @@
  * Author       Roland Klabunde (R.Klabunde@Berkom.de)
  *
  * $Log: bkm_a8.c,v $
+ * Revision 1.9  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.8  1999/09/04 06:20:05  keil
  * Changes from kernel set_current_state()
  *
@@ -49,7 +53,7 @@
 
 extern const char *CardType[];
 
-const char sct_quadro_revision[] = "$Revision: 1.8 $";
+const char sct_quadro_revision[] = "$Revision: 1.9 $";
 
 /* To survive the startup phase */
 typedef struct {
@@ -298,13 +302,13 @@
 
 			save_flags(flags);
 			sti();
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10 * HZ) / 1000);
 
 			/* Remove the soft reset */
 			wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
 
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10 * HZ) / 1000);
 			restore_flags(flags);
 		}
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.3.45/linux/drivers/isdn/hisax/callc.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/callc.c	Tue Feb 15 11:40:42 2000
@@ -1,4 +1,4 @@
-/* $Id: callc.c,v 2.39 1999/10/14 20:25:28 keil Exp $
+/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,10 @@
  *              Fritz Elfert
  *
  * $Log: callc.c,v $
+ * Revision 2.40  1999/12/19 12:59:56  keil
+ * fix leased line handling
+ * and cosmetics
+ *
  * Revision 2.39  1999/10/14 20:25:28  keil
  * add a statistic for error monitoring
  *
@@ -163,7 +167,7 @@
 #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module))
 #endif	/* MODULE */
 
-const char *lli_revision = "$Revision: 2.39 $";
+const char *lli_revision = "$Revision: 2.40 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -199,8 +203,7 @@
 /*
  * Find card with given driverId
  */
-static inline struct IsdnCardState
-*
+static inline struct IsdnCardState *
 hisax_findcard(int driverid)
 {
 	int i;
@@ -239,39 +242,39 @@
 }
 
 enum {
-        ST_NULL,                /*  0 inactive */
-        ST_OUT_DIAL,            /*  1 outgoing, SETUP send; awaiting confirm */
-        ST_IN_WAIT_LL,          /*  2 incoming call received; wait for LL confirm */
-        ST_IN_ALERT_SENT,       /*  3 incoming call received; ALERT send */
-        ST_IN_WAIT_CONN_ACK,    /*  4 incoming CONNECT send; awaiting CONN_ACK */
-        ST_WAIT_BCONN,          /*  5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
-        ST_ACTIVE,              /*  6 active, b channel prot. established */
-        ST_WAIT_BRELEASE,       /*  7 call clear. (initiator), awaiting b channel prot. rel. */
-        ST_WAIT_BREL_DISC,      /*  8 call clear. (receiver), DISCONNECT req. received */
-        ST_WAIT_DCOMMAND,       /*  9 call clear. (receiver), awaiting DCHANNEL message */
-        ST_WAIT_DRELEASE,       /* 10 DISCONNECT sent, awaiting RELEASE */
-        ST_WAIT_D_REL_CNF,      /* 11 RELEASE sent, awaiting RELEASE confirm */
-        ST_IN_PROCEED_SEND,     /* 12 incoming call, proceeding send */ 
+	ST_NULL,		/*  0 inactive */
+	ST_OUT_DIAL,		/*  1 outgoing, SETUP send; awaiting confirm */
+	ST_IN_WAIT_LL,		/*  2 incoming call received; wait for LL confirm */
+	ST_IN_ALERT_SENT,	/*  3 incoming call received; ALERT send */
+	ST_IN_WAIT_CONN_ACK,	/*  4 incoming CONNECT send; awaiting CONN_ACK */
+	ST_WAIT_BCONN,		/*  5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+	ST_ACTIVE,		/*  6 active, b channel prot. established */
+	ST_WAIT_BRELEASE,	/*  7 call clear. (initiator), awaiting b channel prot. rel. */
+	ST_WAIT_BREL_DISC,	/*  8 call clear. (receiver), DISCONNECT req. received */
+	ST_WAIT_DCOMMAND,	/*  9 call clear. (receiver), awaiting DCHANNEL message */
+	ST_WAIT_DRELEASE,	/* 10 DISCONNECT sent, awaiting RELEASE */
+	ST_WAIT_D_REL_CNF,	/* 11 RELEASE sent, awaiting RELEASE confirm */
+	ST_IN_PROCEED_SEND,	/* 12 incoming call, proceeding send */ 
 };
   
 
 #define STATE_COUNT (ST_IN_PROCEED_SEND + 1)
 
-  static char *strState[] =
-  {
-        "ST_NULL",
-        "ST_OUT_DIAL",
-        "ST_IN_WAIT_LL",
-        "ST_IN_ALERT_SENT",
-        "ST_IN_WAIT_CONN_ACK",
-        "ST_WAIT_BCONN",
-        "ST_ACTIVE",
+static char *strState[] =
+{
+	"ST_NULL",
+	"ST_OUT_DIAL",
+	"ST_IN_WAIT_LL",
+	"ST_IN_ALERT_SENT",
+	"ST_IN_WAIT_CONN_ACK",
+	"ST_WAIT_BCONN",
+	"ST_ACTIVE",
 	"ST_WAIT_BRELEASE",
 	"ST_WAIT_BREL_DISC",
 	"ST_WAIT_DCOMMAND",
 	"ST_WAIT_DRELEASE",
 	"ST_WAIT_D_REL_CNF",
-        "ST_IN_PROCEED_SEND",
+	"ST_IN_PROCEED_SEND",
 };
 
 enum {
@@ -333,19 +336,19 @@
 static inline void
 HL_LL(struct Channel *chanp, int command)
 {
-        isdn_ctrl ic;
+	isdn_ctrl ic;
 
-        ic.driver = chanp->cs->myid;
-        ic.command = command;
-        ic.arg = chanp->chan;
-        chanp->cs->iif.statcallb(&ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = command;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 }
 
 static inline void
 lli_deliver_cause(struct Channel *chanp)
 {
-        isdn_ctrl ic;
- 
+	isdn_ctrl ic;
+
 	if (chanp->proc->para.cause == NO_CAUSE)
 		return;
 	ic.driver = chanp->cs->myid;
@@ -363,42 +366,42 @@
 static inline void
 lli_close(struct FsmInst *fi)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        FsmChangeState(fi, ST_NULL);
-        chanp->Flags = 0;
-        chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
- static void
+static void
 lli_leased_in(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
 
-        isdn_ctrl ic;
-        int ret;
- 
-        chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
-        FsmChangeState(fi, ST_IN_WAIT_LL);
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_ICALL_LEASED");
-        ic.driver = chanp->cs->myid;
+	if (!chanp->leased)
+		return;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	FsmChangeState(fi, ST_IN_WAIT_LL);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_ICALL_LEASED");
+	ic.driver = chanp->cs->myid;
 	ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
-        ic.arg = chanp->chan;
-        ic.parm.setup.si1 = 7;
-        ic.parm.setup.si2 = 0;
-        ic.parm.setup.plan = 0;
-        ic.parm.setup.screen = 0;
-        sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
-        sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
-        ret = chanp->cs->iif.statcallb(&ic);
-        if (chanp->debug & 1)
-                link_debug(chanp, 1, "statcallb ret=%d", ret);
- 
-        if (!ret) {
-                chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
-                FsmChangeState(fi, ST_NULL);
-        }
+	ic.arg = chanp->chan;
+	ic.parm.setup.si1 = 7;
+	ic.parm.setup.si2 = 0;
+	ic.parm.setup.plan = 0;
+	ic.parm.setup.screen = 0;
+	sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
+	sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
+	ret = chanp->cs->iif.statcallb(&ic);
+	if (chanp->debug & 1)
+		link_debug(chanp, 1, "statcallb ret=%d", ret);
+	if (!ret) {
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+		FsmChangeState(fi, ST_NULL);
+	}
 }
 
 
@@ -408,14 +411,14 @@
 static void
 lli_init_bchan_out(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
- 
-        FsmChangeState(fi, ST_WAIT_BCONN);
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_DCONN");
-        HL_LL(chanp, ISDN_STAT_DCONN);
-        init_b_st(chanp, 0);
-        chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DCONN");
+	HL_LL(chanp, ISDN_STAT_DCONN);
+	init_b_st(chanp, 0);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -427,14 +430,13 @@
 	FsmDelTimer(&chanp->dial_timer, 73);
 	chanp->l2_active_protocol = chanp->l2_protocol;
 	chanp->incoming = 0;
-
-        chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
-        if (chanp->leased) {
-                lli_init_bchan_out(fi, event, arg);
-        } else {
-                FsmChangeState(fi, ST_OUT_DIAL);
-                chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp);
-        }
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (chanp->leased) {
+		lli_init_bchan_out(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_OUT_DIAL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp);
+	}
 }
 
 static void
@@ -442,18 +444,17 @@
 {
 	struct Channel *chanp = fi->userdata;
 
-        FsmDelTimer(&chanp->drel_timer, 60);
-        FsmDelTimer(&chanp->dial_timer, 73);
-        chanp->l2_active_protocol = chanp->l2_protocol;
-        chanp->incoming = 0;
- 
-        chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
-        if (chanp->leased) {
-                lli_init_bchan_out(fi, event, arg);
-        } else {
-                FsmChangeState(fi, ST_OUT_DIAL);
-                chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp);
-        }
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (chanp->leased) {
+		lli_init_bchan_out(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_OUT_DIAL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp);
+	}
 }
 
 static void
@@ -521,15 +522,15 @@
 				FsmChangeState(fi, ST_IN_ALERT_SENT);
 				chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 				break;
-	      	        case 5: /* direct redirect */
-		        case 4: /* Proceeding desired */
+			case 5: /* direct redirect */
+			case 4: /* Proceeding desired */
 				FsmDelTimer(&chanp->drel_timer, 61);
 				FsmChangeState(fi, ST_IN_PROCEED_SEND);
-                                chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
-                                if (ret == 5)
-				 { chanp->setup = ic.parm.setup;
-				   chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
-                                 }   
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
+				if (ret == 5) {
+					chanp->setup = ic.parm.setup;
+					chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+				}
 				break;
 			case 2:	/* Rejecting Call */
 				break;
@@ -590,17 +591,17 @@
 static void
 lli_setup_rsp(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
- 
-        if (chanp->leased) {
-                lli_init_bchan_in(fi, event, arg);
-        } else {
-                FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_init_bchan_in(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
 #ifdef WANT_ALERT
-                chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 #endif
-               chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
-       }
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+	}
 }
 
 /* Call suspend */
@@ -616,51 +617,84 @@
 /* Call clearing */
 
 static void
+lli_leased_hup(struct FsmInst *fi, struct Channel *chanp)
+{
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "L0010");
+	chanp->cs->iif.statcallb(&ic);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	lli_close(fi);
+}
+
+static void
 lli_disconnect_req(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
-	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+			chanp->proc);
+	}
 }
 
 static void
 lli_disconnect_reject(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        FsmChangeState(fi, ST_WAIT_DRELEASE);
-        chanp->proc->para.cause = 0x15;         /* Call Rejected */
-        chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		chanp->proc->para.cause = 0x15;	/* Call Rejected */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+			chanp->proc);
+	}
 }
 
 static void
 lli_dhup_close(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_DHUP");
-        lli_deliver_cause(chanp);
-        HL_LL(chanp, ISDN_STAT_DHUP);
- 
-        lli_close(fi);
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, "STAT_DHUP");
+		lli_deliver_cause(chanp);
+		HL_LL(chanp, ISDN_STAT_DHUP);
+		lli_close(fi);
+	}
 }
 
 static void
 lli_reject_req(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+		return;
+	}
 #ifndef ALERT_REJECT
-        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);
+	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
-        FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
-        FsmChangeState(fi, ST_IN_ALERT_SENT);
-        chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+	FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
+	FsmChangeState(fi, ST_IN_ALERT_SENT);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 #endif
 }
 
@@ -678,54 +712,45 @@
 lli_start_disc(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
-	isdn_ctrl ic;
 
 	if (chanp->leased) {
-		ic.command = ISDN_STAT_CAUSE;
-		ic.arg = chanp->chan;
-		sprintf(ic.parm.num, "L0010");
-		chanp->cs->iif.statcallb(&ic);
-		if (chanp->debug & 1)
-			link_debug(chanp, 0, "STAT_DHUP");
-		HL_LL(chanp, ISDN_STAT_DHUP);
-		lli_close(fi);
+		lli_leased_hup(fi, chanp);
 	} else {
-	        lli_disconnect_req(fi, event, arg);
+		lli_disconnect_req(fi, event, arg);
 	}
 }
 
 static void
 lli_rel_b_disc(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
- 
-        release_b_st(chanp);
-        lli_start_disc(fi, event, arg);
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_start_disc(fi, event, arg);
 }
 
 static void
 lli_bhup_disc(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
-  
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_BHUP");
-        HL_LL(chanp, ISDN_STAT_BHUP);
+	struct Channel *chanp = fi->userdata;
  
-        lli_rel_b_disc(fi, event, arg);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_disc(fi, event, arg);
 }
 
 static void
 lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        FsmChangeState(fi, ST_WAIT_DCOMMAND);
-        chanp->data_open = 0;
-                if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_BHUP");
-        HL_LL(chanp, ISDN_STAT_BHUP);
-        release_b_st(chanp);
+	FsmChangeState(fi, ST_WAIT_DCOMMAND);
+	chanp->data_open = 0;
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	release_b_st(chanp);
 }
 
 static void
@@ -742,63 +767,65 @@
 static void
 lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        release_b_st(chanp);
-        lli_dhup_close(fi, event, arg);
+	release_b_st(chanp);
+	lli_dhup_close(fi, event, arg);
 }
 
 static void
 lli_bhup_dhup(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_BHUP");
-        HL_LL(chanp, ISDN_STAT_BHUP);
- 
-        lli_rel_b_dhup(fi, event, arg);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_dhup(fi, event, arg);
 }
 
 static void
 lli_abort(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        chanp->data_open = 0;
-        chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
- 
-        lli_bhup_dhup(fi, event, arg);
+	chanp->data_open = 0;
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	lli_bhup_dhup(fi, event, arg);
 }
  
 static void
 lli_release_req(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
- 
-        FsmChangeState(fi, ST_WAIT_D_REL_CNF);
-        chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc);
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST,
+			chanp->proc);
+	}
 }
 
 static void
 lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
- 
-        release_b_st(chanp);
-        lli_release_req(fi, event, arg);
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_release_req(fi, event, arg);
 }
 
 static void
 lli_bhup_release_req(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
- 
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_BHUP");
-        HL_LL(chanp, ISDN_STAT_BHUP);
+	struct Channel *chanp = fi->userdata;
  
-        lli_rel_b_release_req(fi, event, arg);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_release_req(fi, event, arg);
 }
 
 
@@ -825,7 +852,7 @@
 
 	if (chanp->debug & 1)
 		link_debug(chanp, 0, "STAT_DHUP");
-        HL_LL(chanp, ISDN_STAT_DHUP); 
+	HL_LL(chanp, ISDN_STAT_DHUP); 
 }
 
 static void
@@ -836,67 +863,65 @@
 	if (chanp->debug & 1)
 		link_debug(chanp, 0, "STAT_DHUP");
 	HL_LL(chanp, ISDN_STAT_DHUP);
-        lli_close(fi); 
+	lli_close(fi); 
 }
 
 static void
 lli_error(struct FsmInst *fi, int event, void *arg)
 {
-        FsmChangeState(fi, ST_WAIT_DRELEASE);
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
 }
 
 static void
 lli_failure_l(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
-        isdn_ctrl ic;
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
 
-        FsmChangeState(fi, ST_NULL);
-        ic.driver = chanp->cs->myid;
-        ic.command = ISDN_STAT_CAUSE;
-        ic.arg = chanp->chan;
-        sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
-        chanp->cs->iif.statcallb(&ic);
-        HL_LL(chanp, ISDN_STAT_DHUP);
-        chanp->Flags = 0;
-        chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+	FsmChangeState(fi, ST_NULL);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
+	chanp->cs->iif.statcallb(&ic);
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	chanp->Flags = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
 static void
 lli_rel_b_fail(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        release_b_st(chanp);
-        lli_failure_l(fi, event, arg);
+	release_b_st(chanp);
+	lli_failure_l(fi, event, arg);
 }
 
 static void
 lli_bhup_fail(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        if (chanp->debug & 1)
-                link_debug(chanp, 0, "STAT_BHUP");
-        HL_LL(chanp, ISDN_STAT_BHUP);
- 
-        lli_rel_b_fail(fi, event, arg);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_fail(fi, event, arg);
 }
 
 static void
 lli_failure_a(struct FsmInst *fi, int event, void *arg)
 {
-        struct Channel *chanp = fi->userdata;
+	struct Channel *chanp = fi->userdata;
 
-        chanp->data_open = 0;
-        chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
- 
-        lli_bhup_fail(fi, event, arg);
+	chanp->data_open = 0;
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	lli_bhup_fail(fi, event, arg);
 }
 
-  /* *INDENT-OFF* */
-  static struct FsmNode fnlist[] HISAX_INITDATA =
-  {
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] HISAX_INITDATA =
+{
         {ST_NULL,               EV_DIAL,                lli_prep_dialout},
         {ST_NULL,               EV_RESUME,              lli_resume},
         {ST_NULL,               EV_SETUP_IND,           lli_deliver_call},
@@ -959,10 +984,9 @@
         {ST_WAIT_D_REL_CNF,     EV_RELEASE,             lli_dhup_close},
         {ST_WAIT_D_REL_CNF,     EV_DIAL,                lli_dchan_not_ready},
 };
-  /* *INDENT-ON* */
-
+/* *INDENT-ON* */
 
-  #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
 
 HISAX_INITFUNC(void
 CallcNew(void))
@@ -985,9 +1009,9 @@
 {
 	struct PStack *st = chanp->b_st;
 
-        if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) {
-                chanp->bcs->BC_Close(chanp->bcs);
-                switch (chanp->l2_active_protocol) {
+	if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) {
+		chanp->bcs->BC_Close(chanp->bcs);
+		switch (chanp->l2_active_protocol) {
 			case (ISDN_PROTO_L2_X75I):
 				releasestack_isdnl2(st);
 				break;
@@ -995,10 +1019,10 @@
 			case (ISDN_PROTO_L2_TRANS):
 			case (ISDN_PROTO_L2_MODEM):
 			case (ISDN_PROTO_L2_FAX):
-                                releasestack_transl2(st);
-                                break;
-                }
-        } 
+				releasestack_transl2(st);
+				break;
+		}
+	} 
 }
 
 struct Channel
@@ -1013,10 +1037,10 @@
 	else
 		i=0;
 
-        if (!bch) 
-         { i = 2; /* virtual channel */
-           chanp += 2;
-         }
+	if (!bch) {
+		i = 2; /* virtual channel */
+		chanp += 2;
+	}
 
 	while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) {
 		if (chanp->fi.state == ST_NULL)
@@ -1025,17 +1049,17 @@
 		i++;
 	}
 
-        if (bch) /* number of channels is limited */ 
-         { i = 2; /* virtual channel */
-           chanp = st->lli.userdata;
-	   chanp += i;
-	   while (i < (2 + MAX_WAITING_CALLS)) {
-		if (chanp->fi.state == ST_NULL)
-			return (chanp);
-		chanp++;
-		i++;
-	   }
-         }
+	if (bch) /* number of channels is limited */ {
+		i = 2; /* virtual channel */
+		chanp = st->lli.userdata;
+		chanp += i;
+		while (i < (2 + MAX_WAITING_CALLS)) {
+			if (chanp->fi.state == ST_NULL)
+				return (chanp);
+			chanp++;
+			i++;
+		}
+	}
 	return (NULL);
 }
 
@@ -1053,19 +1077,19 @@
 dchan_l3l4(struct PStack *st, int pr, void *arg)
 {
 	struct l3_process *pc = arg;
-        struct IsdnCardState *cs = st->l1.hardware;
+	struct IsdnCardState *cs = st->l1.hardware;
 	struct Channel *chanp;
 
-        if(!pc)
-                return;
- 
-        if (pr == (CC_SETUP | INDICATION)) {
-                if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) {
-                        pc->para.cause = 0x11;          /* User busy */
-                        pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc);
-                } else {
-                        chanp->proc = pc;
-                        pc->chan = chanp;
+	if(!pc)
+		return;
+
+	if (pr == (CC_SETUP | INDICATION)) {
+		if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) {
+			pc->para.cause = 0x11;	/* User busy */
+			pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc);
+		} else {
+			chanp->proc = pc;
+			pc->chan = chanp;
 			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
 		}
 		return;
@@ -1121,8 +1145,8 @@
 			break;
 		case (CC_REDIR | INDICATION):
 			stat_redir_result(cs, chanp->chan, pc->redir_result); 
-			break;                         
-		default:
+			break;
+			default:
 			if (chanp->debug & 0x800) {
 				HiSax_putstatus(chanp->cs, "Ch",
 					"%d L3->L4 unknown primitiv %#x",
@@ -1147,7 +1171,7 @@
 	(*stp)->l2.l2l1 = dummy_pstack;
 	(*stp)->l2.l2l3 = dummy_pstack;
 	(*stp)->l3.l3l2 = dummy_pstack;
-        (*stp)->l3.l3ml3 = dummy_pstack;
+	(*stp)->l3.l3ml3 = dummy_pstack;
 	(*stp)->l3.l3l4 = dummy_pstack;
 	(*stp)->lli.l4l3 = dummy_pstack;
 	(*stp)->ma.layer = dummy_pstack;
@@ -1230,8 +1254,8 @@
 }
 
 int
-CallcNewChan(struct IsdnCardState *csta)
-{       int i;
+CallcNewChan(struct IsdnCardState *csta) {
+	int i;
 
 	chancount += 2;
 	init_chan(0, csta);
@@ -1240,8 +1264,7 @@
 
 	for (i = 0; i < MAX_WAITING_CALLS; i++) 
 		init_chan(i+2,csta);
-	printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n");        
-
+	printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n");
 	if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
 		printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
 		csta->channel->d_st->lli.l4l3(csta->channel->d_st,
@@ -1272,13 +1295,13 @@
 	for (i = 0; i < 2; i++) {
 		FsmDelTimer(&csta->channel[i].drel_timer, 74);
 		FsmDelTimer(&csta->channel[i].dial_timer, 75);
-                if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
-                        release_d_st(csta->channel + i);
-                if (csta->channel[i].b_st) {
-                        release_b_st(csta->channel + i);
-                        kfree(csta->channel[i].b_st);
-                        csta->channel[i].b_st = NULL;
-                } else
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
+			release_d_st(csta->channel + i);
+		if (csta->channel[i].b_st) {
+			release_b_st(csta->channel + i);
+			kfree(csta->channel[i].b_st);
+			csta->channel[i].b_st = NULL;
+		} else
 			printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
 		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
 			release_d_st(csta->channel + i);
@@ -1311,7 +1334,7 @@
 			break;
 		default:
 			printk(KERN_WARNING "lldata_handler unknown primitive %#x\n",
-			       pr);
+				pr);
 			break;
 	}
 }
@@ -1341,7 +1364,7 @@
 			break;
 		default:
 			printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n",
-			       pr);
+				pr);
 			break;
 	}
 }
@@ -1444,7 +1467,7 @@
 			break;
 		default:
 			printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n",
-			       pr);
+				pr);
 			break;
 	}
 }
@@ -1598,7 +1621,7 @@
 	if (!csta) {
 		printk(KERN_ERR
 		"HiSax: if_command %d called with invalid driverId %d!\n",
-		       ic->command, ic->driver);
+			ic->command, ic->driver);
 		return -ENODEV;
 	}
 	switch (ic->command) {
@@ -1771,11 +1794,18 @@
 					num = *(unsigned int *) ic->parm.num;
 					chanp = csta->channel + (num & 1);
 					num = num >>1;
-					test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
-					chanp->d_st->l2.tei = num;
-					HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
-					printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
-						num);
+					if (num == 127) {
+						test_and_clear_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+						chanp->d_st->l2.tei = -1;
+						HiSax_putstatus(csta, "set card ", "in VAR TEI mode");
+						printk(KERN_DEBUG "HiSax: set card in VAR TEI mode\n");
+					} else {
+						test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+						chanp->d_st->l2.tei = num;
+						HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+						printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+							num);
+					}
 					chanp->d_st->lli.l4l3(chanp->d_st,
 						DL_ESTABLISH | REQUEST, NULL);
 					break;
@@ -1811,7 +1841,7 @@
 					if (csta->auxcmd)
 						return(csta->auxcmd(csta, ic));
 					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
-					       (int) ic->arg);
+						(int) ic->arg);
 					return (-EINVAL);
 			}
 			break;
@@ -1839,11 +1869,11 @@
 			break;
 
 		/* protocol specific io commands */
-	        case (ISDN_CMD_PROT_IO):
+		case (ISDN_CMD_PROT_IO):
 			for (st = csta->stlist; st; st = st->next)
 				if (st->protocol == (ic->arg & 0xFF))
 					return(st->lli.l4l3_proto(st, ic));
-			return(-EINVAL);                     
+			return(-EINVAL);
 			break;
 		default:
 			if (csta->auxcmd)
@@ -1865,7 +1895,7 @@
 
 	if (!csta) {
 		printk(KERN_ERR
-		    "HiSax: if_sendbuf called with invalid driverId!\n");
+			"HiSax: if_sendbuf called with invalid driverId!\n");
 		return -ENODEV;
 	}
 	chanp = csta->channel + chan;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.3.45/linux/drivers/isdn/hisax/config.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/config.c	Tue Feb 15 11:40:42 2000
@@ -1,10 +1,21 @@
-/* $Id: config.c,v 2.40 1999/10/30 13:09:45 keil Exp $
+/* $Id: config.c,v 2.43 2000/01/20 19:49:36 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
  *
  * $Log: config.c,v $
+ * Revision 2.43  2000/01/20 19:49:36  keil
+ * Support teles 13.3c vendor version 2.1
+ *
+ * Revision 2.42  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
+ * Revision 2.41  1999/11/18 00:00:43  werner
+ *
+ * Added support for HFC-S+ and HFC-SP cards
+ *
  * Revision 2.40  1999/10/30 13:09:45  keil
  * Version 3.3c
  *
@@ -202,6 +213,7 @@
  *   34	Gazel ISDN cards
  *   35 HFC 2BDS0 PCI           none
  *   36 Winbond 6692 PCI        none
+ *   37 HFC 2BDS0 S+/SP         p0=irq p1=iobase
  *
  * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
  *
@@ -217,6 +229,7 @@
  "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI",
  "Sedlbauer Speed Fax +", "Siemens I-Surf", "Acer P10", "HST Saphir",
  "Telekom A4T", "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692",
+ "HFC 2BDS0 SX",
 };
 
 void HiSax_closecard(int cardnr);
@@ -352,6 +365,13 @@
 #define DEFAULT_CFG {0,0,0,0}
 #endif
 
+#ifdef CONFIG_HISAX_HFC_SX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_SX
+#define DEFAULT_CFG {5,0x2E0,0,0}
+#endif
+
 
 #ifdef CONFIG_HISAX_AMD7930
 #undef DEFAULT_CARD
@@ -529,9 +549,9 @@
 
 	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
 #ifdef MODULE
-	printk(KERN_INFO "HiSax: Version 3.3c (module)\n");
+	printk(KERN_INFO "HiSax: Version 3.3d (module)\n");
 #else
-	printk(KERN_INFO "HiSax: Version 3.3c (kernel)\n");
+	printk(KERN_INFO "HiSax: Version 3.3d (kernel)\n");
 #endif
 	strcpy(tmp, l1_revision);
 	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
@@ -687,6 +707,10 @@
 extern int setup_hfcpci(struct IsdnCard *card);
 #endif
 
+#if CARD_HFC_SX
+extern int setup_hfcsx(struct IsdnCard *card);
+#endif
+
 #if CARD_AMD7930
 extern int setup_amd7930(struct IsdnCard *card);
 #endif
@@ -994,7 +1018,7 @@
 	while (cnt) {
 		cs->cardmsg(cs, CARD_INIT, NULL);
 		sti();
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		/* Timeout 10ms */
 		schedule_timeout((10*HZ)/1000);
 		restore_flags(flags);
@@ -1208,6 +1232,11 @@
 				ret = setup_hfcpci(card);
 				break;
 #endif
+#if CARD_HFC_SX
+		        case ISDN_CTYPE_HFC_SX: 
+				ret = setup_hfcsx(card);
+				break;
+#endif
 #if CARD_NICCY
 			case ISDN_CTYPE_NICCY:
 				ret = setup_niccy(card);
@@ -1515,6 +1544,7 @@
 			case ISDN_CTYPE_FRITZPCI:
 			case ISDN_CTYPE_HSTSAPHIR:
 			case ISDN_CTYPE_GAZEL:
+		        case ISDN_CTYPE_HFC_SX:
 				cards[i].para[0] = irq[i];
 				cards[i].para[1] = io[i];
 				break;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c
--- v2.3.45/linux/drivers/isdn/hisax/diva.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/diva.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: diva.c,v 1.17 1999/09/04 06:20:06 keil Exp $
+/* $Id: diva.c,v 1.18 1999/12/19 13:09:41 keil Exp $
 
  * diva.c     low level stuff for Eicon.Diehl Diva Family ISDN cards
  *
@@ -12,6 +12,10 @@
  *
  *
  * $Log: diva.c,v $
+ * Revision 1.18  1999/12/19 13:09:41  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.17  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -80,7 +84,7 @@
 
 extern const char *CardType[];
 
-const char *Diva_revision = "$Revision: 1.17 $";
+const char *Diva_revision = "$Revision: 1.18 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -750,30 +754,30 @@
 	sti();
 	if (cs->subtyp == DIVA_IPAC_ISA) {
 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
 	} else if (cs->subtyp == DIVA_IPAC_PCI) {
 		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
 					PITA_MISC_REG);
 		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		*ireg = PITA_PARA_MPX_MODE;
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
 	} else { /* DIVA 2.0 */
 		cs->hw.diva.ctrl_reg = 0;        /* Reset On */
 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000);
 		if (cs->subtyp == DIVA_ISA)
 			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c
--- v2.3.45/linux/drivers/isdn/hisax/elsa.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/elsa.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: elsa.c,v 2.19 1999/09/04 06:20:06 keil Exp $
+/* $Id: elsa.c,v 2.20 1999/12/19 13:09:42 keil Exp $
 
  * elsa.c     low level stuff for Elsa isdn cards
  *
@@ -14,6 +14,10 @@
  *              for ELSA PCMCIA support
  *
  * $Log: elsa.c,v $
+ * Revision 2.20  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 2.19  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -99,7 +103,7 @@
 
 extern const char *CardType[];
 
-const char *Elsa_revision = "$Revision: 2.19 $";
+const char *Elsa_revision = "$Revision: 2.20 $";
 const char *Elsa_Types[] =
 {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
  "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", 
@@ -578,10 +582,10 @@
 		save_flags(flags);
 		sti();
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
 		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
 		restore_flags(flags);
@@ -785,7 +789,7 @@
 				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
 				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
 				byteout(cs->hw.elsa.timer, 0);
-				set_current_state(TASK_INTERRUPTIBLE);
+				set_current_state(TASK_UNINTERRUPTIBLE);
 				schedule_timeout((110*HZ)/1000);
 				restore_flags(flags);
 				cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c
--- v2.3.45/linux/drivers/isdn/hisax/fsm.c	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/hisax/fsm.c	Tue Feb 15 11:40:43 2000
@@ -1,12 +1,15 @@
-/* $Id: fsm.c,v 1.10 1998/11/15 23:54:39 keil Exp $
+/* $Id: fsm.c,v 1.11 1999/12/23 15:09:32 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
  * $Log: fsm.c,v $
+ * Revision 1.11  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.10  1998/11/15 23:54:39  keil
  * changes from 2.0
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c
--- v2.3.45/linux/drivers/isdn/hisax/hfc_2bds0.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/hfc_2bds0.c	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,14 @@
-/* $Id: hfc_2bds0.c,v 1.10 1999/10/14 20:25:28 keil Exp $
+/* $Id: hfc_2bds0.c,v 1.11 1999/12/23 15:09:32 keil Exp $
  *
  *  specific routines for CCD's HFC 2BDS0
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: hfc_2bds0.c,v $
+ * Revision 1.11  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.10  1999/10/14 20:25:28  keil
  * add a statistic for error monitoring
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_2bds0.h linux/drivers/isdn/hisax/hfc_2bds0.h
--- v2.3.45/linux/drivers/isdn/hisax/hfc_2bds0.h	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/hfc_2bds0.h	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,14 @@
-/* $Id: hfc_2bds0.h,v 1.2 1998/02/02 13:26:15 keil Exp $
+/* $Id: hfc_2bds0.h,v 1.3 1999/12/23 15:09:32 keil Exp $
 
  *  specific defines for CCD's HFC 2BDS0
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: hfc_2bds0.h,v $
+ * Revision 1.3  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.2  1998/02/02 13:26:15  keil
  * New
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c
--- v2.3.45/linux/drivers/isdn/hisax/hfc_2bs0.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/hfc_2bs0.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: hfc_2bs0.c,v 1.10 1999/10/14 20:25:28 keil Exp $
+/* $Id: hfc_2bs0.c,v 1.12 1999/12/19 14:17:12 keil Exp $
 
  *  specific routines for CCD's HFC 2BS0
  *
@@ -6,6 +6,13 @@
  *
  *
  * $Log: hfc_2bs0.c,v $
+ * Revision 1.12  1999/12/19 14:17:12  keil
+ * fix compiler warning
+ *
+ * Revision 1.11  1999/11/21 12:41:18  werner
+ *
+ * Implemented full audio support
+ *
  * Revision 1.10  1999/10/14 20:25:28  keil
  * add a statistic for error monitoring
  *
@@ -210,7 +217,7 @@
 		WaitForBusy(cs);
 		return (NULL);
 	}
-	if (count < 4) {
+	if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) {
 		if (cs->debug & L1_DEB_WARN)
 			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
 		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
@@ -225,47 +232,55 @@
 #endif
 		return (NULL);
 	}
-	if (!(skb = dev_alloc_skb(count - 3)))
+	if (bcs->mode == L1_MODE_TRANS)
+	  count -= 1;
+	else
+	  count -= 3;
+	if (!(skb = dev_alloc_skb(count)))
 		printk(KERN_WARNING "HFC: receive out of memory\n");
 	else {
-		ptr = skb_put(skb, count - 3);
+		ptr = skb_put(skb, count);
 		idx = 0;
 		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
 		while ((idx < count - 3) && WaitNoBusy(cs)) {
 			*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
 			idx++;
 		}
-		if (idx != count - 3) {
+		if (idx != count) {
 			debugl1(cs, "RFIFO BUSY error");
 			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
 			dev_kfree_skb(skb);
-			WaitNoBusy(cs);
-			stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
-					       HFC_CHANNEL(bcs->channel));
-			WaitForBusy(cs);
+			if (bcs->mode != L1_MODE_TRANS) {
+			  WaitNoBusy(cs);
+			  stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+						 HFC_CHANNEL(bcs->channel));
+			  WaitForBusy(cs);
+			}
 			return (NULL);
 		}
-		WaitNoBusy(cs);
-		chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
-		WaitNoBusy(cs);
-		chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
-		WaitNoBusy(cs);
-		stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
-		if (cs->debug & L1_DEB_HSCX)
-			debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
-				bcs->channel, chksum, stat);
-		if (stat) {
-			debugl1(cs, "FIFO CRC error");
-			dev_kfree_skb(skb);
-			skb = NULL;
+		if (bcs->mode != L1_MODE_TRANS) {
+		  WaitNoBusy(cs);
+		  chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
+		  WaitNoBusy(cs);
+		  chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		  WaitNoBusy(cs);
+		  stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		  if (cs->debug & L1_DEB_HSCX)
+		    debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+			    bcs->channel, chksum, stat);
+		  if (stat) {
+		    debugl1(cs, "FIFO CRC error");
+		    dev_kfree_skb(skb);
+		    skb = NULL;
 #ifdef ERROR_STATISTIC
-			bcs->err_crc++;
+		    bcs->err_crc++;
 #endif
+		  }
+		  WaitNoBusy(cs);
+		  stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					 HFC_CHANNEL(bcs->channel));
+		  WaitForBusy(cs);
 		}
-		WaitNoBusy(cs);
-		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
-				       HFC_CHANNEL(bcs->channel));
-		WaitForBusy(cs);
 	}
 	return (skb);
 }
@@ -277,6 +292,7 @@
 	long flags;
 	int idx, fcnt;
 	int count;
+	int z1, z2;
 	u_char cip;
 
 	if (!bcs->tx_skb)
@@ -288,29 +304,39 @@
 	cli();
 	cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
 	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
-		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
-		WaitForBusy(cs);
+	  cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+	  WaitForBusy(cs);
 	}
 	WaitNoBusy(cs);
-	bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
-	cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
-	WaitNoBusy(cs);
-	bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
-	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
-	if (cs->debug & L1_DEB_HSCX)
-		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
-			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
-			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
-	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
-	if (fcnt < 0)
-		fcnt += 32;
-	if (fcnt > 30) {
-		if (cs->debug & L1_DEB_HSCX)
-			debugl1(cs, "hfc_fill_fifo more as 30 frames");
-		restore_flags(flags);
-		return;
-	}
-	count = GetFreeFifoBytes(bcs);
+	if (bcs->mode != L1_MODE_TRANS) {
+	  bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	  cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	  WaitNoBusy(cs);
+	  bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	  bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
+	  if (cs->debug & L1_DEB_HSCX)
+	    debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+		    bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+		    bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	  fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	  if (fcnt < 0)
+	    fcnt += 32;
+	  if (fcnt > 30) {
+	    if (cs->debug & L1_DEB_HSCX)
+	      debugl1(cs, "hfc_fill_fifo more as 30 frames");
+	    restore_flags(flags);
+	    return;
+	  }
+	  count = GetFreeFifoBytes(bcs);
+	} 
+	else {
+	  WaitForBusy(cs);
+	  z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	  z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	  count = z1 - z2;
+	  if (count < 0)
+	    count += cs->hw.hfc.fifosize; 
+	} /* L1_MODE_TRANS */
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
 			bcs->channel, bcs->tx_skb->len,
@@ -335,9 +361,11 @@
 			count = -1;
 		dev_kfree_skb(bcs->tx_skb);
 		bcs->tx_skb = NULL;
-		WaitForBusy(cs);
-		WaitNoBusy(cs);
-		cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+		if (bcs->mode != L1_MODE_TRANS) {
+		  WaitForBusy(cs);
+		  WaitNoBusy(cs);
+		  cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+		}
 		if (bcs->st->lli.l1writewakeup && (count >= 0))
 			bcs->st->lli.l1writewakeup(bcs->st, count);
 		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
@@ -366,32 +394,39 @@
 		WaitForBusy(cs);
 	}
 	WaitNoBusy(cs);
-	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
-	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
-	WaitNoBusy(cs);
-	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
-	if (f1 != f2) {
-		if (cs->debug & L1_DEB_HSCX)
-			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
-				bcs->channel, f1, f2);
+	receive = 0;
+	if (bcs->mode == L1_MODE_HDLC) {
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		if (f1 != f2) {
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+					bcs->channel, f1, f2);
+			receive = 1; 
+		}
+	}
+	if (receive || (bcs->mode == L1_MODE_TRANS)) {
 		WaitForBusy(cs);
 		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
 		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
 		rcnt = z1 - z2;
 		if (rcnt < 0)
 			rcnt += cs->hw.hfc.fifosize;
-		rcnt++;
-		if (cs->debug & L1_DEB_HSCX)
-			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
-				bcs->channel, z1, z2, rcnt);
-/*              sti(); */
-		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
-			skb_queue_tail(&bcs->rqueue, skb);
-			hfc_sched_event(bcs, B_RCVBUFREADY);
+		if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) {
+			rcnt++;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+					bcs->channel, z1, z2, rcnt);
+			/*              sti(); */
+			if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+				skb_queue_tail(&bcs->rqueue, skb);
+				hfc_sched_event(bcs, B_RCVBUFREADY);
+			}
 		}
 		receive = 1;
-	} else
-		receive = 0;
+	}
 	restore_flags(flags);
 	udelay(1);
 	cli();
@@ -432,12 +467,19 @@
 
 	switch (mode) {
 		case (L1_MODE_NULL):
-			if (bc)
+		        if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
 				cs->hw.hfc.isac_spcr &= ~0x03;
-			else
+			}
+			else {
+				cs->hw.hfc.ctmt &= ~2;
 				cs->hw.hfc.isac_spcr &= ~0x0c;
+			}
 			break;
 		case (L1_MODE_TRANS):
+		        cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */ 
+			cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+			hfc_clear_fifo(bcs); /* complete fifo clear */ 
 			if (bc) {
 				cs->hw.hfc.ctmt |= 1;
 				cs->hw.hfc.isac_spcr &= ~0x03;
@@ -462,7 +504,7 @@
 	}
 	cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
 	cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
-	if (mode)
+	if (mode == L1_MODE_HDLC)
 		hfc_clear_fifo(bcs);
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_2bs0.h linux/drivers/isdn/hisax/hfc_2bs0.h
--- v2.3.45/linux/drivers/isdn/hisax/hfc_2bs0.h	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/hfc_2bs0.h	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,14 @@
-/* $Id: hfc_2bs0.h,v 1.1 1997/09/11 17:31:34 keil Exp $
+/* $Id: hfc_2bs0.h,v 1.2 1999/12/23 15:09:32 keil Exp $
 
  *  specific defines for CCD's HFC 2BS0
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: hfc_2bs0.h,v $
+ * Revision 1.2  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.1  1997/09/11 17:31:34  keil
  * Common part for HFC 2BS0 based cards
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_pci.c linux/drivers/isdn/hisax/hfc_pci.c
--- v2.3.45/linux/drivers/isdn/hisax/hfc_pci.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/hfc_pci.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: hfc_pci.c,v 1.23 1999/11/07 17:01:55 keil Exp $
+/* $Id: hfc_pci.c,v 1.26 2000/02/09 20:22:55 werner Exp $
 
  * hfc_pci.c     low level driver for CCD´s hfc-pci based cards
  *
@@ -23,6 +23,18 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hfc_pci.c,v $
+ * Revision 1.26  2000/02/09 20:22:55  werner
+ *
+ * Updated PCI-ID table
+ *
+ * Revision 1.25  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
+ * Revision 1.24  1999/11/17 23:59:55  werner
+ *
+ * removed unneeded data
+ *
  * Revision 1.23  1999/11/07 17:01:55  keil
  * fix for 2.3 pci structs
  *
@@ -114,7 +126,7 @@
 
 extern const char *CardType[];
 
-static const char *hfcpci_revision = "$Revision: 1.23 $";
+static const char *hfcpci_revision = "$Revision: 1.26 $";
 
 /* table entry in the PCI devices list */
 typedef struct {
@@ -143,26 +155,12 @@
 	{0x1051, 0x0100, "Motorola MC145575", "MC145575"},
 	{0x1397, 0xB100, "Seyeon", "B100"},
 	{0x15B0, 0x2BD0, "Zoltrix", "2BD0"},
+	{0x114f, 0x71,   "Digi intl.","Digicom"}, 
 	{0, 0, NULL, NULL},
 };
 
 
 #if CONFIG_PCI
-/*****************************/
-/* release D- and B-channels */
-/*****************************/
-void
-releasehfcpci(struct IsdnCardState *cs)
-{
-	if (cs->bcs[0].hw.hfc.send) {
-		kfree(cs->bcs[0].hw.hfc.send);
-		cs->bcs[0].hw.hfc.send = NULL;
-	}
-	if (cs->bcs[1].hw.hfc.send) {
-		kfree(cs->bcs[1].hw.hfc.send);
-		cs->bcs[1].hw.hfc.send = NULL;
-	}
-}
 
 /******************************************/
 /* free hardware resources used by driver */
@@ -179,13 +177,12 @@
 	restore_flags(flags);
 	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
 	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
 #if CONFIG_PCI
 	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0);	/* disable memory mapped ports + busmaster */
 #endif				/* CONFIG_PCI */
-	releasehfcpci(cs);
 	del_timer(&cs->hw.hfcpci.timer);
 	kfree(cs->hw.hfcpci.share_start);
 	cs->hw.hfcpci.share_start = NULL;
@@ -211,10 +208,10 @@
 	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER);	/* enable memory ports + busmaster */
 	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
 	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
 	if (Read_hfc(cs, HFCPCI_STATUS) & 2)
 		printk(KERN_WARNING "HFC-PCI init bit busy\n");
@@ -1647,24 +1644,6 @@
 }
 
 
-/*************************************/
-/* Alloc memory send data for queues */
-/*************************************/
-__initfunc(unsigned int
-	   *init_send_hfcpci(int cnt))
-{
-	int i, *send;
-
-	if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) {
-		printk(KERN_WARNING
-		       "HiSax: No memory for hfcpci.send\n");
-		return (NULL);
-	}
-	for (i = 0; i < cnt; i++)
-		send[i] = 0x1fff;
-	return (send);
-}
-
 /********************************/
 /* called for card init message */
 /********************************/
@@ -1676,10 +1655,6 @@
 	cs->dbusytimer.data = (long) cs;
 	init_timer(&cs->dbusytimer);
 	cs->tqueue.routine = (void *) (void *) hfcpci_bh;
-	if (!cs->bcs[0].hw.hfc.send)
-		cs->bcs[0].hw.hfc.send = init_send_hfcpci(32);
-	if (!cs->bcs[1].hw.hfc.send)
-		cs->bcs[1].hw.hfc.send = init_send_hfcpci(32);
 	cs->BC_Send_Data = &hfcpci_send_data;
 	cs->bcs[0].BC_SetStack = setstack_2b;
 	cs->bcs[1].BC_SetStack = setstack_2b;
@@ -1712,7 +1687,7 @@
 			inithfcpci(cs);
 			save_flags(flags);
 			sti();
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((80 * HZ) / 1000);	/* Timeout 80ms */
 			/* now switch timer interrupt off */
 			cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
@@ -1746,8 +1721,6 @@
 	printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
 #if CONFIG_PCI
 	cs->hw.hfcpci.int_s1 = 0;
-	cs->bcs[0].hw.hfc.send = NULL;
-	cs->bcs[1].hw.hfc.send = NULL;
 	cs->dc.hfcpci.ph_state = 0;
 	cs->hw.hfcpci.fifo = 255;
 	if (cs->typ == ISDN_CTYPE_HFC_PCI) {
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_sx.c linux/drivers/isdn/hisax/hfc_sx.c
--- v2.3.45/linux/drivers/isdn/hisax/hfc_sx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_sx.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,1583 @@
+/* $Id: hfc_sx.c,v 1.3 2000/01/20 19:49:36 keil Exp $
+
+ * hfc_sx.c     low level driver for CCD´s hfc-s+/sp based cards
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *            based on existing driver for CCD HFC PCI cards
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn4linux.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, 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.
+ *
+ * $Log: hfc_sx.c,v $
+ * Revision 1.3  2000/01/20 19:49:36  keil
+ * Support teles 13.3c vendor version 2.1
+ *
+ * Revision 1.2  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
+ * Revision 1.1  1999/11/18 00:09:18  werner
+ *
+ * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM.
+ * Audio and Echo are supported.
+ *
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_sx.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+
+static const char *hfcsx_revision = "$Revision: 1.3 $";
+
+/***************************************/
+/* IRQ-table for CCDs demo board       */
+/* IRQs 6,5,10,11,12,15 are supported  */
+/***************************************/
+
+/* Teles 16.3c Vendor Id TAG2620, Version 1.0, Vendor version 2.1
+ *
+ * Thanks to Uwe Wisniewski
+ *
+ * ISA-SLOT  Signal      PIN
+ * B25        IRQ3     92 IRQ_G
+ * B23        IRQ5     94 IRQ_A
+ * B4         IRQ2/9   95 IRQ_B
+ * D3         IRQ10    96 IRQ_C
+ * D4         IRQ11    97 IRQ_D
+ * D5         IRQ12    98 IRQ_E
+ * D6         IRQ15    99 IRQ_F
+ */
+
+#undef CCD_DEMO_BOARD
+#ifdef CCD_DEMO_BOARD
+static u_char ccd_sp_irqtab[16] = {
+  0,0,0,0,0,2,1,0,0,0,3,4,5,0,0,6
+};
+#else /* Teles 16.3c */
+static u_char ccd_sp_irqtab[16] = {
+  0,0,0,7,0,1,0,0,0,2,3,4,5,0,0,6
+};
+#endif
+#define NT_T1_COUNT 20		/* number of 3.125ms interrupts for G2 timeout */
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/******************************/
+/* In/Out access to registers */
+/******************************/
+static inline void
+Write_hfc(struct IsdnCardState *cs, u_char regnum, u_char val)
+{       register int flags;
+
+        save_flags(flags);
+	cli();
+        byteout(cs->hw.hfcsx.base+1, regnum);
+	byteout(cs->hw.hfcsx.base, val);
+	restore_flags(flags);
+} 
+
+static inline u_char
+Read_hfc(struct IsdnCardState *cs, u_char regnum)
+{       register int flags;
+        register u_char ret; 
+
+        save_flags(flags);
+	cli();
+        byteout(cs->hw.hfcsx.base+1, regnum);
+	ret = bytein(cs->hw.hfcsx.base);
+	restore_flags(flags);
+	return(ret);
+} 
+
+
+/**************************************************/
+/* select a fifo and remember which one for reuse */
+/**************************************************/
+static void
+fifo_select(struct IsdnCardState *cs, u_char fifo)
+{       int flags;
+
+        if (fifo == cs->hw.hfcsx.last_fifo) 
+	  return; /* still valid */
+
+	save_flags(flags);
+	cli();
+        byteout(cs->hw.hfcsx.base+1, HFCSX_FIF_SEL);
+	byteout(cs->hw.hfcsx.base, fifo);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	udelay(4);
+	byteout(cs->hw.hfcsx.base, fifo);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	restore_flags(flags);
+}
+
+/******************************************/
+/* reset the specified fifo to defaults.  */
+/* If its a send fifo init needed markers */
+/******************************************/
+static void
+reset_fifo(struct IsdnCardState *cs, u_char fifo)
+{       int flags;
+
+        save_flags(flags); 
+	cli();
+	fifo_select(cs, fifo); /* first select the fifo */
+	byteout(cs->hw.hfcsx.base+1, HFCSX_CIRM);
+	byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.cirm | 0x80); /* reset cmd */
+	udelay(1);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	restore_flags(flags);
+} 
+
+
+/*************************************************************/
+/* write_fifo writes the skb contents to the desired fifo    */
+/* if no space is available or an error occurs 0 is returned */
+/* the skb is not released in any way.                       */
+/*************************************************************/
+static int
+write_fifo(struct IsdnCardState *cs, struct sk_buff *skb, u_char fifo, int trans_max)
+{       unsigned short *msp;
+        int fifo_size, count, z1, z2;
+	u_char f_msk, f1, f2, *src;
+
+	if (skb->len <= 0) return(0);
+        if (fifo & 1) return(0); /* no write fifo */
+
+	fifo_select(cs, fifo);
+	if (fifo & 4) {
+	  fifo_size = D_FIFO_SIZE; /* D-channel */
+	  f_msk = MAX_D_FRAMES;
+	  if (trans_max) return(0); /* only HDLC */
+	}
+	else {
+	  fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+	  f_msk = MAX_B_FRAMES;
+	}
+
+        z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+
+	/* Check for transparent mode */
+	if (trans_max) {
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+	  count = z2 - z1;
+	  if (count <= 0)
+	    count += fifo_size; /* free bytes */
+	  if (count < skb->len+1) return(0); /* no room */
+	  count = fifo_size - count; /* bytes still not send */
+	  if (count > 2 * trans_max) return(0); /* delay to long */
+	  count = skb->len;
+	  src = skb->data;
+	  while (count--)
+	    Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+	  return(1); /* success */
+	}
+
+        msp = ((struct hfcsx_extra *)(cs->hw.hfcsx.extra))->marker;
+	msp += (((fifo >> 1) & 3) * (MAX_B_FRAMES+1));
+	f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+	f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+	count = f1 - f2; /* frame count actually buffered */
+	if (count < 0)
+		count += (f_msk + 1);	/* if wrap around */
+	if (count > f_msk-1) {
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_write_fifo %d more as %d frames",fifo,f_msk-1);
+	  return(0);
+	}
+
+	*(msp + f1) = z1; /* remember marker */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO)
+		debugl1(cs, "hfcsx_write_fifo %d f1(%x) f2(%x) z1(f1)(%x)",
+			fifo, f1, f2, z1);
+	/* now determine free bytes in FIFO buffer */
+	count = *(msp + f2) - z1;
+	if (count <= 0)
+	  count += fifo_size;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO)
+	  debugl1(cs, "hfcsx_write_fifo %d count(%ld/%d)",
+		  fifo, skb->len, count);
+	if (count < skb->len) {
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_write_fifo %d no fifo mem", fifo);
+	  return(0);
+	}
+	
+	count = skb->len; /* get frame len */
+	src = skb->data;	/* source pointer */
+	while (count--)
+	  Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+	
+	Read_hfc(cs, HFCSX_FIF_INCF1); /* increment F1 */
+	udelay(1);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	return(1);
+} 
+
+/***************************************************************/
+/* read_fifo reads data to an skb from the desired fifo        */
+/* if no data is available or an error occurs NULL is returned */
+/* the skb is not released in any way.                         */
+/***************************************************************/
+static struct sk_buff * 
+read_fifo(struct IsdnCardState *cs, u_char fifo, int trans_max)
+{       int fifo_size, count, z1, z2;
+	u_char f_msk, f1, f2, *dst;
+	struct sk_buff *skb;
+
+        if (!(fifo & 1)) return(NULL); /* no read fifo */
+	fifo_select(cs, fifo);
+	if (fifo & 4) {
+	  fifo_size = D_FIFO_SIZE; /* D-channel */
+	  f_msk = MAX_D_FRAMES;
+	  if (trans_max) return(NULL); /* only hdlc */
+	}
+	else {
+	  fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+	  f_msk = MAX_B_FRAMES;
+	}
+
+	/* transparent mode */
+	if (trans_max) {
+	  z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	  z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+	  /* now determine bytes in actual FIFO buffer */
+	  count = z1 - z2;
+	  if (count <= 0)
+	    count += fifo_size;	/* count now contains buffered bytes */
+	  count++;
+	  if (count > trans_max) 
+	    count = trans_max; /* limit length */
+	    if ((skb = dev_alloc_skb(count))) {
+	      dst = skb_put(skb, count);
+	      while (count--) 
+		*dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+	      return(skb);
+	    }
+	    else return(NULL); /* no memory */
+	}
+
+	do {
+	  f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+	  f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+	  if (f1 == f2) return(NULL); /* no frame available */
+
+	  z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	  z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_read_fifo %d f1(%x) f2(%x) z1(f2)(%x) z2(f2)(%x)",
+			fifo, f1, f2, z1, z2);
+	  /* now determine bytes in actual FIFO buffer */
+	  count = z1 - z2;
+	  if (count <= 0)
+	    count += fifo_size;	/* count now contains buffered bytes */
+	  count++;
+
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_read_fifo %d count %ld)",
+		    fifo, count);
+
+	  if ((count > fifo_size) || (count < 4)) {
+	    if (cs->debug & L1_DEB_WARN)
+	      debugl1(cs, "hfcsx_read_fifo %d paket inv. len %d ", fifo , count);
+	    while (count) {
+	      count--; /* empty fifo */
+	      Read_hfc(cs, HFCSX_FIF_DRD);
+	    }
+	    skb = NULL;
+	  } else 
+	    if ((skb = dev_alloc_skb(count - 3))) {
+	      count -= 3;
+	      dst = skb_put(skb, count);
+
+	      while (count--) 
+		*dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+		    
+	      Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 1 */
+	      Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 2 */
+	      if (Read_hfc(cs, HFCSX_FIF_DRD)) {
+		dev_kfree_skb(skb);
+		if (cs->debug & L1_DEB_ISAC_FIFO)
+		  debugl1(cs, "hfcsx_read_fifo %d crc error", fifo);
+		skb = NULL;
+	      }
+	    } else {
+	      printk(KERN_WARNING "HFC-SX: receive out of memory\n");
+	      return(NULL);
+	    }
+
+	  Read_hfc(cs, HFCSX_FIF_INCF2); /* increment F2 */
+	  udelay(1);
+	  while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	  udelay(1);
+	} while (!skb); /* retry in case of crc error */
+	return(skb);
+} 
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+void
+release_io_hfcsx(struct IsdnCardState *cs)
+{
+	int flags;
+
+	save_flags(flags);
+	cli();
+	cs->hw.hfcsx.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	restore_flags(flags);
+	Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET);	/* Reset On */
+	sti();
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
+	Write_hfc(cs, HFCSX_CIRM, 0);	/* Reset Off */
+	del_timer(&cs->hw.hfcsx.timer);
+	release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */
+	kfree(cs->hw.hfcsx.extra);
+	cs->hw.hfcsx.extra = NULL;
+}
+
+/**********************************************************/
+/* set_fifo_size determines the size of the RAM and FIFOs */
+/* returning 0 -> need to reset the chip again.           */
+/**********************************************************/
+static int set_fifo_size(struct IsdnCardState *cs)
+{
+        
+        if (cs->hw.hfcsx.b_fifo_size) return(1); /* already determined */
+
+	if ((cs->hw.hfcsx.chip >> 4) == 9) {
+	  cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_32K;
+	  return(1);
+	}
+
+	  cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_8K;
+	  cs->hw.hfcsx.cirm |= 0x10; /* only 8K of ram */
+	  return(0);
+
+}
+
+/********************************************************************************/
+/* function called to reset the HFC SX chip. A complete software reset of chip */
+/* and fifos is done.                                                           */
+/********************************************************************************/
+static void
+reset_hfcsx(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	cs->hw.hfcsx.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+
+	printk(KERN_INFO "HFC_SX: resetting card\n");
+	while (1) {
+	  Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */
+	  sti();
+	  set_current_state(TASK_UNINTERRUPTIBLE);
+	  schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
+	  Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */
+	  set_current_state(TASK_UNINTERRUPTIBLE);
+	  schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
+	  if (Read_hfc(cs, HFCSX_STATUS) & 2)
+	    printk(KERN_WARNING "HFC-SX init bit busy\n");
+	  cs->hw.hfcsx.last_fifo = 0xff; /* invalidate */
+	  if (!set_fifo_size(cs)) continue;
+	  break;
+	}
+
+	cs->hw.hfcsx.trm = 0 + HFCSX_BTRANS_THRESMASK;	/* no echo connect , threshold */
+	Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+
+	Write_hfc(cs, HFCSX_CLKDEL, 0x0e);	/* ST-Bit delay for TE-Mode */
+	cs->hw.hfcsx.sctrl_e = HFCSX_AUTO_AWAKE;
+	Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);	/* S/T Auto awake */
+	cs->hw.hfcsx.bswapped = 0;	/* no exchange */
+	cs->hw.hfcsx.nt_mode = 0;	/* we are in TE mode */
+	cs->hw.hfcsx.ctmt = HFCSX_TIM3_125 | HFCSX_AUTO_TIMER;
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+
+	cs->hw.hfcsx.int_m1 = HFCSX_INTS_DTRANS | HFCSX_INTS_DREC | 
+	    HFCSX_INTS_L1STATE | HFCSX_INTS_TIMER;
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+
+	/* Clear already pending ints */
+	if (Read_hfc(cs, HFCSX_INT_S1));
+
+	Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 2);	/* HFC ST 2 */
+	udelay(10);
+	Write_hfc(cs, HFCSX_STATES, 2);	/* HFC ST 2 */
+	cs->hw.hfcsx.mst_m = HFCSX_MASTER;	/* HFC Master Mode */
+
+	Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+	cs->hw.hfcsx.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	cs->hw.hfcsx.sctrl_r = 0;
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+
+	/* Init GCI/IOM2 in master mode */
+	/* Slots 0 and 1 are set for B-chan 1 and 2 */
+	/* D- and monitor/CI channel are not enabled */
+	/* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
+	/* STIO2 is used as data input, B1+B2 from IOM->ST */
+	/* ST B-channel send disabled -> continous 1s */
+	/* The IOM slots are always enabled */
+	cs->hw.hfcsx.conn = 0x36;	/* set data flow directions */
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	Write_hfc(cs, HFCSX_B1_SSL, 0x80);	/* B1-Slot 0 STIO1 out enabled */
+	Write_hfc(cs, HFCSX_B2_SSL, 0x81);	/* B2-Slot 1 STIO1 out enabled */
+	Write_hfc(cs, HFCSX_B1_RSL, 0x80);	/* B1-Slot 0 STIO2 in enabled */
+	Write_hfc(cs, HFCSX_B2_RSL, 0x81);	/* B2-Slot 1 STIO2 in enabled */
+
+	/* Finally enable IRQ output */
+	cs->hw.hfcsx.int_m2 = HFCSX_IRQ_ENABLE;
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	if (Read_hfc(cs, HFCSX_INT_S2));
+	restore_flags(flags);
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcsx_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*      WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcsx.ctmt | 0x80);
+   add_timer(&cs->hw.hfcsx.timer);
+ */
+}
+
+
+/*********************************/
+/* schedule a new D-channel task */
+/*********************************/
+static void
+sched_event_D_sx(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	queue_task(&cs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/*********************************/
+/* schedule a new b_channel task */
+/*********************************/
+static void
+hfcsx_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return (&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return (&cs->bcs[1]);
+	else
+		return (NULL);
+}
+
+/*******************************/
+/* D-channel receive procedure */
+/*******************************/
+static
+int
+receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int count = 5;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return (1);
+	}
+
+	do {
+	  skb = read_fifo(cs, HFCSX_SEL_D_RX, 0);
+	  if (skb) {
+	    skb_queue_tail(&cs->rq, skb);
+	    sched_event_D_sx(cs, D_RCVBUFREADY);
+	  }
+	} while (--count && skb);
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return (1);
+}
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+void
+main_rec_hfcsx(struct BCState *bcs)
+{
+	long flags;
+	struct IsdnCardState *cs = bcs->cs;
+	int count = 5;
+	struct sk_buff *skb;
+
+	save_flags(flags);
+	  
+      Begin:
+	count--;
+	cli();
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_data %d blocked", bcs->channel);
+		restore_flags(flags);
+		return;
+	}
+	sti();
+	skb = read_fifo(cs, ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? 
+			HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX,
+			(bcs->mode == L1_MODE_TRANS) ? 
+			HFCSX_BTRANS_THRESHOLD : 0);
+
+	if (skb) {
+	  cli();
+	  skb_queue_tail(&bcs->rqueue, skb);
+	  sti();
+	  hfcsx_sched_event(bcs, B_RCVBUFREADY);
+	}
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && skb)
+		goto Begin;
+	restore_flags(flags);
+	return;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcsx_fill_dfifo(struct IsdnCardState *cs)
+{
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	if (write_fifo(cs, cs->tx_skb, HFCSX_SEL_D_TX, 0)) {
+	  dev_kfree_skb(cs->tx_skb);
+	  cs->tx_skb = NULL;
+	}
+	return;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcsx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int flags;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	save_flags(flags);
+	sti();
+
+	if (write_fifo(cs, bcs->tx_skb, 
+		       ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? 
+		       HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX,
+		       (bcs->mode == L1_MODE_TRANS) ? 
+		       HFCSX_BTRANS_THRESHOLD : 0)) {
+
+	  bcs->tx_cnt -= bcs->tx_skb->len;
+	  if (bcs->st->lli.l1writewakeup &&
+	      (PACKET_NOACK != bcs->tx_skb->pkt_type))
+	    bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+	  dev_kfree_skb(bcs->tx_skb);
+	  bcs->tx_skb = NULL;
+	  test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+
+	cli();
+	restore_flags(flags);
+	return;
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL | INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcsx_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+	int flags;
+	int i = *(unsigned int *) ic->parm.num;
+
+	if ((ic->arg == 98) &&
+	    (!(cs->hw.hfcsx.int_m1 & (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC + HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC)))) {
+		save_flags(flags);
+		cli();
+		Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 0);	/* HFC ST G0 */
+		udelay(10);
+		cs->hw.hfcsx.sctrl |= SCTRL_MODE_NT;
+		Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);	/* set NT-mode */
+		udelay(10);
+		Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 1);	/* HFC ST G1 */
+		udelay(10);
+		Write_hfc(cs, HFCSX_STATES, 1 | HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+		cs->dc.hfcsx.ph_state = 1;
+		cs->hw.hfcsx.nt_mode = 1;
+		cs->hw.hfcsx.nt_timer = 0;
+		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		restore_flags(flags);
+		debugl1(cs, "NT mode activated");
+		return (0);
+	}
+	if ((cs->chanlimit > 1) || (cs->hw.hfcsx.bswapped) ||
+	    (cs->hw.hfcsx.nt_mode) || (ic->arg != 12))
+		return (-EINVAL);
+
+	save_flags(flags);
+	cli();
+	if (i) {
+		cs->logecho = 1;
+		cs->hw.hfcsx.trm |= 0x20;	/* enable echo chan */
+		cs->hw.hfcsx.int_m1 |= HFCSX_INTS_B2REC;
+		/* reset Channel !!!!! */
+	} else {
+		cs->logecho = 0;
+		cs->hw.hfcsx.trm &= ~0x20;	/* disable echo chan */
+		cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_B2REC;
+	}
+	cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+	cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+	cs->hw.hfcsx.conn |= 0x10;	/* B2-IOM -> B2-ST */
+	cs->hw.hfcsx.ctmt &= ~2;
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+	restore_flags(flags);
+	return (0);
+}				/* hfcsx_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+	int flags;
+	int count = 5;
+	u_char *ptr;
+	struct sk_buff *skb;
+
+
+	save_flags(flags);
+	cli();
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "echo_rec_data blocked");
+		restore_flags(flags);
+		return;
+	}
+	sti();
+
+	do {
+	  skb = read_fifo(cs, HFCSX_SEL_B2_RX, 0);
+	  if (skb) {
+	    if (cs->debug & DEB_DLOG_HEX) {
+	      ptr = cs->dlog;
+	      if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+		*ptr++ = 'E';
+		*ptr++ = 'C';
+		*ptr++ = 'H';
+		*ptr++ = 'O';
+		*ptr++ = ':';
+		ptr += QuickHex(ptr, skb->data, skb->len);
+		ptr--;
+		*ptr++ = '\n';
+		*ptr = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+	      } else
+		HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", skb->len);
+	    }
+	    dev_kfree_skb(skb);
+	  }
+	} while (--count && skb);
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	restore_flags(flags);
+	return;
+}				/* receive_emsg */
+
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static void
+hfcsx_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char exval;
+	struct BCState *bcs;
+	int count = 15;
+	long flags;
+	u_char val, stat;
+
+	if (!cs) {
+		printk(KERN_WARNING "HFC-SX: Spurious interrupt!\n");
+		return;
+	}
+	if (!(cs->hw.hfcsx.int_m2 & 0x08))
+		return;		/* not initialised */
+
+	if (HFCSX_ANYINT & (stat = Read_hfc(cs, HFCSX_STATUS))) {
+		val = Read_hfc(cs, HFCSX_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFC-SX: stat(%02x) s1(%02x)", stat, val);
+	} else
+		return;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFC-SX irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcsx.int_m1;
+	if (val & 0x40) {	/* state machine irq */
+		exval = Read_hfc(cs, HFCSX_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcsx.ph_state,
+				exval);
+		cs->dc.hfcsx.ph_state = exval;
+		sched_event_D_sx(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	if (val & 0x80) {	/* timer irq */
+		if (cs->hw.hfcsx.nt_mode) {
+			if ((--cs->hw.hfcsx.nt_timer) < 0)
+				sched_event_D_sx(cs, D_L1STATECHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+	}
+	while (val) {
+		save_flags(flags);
+		cli();
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcsx.int_s1 |= val;
+			restore_flags(flags);
+			return;
+		}
+		if (cs->hw.hfcsx.int_s1 & 0x18) {
+			exval = val;
+			val = cs->hw.hfcsx.int_s1;
+			cs->hw.hfcsx.int_s1 = exval;
+		}
+		if (val & 0x08) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x08 IRQ");
+			} else
+				main_rec_hfcsx(bcs);
+		}
+		if (val & 0x10) {
+			if (cs->logecho)
+				receive_emsg(cs);
+			else if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x10 IRQ");
+			} else
+				main_rec_hfcsx(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcsx_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						hfcsx_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcsx_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						hfcsx_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				sched_event_D_sx(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcsx_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+				}
+			} else
+				sched_event_D_sx(cs, D_XMTBUFREADY);
+		}
+	      afterXPR:
+		if (cs->hw.hfcsx.int_s1 && count--) {
+			val = cs->hw.hfcsx.int_s1;
+			cs->hw.hfcsx.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFC-SX irq %x loop %d", val, 15 - count);
+		} else
+			val = 0;
+		restore_flags(flags);
+	}
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcsx_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCSX_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	int flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				        hfcsx_fill_dfifo(cs); 
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfcsx_fill_dfifo blocked");
+
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfcsx_fill_dfifo(cs); 
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfcsx_fill_dfifo blocked");
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 3);	/* HFC ST 3 */
+			udelay(6);
+			Write_hfc(cs, HFCSX_STATES, 3);	/* HFC ST 2 */
+			cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			cs->hw.hfcsx.mst_m &= ~HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			break;
+		case (HW_INFO3 | REQUEST):
+			cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			switch ((int) arg) {
+				case (1):
+					Write_hfc(cs, HFCSX_B1_SSL, 0x80);	/* tx slot */
+					Write_hfc(cs, HFCSX_B1_RSL, 0x80);	/* rx slot */
+					save_flags(flags);
+					cli();
+					cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~7) | 1;
+					Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+					restore_flags(flags);
+					break;
+
+				case (2):
+					Write_hfc(cs, HFCSX_B2_SSL, 0x81);	/* tx slot */
+					Write_hfc(cs, HFCSX_B2_RSL, 0x81);	/* rx slot */
+					save_flags(flags);
+					cli();
+					cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~0x38) | 0x08;
+					Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+					restore_flags(flags);
+					break;
+
+				default:
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "hfcsx_l1hw loop invalid %4x", (int) arg);
+					return;
+			}
+			save_flags(flags);
+			cli();
+			cs->hw.hfcsx.trm |= 0x80;	/* enable IOM-loop */
+			Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+			restore_flags(flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcsx_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+void
+setstack_hfcsx(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCSX_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcsx_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+	  hfcsx_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+void
+mode_hfcsx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int flags, fifo2;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCSX bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	fifo2 = bc;
+	save_flags(flags);
+	cli();
+	if (cs->chanlimit > 1) {
+		cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+		cs->hw.hfcsx.sctrl_e &= ~0x80;
+	} else {
+		if (bc) {
+			if (mode != L1_MODE_NULL) {
+				cs->hw.hfcsx.bswapped = 1;	/* B1 and B2 exchanged */
+				cs->hw.hfcsx.sctrl_e |= 0x80;
+			} else {
+				cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+				cs->hw.hfcsx.sctrl_e &= ~0x80;
+			}
+			fifo2 = 0;
+		} else {
+			cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+			cs->hw.hfcsx.sctrl_e &= ~0x80;
+		}
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl &= ~SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r &= ~SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+			} else {
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+				cs->hw.hfcsx.ctmt |= 2;
+				cs->hw.hfcsx.conn &= ~0x18;
+			} else {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+				cs->hw.hfcsx.ctmt |= 1;
+				cs->hw.hfcsx.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+				cs->hw.hfcsx.ctmt &= ~2;
+				cs->hw.hfcsx.conn &= ~0x18;
+			} else {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+				cs->hw.hfcsx.ctmt &= ~1;
+				cs->hw.hfcsx.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_EXTRN):
+			if (bc) {
+				cs->hw.hfcsx.conn |= 0x10;
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+			} else {
+				cs->hw.hfcsx.conn |= 0x02;
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+			}
+			break;
+	}
+	Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	if (mode != L1_MODE_EXTRN) {
+	  reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX);
+	  reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX);
+	}
+	restore_flags(flags);
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcsx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+/*                              test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ */ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+/*                      test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ */ st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_hfcsx(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_hfcsx(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcsx(struct BCState *bcs)
+{
+	mode_hfcsx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcsxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcsxstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfcsx_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcsx_bh(struct IsdnCardState *cs)
+{
+	int flags;
+/*      struct PStack *stptr;
+ */
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (!cs->hw.hfcsx.nt_mode)
+			switch (cs->dc.hfcsx.ph_state) {
+				case (0):
+					l1_msg(cs, HW_RESET | INDICATION, NULL);
+					break;
+				case (3):
+					l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+					break;
+				case (8):
+					l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+					break;
+				case (6):
+					l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+					break;
+				case (7):
+					l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+					break;
+				default:
+					break;
+		} else {
+			switch (cs->dc.hfcsx.ph_state) {
+				case (2):
+					save_flags(flags);
+					cli();
+					if (cs->hw.hfcsx.nt_timer < 0) {
+						cs->hw.hfcsx.nt_timer = 0;
+						cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+						Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+						/* Clear already pending ints */
+						if (Read_hfc(cs, HFCSX_INT_S1));
+
+						Write_hfc(cs, HFCSX_STATES, 4 | HFCSX_LOAD_STATE);
+						udelay(10);
+						Write_hfc(cs, HFCSX_STATES, 4);
+						cs->dc.hfcsx.ph_state = 4;
+					} else {
+						cs->hw.hfcsx.int_m1 |= HFCSX_INTS_TIMER;
+						Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+						cs->hw.hfcsx.ctmt &= ~HFCSX_AUTO_TIMER;
+						cs->hw.hfcsx.ctmt |= HFCSX_TIM3_125;
+						Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+						Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+						cs->hw.hfcsx.nt_timer = NT_T1_COUNT;
+						Write_hfc(cs, HFCSX_STATES, 2 | HFCSX_NT_G2_G3);	/* allow G2 -> G3 transition */
+					}
+					restore_flags(flags);
+					break;
+				case (1):
+				case (3):
+				case (4):
+					save_flags(flags);
+					cli();
+					cs->hw.hfcsx.nt_timer = 0;
+					cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+					Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+					restore_flags(flags);
+					break;
+				default:
+					break;
+			}
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+__initfunc(void
+	   inithfcsx(struct IsdnCardState *cs))
+{
+	cs->setstack_d = setstack_hfcsx;
+	cs->dbusytimer.function = (void *) hfcsx_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	cs->tqueue.routine = (void *) (void *) hfcsx_bh;
+	cs->BC_Send_Data = &hfcsx_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_hfcsx;
+	cs->bcs[1].BC_Close = close_hfcsx;
+	mode_hfcsx(cs->bcs, 0, 0);
+	mode_hfcsx(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCSX: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			reset_hfcsx(cs);
+			return (0);
+		case CARD_RELEASE:
+			release_io_hfcsx(cs);
+			return (0);
+		case CARD_INIT:
+			inithfcsx(cs);
+			save_flags(flags);
+			sti();
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((80 * HZ) / 1000);	/* Timeout 80ms */
+			/* now switch timer interrupt off */
+			cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+			Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+			/* reinit mode reg */
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			restore_flags(flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+
+
+__initfunc(int
+	   setup_hfcsx(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	int flags;
+
+	strcpy(tmp, hfcsx_revision);
+	printk(KERN_INFO "HiSax: HFC-SX driver Rev. %s\n", HiSax_getrev(tmp));
+	cs->hw.hfcsx.base = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcsx.int_s1 = 0;
+	cs->dc.hfcsx.ph_state = 0;
+	cs->hw.hfcsx.fifo = 255;
+	if (cs->typ == ISDN_CTYPE_HFC_SX) {
+	        if ((!cs->hw.hfcsx.base) || 
+		    check_region((cs->hw.hfcsx.base), 2)) {
+		  printk(KERN_WARNING
+			 "HiSax: HFC-SX io-base 0x%x already in use\n",
+		          cs->hw.hfcsx.base);
+		  return(0);
+		} else {
+		  request_region(cs->hw.hfcsx.base, 2, "HFCSX isdn");
+		}
+		byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.base & 0xFF);
+		byteout(cs->hw.hfcsx.base + 1,
+			((cs->hw.hfcsx.base >> 8) & 3) | 0x54);
+		udelay(10);
+	        cs->hw.hfcsx.chip = Read_hfc(cs,HFCSX_CHIP_ID);
+                switch (cs->hw.hfcsx.chip >> 4) {
+		  case 1: 
+		    tmp[0] ='+';
+		    break;
+		  case 9: 
+		    tmp[0] ='P';
+		    break;
+		  default:
+		    printk(KERN_WARNING
+			   "HFC-SX: invalid chip id 0x%x\n",
+			   cs->hw.hfcsx.chip >> 4);
+		    release_region(cs->hw.hfcsx.base, 2);
+		    return(0);
+		}  
+		if (!ccd_sp_irqtab[cs->irq & 0xF]) {
+		  printk(KERN_WARNING 
+			 "HFC_SX: invalid irq %d specified\n",cs->irq & 0xF);
+		  release_region(cs->hw.hfcsx.base, 2);
+		  return(0);
+		}  
+		save_flags(flags);
+		cli();
+		if (!(cs->hw.hfcsx.extra = (void *)
+		      kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) {
+		  restore_flags(flags);
+		  release_region(cs->hw.hfcsx.base, 2);
+		  printk(KERN_WARNING "HFC-SX: unable to allocate memory\n");
+		  return(0);
+		}
+		restore_flags(flags);
+
+		printk(KERN_INFO
+		       "HFC-S%c chip detected at base 0x%x IRQ %d HZ %d\n",
+		       tmp[0], (u_int) cs->hw.hfcsx.base,
+		       cs->irq, HZ);
+		cs->hw.hfcsx.int_m2 = 0;	/* disable alle interrupts */
+		cs->hw.hfcsx.int_m1 = 0;
+		Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+		Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	} else
+		return (0);	/* no valid card type */
+
+	cs->readisac = NULL;
+	cs->writeisac = NULL;
+	cs->readisacfifo = NULL;
+	cs->writeisacfifo = NULL;
+	cs->BC_Read_Reg = NULL;
+	cs->BC_Write_Reg = NULL;
+	cs->irq_func = &hfcsx_interrupt;
+
+	cs->hw.hfcsx.timer.function = (void *) hfcsx_Timer;
+	cs->hw.hfcsx.timer.data = (long) cs;
+	cs->hw.hfcsx.b_fifo_size = 0; /* fifo size still unknown */
+	cs->hw.hfcsx.cirm = ccd_sp_irqtab[cs->irq & 0xF]; /* RAM not evaluated */
+	init_timer(&cs->hw.hfcsx.timer);
+
+	reset_hfcsx(cs);
+	cs->cardmsg = &hfcsx_card_msg;
+	cs->auxcmd = &hfcsx_auxcmd;
+	return (1);
+}
+
+
+
+
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfc_sx.h linux/drivers/isdn/hisax/hfc_sx.h
--- v2.3.45/linux/drivers/isdn/hisax/hfc_sx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_sx.h	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,216 @@
+/* $Id: hfc_sx.h,v 1.1 1999/11/18 00:09:18 werner Exp $
+
+ *  specific defines for CCD's HFC 2BDS0 S+,SP chips
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)      
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn4linux.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, 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.
+ *
+ * $Log: hfc_sx.h,v $
+ * Revision 1.1  1999/11/18 00:09:18  werner
+ *
+ * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM.
+ * Audio and Echo are supported.
+ *
+ *
+ *
+ */
+
+/*********************************************/
+/* thresholds for transparent B-channel mode */
+/* change mask and threshold simultaneously  */
+/*********************************************/
+#define HFCSX_BTRANS_THRESHOLD 128
+#define HFCSX_BTRANS_THRESMASK 0x00
+
+/* GCI/IOM bus monitor registers */
+
+#define HFCSX_C_I       0x02
+#define HFCSX_TRxR      0x03
+#define HFCSX_MON1_D    0x0A
+#define HFCSX_MON2_D    0x0B
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define HFCSX_B1_SSL    0x20
+#define HFCSX_B2_SSL    0x21
+#define HFCSX_AUX1_SSL  0x22
+#define HFCSX_AUX2_SSL  0x23
+#define HFCSX_B1_RSL    0x24
+#define HFCSX_B2_RSL    0x25
+#define HFCSX_AUX1_RSL  0x26
+#define HFCSX_AUX2_RSL  0x27
+
+/* GCI/IOM bus data registers */
+
+#define HFCSX_B1_D      0x28
+#define HFCSX_B2_D      0x29
+#define HFCSX_AUX1_D    0x2A
+#define HFCSX_AUX2_D    0x2B
+
+/* GCI/IOM bus configuration registers */
+
+#define HFCSX_MST_EMOD  0x2D
+#define HFCSX_MST_MODE	0x2E
+#define HFCSX_CONNECT 	0x2F
+
+
+/* Interrupt and status registers */
+
+#define HFCSX_TRM       0x12
+#define HFCSX_B_MODE    0x13
+#define HFCSX_CHIP_ID   0x16
+#define HFCSX_CIRM  	0x18
+#define HFCSX_CTMT	0x19
+#define HFCSX_INT_M1  	0x1A
+#define HFCSX_INT_M2  	0x1B
+#define HFCSX_INT_S1  	0x1E
+#define HFCSX_INT_S2  	0x1F
+#define HFCSX_STATUS  	0x1C
+
+/* S/T section registers */
+
+#define HFCSX_STATES  	0x30
+#define HFCSX_SCTRL  	0x31
+#define HFCSX_SCTRL_E   0x32
+#define HFCSX_SCTRL_R   0x33
+#define HFCSX_SQ  	0x34
+#define HFCSX_CLKDEL  	0x37
+#define HFCSX_B1_REC    0x3C
+#define HFCSX_B1_SEND   0x3C
+#define HFCSX_B2_REC    0x3D
+#define HFCSX_B2_SEND   0x3D
+#define HFCSX_D_REC     0x3E
+#define HFCSX_D_SEND    0x3E
+#define HFCSX_E_REC     0x3F
+
+/****************/
+/* FIFO section */
+/****************/
+#define HFCSX_FIF_SEL   0x10
+#define HFCSX_FIF_Z1L   0x80
+#define HFCSX_FIF_Z1H   0x84
+#define HFCSX_FIF_Z2L   0x88
+#define HFCSX_FIF_Z2H   0x8C
+#define HFCSX_FIF_INCF1 0xA8
+#define HFCSX_FIF_DWR   0xAC
+#define HFCSX_FIF_F1    0xB0
+#define HFCSX_FIF_F2    0xB4
+#define HFCSX_FIF_INCF2 0xB8
+#define HFCSX_FIF_DRD   0xBC
+
+/* bits in status register (READ) */
+#define HFCSX_SX_PROC    0x02
+#define HFCSX_NBUSY	 0x04 
+#define HFCSX_TIMER_ELAP 0x10
+#define HFCSX_STATINT	 0x20
+#define HFCSX_FRAMEINT	 0x40
+#define HFCSX_ANYINT	 0x80
+
+/* bits in CTMT (Write) */
+#define HFCSX_CLTIMER    0x80
+#define HFCSX_TIM3_125   0x04
+#define HFCSX_TIM25      0x10
+#define HFCSX_TIM50      0x14
+#define HFCSX_TIM400     0x18
+#define HFCSX_TIM800     0x1C
+#define HFCSX_AUTO_TIMER 0x20
+#define HFCSX_TRANSB2    0x02
+#define HFCSX_TRANSB1    0x01
+
+/* bits in CIRM (Write) */
+#define HFCSX_IRQ_SELMSK 0x07
+#define HFCSX_IRQ_SELDIS 0x00
+#define HFCSX_RESET  	 0x08
+#define HFCSX_FIFO_RESET 0x80
+
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCSX_INTS_B1TRANS  0x01
+#define HFCSX_INTS_B2TRANS  0x02
+#define HFCSX_INTS_DTRANS   0x04
+#define HFCSX_INTS_B1REC    0x08
+#define HFCSX_INTS_B2REC    0x10
+#define HFCSX_INTS_DREC     0x20
+#define HFCSX_INTS_L1STATE  0x40
+#define HFCSX_INTS_TIMER    0x80
+
+/* bits in INT_M2 */
+#define HFCSX_PROC_TRANS    0x01
+#define HFCSX_GCI_I_CHG     0x02
+#define HFCSX_GCI_MON_REC   0x04
+#define HFCSX_IRQ_ENABLE    0x08
+
+/* bits in STATES */
+#define HFCSX_STATE_MSK     0x0F
+#define HFCSX_LOAD_STATE    0x10
+#define HFCSX_ACTIVATE	    0x20
+#define HFCSX_DO_ACTION     0x40
+#define HFCSX_NT_G2_G3      0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCSX_MASTER	    0x01
+#define HFCSX_SLAVE         0x00
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA	    0x01
+#define SCTRL_B2_ENA	    0x02
+#define SCTRL_MODE_TE       0x00
+#define SCTRL_MODE_NT       0x04
+#define SCTRL_LOW_PRIO	    0x08
+#define SCTRL_SQ_ENA	    0x10
+#define SCTRL_TEST	    0x20
+#define SCTRL_NONE_CAP	    0x40
+#define SCTRL_PWR_DOWN	    0x80
+
+/* bits in SCTRL_E  */
+#define HFCSX_AUTO_AWAKE    0x01
+#define HFCSX_DBIT_1        0x04
+#define HFCSX_IGNORE_COL    0x08
+#define HFCSX_CHG_B1_B2     0x80
+
+/**********************************/
+/* definitions for FIFO selection */
+/**********************************/
+#define HFCSX_SEL_D_RX      5
+#define HFCSX_SEL_D_TX      4
+#define HFCSX_SEL_B1_RX     1
+#define HFCSX_SEL_B1_TX     0
+#define HFCSX_SEL_B2_RX     3
+#define HFCSX_SEL_B2_TX     2
+
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL_32K       0x0200
+#define B_FIFO_SIZE_32K    (0x2000 - B_SUB_VAL_32K)
+#define B_SUB_VAL_8K        0x1A00
+#define B_FIFO_SIZE_8K     (0x2000 - B_SUB_VAL_8K)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+/************************************************************/
+/* structure holding additional dynamic data -> send marker */
+/************************************************************/
+struct hfcsx_extra {
+  unsigned short marker[2*(MAX_B_FRAMES+1) + (MAX_D_FRAMES+1)];
+};
+
+extern void main_irq_hfcsx(struct BCState *bcs);
+extern void inithfcsx(struct IsdnCardState *cs);
+extern void releasehfcsx(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hfcscard.c linux/drivers/isdn/hisax/hfcscard.c
--- v2.3.45/linux/drivers/isdn/hisax/hfcscard.c	Thu Nov 11 20:11:37 1999
+++ linux/drivers/isdn/hisax/hfcscard.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: hfcscard.c,v 1.5 1999/09/04 06:20:06 keil Exp $
+/* $Id: hfcscard.c,v 1.6 1999/12/19 13:09:42 keil Exp $
 
  * hfcscard.c     low level stuff for hfcs based cards (Teles3c, ACER P10)
  *
@@ -6,6 +6,10 @@
  *
  *
  * $Log: hfcscard.c,v $
+ * Revision 1.6  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.5  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -30,7 +34,7 @@
 
 extern const char *CardType[];
 
-static const char *hfcs_revision = "$Revision: 1.5 $";
+static const char *hfcs_revision = "$Revision: 1.6 $";
 
 static void
 hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs)
@@ -85,13 +89,13 @@
 	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset On */
 	save_flags(flags);
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);
 	cs->hw.hfcD.cirm = 0;
 	if (cs->typ == ISDN_CTYPE_TELES3C)
 		cs->hw.hfcD.cirm |= HFCD_MEM8K;
 	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset Off */
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	if (cs->typ == ISDN_CTYPE_TELES3C)
 		cs->hw.hfcD.cirm |= HFCD_INTB;
@@ -138,7 +142,7 @@
 			init2bds0(cs);
 			save_flags(flags);
 			sti();
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((80*HZ)/1000);
 			cs->hw.hfcD.ctmt |= HFCD_TIM800;
 			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); 
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.3.45/linux/drivers/isdn/hisax/hisax.h	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/hisax/hisax.h	Tue Feb 15 11:40:43 2000
@@ -1,8 +1,16 @@
-/* $Id: hisax.h,v 2.38 1999/11/14 23:37:03 keil Exp $
+/* $Id: hisax.h,v 2.40 2000/01/20 19:51:46 keil Exp $
 
  *   Basic declarations, defines and prototypes
  *
  * $Log: hisax.h,v $
+ * Revision 2.40  2000/01/20 19:51:46  keil
+ * Fix AddTimer message
+ * Change CONFIG defines
+ *
+ * Revision 2.39  1999/11/18 00:00:43  werner
+ *
+ * Added support for HFC-S+ and HFC-SP cards
+ *
  * Revision 2.38  1999/11/14 23:37:03  keil
  * new ISA memory mapped IO
  *
@@ -784,6 +792,31 @@
 	struct timer_list timer;
 };
 
+struct hfcSX_hw {
+        unsigned int  base;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+        unsigned char sctrl_r;
+        unsigned char sctrl_e;
+        unsigned char trm;
+	unsigned char stat;
+	unsigned char fifo;
+        unsigned char bswapped;
+        unsigned char nt_mode;
+        unsigned char chip;
+        int b_fifo_size;
+        unsigned char last_fifo;
+        void *extra;
+        int nt_timer;
+	struct timer_list timer;
+};
+
 struct hfcD_hw {
 	unsigned int addr;
 	unsigned int bfifosize;
@@ -894,6 +927,10 @@
 	int ph_state;
 };
 
+struct hfcsx_chip {
+	int ph_state;
+};
+
 struct w6692_chip {
 	int ph_state;
 };
@@ -934,6 +971,7 @@
 		struct njet_hw njet;
 		struct hfcD_hw hfcD;
 		struct hfcPCI_hw hfcpci;
+		struct hfcSX_hw hfcsx;
 		struct ix1_hw niccy;
 		struct isurf_hw isurf;
 		struct saphir_hw saphir;
@@ -973,6 +1011,7 @@
 		struct isac_chip isac;
 		struct hfcd_chip hfcd;
 		struct hfcpci_chip hfcpci;
+		struct hfcsx_chip hfcsx;
 		struct w6692_chip w6692;
 	} dc;
 	u_char *rcvbuf;
@@ -1032,7 +1071,8 @@
 #define  ISDN_CTYPE_GAZEL	34
 #define  ISDN_CTYPE_HFC_PCI	35
 #define  ISDN_CTYPE_W6692	36
-#define  ISDN_CTYPE_COUNT	36
+#define  ISDN_CTYPE_HFC_SX      37
+#define  ISDN_CTYPE_COUNT	37
 
 
 #ifdef ISDN_CHIP_ISAC
@@ -1201,6 +1241,12 @@
 #define  CARD_HFC_PCI 0
 #endif
 
+#ifdef	CONFIG_HISAX_HFC_SX
+#define  CARD_HFC_SX 1
+#else
+#define  CARD_HFC_SX 0
+#endif
+
 #ifdef  CONFIG_HISAX_AMD7930
 #define CARD_AMD7930 1
 #else
@@ -1298,19 +1344,6 @@
 #ifdef CONFIG_HISAX_EURO
 #undef TEI_PER_CARD
 #define TEI_PER_CARD 1
-#define HISAX_EURO_SENDCOMPLETE 1
-#define EXT_BEARER_CAPS 1
-#define HISAX_SEND_STD_LLC_IE 1
-#ifdef	CONFIG_HISAX_NO_SENDCOMPLETE
-#undef HISAX_EURO_SENDCOMPLETE
-#endif
-#ifdef	CONFIG_HISAX_NO_LLC
-#undef HISAX_SEND_STD_LLC_IE
-#endif
-#undef HISAX_DE_AOC
-#ifdef CONFIG_DE_AOC
-#define HISAX_DE_AOC 1
-#endif
 #endif
 
 /* L1 Debug */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/hscx.h linux/drivers/isdn/hisax/hscx.h
--- v2.3.45/linux/drivers/isdn/hisax/hscx.h	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/hisax/hscx.h	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,14 @@
-/* $Id: hscx.h,v 1.4 1998/04/15 16:45:34 keil Exp $
+/* $Id: hscx.h,v 1.5 1999/12/23 15:09:32 keil Exp $
 
  * hscx.h   HSCX specific defines
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: hscx.h,v $
+ * Revision 1.5  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.4  1998/04/15 16:45:34  keil
  * new init code
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/ipac.h linux/drivers/isdn/hisax/ipac.h
--- v2.3.45/linux/drivers/isdn/hisax/ipac.h	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/hisax/ipac.h	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,14 @@
-/* $Id: ipac.h,v 1.3 1998/04/15 16:48:09 keil Exp $
+/* $Id: ipac.h,v 1.4 1999/12/23 15:09:32 keil Exp $
 
  * ipac.h   IPAC specific defines
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: ipac.h,v $
+ * Revision 1.4  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.3  1998/04/15 16:48:09  keil
  * IPAC_ATX added
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/isac.h linux/drivers/isdn/hisax/isac.h
--- v2.3.45/linux/drivers/isdn/hisax/isac.h	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/hisax/isac.h	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,14 @@
-/* $Id: isac.h,v 1.5 1998/05/25 12:58:03 keil Exp $
+/* $Id: isac.h,v 1.6 1999/12/23 15:09:32 keil Exp $
 
  * isac.h   ISAC specific defines
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: isac.h,v $
+ * Revision 1.6  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 1.5  1998/05/25 12:58:03  keil
  * HiSax golden code from certification, Don't use !!!
  * No leased lines, no X75, but many changes.
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c
--- v2.3.45/linux/drivers/isdn/hisax/isar.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/isar.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isar.c,v 1.7 1999/10/14 20:25:29 keil Exp $
+/* $Id: isar.c,v 1.9 2000/01/20 19:47:45 keil Exp $
 
  * isar.c   ISAR (Siemens PSB 7110) specific routines
  *
@@ -6,6 +6,12 @@
  *
  *
  * $Log: isar.c,v $
+ * Revision 1.9  2000/01/20 19:47:45  keil
+ * Add Fax Class 1 support
+ *
+ * Revision 1.8  1999/12/19 13:00:56  keil
+ * Fix races in setting a new mode
+ *
  * Revision 1.7  1999/10/14 20:25:29  keil
  * add a statistic for error monitoring
  *
@@ -42,7 +48,17 @@
 
 #define MIN(a,b) ((a<b)?a:b)
 
+#define DLE	0x10
+#define ETX	0x03
+
+
+const u_char faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146"; 
+const u_char faxmodulation[] = {3,24,48,72,73,74,96,97,98,121,122,145,146}; 
+#define FAXMODCNT 13
+
 void isar_setup(struct IsdnCardState *cs);
+static void isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para);
+static inline void ll_deliver_faxstat(struct BCState *bcs, u_char status);
 
 static inline int
 waitforHIA(struct IsdnCardState *cs, int timeout)
@@ -432,6 +448,12 @@
 isar_bh(struct BCState *bcs)
 {
 	BChannel_bh(bcs);
+	if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR);
+	if (test_and_clear_bit(B_LL_CONNECT, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+	if (test_and_clear_bit(B_LL_OK, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_OK);
 }
 
 static void
@@ -443,6 +465,42 @@
 }
 
 static inline void
+send_DLE_ETX(struct BCState *bcs)
+{
+	u_char dleetx[2] = {DLE,ETX};
+	struct sk_buff *skb;
+	
+	if ((skb = dev_alloc_skb(2))) {
+		memcpy(skb_put(skb, 2), dleetx, 2);
+		skb_queue_tail(&bcs->rqueue, skb);
+		isar_sched_event(bcs, B_RCVBUFREADY);
+	} else {
+		printk(KERN_WARNING "HiSax: skb out of memory\n");
+	}
+}
+
+static inline int
+dle_count(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
+}
+
+static inline void
+insert_dle(unsigned char *dest, unsigned char *src, int count) {
+	/* <DLE> in input stream have to be flagged as <DLE><DLE> */
+	while (count--) {
+		*dest++ = *src;
+		if (*src++ == DLE)
+			*dest++ = DLE;
+	}
+}
+ 
+static inline void
 isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
 {
 	u_char *ptr;
@@ -512,6 +570,88 @@
 			}
 		}
 		break;
+	case L1_MODE_FAX:
+		if (bcs->hw.isar.state != STFAX_ACTIV) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: not ACTIV");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+			rcv_mbox(cs, ireg, bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvidx = ireg->clsb +
+				dle_count(bcs->hw.isar.rcvbuf, ireg->clsb);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_rcv_frame: raw(%d) dle(%d)",
+					ireg->clsb, bcs->hw.isar.rcvidx);
+			if ((skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
+				insert_dle((u_char *)skb_put(skb, bcs->hw.isar.rcvidx),
+					bcs->hw.isar.rcvbuf, ireg->clsb);
+				skb_queue_tail(&bcs->rqueue, skb);
+				isar_sched_event(bcs, B_RCVBUFREADY);
+				if (ireg->cmsb & SART_NMD) { /* ABORT */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar_rcv_frame: no more data");
+					cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+					bcs->hw.isar.rcvidx = 0;
+					send_DLE_ETX(bcs);
+					sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+						0, NULL);
+					bcs->hw.isar.state = STFAX_ESCAPE;
+					isar_sched_event(bcs, B_LL_NOCARRIER);
+				}
+			} else {
+				printk(KERN_WARNING "HiSax: skb out of memory\n");
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		}
+		if (bcs->hw.isar.cmd != PCTRL_CMD_FRH) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: unknown fax mode %x",
+					bcs->hw.isar.cmd);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		/* PCTRL_CMD_FRH */
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD)
+				bcs->hw.isar.rcvidx = 0;
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
+						bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				} else {
+					memcpy(skb_put(skb, bcs->hw.isar.rcvidx),
+						bcs->hw.isar.rcvbuf,
+						bcs->hw.isar.rcvidx);
+					skb_queue_tail(&bcs->rqueue, skb);
+					isar_sched_event(bcs, B_RCVBUFREADY);
+					send_DLE_ETX(bcs);
+					isar_sched_event(bcs, B_LL_OK);
+				}
+			}
+		}
+		break;
 	default:
 		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
 		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
@@ -544,38 +684,57 @@
 		count = bcs->tx_skb->len;
 		msb = HDLC_FED;
 	}
-	if (!bcs->hw.isar.txcnt)
-		msb |= HDLC_FST;
 	save_flags(flags);
 	cli();
 	ptr = bcs->tx_skb->data;
+	if (!bcs->hw.isar.txcnt) {
+		msb |= HDLC_FST;
+		if ((bcs->mode == L1_MODE_FAX) &&
+			(bcs->hw.isar.cmd == PCTRL_CMD_FTH)) {
+			if (bcs->tx_skb->len > 1) {
+				if ((ptr[0]== 0xff) && (ptr[1] == 0x13))
+					/* last frame */
+					test_and_set_bit(BC_FLG_LASTDATA,
+						&bcs->Flag);
+			}  
+		}
+	}
 	skb_pull(bcs->tx_skb, count);
 	bcs->tx_cnt -= count;
 	bcs->hw.isar.txcnt += count;
 	switch (bcs->mode) {
-	case L1_MODE_NULL:
-		printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
-		break;
-	case L1_MODE_TRANS:
-	case L1_MODE_V32:
-		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
-			0, count, ptr)) {
-			if (cs->debug)
-				debugl1(cs, "isar bin data send dp%d failed",
-					bcs->hw.isar.dpath);
-		}
-		break;
-	case L1_MODE_HDLC:
-		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
-			msb, count, ptr)) {
+		case L1_MODE_NULL:
+			printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_V32:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				0, count, ptr);
+			break;
+		case L1_MODE_HDLC:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				msb, count, ptr);
+			break;
+		case L1_MODE_FAX:
+			if (bcs->hw.isar.state != STFAX_ACTIV) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not ACTIV");
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { 
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					msb, count, ptr);
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					0, count, ptr);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not FTH/FTM");
+			}
+			break;
+		default:
 			if (cs->debug)
-				debugl1(cs, "isar hdlc data send dp%d failed",
-					bcs->hw.isar.dpath);
-		}
-		break;
-	default:
-		printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode);
-		break;
+				debugl1(cs, "isar_fill_fifo mode(%x) error", bcs->mode);
+			printk(KERN_ERR"isar_fill_fifo mode(%x) error\n", bcs->mode);
+			break;
 	}
 	restore_flags(flags);
 }
@@ -603,6 +762,18 @@
 			if (bcs->st->lli.l1writewakeup &&
 				(PACKET_NOACK != bcs->tx_skb->pkt_type))
 					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt);
+			if (bcs->mode == L1_MODE_FAX) {
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					if (test_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+					if (test_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_LASTDATA, &bcs->Flag);
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				}
+			}
 			dev_kfree_skb(bcs->tx_skb);
 			bcs->hw.isar.txcnt = 0; 
 			bcs->tx_skb = NULL;
@@ -613,6 +784,18 @@
 		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
 		isar_fill_fifo(bcs);
 	} else {
+		if (test_and_clear_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+			if (test_and_clear_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+				if (test_and_clear_bit(BC_FLG_NMD_DATA, &bcs->Flag)) {
+					u_char dummy = 0;
+					sendmsg(bcs->cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_SDATA, 0x01, 1, &dummy);
+				}
+				test_and_set_bit(BC_FLG_LL_OK, &bcs->Flag);
+			} else {
+				isar_sched_event(bcs, B_LL_CONNECT);
+			}
+		}
 		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		isar_sched_event(bcs, B_XMTBUFREADY);
 	}
@@ -639,6 +822,7 @@
 	}
 	
 }
+
 const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
 			"300", "600", "1200", "2400", "4800", "7200",
 			"9600nt", "9600t", "12000", "14400", "WRONG"};
@@ -700,7 +884,7 @@
 }
 
 static void
-isar_pump_status_ev(struct BCState *bcs, u_char devt) {
+isar_pump_statev_modem(struct BCState *bcs, u_char devt) {
 	struct IsdnCardState *cs = bcs->cs;
 	u_char dps = SET_DPS(bcs->hw.isar.dpath);
 
@@ -769,6 +953,192 @@
 	}
 }
 
+static inline void
+ll_deliver_faxstat(struct BCState *bcs, u_char status)
+{
+        isdn_ctrl ic;
+	struct Channel *chanp = (struct Channel *) bcs->st->lli.userdata;
+ 
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "HL->LL FAXIND %x", status);
+	ic.driver = bcs->cs->myid;
+	ic.command = ISDN_STAT_FAXIND;
+	ic.arg = chanp->chan;
+	ic.parm.aux.cmd = status;
+	bcs->cs->iif.statcallb(&ic);
+}
+
+static void
+isar_pump_statev_fax(struct BCState *bcs, u_char devt) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char p1;
+
+	switch(devt) {
+		case PSEV_10MS_TIMER:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev TIMER");
+			break;
+		case PSEV_RSP_READY:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_READY");
+			bcs->hw.isar.state = STFAX_READY;
+			l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL);
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FRH, 3);
+			} else {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FTH, 3);
+			}
+			break;
+		case PSEV_LINE_TX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_TX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_RSP_CONN:
+			if (bcs->hw.isar.state == STFAX_CONT) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_CONN");
+				bcs->hw.isar.state = STFAX_ACTIV;
+				test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags);
+				sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					/* 1s Flags before data */
+					if (test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag))
+						del_timer(&bcs->hw.isar.ftimer);
+					/* 1000 ms */
+					bcs->hw.isar.ftimer.expires =
+						jiffies + ((1000 * HZ)/1000);
+					test_and_set_bit(BC_FLG_LL_CONN,
+						&bcs->Flag);
+					add_timer(&bcs->hw.isar.ftimer);
+				} else {
+					isar_sched_event(bcs, B_LL_CONNECT);
+				}
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev RSP_CONN wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_FLAGS_DET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev FLAGS_DET");
+			break;
+		case PSEV_RSP_DISC:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_DISC");
+			if (bcs->hw.isar.state == STFAX_ESCAPE) {
+				switch(bcs->hw.isar.newcmd) {
+					case PCTRL_CMD_FTH:
+					case PCTRL_CMD_FTM:
+						p1 = 10;
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							PCTRL_CMD_SILON, 1, &p1);
+						bcs->hw.isar.state = STFAX_SILDET;
+						break;
+					case PCTRL_CMD_FRH:
+					case PCTRL_CMD_FRM:
+						p1 = bcs->hw.isar.newmod;
+						bcs->hw.isar.newmod = 0;
+						bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+						bcs->hw.isar.newcmd = 0;
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							bcs->hw.isar.cmd, 1, &p1);
+						bcs->hw.isar.state = STFAX_LINE;
+						break;
+					default:
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "RSP_DISC unknown newcmd %x", bcs->hw.isar.newcmd);
+						break;
+				}
+			} else if (bcs->hw.isar.state == STFAX_ACTIV) {
+				if (test_and_clear_bit(BC_FLG_LL_OK, &bcs->Flag)) {
+					isar_sched_event(bcs, B_LL_OK);
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+					send_DLE_ETX(bcs);
+					isar_sched_event(bcs, B_LL_NOCARRIER);
+				} else {
+					ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+				}
+				bcs->hw.isar.state = STFAX_READY;
+			} else {
+				bcs->hw.isar.state = STFAX_READY;
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			}
+			break;
+		case PSEV_RSP_SILDET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILDET");
+			if (bcs->hw.isar.state == STFAX_SILDET) {
+				p1 = bcs->hw.isar.newmod;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+				bcs->hw.isar.newcmd = 0;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+					bcs->hw.isar.cmd, 1, &p1);
+				bcs->hw.isar.state = STFAX_LINE;
+			}
+			break;
+		case PSEV_RSP_SILOFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILOFF");
+			break;
+		case PSEV_RSP_FCERR:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_FCERR");
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			break;
+		default:
+			break;
+	}
+}
+
 static char debbuf[128];
 
 void
@@ -788,8 +1158,6 @@
 			} else {
 				debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
 					ireg->iis, ireg->cmsb, ireg->clsb);
-				printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n",
-					ireg->iis, ireg->cmsb, ireg->clsb);
 				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 			}
 			break;
@@ -815,12 +1183,18 @@
 		case ISAR_IIS_PSTEV:
 			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
 				rcv_mbox(cs, ireg, (u_char *)ireg->par);
-				isar_pump_status_ev(bcs, ireg->cmsb);
+				if (bcs->mode == L1_MODE_V32) {
+					isar_pump_statev_modem(bcs, ireg->cmsb);
+				} else if (bcs->mode == L1_MODE_FAX) {
+					isar_pump_statev_fax(bcs, ireg->cmsb);
+				} else {
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar IIS_PSTEV pmode %d stat %x",
+							bcs->mode, ireg->cmsb);
+				}
 			} else {
 				debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x",
 					ireg->iis, ireg->cmsb, ireg->clsb);
-				printk(KERN_WARNING"isar spurious IIS_PSTEV %x/%x/%x\n",
-					ireg->iis, ireg->cmsb, ireg->clsb);
 				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 			}
 			break;
@@ -831,8 +1205,6 @@
 			} else {
 				debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x",
 					ireg->iis, ireg->cmsb, ireg->clsb);
-				printk(KERN_WARNING"isar spurious IIS_PSTRSP %x/%x/%x\n",
-					ireg->iis, ireg->cmsb, ireg->clsb);
 				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 			}
 			break;
@@ -867,6 +1239,17 @@
 }
 
 static void
+ftimer_handler(struct BCState *bcs) {
+	if (bcs->cs->debug)
+		debugl1(bcs->cs, "ftimer flags %04x",
+			bcs->Flag);
+	test_and_clear_bit(BC_FLG_FTI_RUN, &bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_LL_CONN, &bcs->Flag)) {
+		isar_sched_event(bcs, B_LL_CONNECT);
+	}
+}
+
+static void
 setup_pump(struct BCState *bcs) {
 	struct IsdnCardState *cs = bcs->cs;
 	u_char dps = SET_DPS(bcs->hw.isar.dpath);
@@ -876,11 +1259,7 @@
 		case L1_MODE_NULL:
 		case L1_MODE_TRANS:
 		case L1_MODE_HDLC:
-			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) {
-				if (cs->debug)
-					debugl1(cs, "isar pump bypass cfg dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
 			break;
 		case L1_MODE_V32:
 			ctrl = PMOD_DATAMODEM;
@@ -896,11 +1275,7 @@
 			param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
 			param[3] = PV32P4_UT144;
 			param[4] = PV32P5_UT144;
-			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar pump datamodem cfg dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
 			break;
 		case L1_MODE_FAX:
 			ctrl = PMOD_FAX;
@@ -911,18 +1286,16 @@
 				param[1] = PFAXP2_ATN;
 			}
 			param[0] = 6; /* 6 db */
-			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar pump faxmodem cfg dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+			bcs->hw.isar.state = STFAX_NULL;
+			bcs->hw.isar.newcmd = 0;
+			bcs->hw.isar.newmod = 0;
+			test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag);
 			break;
 	}
-	if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) {
-		if (cs->debug)
-			debugl1(cs, "isar pump status req dp%d failed",
-				bcs->hw.isar.dpath);
-	}
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+	udelay(1000);
 }
 
 static void
@@ -933,44 +1306,30 @@
 	
 	switch (bcs->mode) {
 		case L1_MODE_NULL:
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) {
-				if (cs->debug)
-					debugl1(cs, "isar sart disable dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0,
+				NULL);
 			break;
 		case L1_MODE_TRANS:
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) {
-				if (cs->debug)
-					debugl1(cs, "isar sart binary dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2,
+				"\0\0");
 			break;
 		case L1_MODE_HDLC:
 		case L1_MODE_FAX:
 			param[0] = 0;
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar sart hdlc dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1,
+				param);
 			break;
 		case L1_MODE_V32:
 			ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
 			param[0] = S_P1_CHS_8;
 			param[1] = S_P2_BFT_DEF;
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar sart v14 dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2,
+				param);
 			break;
 	}
-	if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) {
-		if (cs->debug)
-			debugl1(cs, "isar buf stat req dp%d failed",
-				bcs->hw.isar.dpath);
-	}
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
+	udelay(1000);
 }
 
 static void
@@ -995,15 +1354,10 @@
 			cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV;
 			break;
 	}
-	if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) {
-		if (cs->debug)
-			debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath);
-	}
-	if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) {
-		if (cs->debug)
-			debugl1(cs, "isar IOM2 cfg req dp%d failed",
-				bcs->hw.isar.dpath);
-	}
+	sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
+	udelay(1000);
 }
 
 int
@@ -1041,8 +1395,8 @@
 					&bcs->hw.isar.reg->Flags))
 					bcs->hw.isar.dpath = 1;
 				else {
-					printk(KERN_WARNING"isar modeisar analog works only with DP1\n");
-					debugl1(cs, "isar modeisar analog works only with DP1");
+					printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n");
+					debugl1(cs, "isar modeisar analog funktions only with DP1");
 					return(1);
 				}
 				break;
@@ -1066,6 +1420,107 @@
 	return(0);
 }
 
+static void
+isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) 
+{
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl = 0, nom = 0, p1 = 0;
+
+	switch(cmd) {
+		case ISDN_FAX_CLASS1_FTM:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FTH:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRM:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRH:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+	}
+	if (ctrl)
+		sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
 void
 isar_setup(struct IsdnCardState *cs)
 {
@@ -1076,11 +1531,8 @@
 	msg = 61;
 	for (i=0; i<2; i++) {
 		/* Buffer Config */
-		if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
-			ISAR_HIS_P12CFG, 4, 1, &msg)) {
-			if (cs->debug)
-				debugl1(cs, "isar P%dCFG failed", i+1);
-		}
+		sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg);
 		cs->bcs[i].hw.isar.mml = msg;
 		cs->bcs[i].mode = 0;
 		cs->bcs[i].hw.isar.dpath = i + 1;
@@ -1147,6 +1599,7 @@
 						l1_msg_b(st, PH_ACTIVATE | REQUEST, arg);
 					break;
 				case L1_MODE_V32:
+				case L1_MODE_FAX:
 					if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc))
 						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
 					break;
@@ -1185,6 +1638,7 @@
 				debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
 		}
 	}
+	del_timer(&bcs->hw.isar.ftimer);
 }
 
 int
@@ -1206,6 +1660,9 @@
 	bcs->event = 0;
 	bcs->hw.isar.rcvidx = 0;
 	bcs->tx_cnt = 0;
+	bcs->hw.isar.ftimer.function = (void *) ftimer_handler;
+	bcs->hw.isar.ftimer.data = (long) bcs;
+	init_timer(&bcs->hw.isar.ftimer);
 	return (0);
 }
 
@@ -1226,15 +1683,66 @@
 int
 isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
 	u_long adr;
-	int features;
+	int features, i;
+	struct BCState *bcs;
 
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "isar_auxcmd cmd/ch %x/%d", ic->command, ic->arg);
 	switch (ic->command) {
+		case (ISDN_CMD_FAXCMD):
+			bcs = cs->channel[ic->arg].bcs;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_auxcmd cmd/subcmd %d/%d",
+					ic->parm.aux.cmd, ic->parm.aux.subcmd);
+			switch(ic->parm.aux.cmd) {
+				case ISDN_FAX_CLASS1_CTRL:
+					if (ic->parm.aux.subcmd == ETX)
+						test_and_set_bit(BC_FLG_DLEETX,
+							&bcs->Flag);
+					break;
+				case ISDN_FAX_CLASS1_FRM:
+				case ISDN_FAX_CLASS1_FRH:
+				case ISDN_FAX_CLASS1_FTM:
+				case ISDN_FAX_CLASS1_FTH:
+					if (ic->parm.aux.subcmd == AT_QUERY) {
+						sprintf(ic->parm.aux.para,
+							"%d", bcs->hw.isar.mod);
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_QUERY) {
+						strcpy(ic->parm.aux.para, faxmodulation_s);
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_VALUE) {
+						for(i=0;i<FAXMODCNT;i++)
+							if (faxmodulation[i]==ic->parm.aux.para[0])
+								break;
+						if ((FAXMODCNT > i) && 
+							test_bit(BC_FLG_INIT, &bcs->Flag)) {
+							isar_pump_cmd(bcs,
+								ic->parm.aux.cmd,
+								ic->parm.aux.para[0]);
+							return(0);
+						}
+					}
+					/* wrong modulation or not activ */
+					/* fall through */
+				default:
+					ic->command = ISDN_STAT_FAXIND;
+					ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR;
+					cs->iif.statcallb(ic);
+			}
+			break;
 		case (ISDN_CMD_IOCTL):
 			switch (ic->arg) {
 				case (9): /* load firmware */
-					features = ISDN_FEATURE_L2_MODEM;
+					features = ISDN_FEATURE_L2_MODEM |
+						ISDN_FEATURE_L2_FAX |
+						ISDN_FEATURE_L3_FCLASS1;
 					memcpy(&adr, ic->parm.num, sizeof(ulong));
 					if (isar_load_firmware(cs, (u_char *)adr))
 						return(1);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/isar.h linux/drivers/isdn/hisax/isar.h
--- v2.3.45/linux/drivers/isdn/hisax/isar.h	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/isar.h	Tue Feb 15 11:40:43 2000
@@ -1,10 +1,13 @@
-/* $Id: isar.h,v 1.6 1999/10/14 20:25:29 keil Exp $
+/* $Id: isar.h,v 1.7 2000/01/20 19:47:45 keil Exp $
  * isar.h   ISAR (Siemens PSB 7110) specific defines
  *
  * Author Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: isar.h,v $
+ * Revision 1.7  2000/01/20 19:47:45  keil
+ * Add Fax Class 1 support
+ *
  * Revision 1.6  1999/10/14 20:25:29  keil
  * add a statistic for error monitoring
  *
@@ -145,6 +148,28 @@
 #define PSEV_REM_REN	0xcd
 #define PSEV_GSTN_CLR	0xd4
 
+#define PSEV_RSP_READY	0xbc
+#define PSEV_LINE_TX_H	0xb3
+#define PSEV_LINE_TX_B	0xb2
+#define PSEV_LINE_RX_H	0xb1
+#define PSEV_LINE_RX_B	0xb0
+#define PSEV_RSP_CONN	0xb5
+#define PSEV_RSP_DISC	0xb7
+#define PSEV_RSP_FCERR	0xb9
+#define PSEV_RSP_SILDET	0xbe
+#define PSEV_RSP_SILOFF	0xab
+#define PSEV_FLAGS_DET	0xba
+
+#define PCTRL_CMD_FTH	0xa7
+#define PCTRL_CMD_FRH	0xa5
+#define PCTRL_CMD_FTM	0xa8
+#define PCTRL_CMD_FRM	0xa6
+#define PCTRL_CMD_SILON	0xac
+#define PCTRL_CMD_CONT	0xa2
+#define PCTRL_CMD_ESC	0xa4
+#define PCTRL_CMD_SILOFF 0xab
+#define PCTRL_CMD_HALT	0xa9
+
 #define PCTRL_LOC_RET	0xcf
 #define PCTRL_LOC_REN	0xce
 
@@ -192,6 +217,15 @@
 #define BSTAT_RDM3	0x8
 #define BSTEV_TBO	0x1f
 #define BSTEV_RBO	0x2f
+
+/* FAX State Machine */
+#define STFAX_NULL	0
+#define STFAX_READY	1
+#define STFAX_LINE	2
+#define STFAX_CONT	3
+#define STFAX_ACTIV	4
+#define STFAX_ESCAPE	5
+#define STFAX_SILDET	6
 
 extern int ISARVersion(struct IsdnCardState *cs, char *s);
 extern void isar_int_main(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.3.45/linux/drivers/isdn/hisax/isdnl1.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/isdnl1.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdnl1.c,v 2.36 1999/08/25 16:50:57 keil Exp $
+/* $Id: isdnl1.c,v 2.37 2000/01/20 19:51:46 keil Exp $
 
  * isdnl1.c     common low level stuff for Siemens Chipsetbased isdn cards
  *              based on the teles driver from Jan den Ouden
@@ -15,6 +15,10 @@
  *
  *
  * $Log: isdnl1.c,v $
+ * Revision 2.37  2000/01/20 19:51:46  keil
+ * Fix AddTimer message
+ * Change CONFIG defines
+ *
  * Revision 2.36  1999/08/25 16:50:57  keil
  * Fix bugs which cause 2.3.14 hangs (waitqueue init)
  *
@@ -138,7 +142,7 @@
  *
  */
 
-const char *l1_revision = "$Revision: 2.36 $";
+const char *l1_revision = "$Revision: 2.37 $";
 
 #define __NO_VERSION__
 #include "hisax.h"
@@ -362,7 +366,8 @@
 					stptr = stptr->next;
 			if (!found)
 				dev_kfree_skb(skb);
-		}
+		} else
+			dev_kfree_skb(skb);
 	}
 }
 
@@ -559,11 +564,8 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L1_F3);
-//	if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
-		FsmDelTimer(&st->l1.timer, 1);
-		FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
-		test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
-//	}
+	FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
 }
 
 static void
@@ -574,8 +576,7 @@
 	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
 		FsmChangeState(fi, ST_L1_F4);
 		st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
-		FsmDelTimer(&st->l1.timer, 1);
-		FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+		FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
 		test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
 	} else
 		FsmChangeState(fi, ST_L1_F3);
@@ -614,7 +615,7 @@
 	if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
 		if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
 			FsmDelTimer(&st->l1.timer, 3);
-		FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
+		FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
 		test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
 	}
 }
@@ -729,7 +730,7 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L1_WAIT_ACT);
-	FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
+	FsmRestartTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
 }
 
 static void
@@ -738,7 +739,7 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L1_WAIT_DEACT);
-	FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
+	FsmRestartTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
 }
 
 static void
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/isurf.c linux/drivers/isdn/hisax/isurf.c
--- v2.3.45/linux/drivers/isdn/hisax/isurf.c	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/hisax/isurf.c	Tue Feb 15 11:40:43 2000
@@ -1,10 +1,14 @@
-/* $Id: isurf.c,v 1.7 1999/11/14 23:37:03 keil Exp $
+/* $Id: isurf.c,v 1.8 1999/12/19 13:09:42 keil Exp $
 
  * isurf.c  low level stuff for Siemens I-Surf/I-Talk cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * $Log: isurf.c,v $
+ * Revision 1.8  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.7  1999/11/14 23:37:03  keil
  * new ISA memory mapped IO
  *
@@ -40,7 +44,7 @@
 
 extern const char *CardType[];
 
-static const char *ISurf_revision = "$Revision: 1.7 $";
+static const char *ISurf_revision = "$Revision: 1.8 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -162,10 +166,10 @@
 	byteout(cs->hw.isurf.reset, chips); /* Reset On */
 	save_flags(flags);
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	restore_flags(flags);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.3.45/linux/drivers/isdn/hisax/l3_1tr6.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: l3_1tr6.c,v 2.9 1999/07/01 08:11:55 keil Exp $
+/* $Id: l3_1tr6.c,v 2.10 2000/01/20 19:42:01 keil Exp $
 
  *  German 1TR6 D-channel protocol
  *
@@ -10,6 +10,9 @@
  *
  *
  * $Log: l3_1tr6.c,v $
+ * Revision 2.10  2000/01/20 19:42:01  keil
+ * Fixed uninitialiesed location
+ *
  * Revision 2.9  1999/07/01 08:11:55  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -59,7 +62,7 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 2.9 $";
+const char *l3_1tr6_revision = "$Revision: 2.10 $";
 
 #define MsgHead(ptr, cref, mty, dis) \
 	*ptr++ = dis; \
@@ -699,6 +702,7 @@
 {
         newl3state(pc, 0);
         pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
         pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
         release_l3_process(pc);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.3.45/linux/drivers/isdn/hisax/l3dss1.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/l3dss1.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: l3dss1.c,v 2.20 1999/10/11 22:16:27 keil Exp $
+/* $Id: l3dss1.c,v 2.22 2000/01/20 19:44:20 keil Exp $
 
  * EURO/DSS1 D-channel protocol
  *
@@ -13,6 +13,16 @@
  *              Fritz Elfert
  *
  * $Log: l3dss1.c,v $
+ * Revision 2.22  2000/01/20 19:44:20  keil
+ * Fixed uninitialiesed location
+ * Fixed redirecting number IE in Setup
+ * Changes from certification
+ * option for disabling use of KEYPAD protocol
+ *
+ * Revision 2.21  1999/12/19 20:25:17  keil
+ * fixed LLC for outgoing analog calls
+ * IE Signal is valid on older local switches
+ *
  * Revision 2.20  1999/10/11 22:16:27  keil
  * Suspend/Resume is possible without explicit ID too
  *
@@ -91,9 +101,10 @@
 #include "isdnl3.h"
 #include "l3dss1.h"
 #include <linux/ctype.h>
+#include <linux/config.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 2.20 $";
+const char *dss1_revision = "$Revision: 2.22 $";
 
 #define EXT_BEARER_CAPS 1
 
@@ -668,34 +679,36 @@
 }
 
 static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
-		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC,
+		IE_USER_USER, -1};
 static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
 		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1};
 static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, 
-		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_CONNECT_PN,
-		IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
-static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, -1};
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL,
+		IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1};
 static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
-		IE_PROGRESS, IE_DISPLAY, IE_USER_USER, -1};
-static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL,
 		IE_CALLED_PN, -1};
 static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1};
 static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS |
 		IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
-static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_USER_USER, -1};
+static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY,
+		IE_SIGNAL, IE_USER_USER, -1};
 /* a RELEASE_COMPLETE with errors don't require special actions 
-static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_USER_USER, -1};
+static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
 */
 static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY,
 		IE_DISPLAY, -1};
 static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
 static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER  | IE_MANDATORY,
 		IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
-		IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_CALLING_PN,
-		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_HLC,
-		IE_USER_USER, -1};
+		IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN,
+		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR,
+		IE_LLC, IE_HLC, IE_USER_USER, -1};
 static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
-		IE_PROGRESS, IE_DISPLAY, -1};
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1};
 static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
 		IE_MANDATORY, IE_DISPLAY, -1};
 static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1};
@@ -1272,6 +1285,7 @@
 	u_char tmp[128];
 	u_char *p = tmp;
 	u_char channel = 0;
+
         u_char send_keypad;
 	u_char screen = 0x80;
 	u_char *teln;
@@ -1283,14 +1297,18 @@
 	MsgHead(p, pc->callref, MT_SETUP);
 
 	teln = pc->para.setup.phone;
+#ifndef CONFIG_HISAX_NO_KEYPAD
         send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0; 
+#else
+	send_keypad = 0;
+#endif
+#ifndef CONFIG_HISAX_NO_SENDCOMPLETE
+	if (!send_keypad)
+		*p++ = 0xa1;		/* complete indicator */
+#endif
 	/*
 	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
 	 */
-#if HISAX_EURO_SENDCOMPLETE
-	if (!send_keypad)
-	  *p++ = 0xa1;		/* complete indicator */
-#endif
         if (!send_keypad)
 	  switch (pc->para.setup.si1) {
 		case 1:	/* Telephony                               */
@@ -1452,12 +1470,25 @@
 		*p++ = 0x90;
 		*p++ = 0x21;
 		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
-#if HISAX_SEND_STD_LLC_IE
+#ifndef CONFIG_HISAX_NO_LLC
 	} else {
-		*p++ = 0x7c;
-		*p++ = 0x02;
-		*p++ = 0x88;
-		*p++ = 0x90;
+	  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                             */
+			break;
+		case 5:	/* Datatransmission 64k, BTX               */
+		case 7:	/* Datatransmission 64k                    */
+		default:
+			*p++ = 0x7c;	/* BC-IE-code                              */
+			*p++ = 0x2;	/* Length                                  */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	  }
 #endif
 	}
 #endif
@@ -2738,6 +2769,7 @@
 l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
 {
         pc->para.cause = 0x29;          /* Temporary failure */
+        pc->para.loc = 0;
         l3dss1_disconnect_req(pc, pr, NULL);
         pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 }
@@ -2747,6 +2779,7 @@
 {
         newl3state(pc, 0);
         pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
         pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
         release_l3_process(pc);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/l3dss1.h linux/drivers/isdn/hisax/l3dss1.h
--- v2.3.45/linux/drivers/isdn/hisax/l3dss1.h	Thu Aug 12 09:42:33 1999
+++ linux/drivers/isdn/hisax/l3dss1.h	Tue Feb 15 11:40:43 2000
@@ -1,8 +1,11 @@
-/* $Id: l3dss1.h,v 1.7 1999/07/01 08:12:02 keil Exp $
+/* $Id: l3dss1.h,v 1.8 2000/01/20 19:46:15 keil Exp $
  *
  *  DSS1 (Euro) D-channel protocol defines
  *
  * $Log: l3dss1.h,v $
+ * Revision 1.8  2000/01/20 19:46:15  keil
+ * Changes from certification
+ *
  * Revision 1.7  1999/07/01 08:12:02  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -35,6 +38,8 @@
 #define T304	30000
 #define T305	30000
 #define T308	4000
+/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
+/* This makes some tests easier and quicker */
 #define T309	40000
 #define T310	30000
 #define T313	4000
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c
--- v2.3.45/linux/drivers/isdn/hisax/netjet.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/netjet.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: netjet.c,v 1.16 1999/10/14 20:25:29 keil Exp $
+/* $Id: netjet.c,v 1.17 1999/12/19 13:09:42 keil Exp $
 
  * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
  *
@@ -7,6 +7,10 @@
  * Thanks to Traverse Technologie Australia for documents and informations
  *
  * $Log: netjet.c,v $
+ * Revision 1.17  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.16  1999/10/14 20:25:29  keil
  * add a statistic for error monitoring
  *
@@ -81,7 +85,7 @@
 
 extern const char *CardType[];
 
-const char *NETjet_revision = "$Revision: 1.16 $";
+const char *NETjet_revision = "$Revision: 1.17 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -1054,11 +1058,11 @@
 	sti();
 	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
 	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
 	cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
 	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
 	restore_flags(flags);
 	cs->hw.njet.auxd = 0;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/rawhdlc.c linux/drivers/isdn/hisax/rawhdlc.c
--- v2.3.45/linux/drivers/isdn/hisax/rawhdlc.c	Sun May 23 10:03:41 1999
+++ linux/drivers/isdn/hisax/rawhdlc.c	Tue Feb 15 11:40:43 2000
@@ -1,8 +1,8 @@
-/* $Id: rawhdlc.c,v 1.3 1998/06/17 19:51:21 he Exp $
+/* $Id: rawhdlc.c,v 1.4 1999/12/23 15:09:32 keil Exp $
 
  * rawhdlc.c     support routines for cards that don't support HDLC
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *            Brent Baccala <baccala@FreeSoft.org>
  *
  *
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/saphir.c linux/drivers/isdn/hisax/saphir.c
--- v2.3.45/linux/drivers/isdn/hisax/saphir.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/saphir.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: saphir.c,v 1.4 1999/09/04 06:20:06 keil Exp $
+/* $Id: saphir.c,v 1.5 1999/12/19 13:09:42 keil Exp $
 
  * saphir.c low level stuff for HST Saphir 1
  *
@@ -8,6 +8,10 @@
  *
  *
  * $Log: saphir.c,v $
+ * Revision 1.5  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.4  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -29,7 +33,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-static char *saphir_rev = "$Revision: 1.4 $";
+static char *saphir_rev = "$Revision: 1.5 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -237,10 +241,10 @@
 	save_flags(flags);
 	sti();
 	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);	/* Timeout 30ms */
 	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);	/* Timeout 30ms */
 	restore_flags(flags);
 	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c
--- v2.3.45/linux/drivers/isdn/hisax/sedlbauer.c	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/hisax/sedlbauer.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: sedlbauer.c,v 1.18 1999/11/13 21:25:03 keil Exp $
+/* $Id: sedlbauer.c,v 1.20 2000/01/20 19:47:45 keil Exp $
 
  * sedlbauer.c  low level stuff for Sedlbauer cards
  *              includes support for the Sedlbauer speed star (speed star II),
@@ -17,6 +17,13 @@
  *            Edgar Toernig
  *
  * $Log: sedlbauer.c,v $
+ * Revision 1.20  2000/01/20 19:47:45  keil
+ * Add Fax Class 1 support
+ *
+ * Revision 1.19  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.18  1999/11/13 21:25:03  keil
  * Support for Speedfax+ PCI
  *
@@ -110,7 +117,7 @@
 
 extern const char *CardType[];
 
-const char *Sedlbauer_revision = "$Revision: 1.18 $";
+const char *Sedlbauer_revision = "$Revision: 1.20 $";
 
 const char *Sedlbauer_Types[] =
 	{"None", "speed card/win", "speed star", "speed fax+", 
@@ -490,10 +497,10 @@
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
 			save_flags(flags);
 			sti();
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
 			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
@@ -506,20 +513,20 @@
 			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
 			save_flags(flags);
 			sti();
-			current->state = TASK_INTERRUPTIBLE;
+			current->state = TASK_UNINTERRUPTIBLE;
 			schedule_timeout((20*HZ)/1000);
 			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
-			current->state = TASK_INTERRUPTIBLE;
+			current->state = TASK_UNINTERRUPTIBLE;
 			schedule_timeout((20*HZ)/1000);
 			restore_flags(flags);
 		} else {		
 			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
 			save_flags(flags);
 			sti();
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout((10*HZ)/1000);
 			restore_flags(flags);
 		}
@@ -659,15 +666,13 @@
 			(sub_id == PCI_SUB_ID_SPEEDFAXP)) {
 			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
 			cs->subtyp = SEDL_SPEEDFAX_PCI;
-			cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg +
-						SEDL_ISAR_PCI_ISAR_RESET_ON;
-			cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg +
-						SEDL_ISAR_PCI_ISAR_RESET_OFF;
 		} else {
 			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
 			cs->subtyp = SEDL_SPEED_PCI;
 		}
 		bytecnt = 256;
+		cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON;
+		cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF;
 		byteout(cs->hw.sedl.cfg_reg, 0xff);
 		byteout(cs->hw.sedl.cfg_reg, 0x00);
 		byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
@@ -675,7 +680,7 @@
 		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
 		save_flags(flags);
 		sti();
-		current->state = TASK_INTERRUPTIBLE;
+		current->state = TASK_UNINTERRUPTIBLE;
 		schedule_timeout((10*HZ)/1000);
 		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
 		restore_flags(flags);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/sportster.c linux/drivers/isdn/hisax/sportster.c
--- v2.3.45/linux/drivers/isdn/hisax/sportster.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/sportster.c	Tue Feb 15 11:40:43 2000
@@ -1,12 +1,19 @@
-/* $Id: sportster.c,v 1.10 1999/09/04 06:20:06 keil Exp $
+/* $Id: sportster.c,v 1.12 1999/12/23 15:09:32 keil Exp $
 
  * sportster.c     low level stuff for USR Sportster internal TA
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
  *
  * $Log: sportster.c,v $
+ * Revision 1.12  1999/12/23 15:09:32  keil
+ * change email
+ *
+ * Revision 1.11  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.10  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -46,7 +53,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *sportster_revision = "$Revision: 1.10 $";
+const char *sportster_revision = "$Revision: 1.12 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -180,11 +187,11 @@
 	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
 	save_flags(flags);
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
 	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	restore_flags(flags);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/teleint.c linux/drivers/isdn/hisax/teleint.c
--- v2.3.45/linux/drivers/isdn/hisax/teleint.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/teleint.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: teleint.c,v 1.11 1999/09/04 06:20:06 keil Exp $
+/* $Id: teleint.c,v 1.12 1999/12/19 13:09:42 keil Exp $
 
  * teleint.c     low level stuff for TeleInt isdn cards
  *
@@ -6,6 +6,10 @@
  *
  *
  * $Log: teleint.c,v $
+ * Revision 1.12  1999/12/19 13:09:42  keil
+ * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for
+ * signal proof delays
+ *
  * Revision 1.11  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -51,7 +55,7 @@
 
 extern const char *CardType[];
 
-const char *TeleInt_revision = "$Revision: 1.11 $";
+const char *TeleInt_revision = "$Revision: 1.12 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -260,11 +264,11 @@
 	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset On */
 	save_flags(flags);
 	sti();
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((30*HZ)/1000);
 	cs->hw.hfc.cirm &= ~HFC_RESET;
 	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset Off */
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout((10*HZ)/1000);
 	restore_flags(flags);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c
--- v2.3.45/linux/drivers/isdn/hisax/teles0.c	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/hisax/teles0.c	Tue Feb 15 11:40:43 2000
@@ -1,15 +1,18 @@
-/* $Id: teles0.c,v 2.10 1999/11/14 23:37:03 keil Exp $
+/* $Id: teles0.c,v 2.11 1999/12/23 15:09:32 keil Exp $
 
  * teles0.c     low level stuff for Teles Memory IO isdn cards
  *              based on the teles driver from Jan den Ouden
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *              Beat Doebeli
  *
  * $Log: teles0.c,v $
+ * Revision 2.11  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 2.10  1999/11/14 23:37:03  keil
  * new ISA memory mapped IO
  *
@@ -61,7 +64,7 @@
 
 extern const char *CardType[];
 
-const char *teles0_revision = "$Revision: 2.10 $";
+const char *teles0_revision = "$Revision: 2.11 $";
 
 #define TELES_IOMEM_SIZE	0x400
 #define byteout(addr,val) outb(val,addr)
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c
--- v2.3.45/linux/drivers/isdn/hisax/teles3.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/hisax/teles3.c	Tue Feb 15 11:40:43 2000
@@ -1,16 +1,22 @@
-/* $Id: teles3.c,v 2.13 1999/08/30 12:01:28 keil Exp $
+/* $Id: teles3.c,v 2.15 2000/02/03 16:40:10 keil Exp $
 
  * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
  *
  *              based on the teles driver from Jan den Ouden
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *              Beat Doebeli
  *
  * $Log: teles3.c,v $
+ * Revision 2.15  2000/02/03 16:40:10  keil
+ * Fix teles pcmcia
+ *
+ * Revision 2.14  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 2.13  1999/08/30 12:01:28  keil
  * HW version v1.3 support
  *
@@ -88,7 +94,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *teles3_revision = "$Revision: 2.13 $";
+const char *teles3_revision = "$Revision: 2.15 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -225,7 +231,7 @@
 release_io_teles3(struct IsdnCardState *cs)
 {
 	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
-		release_region(cs->hw.teles3.hscx[0], 97);
+		release_region(cs->hw.teles3.hscx[1], 96);
 	} else {
 		if (cs->hw.teles3.cfg_reg) {
 			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
@@ -367,15 +373,15 @@
 	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
 	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
 	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
-		if (check_region((cs->hw.teles3.hscx[0]), 97)) {
+		if (check_region((cs->hw.teles3.hscx[1]), 96 )) {
 			printk(KERN_WARNING
 			       "HiSax: %s ports %x-%x already in use\n",
 			       CardType[cs->typ],
-			       cs->hw.teles3.hscx[0],
-			       cs->hw.teles3.hscx[0] + 96);
+			       cs->hw.teles3.hscx[1],
+			       cs->hw.teles3.hscx[1] + 96);
 			return (0);
 		} else
-			request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA");
+			request_region(cs->hw.teles3.hscx[1], 96, "HiSax Teles PCMCIA");
 	} else {
 		if (cs->hw.teles3.cfg_reg) {
 			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hisax/telespci.c linux/drivers/isdn/hisax/telespci.c
--- v2.3.45/linux/drivers/isdn/hisax/telespci.c	Tue Nov 23 22:42:20 1999
+++ linux/drivers/isdn/hisax/telespci.c	Tue Feb 15 11:40:43 2000
@@ -1,12 +1,15 @@
-/* $Id: telespci.c,v 2.10 1999/11/15 14:20:05 keil Exp $
+/* $Id: telespci.c,v 2.11 1999/12/23 15:09:32 keil Exp $
 
  * telespci.c     low level stuff for Teles PCI isdn cards
  *
  * Author       Ton van Rosmalen 
- *              Karsten Keil (keil@temic-ech.spacenet.de)
+ *              Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: telespci.c,v $
+ * Revision 2.11  1999/12/23 15:09:32  keil
+ * change email
+ *
  * Revision 2.10  1999/11/15 14:20:05  keil
  * 64Bit compatibility
  *
@@ -47,7 +50,7 @@
 #include <linux/pci.h>
 
 extern const char *CardType[];
-const char *telespci_revision = "$Revision: 2.10 $";
+const char *telespci_revision = "$Revision: 2.11 $";
 
 #define ZORAN_PO_RQ_PEN	0x02000000
 #define ZORAN_PO_WR	0x00800000
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/Makefile linux/drivers/isdn/hysdn/Makefile
--- v2.3.45/linux/drivers/isdn/hysdn/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/Makefile	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,24 @@
+SUB_DIRS     :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS :=
+
+L_OBJS :=
+LX_OBJS :=
+M_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+ifeq ($(CONFIG_PROC_FS),y)
+  ifeq ($(CONFIG_HYSDN),y)
+    M_OBJS += hysdn.o
+    O_TARGET += hysdn.o
+    O_OBJS += hysdn_procconf.o hysdn_proclog.o boardergo.o hysdn_boot.o hysdn_sched.o hysdn_net.o
+    OX_OBJS += hysdn_init.o 
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/boardergo.c linux/drivers/isdn/hysdn/boardergo.c
--- v2.3.45/linux/drivers/isdn/hysdn/boardergo.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/boardergo.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,467 @@
+/* $Id: boardergo.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, specific routines for ergo type boards.
+ *
+ * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
+ * DPRAM interface and layout with only minor differences all related
+ * stuff is done here, not in separate modules.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: boardergo.c,v $
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include "hysdn_defs.h"
+#include "boardergo.h"
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/***************************************************/
+/* The cards interrupt handler. Called from system */
+/***************************************************/
+static void
+ergo_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	hysdn_card *card = dev_id;	/* parameter from irq */
+	tErgDpram *dpr;
+	ulong flags;
+	uchar volatile b;
+
+	if (!card)
+		return;		/* error -> spurious interrupt */
+	if (!card->irq_enabled)
+		return;		/* other device interrupting or irq switched off */
+
+	save_flags(flags);
+	cli();			/* no further irqs allowed */
+
+	if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
+		restore_flags(flags);	/* restore old state */
+		return;		/* no interrupt requested by E1 */
+	}
+	/* clear any pending ints on the board */
+	dpr = card->dpram;
+	b = dpr->ToPcInt;	/* clear for ergo */
+	b |= dpr->ToPcIntMetro;	/* same for metro */
+	b |= dpr->ToHyInt;	/* and for champ */
+
+	/* start kernel task immediately after leaving all interrupts */
+	if (!card->hw_lock) {
+		queue_task(&card->irq_queue, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+	restore_flags(flags);
+}				/* ergo_interrupt */
+
+/******************************************************************************/
+/* ergo_irq_bh is the function called by the immediate kernel task list after */
+/* being activated with queue_task and no interrupts active. This task is the */
+/* only one handling data transfer from or to the card after booting. The task */
+/* may be queued from everywhere (interrupts included).                       */
+/******************************************************************************/
+static void
+ergo_irq_bh(hysdn_card * card)
+{
+	tErgDpram *dpr;
+	int again;
+	ulong flags;
+
+	if (card->state != CARD_STATE_RUN)
+		return;		/* invalid call */
+
+	dpr = card->dpram;	/* point to DPRAM */
+
+	save_flags(flags);
+	cli();
+	if (card->hw_lock) {
+		restore_flags(flags);	/* hardware currently unavailable */
+		return;
+	}
+	card->hw_lock = 1;	/* we now lock the hardware */
+
+	do {
+		sti();		/* reenable other ints */
+		again = 0;	/* assume loop not to be repeated */
+
+		if (!dpr->ToHyFlag) {
+			/* we are able to send a buffer */
+
+			if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
+					   ERG_TO_HY_BUF_SIZE)) {
+				dpr->ToHyFlag = 1;	/* enable tx */
+				again = 1;	/* restart loop */
+			}
+		}		/* we are able to send a buffer */
+		if (dpr->ToPcFlag) {
+			/* a message has arrived for us, handle it */
+
+			if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
+				dpr->ToPcFlag = 0;	/* we worked the data */
+				again = 1;	/* restart loop */
+			}
+		}		/* a message has arrived for us */
+		cli();		/* no further ints */
+		if (again) {
+			dpr->ToHyInt = 1;
+			dpr->ToPcInt = 1;	/* interrupt to E1 for all cards */
+		} else
+			card->hw_lock = 0;	/* free hardware again */
+	} while (again);	/* until nothing more to do */
+
+	restore_flags(flags);
+}				/* ergo_irq_bh */
+
+
+/*********************************************************/
+/* stop the card (hardware reset) and disable interrupts */
+/*********************************************************/
+static void
+ergo_stopcard(hysdn_card * card)
+{
+	ulong flags;
+	uchar val;
+
+	hysdn_net_release(card);	/* first release the net device if existing */
+	save_flags(flags);
+	cli();
+	val = bytein(card->iobase + PCI9050_INTR_REG);	/* get actual value */
+	val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1);	/* mask irq */
+	byteout(card->iobase + PCI9050_INTR_REG, val);
+	card->irq_enabled = 0;
+	byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET);	/* reset E1 processor */
+	card->state = CARD_STATE_UNUSED;
+	card->err_log_state = ERRLOG_STATE_OFF;		/* currently no log active */
+
+	restore_flags(flags);
+}				/* ergo_stopcard */
+
+/**************************************************************************/
+/* enable or disable the cards error log. The event is queued if possible */
+/**************************************************************************/
+static void
+ergo_set_errlog_state(hysdn_card * card, int on)
+{
+	ulong flags;
+
+	if (card->state != CARD_STATE_RUN) {
+		card->err_log_state = ERRLOG_STATE_OFF;		/* must be off */
+		return;
+	}
+	save_flags(flags);
+	cli();
+
+	if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
+	    ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
+		restore_flags(flags);
+		return;		/* nothing to do */
+	}
+	if (on)
+		card->err_log_state = ERRLOG_STATE_START;	/* request start */
+	else
+		card->err_log_state = ERRLOG_STATE_STOP;	/* request stop */
+
+	restore_flags(flags);
+	queue_task(&card->irq_queue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}				/* ergo_set_errlog_state */
+
+/******************************************/
+/* test the cards RAM and return 0 if ok. */
+/******************************************/
+static const char TestText[36] = "This Message is filler, why read it";
+
+static int
+ergo_testram(hysdn_card * card)
+{
+	tErgDpram *dpr = card->dpram;
+
+	memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable));	/* clear all Traps */
+	dpr->ToHyInt = 1;	/* E1 INTR state forced */
+
+	memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+	       sizeof(TestText));
+	if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+		   sizeof(TestText)))
+		return (-1);
+
+	memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+	       sizeof(TestText));
+	if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+		   sizeof(TestText)))
+		return (-1);
+
+	return (0);
+}				/* ergo_testram */
+
+/*****************************************************************************/
+/* this function is intended to write stage 1 boot image to the cards buffer */
+/* this is done in two steps. First the 1024 hi-words are written (offs=0),  */
+/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the   */
+/* PCI-write-buffers flushed and the card is taken out of reset.             */
+/* The function then waits for a reaction of the E1 processor or a timeout.  */
+/* Negative return values are interpreted as errors.                         */
+/*****************************************************************************/
+static int
+ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs)
+{
+	uchar *dst;
+	tErgDpram *dpram;
+	int cnt = (BOOT_IMG_SIZE >> 2);		/* number of words to move and swap (byte order!) */
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
+
+	dst = card->dpram;	/* pointer to start of DPRAM */
+	dst += (offs + ERG_DPRAM_FILL_SIZE);	/* offset in the DPRAM */
+	while (cnt--) {
+		*dst++ = *(buf + 1);	/* high byte */
+		*dst++ = *buf;	/* low byte */
+		dst += 2;	/* point to next longword */
+		buf += 2;	/* buffer only filled with words */
+	}
+
+	/* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
+	/* flush the PCI-write-buffer and take the E1 out of reset */
+	if (offs) {
+		memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE);	/* fill the DPRAM still not cleared */
+		dpram = card->dpram;	/* get pointer to dpram structure */
+		dpram->ToHyNoDpramErrLog = 0xFF;	/* write a dpram register */
+		while (!dpram->ToHyNoDpramErrLog);	/* reread volatile register to flush PCI */
+
+		byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN);	/* start E1 processor */
+		/* the interrupts are still masked */
+
+		sti();
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
+
+		if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
+			if (card->debug_flags & LOG_POF_CARD)
+				hysdn_addlog(card, "ERGO: write bootldr no answer");
+			return (-ERR_BOOTIMG_FAIL);
+		}
+	}			/* start_boot_img */
+	return (0);		/* successfull */
+}				/* ergo_writebootimg */
+
+/********************************************************************************/
+/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
+/* using the boot spool mechanism. If everything works fine 0 is returned. In   */
+/* case of errors a negative error value is returned.                           */
+/********************************************************************************/
+static int
+ergo_writebootseq(struct HYSDN_CARD *card, uchar * buf, int len)
+{
+	tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
+	uchar *dst;
+	uchar buflen;
+	int nr_write;
+	uchar tmp_rdptr;
+	uchar wr_mirror;
+	int i;
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
+
+	dst = sp->Data;		/* point to data in spool structure */
+	buflen = sp->Len;	/* maximum len of spooled data */
+	wr_mirror = sp->WrPtr;	/* only once read */
+	sti();
+
+	/* try until all bytes written or error */
+	i = 0x1000;		/* timeout value */
+	while (len) {
+
+		/* first determine the number of bytes that may be buffered */
+		do {
+			tmp_rdptr = sp->RdPtr;	/* first read the pointer */
+			i--;	/* decrement timeout */
+		} while (i && (tmp_rdptr != sp->RdPtr));	/* wait for stable pointer */
+
+		if (!i) {
+			if (card->debug_flags & LOG_POF_CARD)
+				hysdn_addlog(card, "ERGO: write boot seq timeout");
+			return (-ERR_BOOTSEQ_FAIL);	/* value not stable -> timeout */
+		}
+		if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
+			nr_write += buflen;	/* now we got number of free bytes - 1 in buffer */
+
+		if (!nr_write)
+			continue;	/* no free bytes in buffer */
+
+		if (nr_write > len)
+			nr_write = len;		/* limit if last few bytes */
+		i = 0x1000;	/* reset timeout value */
+
+		/* now we know how much bytes we may put in the puffer */
+		len -= nr_write;	/* we savely could adjust len before output */
+		while (nr_write--) {
+			*(dst + wr_mirror) = *buf++;	/* output one byte */
+			if (++wr_mirror >= buflen)
+				wr_mirror = 0;
+			sp->WrPtr = wr_mirror;	/* announce the next byte to E1 */
+		}		/* while (nr_write) */
+
+	}			/* while (len) */
+
+	return (0);
+}				/* ergo_writebootseq */
+
+/***********************************************************************************/
+/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
+/* boot process. If the process has been successfull 0 is returned otherwise a     */
+/* negative error code is returned.                                                */
+/***********************************************************************************/
+static int
+ergo_waitpofready(struct HYSDN_CARD *card)
+{
+	tErgDpram *dpr = card->dpram;	/* pointer to DPRAM structure */
+	int timecnt = 10000 / 50;	/* timeout is 10 secs max. */
+	ulong flags;
+	int msg_size;
+	int i;
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: waiting for pof ready");
+
+	while (timecnt--) {
+		/* wait until timeout  */
+
+		if (dpr->ToPcFlag) {
+			/* data has arrived */
+
+			if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
+			    (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
+			    (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
+			    ((*(ulong *) dpr->ToPcBuf) != RDY_MAGIC))
+				break;	/* an error occured */
+
+			/* Check for additional data delivered during SysReady */
+			msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
+			if (msg_size > 0)
+				if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
+					break;
+
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "ERGO: pof boot success");
+
+			save_flags(flags);
+			cli();
+
+			card->state = CARD_STATE_RUN;	/* now card is running */
+			/* enable the cards interrupt */
+			byteout(card->iobase + PCI9050_INTR_REG,
+				bytein(card->iobase + PCI9050_INTR_REG) |
+			(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
+			card->irq_enabled = 1;	/* we are ready to receive interrupts */
+
+			dpr->ToPcFlag = 0;	/* reset data indicator */
+			dpr->ToHyInt = 1;
+			dpr->ToPcInt = 1;	/* interrupt to E1 for all cards */
+
+			restore_flags(flags);
+			if ((i = hysdn_net_create(card))) {
+				ergo_stopcard(card);
+				card->state = CARD_STATE_BOOTERR;
+				return (i);
+			}
+			return (0);	/* success */
+		}		/* data has arrived */
+		sti();
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout((50 * HZ) / 1000);	/* Timeout 50ms */
+	}			/* wait until timeout */
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: pof boot ready timeout");
+	return (-ERR_POF_TIMEOUT);
+}				/* ergo_waitpofready */
+
+
+
+/************************************************************************************/
+/* release the cards hardware. Before releasing do a interrupt disable and hardware */
+/* reset. Also unmap dpram.                                                         */
+/* Use only during module release.                                                  */
+/************************************************************************************/
+static void
+ergo_releasehardware(hysdn_card * card)
+{
+	ergo_stopcard(card);	/* first stop the card if not already done */
+	free_irq(card->irq, card);	/* release interrupt */
+	release_region(card->iobase + PCI9050_INTR_REG, 1);	/* release all io ports */
+	release_region(card->iobase + PCI9050_USER_IO, 1);
+	vfree(card->dpram);
+	card->dpram = NULL;	/* release shared mem */
+}				/* ergo_releasehardware */
+
+
+/*********************************************************************************/
+/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
+/* value is returned.                                                            */
+/* Use only during module init.                                                  */
+/*********************************************************************************/
+int
+ergo_inithardware(hysdn_card * card)
+{
+	if (check_region(card->iobase + PCI9050_INTR_REG, 1) ||
+	    check_region(card->iobase + PCI9050_USER_IO, 1))
+		return (-1);	/* ports already in use */
+
+	card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
+	if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE)))
+		return (-1);
+
+	request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN");
+	request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN");
+	ergo_stopcard(card);	/* disable interrupts */
+	if (request_irq(card->irq, ergo_interrupt, SA_SHIRQ, "HYSDN", card)) {
+		ergo_releasehardware(card);	/* return the aquired hardware */
+		return (-1);
+	}
+	/* success, now setup the function pointers */
+	card->stopcard = ergo_stopcard;
+	card->releasehardware = ergo_releasehardware;
+	card->testram = ergo_testram;
+	card->writebootimg = ergo_writebootimg;
+	card->writebootseq = ergo_writebootseq;
+	card->waitpofready = ergo_waitpofready;
+	card->set_errlog_state = ergo_set_errlog_state;
+	card->irq_queue.next = 0;
+	card->irq_queue.sync = 0;
+	card->irq_queue.data = card;	/* init task queue for interrupt */
+	card->irq_queue.routine = (void *) (void *) ergo_irq_bh;
+
+	return (0);
+}				/* ergo_inithardware */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/boardergo.h linux/drivers/isdn/hysdn/boardergo.h
--- v2.3.45/linux/drivers/isdn/hysdn/boardergo.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/boardergo.h	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,117 @@
+/* $Id: boardergo.h,v 1.1 2000/02/10 19:44:30 werner Exp $
+
+ * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: boardergo.h,v $
+ * Revision 1.1  2000/02/10 19:44:30  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+
+/************************************************/
+/* defines for the dual port memory of the card */
+/************************************************/
+#define ERG_DPRAM_PAGE_SIZE 0x2000	/* DPRAM occupies a 8K page */
+#define BOOT_IMG_SIZE 4096
+#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
+
+#define ERG_TO_HY_BUF_SIZE  0x0E00	/* 3072 bytes buffer size to card */
+#define ERG_TO_PC_BUF_SIZE  0x0E00	/* 3072 bytes to PC, too */
+
+/* following DPRAM layout copied from OS2-driver boarderg.h */
+typedef struct ErgDpram_tag {
+/*0000 */ uchar ToHyBuf[ERG_TO_HY_BUF_SIZE];
+/*0E00 */ uchar ToPcBuf[ERG_TO_PC_BUF_SIZE];
+
+	/*1C00 */ uchar bSoftUart[SIZE_RSV_SOFT_UART];
+	/* size 0x1B0 */
+
+	/*1DB0 *//* tErrLogEntry */ uchar volatile ErrLogMsg[64];
+	/* size 64 bytes */
+	/*1DB0  ulong ulErrType;               */
+	/*1DB4  ulong ulErrSubtype;            */
+	/*1DB8  ulong ucTextSize;              */
+	/*1DB9  ulong ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
+	/*1DF0 */
+
+/*1DF0 */ word volatile ToHyChannel;
+/*1DF2 */ word volatile ToHySize;
+	/*1DF4 */ uchar volatile ToHyFlag;
+	/* !=0: msg for Hy waiting */
+	/*1DF5 */ uchar volatile ToPcFlag;
+	/* !=0: msg for PC waiting */
+/*1DF6 */ word volatile ToPcChannel;
+/*1DF8 */ word volatile ToPcSize;
+	/*1DFA */ uchar bRes1DBA[0x1E00 - 0x1DFA];
+	/* 6 bytes */
+
+/*1E00 */ uchar bRestOfEntryTbl[0x1F00 - 0x1E00];
+/*1F00 */ ulong TrapTable[62];
+	/*1FF8 */ uchar bRes1FF8[0x1FFB - 0x1FF8];
+	/* low part of reset vetor */
+/*1FFB */ uchar ToPcIntMetro;
+	/* notes:
+	 * - metro has 32-bit boot ram - accessing
+	 *   ToPcInt and ToHyInt would be the same;
+	 *   so we moved ToPcInt to 1FFB.
+	 *   Because on the PC side both vars are
+	 *   readonly (reseting on int from E1 to PC),
+	 *   we can read both vars on both cards
+	 *   without destroying anything.
+	 * - 1FFB is the high byte of the reset vector,
+	 *   so E1 side should NOT change this byte
+	 *   when writing!
+	 */
+/*1FFC */ uchar volatile ToHyNoDpramErrLog;
+	/* note: ToHyNoDpramErrLog is used to inform
+	 *       boot loader, not to use DPRAM based
+	 *       ErrLog; when DOS driver is rewritten
+	 *       this becomes obsolete
+	 */
+/*1FFD */ uchar bRes1FFD;
+	/*1FFE */ uchar ToPcInt;
+	/* E1_intclear; on CHAMP2: E1_intset   */
+	/*1FFF */ uchar ToHyInt;
+	/* E1_intset;   on CHAMP2: E1_intclear */
+} tErgDpram;
+
+/**********************************************/
+/* PCI9050 controller local register offsets: */
+/* copied from boarderg.c                     */
+/**********************************************/
+#define PCI9050_INTR_REG    0x4C	/* Interrupt register */
+#define PCI9050_USER_IO     0x51	/* User I/O  register */
+
+				    /* bitmask for PCI9050_INTR_REG: */
+#define PCI9050_INTR_REG_EN1    0x01	/* 1= enable (def.), 0= disable */
+#define PCI9050_INTR_REG_POL1   0x02	/* 1= active high (def.), 0= active low */
+#define PCI9050_INTR_REG_STAT1  0x04	/* 1= intr. active, 0= intr. not active (def.) */
+#define PCI9050_INTR_REG_ENPCI  0x40	/* 1= PCI interrupts enable (def.) */
+
+				    /* bitmask for PCI9050_USER_IO: */
+#define PCI9050_USER_IO_EN3     0x02	/* 1= disable      , 0= enable (def.) */
+#define PCI9050_USER_IO_DIR3    0x04	/* 1= output (def.), 0= input         */
+#define PCI9050_USER_IO_DAT3    0x08	/* 1= high (def.)  , 0= low           */
+
+#define PCI9050_E1_RESET    (                     PCI9050_USER_IO_DIR3)		/* 0x04 */
+#define PCI9050_E1_RUN      (PCI9050_USER_IO_DAT3|PCI9050_USER_IO_DIR3)		/* 0x0C */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_boot.c linux/drivers/isdn/hysdn/hysdn_boot.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_boot.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_boot.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,420 @@
+/* $Id: hysdn_boot.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, specific routines for booting and pof handling.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_boot.c,v $
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/vmalloc.h>
+#include <linux/malloc.h>
+#include <asm/uaccess.h>
+
+#include "hysdn_defs.h"
+#include "hysdn_pof.h"
+
+/********************************/
+/* defines for pof read handler */
+/********************************/
+#define POF_READ_FILE_HEAD  0
+#define POF_READ_TAG_HEAD   1
+#define POF_READ_TAG_DATA   2
+
+/************************************************************/
+/* definition of boot specific data area. This data is only */
+/* needed during boot and so allocated dynamically.         */
+/************************************************************/
+struct boot_data {
+	word Cryptor;		/* for use with Decrypt function */
+	word Nrecs;		/* records remaining in file */
+	uchar pof_state;	/* actual state of read handler */
+	uchar is_crypted;	/* card data is crypted */
+	int BufSize;		/* actual number of bytes bufferd */
+	int last_error;		/* last occured error */
+	word pof_recid;		/* actual pof recid */
+	ulong pof_reclen;	/* total length of pof record data */
+	ulong pof_recoffset;	/* actual offset inside pof record */
+	union {
+		uchar BootBuf[BOOT_BUF_SIZE];	/* buffer as byte count */
+		tPofRecHdr PofRecHdr;	/* header for actual record/chunk */
+		tPofFileHdr PofFileHdr;		/* header from POF file */
+		tPofTimeStamp PofTime;	/* time information */
+	} buf;
+};
+
+/*****************************************************/
+/*  start decryption of sucessive POF file chuncks.  */
+/*                                                   */
+/*  to be called at start of POF file reading,       */
+/*  before starting any decryption on any POF record. */
+/*****************************************************/
+void
+StartDecryption(struct boot_data *boot)
+{
+	boot->Cryptor = CRYPT_STARTTERM;
+}				/* StartDecryption */
+
+
+/***************************************************************/
+/* decrypt complete BootBuf                                    */
+/* NOTE: decryption must be applied to all or none boot tags - */
+/*       to HI and LO boot loader and (all) seq tags, because  */
+/*       global Cryptor is started for whole POF.              */
+/***************************************************************/
+void
+DecryptBuf(struct boot_data *boot, int cnt)
+{
+	uchar *bufp = boot->buf.BootBuf;
+
+	while (cnt--) {
+		boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
+		*bufp++ ^= (uchar) boot->Cryptor;
+	}
+}				/* DecryptBuf */
+
+/********************************************************************************/
+/* pof_handle_data executes the required actions dependant on the active record */
+/* id. If successfull 0 is returned, a negative value shows an error.           */
+/********************************************************************************/
+static int
+pof_handle_data(hysdn_card * card, int datlen)
+{
+	struct boot_data *boot = card->boot;	/* pointer to boot specific data */
+	long l;
+	uchar *imgp;
+	int img_len;
+
+	/* handle the different record types */
+	switch (boot->pof_recid) {
+
+		case TAG_TIMESTMP:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
+			break;
+
+		case TAG_CBOOTDTA:
+			DecryptBuf(boot, datlen);	/* we need to encrypt the buffer */
+		case TAG_BOOTDTA:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+					     (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
+					     datlen, boot->pof_recoffset);
+
+			if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
+				boot->last_error = EPOF_BAD_IMG_SIZE;	/* invalid length */
+				return (boot->last_error);
+			}
+			imgp = boot->buf.BootBuf;	/* start of buffer */
+			img_len = datlen;	/* maximum length to transfer */
+
+			l = POF_BOOT_LOADER_OFF_IN_PAGE -
+			    (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
+			if (l > 0) {
+				/* buffer needs to be truncated */
+				imgp += l;	/* advance pointer */
+				img_len -= l;	/* adjust len */
+			}
+			/* at this point no special handling for data wrapping over buffer */
+			/* is necessary, because the boot image always will be adjusted to */
+			/* match a page boundary inside the buffer.                        */
+			/* The buffer for the boot image on the card is filled in 2 cycles */
+			/* first the 1024 hi-words are put in the buffer, then the low 1024 */
+			/* word are handled in the same way with different offset.         */
+
+			if (img_len > 0) {
+				/* data available for copy */
+				if ((boot->last_error =
+				     card->writebootimg(card, imgp,
+							(boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
+					return (boot->last_error);
+			}
+			break;	/* end of case boot image hi/lo */
+
+		case TAG_CABSDATA:
+			DecryptBuf(boot, datlen);	/* we need to encrypt the buffer */
+		case TAG_ABSDATA:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+					     (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
+					     datlen, boot->pof_recoffset);
+
+			if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
+				return (boot->last_error);	/* error writing data */
+
+			if (boot->pof_recoffset + datlen >= boot->pof_reclen)
+				return (card->waitpofready(card));	/* data completely spooled, wait for ready */
+
+			break;	/* end of case boot seq data */
+
+		default:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
+					     datlen, boot->pof_recoffset);
+
+			break;	/* simply skip record */
+	}			/* switch boot->pof_recid */
+
+	return (0);
+}				/* pof_handle_data */
+
+
+/******************************************************************************/
+/* pof_write_buffer is called when the buffer has been filled with the needed */
+/* number of data bytes. The number delivered is additionally supplied for    */
+/* verification. The functions handles the data and returns the needed number */
+/* of bytes for the next action. If the returned value is 0 or less an error  */
+/* occured and booting must be aborted.                                       */
+/******************************************************************************/
+int
+pof_write_buffer(hysdn_card * card, int datlen)
+{
+	struct boot_data *boot = card->boot;	/* pointer to boot specific data */
+
+	if (!boot)
+		return (-EFAULT);	/* invalid call */
+	if (boot->last_error < 0)
+		return (boot->last_error);	/* repeated error */
+
+	if (card->debug_flags & LOG_POF_WRITE)
+		hysdn_addlog(card, "POF write: got %d bytes ", datlen);
+
+	switch (boot->pof_state) {
+		case POF_READ_FILE_HEAD:
+			if (card->debug_flags & LOG_POF_WRITE)
+				hysdn_addlog(card, "POF write: checking file header");
+
+			if (datlen != sizeof(tPofFileHdr)) {
+				boot->last_error = -EPOF_INTERNAL;
+				break;
+			}
+			if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
+				boot->last_error = -EPOF_BAD_MAGIC;
+				break;
+			}
+			/* Setup the new state and vars */
+			boot->Nrecs = (word) (boot->buf.PofFileHdr.N_PofRecs);	/* limited to 65535 */
+			boot->pof_state = POF_READ_TAG_HEAD;	/* now start with single tags */
+			boot->last_error = sizeof(tPofRecHdr);	/* new length */
+			break;
+
+		case POF_READ_TAG_HEAD:
+			if (card->debug_flags & LOG_POF_WRITE)
+				hysdn_addlog(card, "POF write: checking tag header");
+
+			if (datlen != sizeof(tPofRecHdr)) {
+				boot->last_error = -EPOF_INTERNAL;
+				break;
+			}
+			boot->pof_recid = boot->buf.PofRecHdr.PofRecId;		/* actual pof recid */
+			boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen;	/* total length */
+			boot->pof_recoffset = 0;	/* no starting offset */
+
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
+				      boot->pof_recid, boot->pof_reclen);
+
+			boot->pof_state = POF_READ_TAG_DATA;	/* now start with tag data */
+			if (boot->pof_reclen < BOOT_BUF_SIZE)
+				boot->last_error = boot->pof_reclen;	/* limit size */
+			else
+				boot->last_error = BOOT_BUF_SIZE;	/* maximum */
+
+			if (!boot->last_error) {	/* no data inside record */
+				boot->pof_state = POF_READ_TAG_HEAD;	/* now start with single tags */
+				boot->last_error = sizeof(tPofRecHdr);	/* new length */
+			}
+			break;
+
+		case POF_READ_TAG_DATA:
+			if (card->debug_flags & LOG_POF_WRITE)
+				hysdn_addlog(card, "POF write: getting tag data");
+
+			if (datlen != boot->last_error) {
+				boot->last_error = -EPOF_INTERNAL;
+				break;
+			}
+			if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
+				return (boot->last_error);	/* an error occured */
+
+			boot->pof_recoffset += datlen;
+			if (boot->pof_recoffset >= boot->pof_reclen) {
+				boot->pof_state = POF_READ_TAG_HEAD;	/* now start with single tags */
+				boot->last_error = sizeof(tPofRecHdr);	/* new length */
+			} else {
+				if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
+					boot->last_error = boot->pof_reclen - boot->pof_recoffset;	/* limit size */
+				else
+					boot->last_error = BOOT_BUF_SIZE;	/* maximum */
+			}
+			break;
+
+		default:
+			boot->last_error = -EPOF_INTERNAL;	/* unknown state */
+			break;
+	}			/* switch (boot->pof_state) */
+
+	return (boot->last_error);
+}				/* pof_write_buffer */
+
+
+/*******************************************************************************/
+/* pof_write_open is called when an open for boot on the cardlog device occurs. */
+/* The function returns the needed number of bytes for the next operation. If  */
+/* the returned number is less or equal 0 an error specified by this code      */
+/* occurred. Additionally the pointer to the buffer data area is set on success */
+/*******************************************************************************/
+int
+pof_write_open(hysdn_card * card, uchar ** bufp)
+{
+	struct boot_data *boot;	/* pointer to boot specific data */
+
+	if (card->boot) {
+		if (card->debug_flags & LOG_POF_OPEN)
+			hysdn_addlog(card, "POF open: already opened for boot");
+		return (-ERR_ALREADY_BOOT);	/* boot already active */
+	}
+	/* error no mem available */
+	if (!(boot = kmalloc(sizeof(struct boot_data), GFP_KERNEL))) {
+		if (card->debug_flags & LOG_MEM_ERR)
+			hysdn_addlog(card, "POF open: unable to allocate mem");
+		return (-EFAULT);
+	}
+	card->boot = boot;
+	card->state = CARD_STATE_BOOTING;
+	memset(boot, 0, sizeof(struct boot_data));
+
+	card->stopcard(card);	/* first stop the card */
+	if (card->testram(card)) {
+		if (card->debug_flags & LOG_POF_OPEN)
+			hysdn_addlog(card, "POF open: DPRAM test failure");
+		boot->last_error = -ERR_BOARD_DPRAM;
+		card->state = CARD_STATE_BOOTERR;	/* show boot error */
+		return (boot->last_error);
+	}
+	boot->BufSize = 0;	/* Buffer is empty */
+	boot->pof_state = POF_READ_FILE_HEAD;	/* read file header */
+	StartDecryption(boot);	/* if POF File should be encrypted */
+
+	if (card->debug_flags & LOG_POF_OPEN)
+		hysdn_addlog(card, "POF open: success");
+
+	*bufp = boot->buf.BootBuf;	/* point to buffer */
+	return (sizeof(tPofFileHdr));
+}				/* pof_write_open */
+
+/********************************************************************************/
+/* pof_write_close is called when an close of boot on the cardlog device occurs. */
+/* The return value must be 0 if everything has happened as desired.            */
+/********************************************************************************/
+int
+pof_write_close(hysdn_card * card)
+{
+	struct boot_data *boot = card->boot;	/* pointer to boot specific data */
+
+	if (!boot)
+		return (-EFAULT);	/* invalid call */
+
+	card->boot = NULL;	/* no boot active */
+	kfree(boot);
+
+	if (card->state == CARD_STATE_RUN)
+		card->set_errlog_state(card, 1);	/* activate error log */
+
+	if (card->debug_flags & LOG_POF_OPEN)
+		hysdn_addlog(card, "POF close: success");
+
+	return (0);
+}				/* pof_write_close */
+
+/*********************************************************************************/
+/* EvalSysrTokData checks additional records delivered with the Sysready Message */
+/* when POF has been booted. A return value of 0 is used if no error occured.    */
+/*********************************************************************************/
+int
+EvalSysrTokData(hysdn_card * card, uchar * cp, int len)
+{
+	u_char *p;
+	u_char crc;
+
+	if (card->debug_flags & LOG_POF_RECORD)
+		hysdn_addlog(card, "SysReady Token data length %d", len);
+
+	if (len < 2) {
+		hysdn_addlog(card, "SysReady Token Data to short");
+		return (1);
+	}
+	for (p = cp, crc = 0; p < (cp + len - 2); p++)
+		if ((crc & 0x80))
+			crc = (((u_char) (crc << 1)) + 1) + *p;
+		else
+			crc = ((u_char) (crc << 1)) + *p;
+	crc = ~crc;
+	if (crc != *(cp + len - 1)) {
+		hysdn_addlog(card, "SysReady Token Data invalid CRC");
+		return (1);
+	}
+	len--;			/* dont check CRC byte */
+	while (len > 0) {
+
+		if (*cp == SYSR_TOK_END)
+			return (0);	/* End of Token stream */
+
+		if (len < (*(cp + 1) + 2)) {
+			hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
+			return (1);
+		}
+		switch (*cp) {
+			case SYSR_TOK_B_CHAN:	/* 1 */
+				if (*(cp + 1) != 1)
+					return (1);	/* length invalid */
+				card->bchans = *(cp + 2);
+				break;
+
+			case SYSR_TOK_FAX_CHAN:	/* 2 */
+				if (*(cp + 1) != 1)
+					return (1);	/* length invalid */
+				card->faxchans = *(cp + 2);
+				break;
+
+			case SYSR_TOK_MAC_ADDR:	/* 3 */
+				if (*(cp + 1) != 6)
+					return (1);	/* length invalid */
+				memcpy(card->mac_addr, cp + 2, 6);
+				break;
+
+			default:
+				hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
+				break;
+		}
+		len -= (*(cp + 1) + 2);		/* adjust len */
+		cp += (*(cp + 1) + 2);	/* and pointer */
+	}
+
+	hysdn_addlog(card, "no end token found");
+	return (1);
+}				/* EvalSysrTokData */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_defs.h linux/drivers/isdn/hysdn/hysdn_defs.h
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_defs.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_defs.h	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,229 @@
+/* $Id: hysdn_defs.h,v 1.1 2000/02/10 19:44:30 werner Exp $
+
+ * Linux driver for HYSDN cards, global definitions and exported vars and functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_defs.h,v $
+ * Revision 1.1  2000/02/10 19:44:30  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#include <linux/hysdn_if.h>
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
+#include <linux/skbuff.h>
+
+/****************************/
+/* storage type definitions */
+/****************************/
+#define uchar unsigned char
+#define uint unsigned int
+#define ulong unsigned long
+#define word unsigned short
+
+#include "ince1pc.h"
+
+/************************************************/
+/* constants and bits for debugging/log outputs */
+/************************************************/
+#define LOG_MAX_LINELEN 120
+#define DEB_OUT_SYSLOG  0x80000000	/* output to syslog instead of proc fs */
+#define LOG_MEM_ERR     0x00000001	/* log memory errors like kmalloc failure */
+#define LOG_POF_OPEN    0x00000010	/* log pof open and close activities */
+#define LOG_POF_RECORD  0x00000020	/* log pof record parser */
+#define LOG_POF_WRITE   0x00000040	/* log detailed pof write operation */
+#define LOG_POF_CARD    0x00000080	/* log pof related card functions */
+#define LOG_CNF_LINE    0x00000100	/* all conf lines are put to procfs */
+#define LOG_CNF_DATA    0x00000200	/* non comment conf lines are shown with channel */
+#define LOG_CNF_MISC    0x00000400	/* additional conf line debug outputs */
+#define LOG_SCHED_ASYN  0x00001000	/* debug schedulers async tx routines */
+#define LOG_PROC_OPEN   0x00100000	/* open and close from procfs are logged */
+#define LOG_PROC_ALL    0x00200000	/* all actions from procfs are logged */
+#define LOG_NET_INIT    0x00010000	/* network init and deinit logging */
+
+#define DEF_DEB_FLAGS   0x7fff000f	/* everything is logged to procfs */
+
+/**********************************/
+/* proc filesystem name constants */
+/**********************************/
+#define PROC_SUBDIR_NAME "hysdn"
+#define PROC_CONF_BASENAME "cardconf"
+#define PROC_LOG_BASENAME "cardlog"
+
+/************************/
+/* PCI constant defines */
+/************************/
+#define PCI_VENDOR_ID_HYPERCOPE 0x1365
+#define PCI_DEVICE_ID_PLX 0x9050	/* all DPRAM cards use the same id */
+
+/*****************************/
+/* sub ids determining cards */
+/*****************************/
+#define PCI_SUB_ID_OLD_ERGO 0x0104
+#define PCI_SUB_ID_ERGO     0x0106
+#define PCI_SUB_ID_METRO    0x0107
+#define PCI_SUB_ID_CHAMP2   0x0108
+#define PCI_SUB_ID_PLEXUS   0x0109
+
+/***********************************/
+/* PCI 32 bit parms for IO and MEM */
+/***********************************/
+#define PCI_REG_PLX_MEM_BASE    0
+#define PCI_REG_PLX_IO_BASE     1
+#define PCI_REG_MEMORY_BASE     3
+
+/**************/
+/* card types */
+/**************/
+#define BD_NONE         0U
+#define BD_PERFORMANCE  1U
+#define BD_VALUE        2U
+#define BD_PCCARD       3U
+#define BD_ERGO         4U
+#define BD_METRO        5U
+#define BD_CHAMP2       6U
+#define BD_PLEXUS       7U
+
+/******************************************************/
+/* defined states for cards shown by reading cardconf */
+/******************************************************/
+#define CARD_STATE_UNUSED   0	/* never been used or booted */
+#define CARD_STATE_BOOTING  1	/* booting is in progress */
+#define CARD_STATE_BOOTERR  2	/* a previous boot was aborted */
+#define CARD_STATE_RUN      3	/* card is active */
+
+/*******************************/
+/* defines for error_log_state */
+/*******************************/
+#define ERRLOG_STATE_OFF   0	/* error log is switched off, nothing to do */
+#define ERRLOG_STATE_ON    1	/* error log is switched on, wait for data */
+#define ERRLOG_STATE_START 2	/* start error logging */
+#define ERRLOG_STATE_STOP  3	/* stop error logging */
+
+/*******************************/
+/* data structure for one card */
+/*******************************/
+typedef struct HYSDN_CARD {
+
+	/* general variables for the cards */
+	int myid;		/* own driver card id */
+	uchar bus;		/* pci bus the card is connected to */
+	uchar devfn;		/* slot+function bit encoded */
+	word subsysid;		/* PCI subsystem id */
+	uchar brdtype;		/* type of card */
+	uint bchans;		/* number of available B-channels */
+	uint faxchans;		/* number of available fax-channels */
+	uchar mac_addr[6];	/* MAC Address read from card */
+	uint irq;		/* interrupt number */
+	uint iobase;		/* IO-port base address */
+	ulong plxbase;		/* PLX memory base */
+	ulong membase;		/* DPRAM memory base */
+	ulong memend;		/* DPRAM memory end */
+	void *dpram;		/* mapped dpram */
+	int state;		/* actual state of card -> CARD_STATE_** */
+	struct HYSDN_CARD *next;	/* pointer to next card */
+
+	/* data areas for the /proc file system */
+	void *proclog;		/* pointer to proclog filesystem specific data */
+	void *procconf;		/* pointer to procconf filesystem specific data */
+
+	/* debugging and logging */
+	uchar err_log_state;	/* actual error log state of the card */
+	ulong debug_flags;	/* tells what should be debugged and where */
+	void (*set_errlog_state) (struct HYSDN_CARD *, int);
+
+	/* interrupt handler + interrupt synchronisation */
+	struct tq_struct irq_queue;	/* interrupt task queue */
+	uchar volatile irq_enabled;	/* interrupt enabled if != 0 */
+	uchar volatile hw_lock;	/* hardware is currently locked -> no access */
+
+	/* boot process */
+	void *boot;		/* pointer to boot private data */
+	int (*writebootimg) (struct HYSDN_CARD *, uchar *, ulong);
+	int (*writebootseq) (struct HYSDN_CARD *, uchar *, int);
+	int (*waitpofready) (struct HYSDN_CARD *);
+	int (*testram) (struct HYSDN_CARD *);
+
+	/* scheduler for data transfer (only async parts) */
+	uchar async_data[256];	/* async data to be sent (normally for config) */
+	word volatile async_len;	/* length of data to sent */
+	word volatile async_channel;	/* channel number for async transfer */
+	int volatile async_busy;	/* flag != 0 sending in progress */
+	int volatile net_tx_busy;	/* a network packet tx is in progress */
+
+	/* network interface */
+	void *netif;		/* pointer to network structure */
+
+	/* init and deinit stopcard for booting, too */
+	void (*stopcard) (struct HYSDN_CARD *);
+	void (*releasehardware) (struct HYSDN_CARD *);
+} hysdn_card;
+
+
+/*****************/
+/* exported vars */
+/*****************/
+extern int cardmax;		/* number of found cards */
+extern hysdn_card *card_root;	/* pointer to first card */
+
+
+
+/*************************/
+/* im/exported functions */
+/*************************/
+extern int printk(const char *fmt,...);
+extern char *hysdn_getrev(const char *);
+
+/* hysdn_procconf.c */
+extern int hysdn_procconf_init(void);	/* init proc config filesys */
+extern void hysdn_procconf_release(void);	/* deinit proc config filesys */
+
+/* hysdn_proclog.c */
+extern int hysdn_proclog_init(hysdn_card *);	/* init proc log entry */
+extern void hysdn_proclog_release(hysdn_card *);	/* deinit proc log entry */
+extern void put_log_buffer(hysdn_card *, char *);	/* output log data */
+extern void hysdn_addlog(hysdn_card *, char *,...);	/* output data to log */
+extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int);	/* output card log */
+
+/* boardergo.c */
+extern int ergo_inithardware(hysdn_card * card);	/* get hardware -> module init */
+
+/* hysdn_boot.c */
+extern int pof_write_close(hysdn_card *);	/* close proc file after writing pof */
+extern int pof_write_open(hysdn_card *, uchar **);	/* open proc file for writing pof */
+extern int pof_write_buffer(hysdn_card *, int);		/* write boot data to card */
+extern int EvalSysrTokData(hysdn_card *, uchar *, int);		/* Check Sysready Token Data */
+
+/* hysdn_sched.c */
+extern int hysdn_sched_tx(hysdn_card *, uchar *, word volatile *, word volatile *,
+			  word);
+extern int hysdn_sched_rx(hysdn_card *, uchar *, word, word);
+extern int hysdn_tx_cfgline(hysdn_card *, uchar *, word);	/* send one cfg line */
+
+/* hysdn_net.c */
+extern char *hysdn_net_revision;
+extern int hysdn_net_create(hysdn_card *);	/* create a new net device */
+extern int hysdn_net_release(hysdn_card *);	/* delete the device */
+extern char *hysdn_net_getname(hysdn_card *);	/* get name of net interface */
+extern void hysdn_tx_netack(hysdn_card *);	/* acknowledge a packet tx */
+extern struct sk_buff *hysdn_tx_netget(hysdn_card *);	/* get next network packet */
+extern void hysdn_rx_netpkt(hysdn_card *, uchar *, word);	/* rxed packet from network */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_init.c linux/drivers/isdn/hysdn/hysdn_init.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_init.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_init.c	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,243 @@
+/* $Id: hysdn_init.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, init functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_init.c,v $
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_init_revision = "$Revision: 1.1 $";
+int cardmax;			/* number of found cards */
+hysdn_card *card_root = NULL;	/* pointer to first card */
+
+/**********************************************/
+/* table assigning PCI-sub ids to board types */
+/* the last entry contains all 0              */
+/**********************************************/
+static struct {
+	word subid;		/* PCI sub id */
+	uchar cardtyp;		/* card type assigned */
+} pci_subid_map[] = {
+
+	{
+		PCI_SUB_ID_METRO, BD_METRO
+	},
+	{
+		PCI_SUB_ID_CHAMP2, BD_CHAMP2
+	},
+	{
+		PCI_SUB_ID_ERGO, BD_ERGO
+	},
+	{
+		PCI_SUB_ID_OLD_ERGO, BD_ERGO
+	},
+	{
+		0, 0
+	}			/* terminating entry */
+};
+
+/*********************************************************************/
+/* search_cards searches for available cards in the pci config data. */
+/* If a card is found, the card structure is allocated and the cards */
+/* ressources are reserved. cardmax is incremented.                  */
+/*********************************************************************/
+static void
+search_cards(void)
+{
+	struct pci_dev *akt_pcidev = NULL;
+	hysdn_card *card, *card_last;
+	uchar irq;
+	int i;
+
+	card_root = NULL;
+	card_last = NULL;
+	while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_PLX,
+					     akt_pcidev)) != NULL) {
+
+		if (!(card = kmalloc(sizeof(hysdn_card), GFP_KERNEL))) {
+			printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
+			return;
+		}
+		memset(card, 0, sizeof(hysdn_card));
+		card->myid = cardmax;	/* set own id */
+		card->bus = akt_pcidev->bus->number;
+		card->devfn = akt_pcidev->devfn;	/* slot + function */
+		pcibios_read_config_word(card->bus, card->devfn, PCI_SUBSYSTEM_ID, &card->subsysid);
+		pcibios_read_config_byte(card->bus, card->devfn, PCI_INTERRUPT_LINE, &irq);
+		card->irq = irq;
+		card->iobase = akt_pcidev->resource[ PCI_REG_PLX_IO_BASE].start & PCI_BASE_ADDRESS_IO_MASK;
+		card->plxbase = akt_pcidev->resource[ PCI_REG_PLX_MEM_BASE].start;
+		card->membase = akt_pcidev->resource[ PCI_REG_MEMORY_BASE].start;
+		card->brdtype = BD_NONE;	/* unknown */
+		card->debug_flags = DEF_DEB_FLAGS;	/* set default debug */
+		card->faxchans = 0;	/* default no fax channels */
+		card->bchans = 2;	/* and 2 b-channels */
+		for (i = 0; pci_subid_map[i].subid; i++)
+			if (pci_subid_map[i].subid == card->subsysid) {
+				card->brdtype = pci_subid_map[i].cardtyp;
+				break;
+			}
+		if (card->brdtype != BD_NONE) {
+			if (ergo_inithardware(card)) {
+				printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
+				kfree(card);
+				continue;
+			}
+		} else {
+			printk(KERN_WARNING "HYSDN: unknown card id 0x%04x\n", card->subsysid);
+			kfree(card);	/* release mem */
+			continue;
+		}
+		cardmax++;
+		card->next = NULL;	/*end of chain */
+		if (card_last)
+			card_last->next = card;		/* pointer to next card */
+		else
+			card_root = card;
+		card_last = card;	/* new chain end */
+	}			/* device found */
+}				/* search_cards */
+
+/************************************************************************************/
+/* free_resources frees the acquired PCI resources and returns the allocated memory */
+/************************************************************************************/
+static void
+free_resources(void)
+{
+	hysdn_card *card;
+
+	while (card_root) {
+		card = card_root;
+		if (card->releasehardware)
+			card->releasehardware(card);	/* free all hardware resources */
+		card_root = card_root->next;	/* remove card from chain */
+		kfree(card);	/* return mem */
+
+	}			/* while card_root */
+}				/* free_resources */
+
+/**************************************************************************/
+/* stop_cards disables (hardware resets) all cards and disables interrupt */
+/**************************************************************************/
+static void
+stop_cards(void)
+{
+	hysdn_card *card;
+
+	card = card_root;	/* first in chain */
+	while (card) {
+		if (card->stopcard)
+			card->stopcard(card);
+		card = card->next;	/* remove card from chain */
+	}			/* while card */
+}				/* stop_cards */
+
+
+/****************************************************************************/
+/* The module startup and shutdown code. Only compiled when used as module. */
+/* Using the driver as module is always advisable, because the booting      */
+/* image becomes smaller and the driver code is only loaded when needed.    */
+/* Additionally newer versions may be activated without rebooting.          */
+/****************************************************************************/
+#ifdef CONFIG_MODULES
+
+/******************************************************/
+/* extract revision number from string for log output */
+/******************************************************/
+char *
+hysdn_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+
+/****************************************************************************/
+/* init_module is called once when the module is loaded to do all necessary */
+/* things like autodetect...                                                */
+/* If the return value of this function is 0 the init has been successfull  */
+/* and the module is added to the list in /proc/modules, otherwise an error */
+/* is assumed and the module will not be kept in memory.                    */
+/****************************************************************************/
+int
+init_module(void)
+{
+	char tmp[50];
+
+	strcpy(tmp, hysdn_init_revision);
+	printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp));
+	strcpy(tmp, hysdn_net_revision);
+	printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp));
+	if (!pci_present()) {
+		printk(KERN_ERR "HYSDN: no PCI bus present, module not loaded\n");
+		return (-1);
+	}
+	search_cards();
+	printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
+
+	if (hysdn_procconf_init()) {
+		free_resources();	/* proc file_sys not created */
+		return (-1);
+	}
+	return (0);		/* no error */
+}				/* init_module */
+
+
+/***********************************************************************/
+/* cleanup_module is called when the module is released by the kernel. */
+/* The routine is only called if init_module has been successfull and  */
+/* the module counter has a value of 0. Otherwise this function will   */
+/* not be called. This function must release all resources still allo- */
+/* cated as after the return from this function the module code will   */
+/* be removed from memory.                                             */
+/***********************************************************************/
+void
+cleanup_module(void)
+{
+
+	stop_cards();
+	hysdn_procconf_release();
+	free_resources();
+	printk(KERN_NOTICE "HYSDN: module unloaded\n");
+}				/* cleanup_module */
+
+#endif				/* CONFIG_MODULES */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_net.c linux/drivers/isdn/hysdn/hysdn_net.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_net.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_net.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,378 @@
+/* $Id: hysdn_net.c,v 1.3 2000/02/14 19:24:12 werner Exp $
+
+ * Linux driver for HYSDN cards, net (ethernet type) handling routines.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.de)
+ *
+ * This net module has been inspired by the skeleton driver from
+ * Donald Becker (becker@CESDIS.gsfc.nasa.gov)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: hysdn_net.c,v $
+ * Revision 1.3  2000/02/14 19:24:12  werner
+ *
+ * Removed superflous file
+ *
+ * Revision 1.2  2000/02/13 17:32:19  werner
+ *
+ * Added support for new network layer of 2.3.43 and 44 kernels and tested driver.
+ *
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+
+#include "hysdn_defs.h"
+
+/* store the actual version for log reporting */
+char *hysdn_net_revision = "$Revision: 1.3 $";
+
+#define MAX_SKB_BUFFERS 20	/* number of buffers for keeping TX-data */
+
+/****************************************************************************/
+/* structure containing the complete network data. The structure is aligned */
+/* in a way that both, the device and statistics are kept inside it.        */
+/* for proper access, the device structure MUST be the first var/struct     */
+/* inside the definition.                                                   */
+/****************************************************************************/
+struct net_local {
+	struct net_device netdev;	/* the network device */
+	struct net_device_stats stats;
+	/* additional vars may be added here */
+	char dev_name[9];	/* our own device name */
+
+	/* Tx control lock.  This protects the transmit buffer ring
+	 * state along with the "tx full" state of the driver.  This
+	 * means all netif_queue flow control actions are protected
+	 * by this lock as well.
+	 */
+	spinlock_t lock;
+	struct sk_buff *skbs[MAX_SKB_BUFFERS];	/* pointers to tx-skbs */
+	int in_idx, out_idx;	/* indexes to buffer ring */
+	int sk_count;		/* number of buffers currently in ring */
+
+	int is_open;		/* flag controlling module locking */
+};				/* net_local */
+
+
+/*****************************************************/
+/* Get the current statistics for this card.         */
+/* This may be called with the card open or closed ! */
+/*****************************************************/
+static struct net_device_stats *
+net_get_stats(struct net_device *dev)
+{
+	return (&((struct net_local *) dev)->stats);
+}				/* net_device_stats */
+
+/*********************************************************************/
+/* Open/initialize the board. This is called (in the current kernel) */
+/* sometime after booting when the 'ifconfig' program is run.        */
+/* This routine should set everything up anew at each open, even     */
+/* registers that "should" only need to be set once at boot, so that */
+/* there is non-reboot way to recover if something goes wrong.       */
+/*********************************************************************/
+static int
+net_open(struct net_device *dev)
+{
+	struct in_device *in_dev;
+	hysdn_card *card = dev->priv;
+	int i;
+
+	if (!((struct net_local *) dev)->is_open)
+		MOD_INC_USE_COUNT;	/* increment only if interface is actually down */
+	((struct net_local *) dev)->is_open = 1;	/* device actually open */
+
+	netif_start_queue(dev);	/* start tx-queueing */
+
+	/* Fill in the MAC-level header (if not already set) */
+	if (!card->mac_addr[0]) {
+		for (i = 0; i < ETH_ALEN - sizeof(ulong); i++)
+			dev->dev_addr[i] = 0xfc;
+		if ((in_dev = dev->ip_ptr) != NULL) {
+			struct in_ifaddr *ifa = in_dev->ifa_list;
+			if (ifa != NULL)
+				memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ulong)), &ifa->ifa_local, sizeof(ulong));
+		}
+	} else
+		memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
+
+	return (0);
+}				/* net_open */
+
+/*******************************************/
+/* flush the currently occupied tx-buffers */
+/* must only be called when device closed  */
+/*******************************************/
+static void
+flush_tx_buffers(struct net_local *nl)
+{
+
+	while (nl->sk_count) {
+		dev_kfree_skb(nl->skbs[nl->out_idx++]);		/* free skb */
+		if (nl->out_idx >= MAX_SKB_BUFFERS)
+			nl->out_idx = 0;	/* wrap around */
+		nl->sk_count--;
+	}
+}				/* flush_tx_buffers */
+
+
+/*********************************************************************/
+/* close/decativate the device. The device is not removed, but only  */
+/* deactivated.                                                      */
+/*********************************************************************/
+static int
+net_close(struct net_device *dev)
+{
+
+	netif_stop_queue(dev);	/* disable queueing */
+
+	if (((struct net_local *) dev)->is_open)
+		MOD_DEC_USE_COUNT;	/* adjust module counter */
+	((struct net_local *) dev)->is_open = 0;
+	flush_tx_buffers((struct net_local *) dev);
+
+	return (0);		/* success */
+}				/* net_close */
+
+/************************************/
+/* send a packet on this interface. */
+/* new style for kernel >= 2.3.33   */
+/************************************/
+static int
+net_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) dev;
+
+	spin_lock_irq(&lp->lock);
+
+	lp->skbs[lp->in_idx++] = skb;	/* add to buffer list */
+	if (lp->in_idx >= MAX_SKB_BUFFERS)
+		lp->in_idx = 0;	/* wrap around */
+	lp->sk_count++;		/* adjust counter */
+	dev->trans_start = jiffies;
+
+	/* If we just used up the very last entry in the
+	 * TX ring on this device, tell the queueing
+	 * layer to send no more.
+	 */
+	if (lp->sk_count >= MAX_SKB_BUFFERS)
+		netif_stop_queue(dev);
+
+	/* When the TX completion hw interrupt arrives, this
+	 * is when the transmit statistics are updated.
+	 */
+
+	spin_unlock_irq(&lp->lock);
+
+	if (lp->sk_count <= 3) {
+		queue_task(&((hysdn_card *) dev->priv)->irq_queue, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+	return (0);		/* success */
+}				/* net_send_packet */
+
+
+
+/***********************************************************************/
+/* acknowlegde a packet send. The network layer will be informed about */
+/* completion                                                          */
+/***********************************************************************/
+void
+hysdn_tx_netack(hysdn_card * card)
+{
+	struct net_local *lp = card->netif;
+
+	if (!lp)
+		return;		/* non existing device */
+
+
+	if (!lp->sk_count)
+		return;		/* error condition */
+
+	lp->stats.tx_packets++;
+	lp->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
+
+	dev_kfree_skb(lp->skbs[lp->out_idx++]);		/* free skb */
+	if (lp->out_idx >= MAX_SKB_BUFFERS)
+		lp->out_idx = 0;	/* wrap around */
+
+	if (lp->sk_count-- == MAX_SKB_BUFFERS)	/* dec usage count */
+		netif_start_queue((struct net_device *) lp);
+}				/* hysdn_tx_netack */
+
+/*****************************************************/
+/* we got a packet from the network, go and queue it */
+/*****************************************************/
+void
+hysdn_rx_netpkt(hysdn_card * card, uchar * buf, word len)
+{
+	struct net_local *lp = card->netif;
+	struct sk_buff *skb;
+
+	if (!lp)
+		return;		/* non existing device */
+
+	lp->stats.rx_bytes += len;
+
+	skb = dev_alloc_skb(len);
+	if (skb == NULL) {
+		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+		       lp->netdev.name);
+		lp->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = &lp->netdev;
+
+	/* copy the data */
+	memcpy(skb_put(skb, len), buf, len);
+
+	/* determine the used protocol */
+	skb->protocol = eth_type_trans(skb, &lp->netdev);
+
+	netif_rx(skb);
+	lp->stats.rx_packets++;	/* adjust packet count */
+
+}				/* hysdn_rx_netpkt */
+
+/*****************************************************/
+/* return the pointer to a network packet to be send */
+/*****************************************************/
+struct sk_buff *
+hysdn_tx_netget(hysdn_card * card)
+{
+	struct net_local *lp = card->netif;
+
+	if (!lp)
+		return (NULL);	/* non existing device */
+
+	if (!lp->sk_count)
+		return (NULL);	/* nothing available */
+
+	return (lp->skbs[lp->out_idx]);		/* next packet to send */
+}				/* hysdn_tx_netget */
+
+
+/*******************************************/
+/* init function called by register device */
+/*******************************************/
+static int
+net_init(struct net_device *dev)
+{
+	/* setup the function table */
+	dev->open = net_open;
+	dev->stop = net_close;
+	dev->hard_start_xmit = net_send_packet;
+	dev->get_stats = net_get_stats;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	return (0);		/* success */
+}				/* net_init */
+
+/*****************************************************************************/
+/* hysdn_net_create creates a new net device for the given card. If a device */
+/* already exists, it will be deleted and created a new one. The return value */
+/* 0 announces success, else a negative error code will be returned.         */
+/*****************************************************************************/
+int
+hysdn_net_create(hysdn_card * card)
+{
+	struct net_device *dev;
+	int i;
+
+	hysdn_net_release(card);	/* release an existing net device */
+	if ((dev = kmalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) {
+		printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
+		if (card->debug_flags & LOG_NET_INIT)
+			return (-ENOMEM);
+	}
+	memset(dev, 0, sizeof(struct net_local));	/* clean the structure */
+
+	spin_lock_init(&((struct net_local *) dev)->lock);
+
+	/* initialise necessary or informing fields */
+	dev->base_addr = card->iobase;	/* IO address */
+	dev->irq = card->irq;	/* irq */
+	dev->init = net_init;	/* the init function of the device */
+	dev->name = ((struct net_local *) dev)->dev_name;	/* device name */
+	if ((i = register_netdev(dev))) {
+		printk(KERN_WARNING "HYSDN: unable to create network device\n");
+		kfree(dev);
+		return (i);
+	}
+	dev->priv = card;	/* remember pointer to own data structure */
+	card->netif = dev;	/* setup the local pointer */
+
+	if (card->debug_flags & LOG_NET_INIT)
+		hysdn_addlog(card, "network device created");
+	return (0);		/* and return success */
+}				/* hysdn_net_create */
+
+/***************************************************************************/
+/* hysdn_net_release deletes the net device for the given card. The return */
+/* value 0 announces success, else a negative error code will be returned. */
+/***************************************************************************/
+int
+hysdn_net_release(hysdn_card * card)
+{
+	struct net_device *dev = card->netif;
+
+	if (!dev)
+		return (0);	/* non existing */
+
+	card->netif = NULL;	/* clear out pointer */
+	dev->stop(dev);		/* close the device */
+
+	flush_tx_buffers((struct net_local *) dev);	/* empty buffers */
+
+	unregister_netdev(dev);	/* release the device */
+	kfree(dev);		/* release the memory allocated */
+	if (card->debug_flags & LOG_NET_INIT)
+		hysdn_addlog(card, "network device deleted");
+
+	return (0);		/* always successfull */
+}				/* hysdn_net_release */
+
+/*****************************************************************************/
+/* hysdn_net_getname returns a pointer to the name of the network interface. */
+/* if the interface is not existing, a "-" is returned.                      */
+/*****************************************************************************/
+char *
+hysdn_net_getname(hysdn_card * card)
+{
+	struct net_device *dev = card->netif;
+
+	if (!dev)
+		return ("-");	/* non existing */
+
+	return (dev->name);
+}				/* hysdn_net_getname */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_pof.h linux/drivers/isdn/hysdn/hysdn_pof.h
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_pof.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_pof.h	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,95 @@
+/* $Id: hysdn_pof.h,v 1.1 2000/02/10 19:44:30 werner Exp $
+
+ * Linux driver for HYSDN cards, definitions used for handling pof-files.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_pof.h,v $
+ * Revision 1.1  2000/02/10 19:44:30  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+/************************/
+/* POF specific defines */
+/************************/
+#define BOOT_BUF_SIZE   0x1000	/* =4096, maybe moved to other h file */
+#define CRYPT_FEEDTERM  0x8142
+#define CRYPT_STARTTERM 0x81a5
+				    /*  max. timeout time in seconds
+				     *  from end of booting to POF is ready
+				     */
+#define POF_READY_TIME_OUT_SEC  10
+
+/**********************************/
+/* defines for 1.stage boot image */
+/**********************************/
+
+/*  the POF file record containing the boot loader image
+ *  has 2 pages a 16KB:
+ *  1. page contains the high 16-bit part of the 32-bit E1 words
+ *  2. page contains the low  16-bit part of the 32-bit E1 words
+ *
+ *  In each 16KB page we assume the start of the boot loader code
+ *  in the highest 2KB part (at offset 0x3800);
+ *  the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
+ */
+
+#define POF_BOOT_LOADER_PAGE_SIZE   0x4000	/* =16384U */
+#define POF_BOOT_LOADER_TOTAL_SIZE  (2U*POF_BOOT_LOADER_PAGE_SIZE)
+
+#define POF_BOOT_LOADER_CODE_SIZE   0x0800	/* =2KB =2048U */
+
+		    /* offset in boot page, where loader code may start */
+					    /* =0x3800= 14336U */
+#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
+
+
+/*--------------------------------------POF file record structs------------*/
+typedef struct PofFileHdr_tag {	/* Pof file header */
+/*00 */ ulong Magic __attribute__((packed));
+/*04 */ ulong N_PofRecs __attribute__((packed));
+/*08 */
+} tPofFileHdr;
+
+typedef struct PofRecHdr_tag {	/* Pof record header */
+/*00 */ word PofRecId __attribute__((packed));
+/*02 */ ulong PofRecDataLen __attribute__((packed));
+/*06 */
+} tPofRecHdr;
+
+typedef struct PofTimeStamp_tag {
+/*00 */ ulong UnixTime __attribute__((packed));
+	/*04 */ uchar DateTimeText[0x28] __attribute__((packed));
+	/* =40 */
+/*2C */
+} tPofTimeStamp;
+
+				    /* tPofFileHdr.Magic value: */
+#define TAGFILEMAGIC 0x464F501AUL
+				    /* tPofRecHdr.PofRecId values: */
+#define TAG_ABSDATA  0x1000	/* abs. data */
+#define TAG_BOOTDTA  0x1001	/* boot data */
+#define TAG_COMMENT  0x0020
+#define TAG_SYSCALL  0x0021
+#define TAG_FLOWCTRL 0x0022
+#define TAG_TIMESTMP 0x0010	/* date/time stamp of version */
+#define TAG_CABSDATA 0x1100	/* crypted abs. data */
+#define TAG_CBOOTDTA 0x1101	/* crypted boot data */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_procconf.c linux/drivers/isdn/hysdn/hysdn_procconf.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_procconf.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_procconf.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,485 @@
+/* $Id: hysdn_procconf.c,v 1.2 2000/02/14 19:23:03 werner Exp $
+
+ * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_procconf.c,v $
+ * Revision 1.2  2000/02/14 19:23:03  werner
+ *
+ * Changed handling of proc filesystem tables to a more portable version
+ *
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_procconf_revision = "$Revision: 1.2 $";
+
+#define INFO_OUT_LEN 80		/* length of info line including lf */
+
+/********************************************************/
+/* defines and data structure for conf write operations */
+/********************************************************/
+#define CONF_STATE_DETECT 0	/* waiting for detect */
+#define CONF_STATE_CONF   1	/* writing config data */
+#define CONF_STATE_POF    2	/* writing pof data */
+#define CONF_LINE_LEN    80	/* 80 chars max */
+
+struct conf_writedata {
+	hysdn_card *card;	/* card the device is connected to */
+	int buf_size;		/* actual number of bytes in the buffer */
+	int needed_size;	/* needed size when reading pof */
+	int state;		/* actual interface states from above constants */
+	uchar conf_line[CONF_LINE_LEN];		/* buffered conf line */
+	word channel;		/* active channel number */
+	uchar *pof_buffer;	/* buffer when writing pof */
+};
+
+/***********************************************************************/
+/* process_line parses one config line and transfers it to the card if */
+/* necessary.                                                          */
+/* if the return value is negative an error occured.                   */
+/***********************************************************************/
+static int
+process_line(struct conf_writedata *cnf)
+{
+	uchar *cp = cnf->conf_line;
+	int i;
+
+	if (cnf->card->debug_flags & LOG_CNF_LINE)
+		hysdn_addlog(cnf->card, "conf line: %s", cp);
+
+	if (*cp == '-') {	/* option */
+		cp++;		/* point to option char */
+
+		if (*cp++ != 'c')
+			return (0);	/* option unknown or used */
+		i = 0;		/* start value for channel */
+		while ((*cp <= '9') && (*cp >= '0'))
+			i = i * 10 + *cp++ - '0';	/* get decimal number */
+		if (i > 65535) {
+			if (cnf->card->debug_flags & LOG_CNF_MISC)
+				hysdn_addlog(cnf->card, "conf channel invalid  %d", i);
+			return (-ERR_INV_CHAN);		/* invalid channel */
+		}
+		cnf->channel = i & 0xFFFF;	/* set new channel number */
+		return (0);	/* success */
+	}			/* option */
+	if (*cp == '*') {	/* line to send */
+		if (cnf->card->debug_flags & LOG_CNF_DATA)
+			hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
+		return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
+					 cnf->channel));	/* send the line without * */
+	}			/* line to send */
+	return (0);
+}				/* process_line */
+
+/*************************/
+/* dummy file operations */
+/*************************/
+static loff_t
+hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}				/* hysdn_dummy_lseek */
+
+/***********************************/
+/* conf file operations and tables */
+/***********************************/
+
+/****************************************************/
+/* write conf file -> boot or send cfg line to card */
+/****************************************************/
+static ssize_t
+hysdn_conf_write(struct file *file, const char *buf, size_t count, loff_t * off)
+{
+	struct conf_writedata *cnf;
+	int i;
+	uchar ch, *cp;
+
+	if (&file->f_pos != off)	/* fs error check */
+		return (-ESPIPE);
+	if (!count)
+		return (0);	/* nothing to handle */
+
+	if (!(cnf = file->private_data))
+		return (-EFAULT);	/* should never happen */
+
+	if (cnf->state == CONF_STATE_DETECT) {	/* auto detect cnf or pof data */
+		if (copy_from_user(&ch, buf, 1))	/* get first char for detect */
+			return (-EFAULT);
+
+		if (ch == 0x1A) {
+			/* we detected a pof file */
+			if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
+				return (cnf->needed_size);	/* an error occured -> exit */
+			cnf->buf_size = 0;	/* buffer is empty */
+			cnf->state = CONF_STATE_POF;	/* new state */
+		} else {
+			/* conf data has been detected */
+			cnf->buf_size = 0;	/* buffer is empty */
+			cnf->state = CONF_STATE_CONF;	/* requested conf data write */
+			if (cnf->card->state != CARD_STATE_RUN)
+				return (-ERR_NOT_BOOTED);
+			cnf->conf_line[CONF_LINE_LEN - 1] = 0;	/* limit string length */
+			cnf->channel = 4098;	/* default channel for output */
+		}
+	}			/* state was auto detect */
+	if (cnf->state == CONF_STATE_POF) {	/* pof write active */
+		i = cnf->needed_size - cnf->buf_size;	/* bytes still missing for write */
+		if (i <= 0)
+			return (-EINVAL);	/* size error handling pof */
+
+		if (i < count)
+			count = i;	/* limit requested number of bytes */
+		if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
+			return (-EFAULT);	/* error while copying */
+		cnf->buf_size += count;
+
+		if (cnf->needed_size == cnf->buf_size) {
+			cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size);	/* write data */
+			if (cnf->needed_size <= 0) {
+				cnf->card->state = CARD_STATE_BOOTERR;	/* show boot error */
+				return (cnf->needed_size);	/* an error occured */
+			}
+			cnf->buf_size = 0;	/* buffer is empty again */
+		}
+	}
+	/* pof write active */
+	else {			/* conf write active */
+
+		if (cnf->card->state != CARD_STATE_RUN) {
+			if (cnf->card->debug_flags & LOG_CNF_MISC)
+				hysdn_addlog(cnf->card, "cnf write denied -> not booted");
+			return (-ERR_NOT_BOOTED);
+		}
+		i = (CONF_LINE_LEN - 1) - cnf->buf_size;	/* bytes available in buffer */
+		if (i > 0) {
+			/* copy remaining bytes into buffer */
+
+			if (count > i)
+				count = i;	/* limit transfer */
+			if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
+				return (-EFAULT);	/* error while copying */
+
+			i = count;	/* number of chars in buffer */
+			cp = cnf->conf_line + cnf->buf_size;
+			while (i) {
+				/* search for end of line */
+				if ((*cp < ' ') && (*cp != 9))
+					break;	/* end of line found */
+				cp++;
+				i--;
+			}	/* search for end of line */
+
+			if (i) {
+				/* delimiter found */
+				*cp++ = 0;	/* string termination */
+				count -= (i - 1);	/* subtract remaining bytes from count */
+				while ((i) && (*cp < ' ') && (*cp != 9)) {
+					i--;	/* discard next char */
+					count++;	/* mark as read */
+					cp++;	/* next char */
+				}
+				cnf->buf_size = 0;	/* buffer is empty after transfer */
+				if ((i = process_line(cnf)) < 0)	/* handle the line */
+					count = i;	/* return the error */
+			}
+			/* delimiter found */
+			else {
+				cnf->buf_size += count;		/* add chars to string */
+				if (cnf->buf_size >= CONF_LINE_LEN - 1) {
+					if (cnf->card->debug_flags & LOG_CNF_MISC)
+						hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
+					return (-ERR_CONF_LONG);
+				}
+			}	/* not delimited */
+
+		}
+		/* copy remaining bytes into buffer */
+		else {
+			if (cnf->card->debug_flags & LOG_CNF_MISC)
+				hysdn_addlog(cnf->card, "cnf line too long");
+			return (-ERR_CONF_LONG);
+		}
+	}			/* conf write active */
+
+	return (count);
+}				/* hysdn_conf_write */
+
+/*******************************************/
+/* read conf file -> output card info data */
+/*******************************************/
+static ssize_t
+hysdn_conf_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+	char *cp;
+	int i;
+
+	if (off != &file->f_pos)	/* fs error check */
+		return -ESPIPE;
+
+	if (file->f_mode & FMODE_READ) {
+		if (!(cp = file->private_data))
+			return (-EFAULT);	/* should never happen */
+		i = strlen(cp);	/* get total string length */
+		if (*off < i) {
+			/* still bytes to transfer */
+			cp += *off;	/* point to desired data offset */
+			i -= *off;	/* remaining length */
+			if (i > count)
+				i = count;	/* limit length to transfer */
+			if (copy_to_user(buf, cp, i))
+				return (-EFAULT);	/* copy error */
+			*off += i;	/* adjust offset */
+		} else
+			return (0);
+	} else
+		return (-EPERM);	/* no permission to read */
+
+	return (i);
+}				/* hysdn_conf_read */
+
+/******************/
+/* open conf file */
+/******************/
+static int
+hysdn_conf_open(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct proc_dir_entry *pd;
+	struct conf_writedata *cnf;
+	char *cp, *tmp;
+
+	MOD_INC_USE_COUNT;	/* lock module */
+
+	/* now search the addressed card */
+	card = card_root;
+	while (card) {
+		pd = card->procconf;
+		if (pd->low_ino == (ino->i_ino & 0xFFFF))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		MOD_DEC_USE_COUNT;	/* unlock module */
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+		hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
+			     filep->f_uid, filep->f_gid, filep->f_mode);
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write boot file or conf line */
+
+		if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
+			MOD_DEC_USE_COUNT;
+			return (-EFAULT);
+		}
+		cnf->card = card;
+		cnf->buf_size = 0;	/* nothing buffered */
+		cnf->state = CONF_STATE_DETECT;		/* start auto detect */
+		filep->private_data = cnf;
+
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+		/* read access -> output card info data */
+
+		if (!(tmp = (char *) kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
+			MOD_DEC_USE_COUNT;
+			return (-EFAULT);	/* out of memory */
+		}
+		filep->private_data = tmp;	/* start of string */
+
+		/* first output a headline */
+		sprintf(tmp, "id bus slot type irq iobase dp-mem     b-chans fax-chans state device");
+		cp = tmp;	/* start of string */
+		while (*cp)
+			cp++;
+		while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+			*cp++ = ' ';
+		*cp++ = '\n';
+
+		/* and now the data */
+		sprintf(cp, "%d  %3d %4d %4d %3d 0x%04x 0x%08x %7d %9d %3d   %s",
+			card->myid,
+			card->bus,
+			PCI_SLOT(card->devfn),
+			card->brdtype,
+			card->irq,
+			card->iobase,
+			card->membase,
+			card->bchans,
+			card->faxchans,
+			card->state,
+			hysdn_net_getname(card));
+		while (*cp)
+			cp++;
+		while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+			*cp++ = ' ';
+		*cp++ = '\n';
+		*cp = 0;	/* end of string */
+	} else {		/* simultaneous read/write access forbidden ! */
+		MOD_DEC_USE_COUNT;	/* unlock module */
+		return (-EPERM);	/* no permission this time */
+	}
+	return (0);
+}				/* hysdn_conf_open */
+
+/***************************/
+/* close a config file.    */
+/***************************/
+static int
+hysdn_conf_close(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct conf_writedata *cnf;
+	int retval = 0;
+	struct proc_dir_entry *pd;
+
+	/* search the addressed card */
+	card = card_root;
+	while (card) {
+		pd = card->procconf;
+		if (pd->low_ino == (ino->i_ino & 0xFFFF))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+		hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
+			     filep->f_uid, filep->f_gid, filep->f_mode);
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write boot file or conf line */
+		if (filep->private_data) {
+			cnf = filep->private_data;
+
+			if (cnf->state == CONF_STATE_POF)
+				retval = pof_write_close(cnf->card);	/* close the pof write */
+			kfree(filep->private_data);	/* free allocated memory for buffer */
+
+		}		/* handle write private data */
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+		/* read access -> output card info data */
+
+		if (filep->private_data)
+			kfree(filep->private_data);	/* release memory */
+	}
+	MOD_DEC_USE_COUNT;	/* reduce usage count */
+	return (retval);
+}				/* hysdn_conf_close */
+
+/******************************************************/
+/* table for conf filesystem functions defined above. */
+/******************************************************/
+static struct file_operations conf_fops =
+{
+	hysdn_dummy_lseek,
+	hysdn_conf_read,
+	hysdn_conf_write,
+	NULL,			/* readdir */
+	NULL,			/* poll */
+	NULL,			/* ioctl */
+	NULL,			/* mmap */
+	hysdn_conf_open,
+	NULL,			/* flush */
+	hysdn_conf_close,
+	NULL			/* fsync */
+};
+
+static struct inode_operations conf_inode_operations;
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+struct proc_dir_entry *hysdn_proc_entry = NULL;
+
+/*******************************************************************************/
+/* hysdn_procconf_init is called when the module is loaded and after the cards */
+/* have been detected. The needed proc dir and card config files are created.  */
+/* The log init is called at last.                                             */
+/*******************************************************************************/
+int
+hysdn_procconf_init(void)
+{
+	hysdn_card *card;
+	uchar conf_name[20];
+
+	hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+	if (!hysdn_proc_entry) {
+		printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+		return (-1);
+	}
+	card = card_root;	/* point to first card */
+	while (card) {
+
+		sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+		if ((card->procconf = (void *) create_proc_entry(conf_name,
+					     S_IFREG | S_IRUGO | S_IWUSR,
+					    hysdn_proc_entry)) != NULL) {
+			memset(&conf_inode_operations, 0, sizeof(struct inode_operations));
+			conf_inode_operations.default_file_ops = &conf_fops;
+
+			((struct proc_dir_entry *) card->procconf)->ops = &conf_inode_operations;
+			hysdn_proclog_init(card);	/* init the log file entry */
+		}
+		card = card->next;	/* next entry */
+	}
+
+	printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procconf_revision));
+	return (0);
+}				/* hysdn_procconf_init */
+
+/*************************************************************************************/
+/* hysdn_procconf_release is called when the module is unloaded and before the cards */
+/* resources are released. The module counter is assumed to be 0 !                   */
+/*************************************************************************************/
+void
+hysdn_procconf_release(void)
+{
+	hysdn_card *card;
+	uchar conf_name[20];
+
+	card = card_root;	/* start with first card */
+	while (card) {
+
+		sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+		if (card->procconf)
+			remove_proc_entry(conf_name, hysdn_proc_entry);
+
+		hysdn_proclog_release(card);	/* init the log file entry */
+
+		card = card->next;	/* point to next card */
+	}
+
+	remove_proc_entry(PROC_SUBDIR_NAME, proc_net);
+}				/* hysdn_procfs_release */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_procfs.c linux/drivers/isdn/hysdn/hysdn_procfs.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_procfs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_procfs.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,502 @@
+/* $Id: hysdn_procfs.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_procfs.c,v $
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_procfs_revision = "$Revision: 1.1 $";
+
+#define INFO_OUT_LEN 80		/* length of info line including lf */
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+	struct log_data *next;
+	ulong usage_cnt;	/* number of files still to work */
+	void *proc_ctrl;	/* pointer to own control procdata structure */
+	char log_start[2];	/* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+	struct proc_dir_entry *log;	/* log entry */
+	char log_name[15];	/* log filename */
+	struct log_data *log_head, *log_tail;	/* head and tail for queue */
+	int if_used;		/* open count for interface */
+	wait_queue_head_t rd_queue;
+};
+
+/********************************************/
+/* put an log buffer into the log queue.    */
+/* This buffer will be kept until all files */
+/* opened for read got the contents.        */
+/* Flushes buffers not longer in use.       */
+/********************************************/
+void
+put_log_buffer(hysdn_card * card, char *cp)
+{
+	struct log_data *ib;
+	struct procdata *pd = card->procfs;
+	int flags;
+
+	if (!pd)
+		return;
+	if (!cp)
+		return;
+	if (!*cp)
+		return;
+	if (pd->if_used <= 0)
+		return;		/* no open file for read */
+
+	if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+		 return;	/* no memory */
+	strcpy(ib->log_start, cp);	/* set output string */
+	ib->next = NULL;
+	ib->proc_ctrl = pd;	/* point to own control structure */
+	save_flags(flags);
+	cli();
+	ib->usage_cnt = pd->if_used;
+	if (!pd->log_head)
+		pd->log_head = ib;	/* new head */
+	else
+		pd->log_tail->next = ib;	/* follows existing messages */
+	pd->log_tail = ib;	/* new tail */
+	restore_flags(flags);
+
+	/* delete old entrys */
+	while (pd->log_head->next) {
+		if ((pd->log_head->usage_cnt <= 0) &&
+		    (pd->log_head->next->usage_cnt <= 0)) {
+			ib = pd->log_head;
+			pd->log_head = pd->log_head->next;
+			kfree(ib);
+		} else
+			break;
+	}			/* pd->log_head->next */
+	wake_up_interruptible(&(pd->rd_queue));		/* announce new entry */
+}				/* put_log_buffer */
+
+
+/*************************/
+/* dummy file operations */
+/*************************/
+static loff_t
+hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}				/* hysdn_dummy_lseek */
+
+/**********************************/
+/* log file operations and tables */
+/**********************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
+{
+	int retval;
+	hysdn_card *card = (hysdn_card *) file->private_data;
+
+	if (&file->f_pos != off)	/* fs error check */
+		return (-ESPIPE);
+
+	if ((retval = pof_boot_write(card, buf, count)) < 0)
+		retval = -EFAULT;	/* an error occured */
+
+	return (retval);
+}				/* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+	struct log_data *inf;
+	int len;
+	word ino;
+	struct procdata *pd;
+	hysdn_card *card;
+
+	if (!*((struct log_data **) file->private_data)) {
+		if (file->f_flags & O_NONBLOCK)
+			return (-EAGAIN);
+
+		/* sorry, but we need to search the card */
+		ino = file->f_dentry->d_inode->i_ino & 0xFFFF;	/* low-ino */
+		card = card_root;
+		while (card) {
+			pd = card->procfs;
+			if (pd->log->low_ino == ino)
+				break;
+			card = card->next;	/* search next entry */
+		}
+		if (card)
+			interruptible_sleep_on(&(pd->rd_queue));
+		else
+			return (-EAGAIN);
+
+	}
+	if (!(inf = *((struct log_data **) file->private_data)))
+		return (0);
+
+	inf->usage_cnt--;	/* new usage count */
+	(struct log_data **) file->private_data = &inf->next;	/* next structure */
+	if ((len = strlen(inf->log_start)) <= count) {
+		if (copy_to_user(buf, inf->log_start, len))
+			return -EFAULT;
+		file->f_pos += len;
+		return (len);
+	}
+	return (0);
+}				/* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct procdata *pd;
+	ulong flags;
+
+	MOD_INC_USE_COUNT;	/* lock module */
+	card = card_root;
+	while (card) {
+		pd = card->procfs;
+		if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		MOD_DEC_USE_COUNT;	/* unlock module */
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	filep->private_data = card;	/* remember our own card */
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> boot pof data */
+		if (pof_boot_open(card)) {
+			MOD_DEC_USE_COUNT;	/* unlock module */
+			return (-EPERM);	/* no permission this time */
+		}
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+
+		/* read access -> log/debug read */
+		save_flags(flags);
+		cli();
+		pd->if_used++;
+		if (pd->log_head)
+			(struct log_data **) filep->private_data = &(pd->log_tail->next);
+		else
+			(struct log_data **) filep->private_data = &(pd->log_head);
+		restore_flags(flags);
+
+	} else {		/* simultaneous read/write access forbidden ! */
+		MOD_DEC_USE_COUNT;	/* unlock module */
+		return (-EPERM);	/* no permission this time */
+	}
+	return (0);
+}				/* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about.              */
+/* Otherwise file is handled as log output. In this case the interface usage   */
+/* count is decremented and all buffers are noticed of closing. If this file   */
+/* was the last one to be closed, all buffers are freed.                       */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+	struct log_data *inf;
+	struct procdata *pd;
+	hysdn_card *card;
+	int flags, retval = 0;
+
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write debug completely written */
+		retval = 0;	/* success */
+	} else {
+		/* read access -> log/debug read, mark one further file as closed */
+
+		pd = NULL;
+		save_flags(flags);
+		cli();
+		inf = *((struct log_data **) filep->private_data);	/* get first log entry */
+		if (inf)
+			pd = (struct procdata *) inf->proc_ctrl;	/* still entries there */
+		else {
+			/* no info available -> search card */
+			card = card_root;
+			while (card) {
+				pd = card->procfs;
+				if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+					break;
+				card = card->next;	/* search next entry */
+			}
+			if (card)
+				pd = card->procfs;	/* pointer to procfs ctrl */
+		}
+		if (pd)
+			pd->if_used--;	/* decrement interface usage count by one */
+
+		while (inf) {
+			inf->usage_cnt--;	/* decrement usage count for buffers */
+			inf = inf->next;
+		}
+		restore_flags(flags);
+
+		if (pd)
+			if (pd->if_used <= 0)	/* delete buffers if last file closed */
+				while (pd->log_head) {
+					inf = pd->log_head;
+					pd->log_head = pd->log_head->next;
+					kfree(inf);
+				}
+	}			/* read access */
+
+	MOD_DEC_USE_COUNT;
+	return (retval);
+}				/* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static unsigned int
+hysdn_log_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	word ino;
+	hysdn_card *card;
+	struct procdata *pd;
+
+	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+		return (mask);	/* no polling for write supported */
+
+	/* we need to search the card */
+	ino = file->f_dentry->d_inode->i_ino & 0xFFFF;	/* low-ino */
+	card = card_root;
+	while (card) {
+		pd = card->procfs;
+		if (pd->log->low_ino == ino)
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card)
+		return (mask);	/* card not found */
+
+	poll_wait(file, &(pd->rd_queue), wait);
+
+	if (*((struct log_data **) file->private_data))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}				/* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static struct file_operations log_fops =
+{
+	hysdn_dummy_lseek,
+	hysdn_log_read,
+	hysdn_log_write,
+	NULL,			/* readdir */
+	hysdn_log_poll,		/* poll */
+	NULL,			/*hysdn_log_ioctl, *//* ioctl */
+	NULL,			/* mmap */
+	hysdn_log_open,
+	NULL,			/* flush */
+	hysdn_log_close,
+	NULL			/* fsync */
+};
+
+struct inode_operations log_inode_operations =
+{
+	&log_fops,		/* log proc file-ops */
+	NULL,			/* create      */
+	NULL,			/* lookup      */
+	NULL,			/* link        */
+	NULL,			/* unlink      */
+	NULL,			/* symlink     */
+	NULL,			/* mkdir       */
+	NULL,			/* rmdir       */
+	NULL,			/* mknod       */
+	NULL,			/* rename      */
+	NULL,			/* readlink    */
+	NULL,			/* follow_link */
+	NULL,			/* readpage    */
+	NULL,			/* writepage   */
+	NULL,			/* bmap        */
+	NULL,			/* truncate    */
+	NULL			/* permission  */
+};
+
+/*****************************************/
+/* Output info data to the cardinfo file */
+/*****************************************/
+static int
+info_read(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+	char tmp[INFO_OUT_LEN * 11 + 2];
+	int i;
+	char *cp;
+	hysdn_card *card;
+
+	sprintf(tmp, "id bus slot type irq iobase plx-mem    dp-mem     boot device");
+	cp = tmp;		/* start of string */
+	while (*cp)
+		cp++;
+	while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+		*cp++ = ' ';
+	*cp++ = '\n';
+
+	card = card_root;	/* start of list */
+	while (card) {
+		sprintf(cp, "%d  %3d %4d %4d %3d 0x%04x 0x%08x 0x%08x",
+			card->myid,
+			card->bus,
+			PCI_SLOT(card->devfn),
+			card->brdtype,
+			card->irq,
+			card->iobase,
+			card->plxbase,
+			card->membase);
+		card = card->next;
+		while (*cp)
+			cp++;
+		while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+			*cp++ = ' ';
+		*cp++ = '\n';
+	}
+
+	i = cp - tmp;
+	*start = buffer;
+	if (offset + length > i) {
+		length = i - offset;
+		*eof = 1;
+	} else if (offset > i) {
+		length = 0;
+		*eof = 1;
+	}
+	cp = tmp + offset;
+
+	if (length > 0) {
+		/*   start_bh_atomic(); */
+		memcpy(buffer, cp, length);
+		/* end_bh_atomic(); */
+		return length;
+	}
+	return 0;
+}				/* info_read */
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+static struct proc_dir_entry *hysdn_proc_entry = NULL;
+static struct proc_dir_entry *hysdn_info_entry = NULL;
+
+/***************************************************************************************/
+/* hysdn_procfs_init is called when the module is loaded and after the cards have been */
+/* detected. The needed proc dir and card entries are created.                         */
+/***************************************************************************************/
+int
+hysdn_procfs_init(void)
+{
+	struct procdata *pd;
+	hysdn_card *card;
+
+	hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+	if (!hysdn_proc_entry) {
+		printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+		return (-1);
+	}
+	hysdn_info_entry = create_proc_entry("cardinfo", 0, hysdn_proc_entry);
+	if (hysdn_info_entry)
+		hysdn_info_entry->read_proc = info_read;	/* read info function */
+
+	/* create all cardlog proc entries */
+
+	card = card_root;	/* start with first card */
+	while (card) {
+		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->ops = &log_inode_operations;	/* set new operations table */
+
+			init_waitqueue_head(&(pd->rd_queue));
+
+			card->procfs = (void *) pd;	/* remember procfs structure */
+		}
+		card = card->next;	/* point to next card */
+	}
+
+	printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procfs_revision));
+	return (0);
+}				/* hysdn_procfs_init */
+
+/***************************************************************************************/
+/* hysdn_procfs_release is called when the module is unloaded and before the cards     */
+/* resources are released. The module counter is assumed to be 0 !                     */
+/***************************************************************************************/
+void
+hysdn_procfs_release(void)
+{
+	struct procdata *pd;
+	hysdn_card *card;
+
+	card = card_root;	/* start with first card */
+	while (card) {
+		if ((pd = (struct procdata *) card->procfs) != NULL) {
+			if (pd->log)
+				remove_proc_entry(pd->log_name, hysdn_proc_entry);
+			kfree(pd);	/* release memory */
+		}
+		card = card->next;	/* point to next card */
+	}
+
+	remove_proc_entry("cardinfo", hysdn_proc_entry);
+	remove_proc_entry(PROC_SUBDIR_NAME, proc_net);
+}				/* hysdn_procfs_release */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_proclog.c linux/drivers/isdn/hysdn/hysdn_proclog.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_proclog.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_proclog.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,481 @@
+/* $Id: hysdn_proclog.c,v 1.2 2000/02/14 19:23:03 werner Exp $
+
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_proclog.c,v $
+ * Revision 1.2  2000/02/14 19:23:03  werner
+ *
+ * Changed handling of proc filesystem tables to a more portable version
+ *
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_proclog_revision = "$Revision: 1.2 $";
+
+/* the proc subdir for the interface is defined in the procconf module */
+extern struct proc_dir_entry *hysdn_proc_entry;
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+	struct log_data *next;
+	ulong usage_cnt;	/* number of files still to work */
+	void *proc_ctrl;	/* pointer to own control procdata structure */
+	char log_start[2];	/* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+	struct proc_dir_entry *log;	/* log entry */
+	char log_name[15];	/* log filename */
+	struct log_data *log_head, *log_tail;	/* head and tail for queue */
+	int if_used;		/* open count for interface */
+	int volatile del_lock;	/* lock for delete operations */
+	uchar logtmp[LOG_MAX_LINELEN];
+	wait_queue_head_t rd_queue;
+};
+
+
+/**********************************************/
+/* log function for cards error log interface */
+/**********************************************/
+void
+hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize)
+{
+	char buf[ERRLOG_TEXT_SIZE + 40];
+
+	sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
+	put_log_buffer(card, buf);	/* output the string */
+}				/* hysdn_card_errlog */
+
+/***************************************************/
+/* Log function using format specifiers for output */
+/***************************************************/
+void
+hysdn_addlog(hysdn_card * card, char *fmt,...)
+{
+	struct procdata *pd = card->proclog;
+	char *cp;
+	va_list args;
+
+	if (!pd)
+		return;		/* log structure non existent */
+
+	cp = pd->logtmp;
+	cp += sprintf(cp, "HYSDN: card %d ", card->myid);
+
+	va_start(args, fmt);
+	cp += vsprintf(cp, fmt, args);
+	va_end(args);
+	*cp++ = '\n';
+	*cp = 0;
+
+	if (card->debug_flags & DEB_OUT_SYSLOG)
+		printk(KERN_INFO "%s", pd->logtmp);
+	else
+		put_log_buffer(card, pd->logtmp);
+
+}				/* hysdn_addlog */
+
+/********************************************/
+/* put an log buffer into the log queue.    */
+/* This buffer will be kept until all files */
+/* opened for read got the contents.        */
+/* Flushes buffers not longer in use.       */
+/********************************************/
+void
+put_log_buffer(hysdn_card * card, char *cp)
+{
+	struct log_data *ib;
+	struct procdata *pd = card->proclog;
+	int i, flags;
+
+	if (!pd)
+		return;
+	if (!cp)
+		return;
+	if (!*cp)
+		return;
+	if (pd->if_used <= 0)
+		return;		/* no open file for read */
+
+	if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+		 return;	/* no memory */
+	strcpy(ib->log_start, cp);	/* set output string */
+	ib->next = NULL;
+	ib->proc_ctrl = pd;	/* point to own control structure */
+	save_flags(flags);
+	cli();
+	ib->usage_cnt = pd->if_used;
+	if (!pd->log_head)
+		pd->log_head = ib;	/* new head */
+	else
+		pd->log_tail->next = ib;	/* follows existing messages */
+	pd->log_tail = ib;	/* new tail */
+	i = pd->del_lock++;	/* get lock state */
+	restore_flags(flags);
+
+	/* delete old entrys */
+	if (!i)
+		while (pd->log_head->next) {
+			if ((pd->log_head->usage_cnt <= 0) &&
+			    (pd->log_head->next->usage_cnt <= 0)) {
+				ib = pd->log_head;
+				pd->log_head = pd->log_head->next;
+				kfree(ib);
+			} else
+				break;
+		}		/* pd->log_head->next */
+	pd->del_lock--;		/* release lock level */
+	wake_up_interruptible(&(pd->rd_queue));		/* announce new entry */
+}				/* put_log_buffer */
+
+
+/*************************/
+/* dummy file operations */
+/*************************/
+static loff_t
+hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}				/* hysdn_dummy_lseek */
+
+/******************************/
+/* file operations and tables */
+/******************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
+{
+	ulong u = 0;
+	int found = 0;
+	uchar *cp, valbuf[128];
+	long base = 10;
+	hysdn_card *card = (hysdn_card *) file->private_data;
+
+	if (&file->f_pos != off)	/* fs error check */
+		return (-ESPIPE);
+
+	if (count > (sizeof(valbuf) - 1))
+		count = sizeof(valbuf) - 1;	/* limit length */
+	if (copy_from_user(valbuf, buf, count))
+		return (-EFAULT);	/* copy failed */
+
+	valbuf[count] = 0;	/* terminating 0 */
+	cp = valbuf;
+	if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) {
+		cp += 2;	/* pointer after hex modifier */
+		base = 16;
+	}
+	/* scan the input for debug flags */
+	while (*cp) {
+		if ((*cp >= '0') && (*cp <= '9')) {
+			found = 1;
+			u *= base;	/* adjust to next digit */
+			u += *cp++ - '0';
+			continue;
+		}
+		if (base != 16)
+			break;	/* end of number */
+
+		if ((*cp >= 'a') && (*cp <= 'f')) {
+			found = 1;
+			u *= base;	/* adjust to next digit */
+			u += *cp++ - 'a' + 10;
+			continue;
+		}
+		break;		/* terminated */
+	}
+
+	if (found) {
+		card->debug_flags = u;	/* remember debug flags */
+		hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
+	}
+	return (count);
+}				/* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+	struct log_data *inf;
+	int len;
+	word ino;
+	struct procdata *pd;
+	hysdn_card *card;
+
+	if (!*((struct log_data **) file->private_data)) {
+		if (file->f_flags & O_NONBLOCK)
+			return (-EAGAIN);
+
+		/* sorry, but we need to search the card */
+		ino = file->f_dentry->d_inode->i_ino & 0xFFFF;	/* low-ino */
+		card = card_root;
+		while (card) {
+			pd = card->proclog;
+			if (pd->log->low_ino == ino)
+				break;
+			card = card->next;	/* search next entry */
+		}
+		if (card)
+			interruptible_sleep_on(&(pd->rd_queue));
+		else
+			return (-EAGAIN);
+
+	}
+	if (!(inf = *((struct log_data **) file->private_data)))
+		return (0);
+
+	inf->usage_cnt--;	/* new usage count */
+	(struct log_data **) file->private_data = &inf->next;	/* next structure */
+	if ((len = strlen(inf->log_start)) <= count) {
+		if (copy_to_user(buf, inf->log_start, len))
+			return -EFAULT;
+		file->f_pos += len;
+		return (len);
+	}
+	return (0);
+}				/* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct procdata *pd;
+	ulong flags;
+
+	MOD_INC_USE_COUNT;	/* lock module */
+	card = card_root;
+	while (card) {
+		pd = card->proclog;
+		if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		MOD_DEC_USE_COUNT;	/* unlock module */
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	filep->private_data = card;	/* remember our own card */
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write log level only */
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+
+		/* read access -> log/debug read */
+		save_flags(flags);
+		cli();
+		pd->if_used++;
+		if (pd->log_head)
+			(struct log_data **) filep->private_data = &(pd->log_tail->next);
+		else
+			(struct log_data **) filep->private_data = &(pd->log_head);
+		restore_flags(flags);
+	} else {		/* simultaneous read/write access forbidden ! */
+		MOD_DEC_USE_COUNT;	/* unlock module */
+		return (-EPERM);	/* no permission this time */
+	}
+	return (0);
+}				/* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about.              */
+/* Otherwise file is handled as log output. In this case the interface usage   */
+/* count is decremented and all buffers are noticed of closing. If this file   */
+/* was the last one to be closed, all buffers are freed.                       */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+	struct log_data *inf;
+	struct procdata *pd;
+	hysdn_card *card;
+	int flags, retval = 0;
+
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write debug level written */
+		retval = 0;	/* success */
+	} else {
+		/* read access -> log/debug read, mark one further file as closed */
+
+		pd = NULL;
+		save_flags(flags);
+		cli();
+		inf = *((struct log_data **) filep->private_data);	/* get first log entry */
+		if (inf)
+			pd = (struct procdata *) inf->proc_ctrl;	/* still entries there */
+		else {
+			/* no info available -> search card */
+			card = card_root;
+			while (card) {
+				pd = card->proclog;
+				if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+					break;
+				card = card->next;	/* search next entry */
+			}
+			if (card)
+				pd = card->proclog;	/* pointer to procfs log */
+		}
+		if (pd)
+			pd->if_used--;	/* decrement interface usage count by one */
+
+		while (inf) {
+			inf->usage_cnt--;	/* decrement usage count for buffers */
+			inf = inf->next;
+		}
+		restore_flags(flags);
+
+		if (pd)
+			if (pd->if_used <= 0)	/* delete buffers if last file closed */
+				while (pd->log_head) {
+					inf = pd->log_head;
+					pd->log_head = pd->log_head->next;
+					kfree(inf);
+				}
+	}			/* read access */
+
+	MOD_DEC_USE_COUNT;
+	return (retval);
+}				/* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static unsigned int
+hysdn_log_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	word ino;
+	hysdn_card *card;
+	struct procdata *pd;
+
+	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+		return (mask);	/* no polling for write supported */
+
+	/* we need to search the card */
+	ino = file->f_dentry->d_inode->i_ino & 0xFFFF;	/* low-ino */
+	card = card_root;
+	while (card) {
+		pd = card->proclog;
+		if (pd->log->low_ino == ino)
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card)
+		return (mask);	/* card not found */
+
+	poll_wait(file, &(pd->rd_queue), wait);
+
+	if (*((struct log_data **) file->private_data))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}				/* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static struct file_operations log_fops =
+{
+	hysdn_dummy_lseek,
+	hysdn_log_read,
+	hysdn_log_write,
+	NULL,			/* readdir */
+	hysdn_log_poll,		/* poll */
+	NULL,
+	NULL,			/* mmap */
+	hysdn_log_open,
+	NULL,			/* flush */
+	hysdn_log_close,
+	NULL			/* fsync */
+};
+
+struct inode_operations log_inode_operations;
+
+/***********************************************************************************/
+/* hysdn_proclog_init is called when the module is loaded after creating the cards */
+/* conf files.                                                                     */
+/***********************************************************************************/
+int
+hysdn_proclog_init(hysdn_card * card)
+{
+	struct procdata *pd;
+
+	/* create a cardlog proc entry */
+
+	if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
+		memset(pd, 0, sizeof(struct procdata));
+		memset(&log_inode_operations, 0, sizeof(struct inode_operations));
+		log_inode_operations.default_file_ops = &log_fops;
+
+		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->ops = &log_inode_operations;	/* set new operations table */
+
+		init_waitqueue_head(&(pd->rd_queue));
+
+		card->proclog = (void *) pd;	/* remember procfs structure */
+	}
+	return (0);
+}				/* hysdn_proclog_init */
+
+/************************************************************************************/
+/* hysdn_proclog_release is called when the module is unloaded and before the cards */
+/* conf file is released                                                            */
+/* The module counter is assumed to be 0 !                                          */
+/************************************************************************************/
+void
+hysdn_proclog_release(hysdn_card * card)
+{
+	struct procdata *pd;
+
+	if ((pd = (struct procdata *) card->proclog) != NULL) {
+		if (pd->log)
+			remove_proc_entry(pd->log_name, hysdn_proc_entry);
+		kfree(pd);	/* release memory */
+		card->proclog = NULL;
+	}
+}				/* hysdn_proclog_release */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/hysdn_sched.c linux/drivers/isdn/hysdn/hysdn_sched.c
--- v2.3.45/linux/drivers/isdn/hysdn/hysdn_sched.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/hysdn_sched.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,202 @@
+/* $Id: hysdn_sched.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, scheduler routines for handling exchange card <-> pc.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_sched.c,v $
+ * Revision 1.1  2000/02/10 19:45:18  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include "hysdn_defs.h"
+
+/*****************************************************************************/
+/* hysdn_sched_rx is called from the cards handler to announce new data is   */
+/* available from the card. The routine has to handle the data and return    */
+/* with a nonzero code if the data could be worked (or even thrown away), if */
+/* no room to buffer the data is available a zero return tells the card      */
+/* to keep the data until later.                                             */
+/*****************************************************************************/
+int
+hysdn_sched_rx(hysdn_card * card, uchar * buf, word len, word chan)
+{
+
+	switch (chan) {
+		case CHAN_NDIS_DATA:
+			hysdn_rx_netpkt(card, buf, len);	/* give packet to network handler */
+			break;
+
+		case CHAN_ERRLOG:
+			hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
+			if (card->err_log_state == ERRLOG_STATE_ON)
+				card->err_log_state = ERRLOG_STATE_START;	/* start new fetch */
+			break;
+
+		default:
+			printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
+			break;
+
+	}			/* switch rx channel */
+
+	return (1);		/* always handled */
+}				/* hysdn_sched_rx */
+
+/*****************************************************************************/
+/* hysdn_sched_tx is called from the cards handler to announce that there is */
+/* room in the tx-buffer to the card and data may be sent if needed.         */
+/* If the routine wants to send data it must fill buf, len and chan with the */
+/* appropriate data and return a nonzero value. With a zero return no new    */
+/* data to send is assumed. maxlen specifies the buffer size available for   */
+/* sending.                                                                  */
+/*****************************************************************************/
+int
+hysdn_sched_tx(hysdn_card * card, uchar * buf, word volatile *len, word volatile *chan, word maxlen)
+{
+	struct sk_buff *skb;
+
+	if (card->net_tx_busy) {
+		card->net_tx_busy = 0;	/* reset flag */
+		hysdn_tx_netack(card);	/* acknowledge packet send */
+	}			/* a network packet has completely been transferred */
+	/* first of all async requests are handled */
+	if (card->async_busy) {
+		if (card->async_len <= maxlen) {
+			memcpy(buf, card->async_data, card->async_len);
+			*len = card->async_len;
+			*chan = card->async_channel;
+			card->async_busy = 0;	/* reset request */
+			return (1);
+		}
+		card->async_busy = 0;	/* in case of length error */
+	}			/* async request */
+	if ((card->err_log_state == ERRLOG_STATE_START) &&
+	    (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
+		strcpy(buf, ERRLOG_CMD_REQ);	/* copy the command */
+		*len = ERRLOG_CMD_REQ_SIZE;	/* buffer length */
+		*chan = CHAN_ERRLOG;	/* and channel */
+		card->err_log_state = ERRLOG_STATE_ON;	/* new state is on */
+		return (1);	/* tell that data should be send */
+	}			/* error log start and able to send */
+	if ((card->err_log_state == ERRLOG_STATE_STOP) &&
+	    (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
+		strcpy(buf, ERRLOG_CMD_STOP);	/* copy the command */
+		*len = ERRLOG_CMD_STOP_SIZE;	/* buffer length */
+		*chan = CHAN_ERRLOG;	/* and channel */
+		card->err_log_state = ERRLOG_STATE_OFF;		/* new state is off */
+		return (1);	/* tell that data should be send */
+	}			/* error log start and able to send */
+	/* now handle network interface packets */
+	if ((skb = hysdn_tx_netget(card)) != NULL) {
+		if (skb->len <= maxlen) {
+			memcpy(buf, skb->data, skb->len);	/* copy the packet to the buffer */
+			*len = skb->len;
+			*chan = CHAN_NDIS_DATA;
+			card->net_tx_busy = 1;	/* we are busy sending network data */
+			return (1);	/* go and send the data */
+		} else
+			hysdn_tx_netack(card);	/* aknowledge packet -> throw away */
+	}			/* send a network packet if available */
+	return (0);		/* nothing to send */
+}				/* hysdn_sched_tx */
+
+
+/*****************************************************************************/
+/* send one config line to the card and return 0 if successfull, otherwise a */
+/* negative error code.                                                      */
+/* The function works with timeouts perhaps not giving the greatest speed    */
+/* sending the line, but this should be meaningless beacuse only some lines  */
+/* are to be sent and this happens very seldom.                              */
+/*****************************************************************************/
+int
+hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan)
+{
+	int cnt = 50;		/* timeout intervalls */
+	ulong flags;
+
+	if (card->debug_flags & LOG_SCHED_ASYN)
+		hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
+
+	save_flags(flags);
+	cli();
+	while (card->async_busy) {
+		sti();
+
+		if (card->debug_flags & LOG_SCHED_ASYN)
+			hysdn_addlog(card, "async tx-cfg delayed");
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
+		if (!--cnt) {
+			restore_flags(flags);
+			return (-ERR_ASYNC_TIME);	/* timed out */
+		}
+		cli();
+	}			/* wait for buffer to become free */
+
+	strcpy(card->async_data, line);
+	card->async_len = strlen(line) + 1;
+	card->async_channel = chan;
+	card->async_busy = 1;	/* request transfer */
+
+	/* now queue the task */
+	queue_task(&card->irq_queue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	sti();
+
+	if (card->debug_flags & LOG_SCHED_ASYN)
+		hysdn_addlog(card, "async tx-cfg data queued");
+
+	cnt++;			/* short delay */
+	cli();
+
+	while (card->async_busy) {
+		sti();
+
+		if (card->debug_flags & LOG_SCHED_ASYN)
+			hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
+		if (!--cnt) {
+			restore_flags(flags);
+			return (-ERR_ASYNC_TIME);	/* timed out */
+		}
+		cli();
+	}			/* wait for buffer to become free again */
+
+	restore_flags(flags);
+
+	if (card->debug_flags & LOG_SCHED_ASYN)
+		hysdn_addlog(card, "async tx-cfg data send");
+
+	return (0);		/* line send correctly */
+}				/* hysdn_tx_cfgline */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/hysdn/ince1pc.h linux/drivers/isdn/hysdn/ince1pc.h
--- v2.3.45/linux/drivers/isdn/hysdn/ince1pc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hysdn/ince1pc.h	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,132 @@
+#ifndef __INCE1PC_H__
+#define __INCE1PC_H__
+
+/****************************************************************************
+
+    FILE:       ince1pc.h
+
+    AUTHOR:     M.Steinkopf
+
+    PURPOSE:    common definitions for both sides of the bus:
+		- conventions both spoolers must know
+		- channel numbers agreed upon
+
+*****************************************************************************/ 
+
+/*  basic scalar definitions have same meanning,
+ *  but their declaration location depends on environment
+ */ 
+
+/*--------------------------------------channel numbers---------------------*/ 
+#define CHAN_SYSTEM     0x0001      /* system channel (spooler to spooler) */
+#define CHAN_ERRLOG     0x0005      /* error logger */
+#define CHAN_CAPI       0x0064      /* CAPI interface */
+#define CHAN_NDIS_DATA  0x1001      /* NDIS data transfer */
+
+/*--------------------------------------POF ready msg-----------------------*/ 
+	    /* NOTE: after booting POF sends system ready message to PC: */ 
+#define RDY_MAGIC       0x52535953UL    /* 'SYSR' reversed */
+#define RDY_MAGIC_SIZE  4               /* size in bytes */
+
+#define MAX_N_TOK_BYTES 255
+
+#define MIN_RDY_MSG_SIZE    RDY_MAGIC_SIZE
+#define MAX_RDY_MSG_SIZE    (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+
+#define SYSR_TOK_END            0
+#define SYSR_TOK_B_CHAN         1   /* nr. of B-Channels;   DataLen=1; def: 2 */
+#define SYSR_TOK_FAX_CHAN       2   /* nr. of FAX Channels; DataLen=1; def: 0 */
+#define SYSR_TOK_MAC_ADDR       3   /* MAC-Address; DataLen=6; def: auto */
+#define SYSR_TOK_ESC            255 /* undefined data size yet */
+			    /* default values, if not corrected by token: */ 
+#define SYSR_TOK_B_CHAN_DEF     2   /* assume 2 B-Channels */
+#define SYSR_TOK_FAX_CHAN_DEF   1   /* assume 1 FAX Channel */
+
+/*  syntax of new SYSR token stream:
+ *  channel: CHAN_SYSTEM
+ *  msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
+ *           RDY_MAGIC_SIZE   <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+ *  msg    : 0 1 2 3 {4 5 6 ..}
+ *           S Y S R  MAX_N_TOK_BYTES bytes of TokenStream
+ *
+ *  TokenStream     :=   empty
+ *                     | {NonEndTokenChunk} EndToken RotlCRC
+ *  NonEndTokenChunk:= NonEndTokenId DataLen [Data]
+ *  NonEndTokenId   := 0x01 .. 0xFE                 1 BYTE
+ *  DataLen         := 0x00 .. 0xFF                 1 BYTE
+ *  Data            := DataLen bytes
+ *  EndToken        := 0x00
+ *  RotlCRC         := special 1 byte CRC over all NonEndTokenChunk bytes
+ *                     s. RotlCRC algorithm
+ *
+ *  RotlCRC algorithm:
+ *      ucSum= 0                        1 uchar
+ *      for all NonEndTokenChunk bytes:
+ *          ROTL(ucSum,1)               rotate left by 1
+ *          ucSum += Char;              add current byte with swap around
+ *      RotlCRC= ~ucSum;                invert all bits for result
+ *
+ *  note:
+ *  - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
+ */ 
+
+/*--------------------------------------error logger------------------------*/ 
+					    /* note: pof needs final 0 ! */ 
+#define ERRLOG_CMD_REQ          "ERRLOG ON"
+#define ERRLOG_CMD_REQ_SIZE     10              /* with final 0 byte ! */
+#define ERRLOG_CMD_STOP         "ERRLOG OFF"
+#define ERRLOG_CMD_STOP_SIZE    11              /* with final 0 byte ! */
+
+#define ERRLOG_ENTRY_SIZE       64      /* sizeof(tErrLogEntry) */
+					/* remaining text size = 55 */ 
+#define ERRLOG_TEXT_SIZE    (ERRLOG_ENTRY_SIZE-2*4-1)
+
+typedef struct ErrLogEntry_tag {
+	
+/*00 */ ulong ulErrType;
+	
+/*04 */ ulong ulErrSubtype;
+	
+/*08 */ uchar ucTextSize;
+	
+	/*09 */ uchar ucText[ERRLOG_TEXT_SIZE];
+	/* ASCIIZ of len ucTextSize-1 */
+	
+/*40 */ 
+} tErrLogEntry;
+
+
+#if defined(__TURBOC__)
+#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
+#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
+#endif				/* 
 */
+#endif				/* 
 */
+
+/*--------------------------------------DPRAM boot spooler------------------*/ 
+				/*  this is the struture used between pc and
+				 *  hyperstone to exchange boot data
+				 */ 
+#define DPRAM_SPOOLER_DATA_SIZE 0x20
+typedef struct DpramBootSpooler_tag {
+	
+/*00 */ uchar Len;
+	
+/*01 */ volatile uchar RdPtr;
+	
+/*02 */ uchar WrPtr;
+	
+/*03 */ uchar Data[DPRAM_SPOOLER_DATA_SIZE];
+	
+/*23 */ 
+} tDpramBootSpooler;
+
+
+#define DPRAM_SPOOLER_MIN_SIZE  5       /* Len+RdPtr+Wrptr+2*data */
+#define DPRAM_SPOOLER_DEF_SIZE  0x23    /* current default size   */
+
+/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/ 
+				    /* at DPRAM offset 0x1C00: */ 
+#define SIZE_RSV_SOFT_UART  0x1B0   /* 432 bytes reserved for SoftUart */
+
+
+#endif	/* __INCE1PC_H__ */
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.3.45/linux/drivers/isdn/isdn_common.c	Thu Feb 10 17:11:09 2000
+++ linux/drivers/isdn/isdn_common.c	Wed Feb 16 15:42:05 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.93 1999/11/04 13:11:36 keil Exp $
+/* $Id: isdn_common.c,v 1.97 2000/01/23 18:45:37 keil Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -21,6 +21,32 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.97  2000/01/23 18:45:37  keil
+ * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
+ *
+ * Revision 1.96  2000/01/20 19:55:33  keil
+ * Add FAX Class 1 support
+ *
+ * Revision 1.95  2000/01/09 20:43:13  detabc
+ * exand logical bind-group's for both call's (in and out).
+ * add first part of kernel-config-help for abc-extension.
+ *
+ * Revision 1.94  1999/11/20 22:14:13  detabc
+ * added channel dial-skip in case of external use
+ * (isdn phone or another isdn device) on the same NTBA.
+ * usefull with two or more card's connected the different NTBA's.
+ * global switchable in kernel-config and also per netinterface.
+ *
+ * add auto disable of netinterface's in case of:
+ * 	to many connection's in short time.
+ * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
+ * 	or remote side.
+ * 	wrong password's or something else to a ISP (syncppp).
+ *
+ * possible encapsulations for this future are:
+ * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
+ * and ISDN_NET_ENCAP_CISCOHDLCK.
+ *
  * Revision 1.93  1999/11/04 13:11:36  keil
  * Reinit of v110 structs
  *
@@ -410,13 +436,14 @@
 #endif CONFIG_ISDN_DIVERSION
 #include "isdn_v110.h"
 #include "isdn_cards.h"
+#include <linux/devfs_fs_kernel.h>
 
 /* Debugflags */
 #undef ISDN_DEBUG_STATCALLB
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.93 $";
+static char *isdn_revision = "$Revision: 1.97 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -438,6 +465,9 @@
 
 
 static int isdn_writebuf_stub(int, int, const u_char *, int, int);
+static void set_global_features(void);
+static void isdn_register_devfs(int);
+static void isdn_unregister_devfs(int);
 
 void
 isdn_MOD_INC_USE_COUNT(void)
@@ -720,29 +750,33 @@
 int
 isdn_command(isdn_ctrl *cmd)
 {
+	if (cmd->driver == -1) {
+		printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command);
+		return(1);
+	}
 	if (cmd->command == ISDN_CMD_SETL2) {
-			int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
-			unsigned long l2prot = (cmd->arg >> 8) & 255;
-			unsigned long features = (dev->drv[cmd->driver]->interface->features
-						 >> ISDN_FEATURE_L2_SHIFT) &
-				ISDN_FEATURE_L2_MASK;
-			unsigned long l2_feature = (1 << l2prot);
-
-			switch (l2prot) {
-				case ISDN_PROTO_L2_V11096:
-				case ISDN_PROTO_L2_V11019:
-				case ISDN_PROTO_L2_V11038:
-						/* If V.110 requested, but not supported by
-						 * HL-driver, set emulator-flag and change
-						 * Layer-2 to transparent
-						 */
-					if (!(features & l2_feature)) {
-						dev->v110emu[idx] = l2prot;
-						cmd->arg = (cmd->arg & 255) |
-								   (ISDN_PROTO_L2_TRANS << 8);
-					} else
-						dev->v110emu[idx] = 0;
-			}
+		int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
+		unsigned long l2prot = (cmd->arg >> 8) & 255;
+		unsigned long features = (dev->drv[cmd->driver]->interface->features
+						>> ISDN_FEATURE_L2_SHIFT) &
+						ISDN_FEATURE_L2_MASK;
+		unsigned long l2_feature = (1 << l2prot);
+
+		switch (l2prot) {
+			case ISDN_PROTO_L2_V11096:
+			case ISDN_PROTO_L2_V11019:
+			case ISDN_PROTO_L2_V11038:
+			/* If V.110 requested, but not supported by
+			 * HL-driver, set emulator-flag and change
+			 * Layer-2 to transparent
+			 */
+				if (!(features & l2_feature)) {
+					dev->v110emu[idx] = l2prot;
+					cmd->arg = (cmd->arg & 255) |
+						(ISDN_PROTO_L2_TRANS << 8);
+				} else
+					dev->v110emu[idx] = 0;
+		}
 	}
 	return dev->drv[cmd->driver]->interface->command(cmd);
 }
@@ -822,6 +856,7 @@
 			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 				if (dev->drvmap[i] == di)
 					isdn_all_eaz(di, dev->chanmap[i]);
+			set_global_features();
 			break;
 		case ISDN_STAT_STOP:
 			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
@@ -1065,6 +1100,7 @@
 					dev->drvmap[i] = -1;
 					dev->chanmap[i] = -1;
 					dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+					isdn_unregister_devfs(i);
 				}
 			dev->drivers--;
 			dev->channels -= dev->drv[di]->channels;
@@ -1078,6 +1114,7 @@
 			dev->drv[di] = NULL;
 			dev->drvid[di][0] = '\0';
 			isdn_info_update();
+			set_global_features();
 			restore_flags(flags);
 			return 0;
 		case ISDN_STAT_L1ERR:
@@ -1563,6 +1600,9 @@
  * are serialized by means of a semaphore.
  */
 		switch (cmd) {
+			case IIOCNETDWRSET:
+				printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
+				return(-EINVAL);
 			case IIOCNETLCR:
 				printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
 				return -ENODEV;
@@ -1854,7 +1894,7 @@
 						for (i = 0; i < 10; i++) {
 							sprintf(bname, "%s%s",
 								strlen(dev->drv[drvidx]->msn2eaz[i]) ?
-								dev->drv[drvidx]->msn2eaz[i] : "-",
+								dev->drv[drvidx]->msn2eaz[i] : "_",
 								(i < 9) ? "," : "\0");
 							if (copy_to_user(p, bname, strlen(bname) + 1))
 								return -EFAULT;
@@ -2024,13 +2064,17 @@
 
 static struct file_operations isdn_fops =
 {
-	llseek:		isdn_lseek,
-	read:		isdn_read,
-	write:		isdn_write,
-	poll:		isdn_poll,
-	ioctl:		isdn_ioctl,
-	open:		isdn_open,
-	release:	isdn_close,
+	isdn_lseek,
+	isdn_read,
+	isdn_write,
+	NULL,                   /* isdn_readdir */
+	isdn_poll,              /* isdn_poll */
+	isdn_ioctl,             /* isdn_ioctl */
+	NULL,                   /* isdn_mmap */
+	isdn_open,
+	NULL,			/* flush */
+	isdn_close,
+	NULL                    /* fsync */
 };
 
 char *
@@ -2056,7 +2100,7 @@
 
 int
 isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
-		      ,int pre_chan)
+		      ,int pre_chan, char *msn)
 {
 	int i;
 	ulong flags;
@@ -2079,6 +2123,8 @@
 			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
 			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
 				continue;
+			if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))
+				continue;
 			if (dev->usage[i] & ISDN_USAGE_DISABLED)
 			        continue; /* usage not allowed */
 			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
@@ -2349,6 +2395,7 @@
 			if (dev->chanmap[k] < 0) {
 				dev->chanmap[k] = j;
 				dev->drvmap[k] = drvidx;
+				isdn_register_devfs(k);
 				break;
 			}
 	restore_flags(flags);
@@ -2360,6 +2407,19 @@
  * Low-level-driver registration
  */
 
+static void
+set_global_features(void)
+{
+	int drvidx;
+
+	dev->global_features = 0;
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) {
+		if (!dev->drv[drvidx])
+			continue;
+		if (dev->drv[drvidx]->interface)
+			dev->global_features |= dev->drv[drvidx]->interface->features;
+	}
+}
 
 #ifdef CONFIG_ISDN_DIVERSION
 extern isdn_divert_if *divert_if;
@@ -2473,6 +2533,7 @@
 	strcpy(dev->drvid[drvidx], i->id);
 	isdn_info_update();
 	dev->drivers++;
+	set_global_features();
 	restore_flags(flags);
 	return 1;
 }
@@ -2504,6 +2565,96 @@
 	return rev;
 }
 
+#ifdef CONFIG_DEVFS_FS
+
+static devfs_handle_t devfs_handle = NULL;
+
+static void isdn_register_devfs(int k)
+{
+	char buf[11];
+
+	sprintf (buf, "isdn%d", k);
+	dev->devfs_handle_isdnX[k] =
+	    devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, 0, 0,
+			    &isdn_fops, NULL);
+	sprintf (buf, "isdnctrl%d", k);
+	dev->devfs_handle_isdnctrlX[k] =
+	    devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR,
+			    0, 0, &isdn_fops, NULL);
+}
+
+static void isdn_unregister_devfs(int k)
+{
+	devfs_unregister (dev->devfs_handle_isdnX[k]);
+	devfs_unregister (dev->devfs_handle_isdnctrlX[k]);
+}
+
+static void isdn_init_devfs(void)
+{
+#  ifdef CONFIG_ISDN_PPP
+	int i;
+#  endif
+
+	devfs_handle = devfs_mk_dir (NULL, "isdn", 4, NULL);
+#  ifdef CONFIG_ISDN_PPP
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		char buf[8];
+
+		sprintf (buf, "ippp%d", i);
+		dev->devfs_handle_ipppX[i] =
+		    devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT,
+				    ISDN_MAJOR, ISDN_MINOR_PPP + i,
+				    0600 | S_IFCHR, 0, 0, &isdn_fops, NULL);
+	}
+#  endif
+
+	dev->devfs_handle_isdninfo =
+	    devfs_register (devfs_handle, "isdninfo", 0, DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR,
+			    0, 0, &isdn_fops, NULL);
+	dev->devfs_handle_isdnctrl =
+	    devfs_register (devfs_handle, "isdnctrl", 0, DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 0, 0, 
+			    &isdn_fops, NULL);
+}
+
+static void isdn_cleanup_devfs(void)
+{
+#  ifdef CONFIG_ISDN_PPP
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) 
+		devfs_unregister (dev->devfs_handle_ipppX[i]);
+#  endif
+	devfs_unregister (dev->devfs_handle_isdninfo);
+	devfs_unregister (dev->devfs_handle_isdnctrl);
+	devfs_unregister (devfs_handle);
+}
+
+#else   /*  CONFIG_DEVFS_FS  */
+static void isdn_register_devfs(int dummy)
+{
+	return;
+}
+
+static void isdn_unregister_devfs(int dummy)
+{
+	return;
+}
+
+static void isdn_init_devfs(void)
+{
+    return;
+}
+
+static void isdn_cleanup_devfs(void)
+{
+    return;
+}
+
+#endif  /*  CONFIG_DEVFS_FS  */
+
 /*
  * Allocate and initialize all data, register modem-devices
  */
@@ -2530,11 +2681,12 @@
 		init_waitqueue_head(&dev->mdm.info[i].open_wait);
 		init_waitqueue_head(&dev->mdm.info[i].close_wait);
 	}
-	if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+	if (devfs_register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
 		printk(KERN_WARNING "isdn: Could not register control devices\n");
 		vfree(dev);
 		return -EIO;
 	}
+	isdn_init_devfs();
 	if ((i = isdn_tty_modem_init()) < 0) {
 		printk(KERN_WARNING "isdn: Could not register tty devices\n");
 		if (i == -3)
@@ -2542,7 +2694,8 @@
 		if (i <= -2)
 			tty_unregister_driver(&dev->mdm.tty_modem);
 		vfree(dev);
-		unregister_chrdev(ISDN_MAJOR, "isdn");
+		isdn_cleanup_devfs();
+		devfs_unregister_chrdev(ISDN_MAJOR, "isdn");
 		return -EIO;
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -2552,7 +2705,8 @@
 		tty_unregister_driver(&dev->mdm.cua_modem);
 		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 			kfree(dev->mdm.info[i].xmit_buf - 4);
-		unregister_chrdev(ISDN_MAJOR, "isdn");
+		isdn_cleanup_devfs();
+		devfs_unregister_chrdev(ISDN_MAJOR, "isdn");
 		vfree(dev);
 		return -EIO;
 	}
@@ -2618,9 +2772,10 @@
 		kfree(dev->mdm.info[i].fax);
 #endif
 	}
-	if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
+	if (devfs_unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
 		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
 	} else {
+		isdn_cleanup_devfs();
 		del_timer(&dev->timer);
 		vfree(dev);
 		printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v2.3.45/linux/drivers/isdn/isdn_common.h	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/isdn_common.h	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.h,v 1.17 1999/10/27 21:21:17 detabc Exp $
+/* $Id: isdn_common.h,v 1.18 2000/01/23 18:45:37 keil Exp $
 
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
@@ -21,6 +21,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.h,v $
+ * Revision 1.18  2000/01/23 18:45:37  keil
+ * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
+ *
  * Revision 1.17  1999/10/27 21:21:17  detabc
  * Added support for building logically-bind-group's per interface.
  * usefull for outgoing call's with more then one isdn-card.
@@ -128,7 +131,7 @@
 extern void isdn_unexclusive_channel(int di, int ch);
 extern int isdn_getnum(char **);
 extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
-extern int isdn_get_free_channel(int, int, int, int, int);
+extern int isdn_get_free_channel(int, int, int, int, int, char *);
 extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
 extern int register_isdn(isdn_if * i);
 extern int isdn_wildmat(char *, char *);
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.3.45/linux/drivers/isdn/isdn_net.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/isdn_net.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.95 1999/10/27 21:21:17 detabc Exp $
+/* $Id: isdn_net.c,v 1.107 2000/02/13 09:52:05 kai Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -21,6 +21,66 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.107  2000/02/13 09:52:05  kai
+ * increased TX_TIMEOUT to 20sec
+ *
+ * Revision 1.106  2000/02/12 19:26:55  kai
+ * adopted to latest 2.3 softnet changes.
+ *
+ * tested with PPP and MPPP, it works here.
+ * can somebody check raw-ip?
+ *
+ * also changed std2kern, stddiff for bash-1 compatibility,
+ * hope this doesn't break anything.
+ *
+ * Revision 1.105  2000/02/12 11:43:26  he
+ * SOFTNET related changes, first try. Compatible with linux 2.2.x, but
+ * not tested for kernels with softnet (>= 2.3.43) yet.
+ *
+ * Revision 1.104  2000/02/06 21:49:59  detabc
+ * add rewriting of socket's and frame's saddr for udp-ipv4 dynip-connections.
+ * Include checksum-recompute of ip- and udp-header's.
+ *
+ * Revision 1.103  2000/01/23 18:45:37  keil
+ * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
+ *
+ * Revision 1.102  2000/01/09 20:43:14  detabc
+ * exand logical bind-group's for both call's (in and out).
+ * add first part of kernel-config-help for abc-extension.
+ *
+ * Revision 1.101  1999/12/05 16:06:08  detabc
+ * add resethandling for rawip-compression.
+ * at now all B2-Protocols are usable with rawip-compression
+ *
+ * Revision 1.100  1999/12/04 15:05:25  detabc
+ * bugfix abc-rawip-bsdcompress with channel-bundeling
+ *
+ * Revision 1.99  1999/11/30 11:29:06  detabc
+ * add a on the fly frame-counter and limit
+ *
+ * Revision 1.98  1999/11/28 14:49:07  detabc
+ * In case of rawip-compress adjust dev[x]->ibytes/obytes to reflect the
+ * uncompressed size.
+ *
+ * Revision 1.97  1999/11/26 15:54:59  detabc
+ * added compression (isdn_bsdcompress) for rawip interfaces with x75i B2-protocol.
+ *
+ * Revision 1.96  1999/11/20 22:14:13  detabc
+ * added channel dial-skip in case of external use
+ * (isdn phone or another isdn device) on the same NTBA.
+ * usefull with two or more card's connected the different NTBA's.
+ * global switchable in kernel-config and also per netinterface.
+ *
+ * add auto disable of netinterface's in case of:
+ * 	to many connection's in short time.
+ * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
+ * 	or remote side.
+ * 	wrong password's or something else to a ISP (syncppp).
+ *
+ * possible encapsulations for this future are:
+ * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
+ * and ISDN_NET_ENCAP_CISCOHDLCK.
+ *
  * Revision 1.95  1999/10/27 21:21:17  detabc
  * Added support for building logically-bind-group's per interface.
  * usefull for outgoing call's with more then one isdn-card.
@@ -393,10 +453,6 @@
 #endif
 
 
-#ifndef ISDN_NEW_TBUSY
-#define ISDN_NEW_TBUSY
-#endif
-#ifdef ISDN_NEW_TBUSY
 /*
  * Outline of new tbusy handling: 
  *
@@ -413,29 +469,60 @@
  */
 
 /*
- * Tell upper layers that the network device is ready to xmit more frames.
- */
-static void __inline__ isdn_net_dev_xon(struct net_device * dev)
-{
-	dev->tbusy = 0;
-	mark_bh(NET_BH);
-}
-
-static void __inline__ isdn_net_lp_xon(isdn_net_local * lp)
-{
-	lp->netdev->dev.tbusy = 0;
-	if(lp->master) lp->master->tbusy = 0;
-	mark_bh(NET_BH);
+ * About SOFTNET:
+ * Most of the changes were pretty obvious and basically done by HE already.
+ *
+ * One problem of the isdn net device code is that is uses struct net_device
+ * for masters and slaves. However, only master interface are registered to 
+ * 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 test_bit(LINK_STATE_START, &dev->state) 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
+ */
+
+/* 
+ * 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)
+{
+	isdn_net_local *lp = n->local;
+	struct net_device *dev;
+	
+	if (lp->master) 
+		dev = lp->master;
+	else
+		dev = &n->dev;
+	return test_bit(LINK_STATE_START, &dev->state);
 }
 
 /*
- * Ask upper layers to temporarily cease passing us more xmit frames.
+ * wake up the network -> net_device queue.
+ * For slaves, wake the corresponding master interface.
  */
-static void __inline__ isdn_net_dev_xoff(struct net_device * dev)
+static void __inline__ isdn_net_lp_xon(isdn_net_local * lp)
 {
-	dev->tbusy = 1;
+	if (lp->master) 
+		netif_wake_queue(lp->master);
+	else
+		netif_wake_queue(&lp->netdev->dev);
 }
-#endif
+
+
+#define ISDN_NET_TX_TIMEOUT (20*HZ) 
 
 /* Prototypes */
 
@@ -443,7 +530,7 @@
 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.95 $";
+char *isdn_net_revision = "$Revision: 1.107 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -482,14 +569,9 @@
 #endif
 	ulong flags;
 
+	/* not sure if the cli() is needed at all --KG */
 	save_flags(flags);
 	cli();                  /* Avoid glitch on writes to CMD regs */
-	dev->interrupt = 0;
-#ifdef ISDN_NEW_TBUSY
-	isdn_net_dev_xon(dev);
-#else
-	dev->tbusy = 0;
-#endif
 #ifdef CONFIG_ISDN_X25
 	if( cprot && cprot -> pops && dops )
 		cprot -> pops -> restart ( cprot, dev, dops );
@@ -505,8 +587,12 @@
 	struct net_device *p;
 	struct in_device *in_dev;
 
+	/* moved here from isdn_net_reset, because only the master has an
+	   interface associated which is supposed to be started. BTW:
+	   we need to call netif_start_queue, not netif_wake_queue here */
+	netif_start_queue(dev);
+
 	isdn_net_reset(dev);
-	dev->start = 1;
 	/* Fill in the MAC-level header (not needed, but for compatibility... */
 	for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
 		dev->dev_addr[i] = 0xfc;
@@ -524,7 +610,6 @@
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
 		while (p) {
 			isdn_net_reset(p);
-			p->start = 1;
 			p = (((isdn_net_local *) p->priv)->slave);
 		}
 	}
@@ -703,19 +788,11 @@
 							mdev = &lp->netdev->dev;
 						if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) {
 							lp->sav_skb = NULL;
-#ifndef ISDN_NEW_TBUSY
-							mark_bh(NET_BH);
-#endif
 						} else {
 							return 1;
 						}
 					}
-#ifdef ISDN_NEW_TBUSY
 					isdn_net_lp_xon(lp);
-#else
-					if (test_and_clear_bit(0, (void *) &(p->dev.tbusy)))
-						mark_bh(NET_BH);
-#endif
 				}
 				return 1;
 			case ISDN_STAT_DCONN:
@@ -814,18 +891,7 @@
 							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
 								lp->first_skb = NULL;
 						}
-#ifdef ISDN_NEW_TBUSY
 						if(! lp->first_skb) isdn_net_lp_xon(lp);
-#else
-						else {
-							/*
-							 * dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb).
-							 * With an empty lp->first_skb, we need to do this ourselves
-							 */
-							lp->netdev->dev.tbusy = 0;
-							mark_bh(NET_BH);
-						}
-#endif /* ISDN_NEW_TBUSY */
 						return 1;
 				}
 				break;
@@ -1259,14 +1325,16 @@
 					strcpy(addinfo, " IDP");
 					break;
 			}
-			printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+			printk(KERN_INFO
+				"OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
 
 			       p[12], p[13], p[14], p[15],
 			       p[16], p[17], p[18], p[19],
 			       addinfo);
 			break;
 		case ETH_P_ARP:
-			printk(KERN_INFO "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+			printk(KERN_INFO
+				"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
 			       p[14], p[15], p[16], p[17],
 			       p[24], p[25], p[26], p[27]);
 			break;
@@ -1280,14 +1348,8 @@
  *
  * Return: 0 on success, !0 on failure.
  */
-#ifndef ISDN_NEW_TBUSY
-/*
- * Side-effects: ndev->tbusy is cleared on success.
- */
-#endif
-int
-isdn_net_send_skb(struct net_device *ndev, isdn_net_local * lp,
-		  struct sk_buff *skb)
+int isdn_net_send_skb
+		(struct net_device *ndev, isdn_net_local * lp,struct sk_buff *skb)
 {
 	int ret;
 	int len = skb->len;     /* save len */
@@ -1295,17 +1357,11 @@
 	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
 	if (ret == len) {
 		lp->transcount += len;
-#ifndef ISDN_NEW_TBUSY
-		clear_bit(0, (void *) &(ndev->tbusy));
-#endif
 		return 0;
 	}
 	if (ret < 0) {
 		dev_kfree_skb(skb);
 		lp->stats.tx_errors++;
-#ifndef ISDN_NEW_TBUSY
-		clear_bit(0, (void *) &(ndev->tbusy));
-#endif
 		return 0;
 	}
 	return 1;
@@ -1351,11 +1407,7 @@
 			if (lp->srobin == ndev)
 				ret = isdn_net_send_skb(ndev, lp, skb);
 			else
-#ifdef ISDN_NEW_TBUSY
 				ret = isdn_net_start_xmit(skb, lp->srobin);
-#else
-				ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin);
-#endif
 			lp->srobin = (slp->slave) ? slp->slave : ndev;
 			slp = (isdn_net_local *) (lp->srobin->priv);
 			if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0)))
@@ -1397,6 +1449,35 @@
 	}
 }
 
+
+void isdn_net_tx_timeout(struct net_device * ndev)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+	printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate);
+	if (!lp->dialstate){
+		lp->stats.tx_errors++;
+                /*
+		 * There is a certain probability that this currently
+		 * works at all because if we always wake up the interface,
+		 * then upper layer will try to send the next packet
+		 * immediately. And then, the old clean_up logic in the
+		 * driver will hopefully continue to work as it used to do.
+		 *
+		 * This is rather primitive right know, we better should
+		 * clean internal queues here, in particular for multilink and
+		 * ppp, and reset HL driver's channel, too.   --HE
+		 *
+		 * 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
+		 */
+	}
+	ndev->trans_start = jiffies;
+	netif_wake_queue(ndev);
+}
+
 /*
  * Try sending a packet.
  * If this interface isn't connected to a ISDN-Channel, find a free channel,
@@ -1409,19 +1490,6 @@
 #ifdef CONFIG_ISDN_X25
 	struct concap_proto * cprot = lp -> netdev -> cprot;
 #endif
-	if (ndev->tbusy) {
-		if (jiffies - ndev->trans_start < (2 * HZ))
-			return 1;
-		if (!lp->dialstate)
-			lp->stats.tx_errors++;
-		ndev->trans_start = jiffies;
-#ifdef ISDN_NEW_TBUSY
-		isdn_net_dev_xon(ndev);
-#endif
-	}
-#ifndef ISDN_NEW_TBUSY
-	ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */
-#endif
 #ifdef CONFIG_ISDN_X25
 /* At this point hard_start_xmit() passes control to the encapsulation
    protocol (if present).
@@ -1436,9 +1504,7 @@
 */
 	if( cprot ) {
 		int ret = cprot -> pops -> encap_and_xmit ( cprot , skb);
-#ifdef ISDN_NEW_TBUSY
-		if(ret) isdn_net_dev_xoff(ndev);
-#endif
+		if(ret) netif_stop_queue(ndev);
 		return ret;
 	} else
 #endif
@@ -1458,9 +1524,6 @@
 			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
 				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
 				dev_kfree_skb(skb);
-#ifndef ISDN_NEW_TBUSY
-				ndev->tbusy = 0;
-#endif
 				return 0;
 			}
 			if (lp->phone[1]) {
@@ -1476,15 +1539,11 @@
 					if(jiffies < lp->dialwait_timer) {
 						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
 						dev_kfree_skb(skb);
-#ifndef ISDN_NEW_TBUSY
-						ndev->tbusy = 0;
-#endif
 						restore_flags(flags);
 						return 0;
 					} else
 						lp->dialwait_timer = 0;
 				}
-
 				/* Grab a free ISDN-Channel */
 				if (((chi =
 				     isdn_get_free_channel(
@@ -1492,7 +1551,8 @@
 							lp->l2_proto,
 							lp->l3_proto,
 							lp->pre_device,
-						 	lp->pre_channel)
+						 	lp->pre_channel,
+							lp->msn)
 							) < 0) &&
 					((chi =
 				     isdn_get_free_channel(
@@ -1500,15 +1560,13 @@
 							lp->l2_proto,
 							lp->l3_proto,
 							lp->pre_device,
-						    lp->pre_channel^1)
+							lp->pre_channel^1,
+							lp->msn)
 							) < 0)) {
 					restore_flags(flags);
 					isdn_net_unreachable(ndev, skb,
 							   "No channel");
 					dev_kfree_skb(skb);
-#ifndef ISDN_NEW_TBUSY
-					ndev->tbusy = 0;
-#endif
 					return 0;
 				}
 				/* Log packet, which triggered dialing */
@@ -1528,9 +1586,7 @@
 					}
 					restore_flags(flags);
 					isdn_net_dial();	/* Initiate dialing */
-#ifdef ISDN_NEW_TBUSY
-					isdn_net_dev_xoff(ndev);
-#endif
+					netif_stop_queue(ndev);
 					return 1;	/* let upper layer requeue skb packet */
 				}
 #endif
@@ -1544,9 +1600,6 @@
 				}
 				lp->first_skb = skb;
 				/* Initiate dialing */
-#ifndef ISDN_NEW_TBUSY
-				ndev->tbusy = 0;
-#endif
 				restore_flags(flags);
 				isdn_net_dial();
 				return 0;
@@ -1554,9 +1607,6 @@
 				isdn_net_unreachable(ndev, skb,
 						     "No phone number");
 				dev_kfree_skb(skb);
-#ifndef ISDN_NEW_TBUSY
-				ndev->tbusy = 0;
-#endif
 				return 0;
 			}
 		} else {
@@ -1567,24 +1617,16 @@
 				int ret;
 				if (lp->first_skb) {
 					if (isdn_net_xmit(ndev, lp, lp->first_skb)){
-#ifdef ISDN_NEW_TBUSY
-						isdn_net_dev_xoff(ndev);
-#endif
+						netif_stop_queue(ndev);
 						return 1;
 }
 					lp->first_skb = NULL;
 				}
 				ret = (isdn_net_xmit(ndev, lp, skb));
-#ifdef ISDN_NEW_TBUSY
-				if(ret) isdn_net_dev_xoff(ndev);
-#endif
+				if(ret) netif_stop_queue(ndev);
 				return ret;
 			} else
-#ifdef ISDN_NEW_TBUSY
-				isdn_net_dev_xoff(ndev);
-#else
-				ndev->tbusy = 1;
-#endif
+				netif_stop_queue(ndev);
 		}
 	}
 	return 1;
@@ -1606,8 +1648,7 @@
 #ifdef CONFIG_ISDN_X25
 	if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
 #endif
-	dev->tbusy = 1;
-	dev->start = 0;
+	netif_stop_queue(dev);
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
 		/* If this interface has slaves, stop them also */
 		while (p) {
@@ -1618,8 +1659,6 @@
 				cprot -> pops -> close( cprot );
 #endif
 			isdn_net_hangup(p);
-			p->tbusy = 1;
-			p->start = 0;
 			p = (((isdn_net_local *) p->priv)->slave);
 		}
 	}
@@ -2360,7 +2399,7 @@
 				 * Is the interface up?
 				 * If not, reject the call actively.
 				 */
-				if (!p->dev.start) {
+				if (!isdn_net_started(p)) {
 					restore_flags(flags);
 					printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
 					       lp->name);
@@ -2389,7 +2428,7 @@
 						p = (isdn_net_dev *) p->next;
 						continue;
 					}
-				}
+				} 
 				if (lp->flags & ISDN_NET_CALLBACK) {
 					int chi;
 					/*
@@ -2411,9 +2450,10 @@
 							isdn_get_free_channel(
 								ISDN_USAGE_NET,
 								lp->l2_proto,
-							    lp->l3_proto,
+								lp->l3_proto,
 							  	lp->pre_device,
-						 		lp->pre_channel)
+						 		lp->pre_channel,
+						 		lp->msn)
 								) < 0) {
 
 							printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
@@ -2528,7 +2568,8 @@
 							lp->l2_proto,
 							lp->l3_proto,
 							lp->pre_device,
-						 	lp->pre_channel)
+						 	lp->pre_channel,
+							lp->msn)
 							) < 0) {
 				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
 				restore_flags(flags);
@@ -2626,11 +2667,13 @@
 			p = (((isdn_net_local *) p->priv)->slave);
 		}
 		((isdn_net_local *) q->priv)->slave = &(netdev->dev);
-		q->interrupt = 0;
-		q->tbusy = 0;
-		q->start = master->start;
 	} else {
 		/* Device shall be a master */
+		/*
+		 * Watchdog timer (currently) for master only.
+		 */
+		netdev->dev.tx_timeout = isdn_net_tx_timeout;
+		netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
 		if (register_netdev(&netdev->dev) != 0) {
 			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
 			kfree(netdev->local);
@@ -2701,7 +2744,7 @@
 		if (n->local->master)
 			return NULL;
 		/* Master must not be started yet */
-		if (n->dev.start)
+		if (isdn_net_started(n)) 
 			return NULL;
 		return (isdn_net_new(newname, &(n->dev)));
 	}
@@ -2744,9 +2787,8 @@
 #ifdef CONFIG_ISDN_X25
 			struct concap_proto * cprot = p -> cprot;
 #endif
-			if (p->dev.start) {
-				printk(KERN_WARNING
-				"%s: cannot change encap when if is up\n",
+			if (isdn_net_started(p)) {
+				printk(KERN_WARNING "%s: cannot change encap when if is up\n",
 				       lp->name);
 				return -EBUSY;
 			}
@@ -2837,10 +2879,9 @@
 
 			/* If binding is exclusive, try to grab the channel */
 			save_flags(flags);
-			if ((i = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
-						       lp->l3_proto,
-						       drvidx,
-						       chidx)) < 0) {
+			if ((i = isdn_get_free_channel(ISDN_USAGE_NET,
+				lp->l2_proto, lp->l3_proto, drvidx,
+				chidx, lp->msn)) < 0) {
 				/* Grab failed, because desired channel is in use */
 				lp->exclusive = -1;
 				restore_flags(flags);
@@ -3181,14 +3222,7 @@
 
 	save_flags(flags);
 	cli();
-	if (p->local->master) {
-		/* If it's a slave, it may be removed even if it is busy. However
-		 * it has to be hung up first.
-		 */
-		isdn_net_hangup(&p->dev);
-		p->dev.start = 0;
-	}
-	if (p->dev.start) {
+	if (isdn_net_started(p)) {
 		restore_flags(flags);
 		return -EBUSY;
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.3.45/linux/drivers/isdn/isdn_ppp.c	Thu Nov 11 20:11:38 1999
+++ linux/drivers/isdn/isdn_ppp.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.60 1999/11/04 20:29:55 he Exp $
+/* $Id: isdn_ppp.c,v 1.62 2000/02/12 19:26:55 kai Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,31 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.62  2000/02/12 19:26:55  kai
+ * adopted to latest 2.3 softnet changes.
+ *
+ * tested with PPP and MPPP, it works here.
+ * can somebody check raw-ip?
+ *
+ * also changed std2kern, stddiff for bash-1 compatibility,
+ * hope this doesn't break anything.
+ *
+ * Revision 1.61  1999/11/20 22:14:14  detabc
+ * added channel dial-skip in case of external use
+ * (isdn phone or another isdn device) on the same NTBA.
+ * usefull with two or more card's connected the different NTBA's.
+ * global switchable in kernel-config and also per netinterface.
+ *
+ * add auto disable of netinterface's in case of:
+ * 	to many connection's in short time.
+ * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
+ * 	or remote side.
+ * 	wrong password's or something else to a ISP (syncppp).
+ *
+ * possible encapsulations for this future are:
+ * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
+ * and ISDN_NET_ENCAP_CISCOHDLCK.
+ *
  * Revision 1.60  1999/11/04 20:29:55  he
  * applied Andre Beck's reset_free fix
  *
@@ -306,7 +331,7 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.60 $";
+char *isdn_ppp_revision = "$Revision: 1.62 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
@@ -699,7 +724,7 @@
 		case PPPIOCGIFNAME:
 			if(!lp)
 				return -EINVAL;
-			if ((r = set_arg((void *) arg, lp->name,strlen(lp->name))))
+			if ((r = set_arg((void *) arg, lp->name, strlen(lp->name))))
 				return r;
 			break;
 		case PPPIOCGMPFLAGS:	/* get configuration flags */
@@ -721,8 +746,8 @@
 			}
 			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
 				if (lp) {
-					lp->netdev->dev.tbusy = 0;
-					mark_bh(NET_BH);	/* OK .. we are ready to send buffers */
+					/* OK .. we are ready to send buffers */
+					netif_wake_queue(&lp->netdev->dev);
 				}
 			}
 			is->pppcfg = val;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.3.45/linux/drivers/isdn/isdn_tty.c	Thu Nov 11 20:11:39 1999
+++ linux/drivers/isdn/isdn_tty.c	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.80 1999/11/07 13:34:30 armin Exp $
+/* $Id: isdn_tty.c,v 1.82 2000/01/23 18:45:37 keil Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -20,6 +20,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.82  2000/01/23 18:45:37  keil
+ * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
+ *
+ * Revision 1.81  2000/01/20 19:55:33  keil
+ * Add FAX Class 1 support
+ *
  * Revision 1.80  1999/11/07 13:34:30  armin
  * Fixed AT command line editor
  *
@@ -348,6 +354,7 @@
 #endif
 
 #define FIX_FILE_TRANSFER
+#define	DUMMY_HAYES_AT
 
 /* Prototypes */
 
@@ -372,7 +379,7 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.80 $";
+char *isdn_tty_revision = "$Revision: 1.82 $";
 
 
 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
@@ -976,7 +983,7 @@
 	m->mdmreg[REG_SI1I] = si2bit[si];
 	save_flags(flags);
 	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
 	if (i < 0) {
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
@@ -1187,7 +1194,7 @@
 	m->mdmreg[REG_SI1I] = si2bit[si];
 	save_flags(flags);
 	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
 	if (i < 0) {
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
@@ -1281,7 +1288,7 @@
 	m->mdmreg[REG_SI1I] = si2bit[si];
 	save_flags(flags);
 	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
 	if (i < 0) {
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
@@ -1555,6 +1562,23 @@
 					}
 				}
 			} else
+			if (TTY_IS_FCLASS1(info)) {
+				int cc = isdn_tty_handleDLEdown(info, m, c);
+				
+				if (info->vonline & 4) { /* ETX seen */
+					isdn_ctrl c;
+
+					c.command = ISDN_CMD_FAXCMD;
+					c.driver = info->isdn_driver;
+					c.arg = info->isdn_channel;
+					c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL;
+					c.parm.aux.subcmd = ETX;
+					isdn_command(&c);
+				}
+				info->vonline = 0;
+				printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc,c);
+				info->xmit_count += cc;
+			} else
 #endif
 				info->xmit_count += c;
 		} else {
@@ -2567,7 +2591,7 @@
 	(info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
 
 int
-isdn_tty_stat_callback(int i, isdn_ctrl * c)
+isdn_tty_stat_callback(int i, isdn_ctrl *c)
 {
 	int mi;
 	modem_info *info;
@@ -2668,8 +2692,7 @@
 						if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
 							strcpy(info->emu.connmsg, c->parm.num);
 							isdn_tty_modem_result(1, info);
-						}
-						else
+						} else
 							isdn_tty_modem_result(5, info);
 					}
 					if (USG_VOICE(dev->usage[i]))
@@ -2720,7 +2743,7 @@
 #ifdef CONFIG_ISDN_TTY_FAX
 			case ISDN_STAT_FAXIND:
 				if (TTY_IS_ACTIVE(info)) {
-					isdn_tty_fax_command(info); 
+					isdn_tty_fax_command(info, c); 
 				}
 				break;
 #endif
@@ -3247,8 +3270,22 @@
 					info->xmit_size /= 10;		
 			}
 			break;
+		case 'C':
+			/* &C - DCD Status */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_DCD] &= ~BIT_DCD;
+					break;
+				case 1:
+					m->mdmreg[REG_DCD] |= BIT_DCD;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
 		case 'D':
-			/* &D - Set DCD-Low-behavior */
+			/* &D - Set DTR-Low-behavior */
 			p[0]++;
 			switch (isdn_getnum(p)) {
 				case 0:
@@ -3280,6 +3317,14 @@
 			isdn_tty_reset_profile(m);
 			isdn_tty_modem_reset_regs(info, 1);
 			break;
+#ifdef DUMMY_HAYES_AT
+		case 'K':
+			/* only for be compilant with common scripts */
+			/* &K Flowcontrol - no function */
+			p[0]++;
+			isdn_getnum(p);
+			break;
+#endif
 		case 'L':
 			/* &L -Set Numbers to listen on */
 			p[0]++;
@@ -3565,8 +3610,10 @@
 				sprintf(rs, "\r\n%d",
 					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
 #ifdef CONFIG_ISDN_TTY_FAX
-				if (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX)
-				sprintf(rs, "\r\n2");
+				if (TTY_IS_FCLASS2(info))
+						sprintf(rs, "\r\n2");
+				else if (TTY_IS_FCLASS1(info))
+						sprintf(rs, "\r\n1");
 #endif
 				isdn_tty_at_cout(rs, info);
 				break;
@@ -3582,11 +3629,25 @@
 						    m->mdmreg[REG_PSIZE] * 16;
 						break;
 #ifdef CONFIG_ISDN_TTY_FAX
+					case '1':
+						p[0]++;
+						if (!(dev->global_features &
+							ISDN_FEATURE_L3_FCLASS1))
+							PARSE_ERROR1;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
 					case '2':
 						p[0]++;
+						if (!(dev->global_features &
+							ISDN_FEATURE_L3_FCLASS2))
+							PARSE_ERROR1;
 						m->mdmreg[REG_SI1] = 1;
 						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
-						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2;
 						info->xmit_size =
 						    m->mdmreg[REG_PSIZE] * 16;
 						break;
@@ -3601,11 +3662,17 @@
 						break;
 					case '?':
 						p[0]++;
+						strcpy(rs, "\r\n0,");
 #ifdef CONFIG_ISDN_TTY_FAX
-						isdn_tty_at_cout("\r\n0,2,8", info);
-#else
-						isdn_tty_at_cout("\r\n0,8", info);
+						if (dev->global_features &
+							ISDN_FEATURE_L3_FCLASS1)
+							strcat(rs, "1,");
+						if (dev->global_features &
+							ISDN_FEATURE_L3_FCLASS2)
+							strcat(rs, "2,");
 #endif
+						strcat(rs, "8");
+						isdn_tty_at_cout(rs, info);
 						break;
 					default:
 						PARSE_ERROR1;
@@ -3995,6 +4062,15 @@
 					default:
 				}
 				break;
+#ifdef DUMMY_HAYES_AT
+			case 'L':
+			case 'M':
+				/* only for be compilant with common scripts */
+				/* no function */
+				p++;
+				isdn_getnum(&p);
+				break;
+#endif
 			case 'O':
 				/* O - Go online */
 				p++;
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- v2.3.45/linux/drivers/isdn/isdn_tty.h	Thu Nov 11 20:11:39 1999
+++ linux/drivers/isdn/isdn_tty.h	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.h,v 1.17 1999/09/21 19:00:35 armin Exp $
+/* $Id: isdn_tty.h,v 1.18 2000/01/20 19:55:33 keil Exp $
 
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
@@ -20,6 +20,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.h,v $
+ * Revision 1.18  2000/01/20 19:55:33  keil
+ * Add FAX Class 1 support
+ *
  * Revision 1.17  1999/09/21 19:00:35  armin
  * Extended FCON message with added CPN
  * can now be activated with Bit 1 of Reg 23.
@@ -160,6 +163,13 @@
 #define BIT_CPN       1
 #define BIT_CPNFCON   2
 
+#define TTY_IS_FCLASS1(info) \
+	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1))
+#define TTY_IS_FCLASS2(info) \
+	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2))
+
 extern void isdn_tty_modem_escape(void);
 extern void isdn_tty_modem_ring(void);
 extern void isdn_tty_carrier_timeout(void);
@@ -175,6 +185,6 @@
 extern void isdn_tty_modem_hup(modem_info *, int);
 #ifdef CONFIG_ISDN_TTY_FAX
 extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *);
-extern int isdn_tty_fax_command(modem_info *);
+extern int isdn_tty_fax_command(modem_info *, isdn_ctrl *);
 extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *);
 #endif
diff -u --recursive --new-file v2.3.45/linux/drivers/isdn/isdn_ttyfax.c linux/drivers/isdn/isdn_ttyfax.c
--- v2.3.45/linux/drivers/isdn/isdn_ttyfax.c	Thu Nov 11 20:11:39 1999
+++ linux/drivers/isdn/isdn_ttyfax.c	Tue Feb 15 11:40:43 2000
@@ -1,9 +1,9 @@
-/* $Id: isdn_ttyfax.c,v 1.4 1999/09/21 19:00:35 armin Exp $
+/* $Id: isdn_ttyfax.c,v 1.6 2000/01/26 00:41:13 keil Exp $
  * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
  *
  * Copyright 1999    by Armin Schindler (mac@melware.de)
  * Copyright 1999    by Ralf Spachmann (mel@melware.de)
- * Copyright 1999    by Cytronics & Melware 
+ * Copyright 1999    by Cytronics & Melware
  *
  * 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
@@ -20,6 +20,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ttyfax.c,v $
+ * Revision 1.6  2000/01/26 00:41:13  keil
+ * add "00" as dummy msn in isdn_get_free_channel call
+ *
+ * Revision 1.5  2000/01/20 19:55:33  keil
+ * Add FAX Class 1 support
+ *
  * Revision 1.4  1999/09/21 19:00:35  armin
  * Extended FCON message with added CPN
  * can now be activated with Bit 1 of Reg 23.
@@ -50,31 +56,32 @@
 #include "isdn_ttyfax.h"
 
 
-static char *isdn_tty_fax_revision = "$Revision: 1.4 $";
+static char *isdn_tty_fax_revision = "$Revision: 1.6 $";
 
 #define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
 
 static char *
 isdn_getrev(const char *revision)
 {
-        char *rev;
-        char *p;
+	char *rev;
+	char *p;
 
-        if ((p = strchr(revision, ':'))) {
-                rev = p + 2;
-                p = strchr(rev, '$');
-                *--p = 0;
-        } else
-                rev = "???";
-        return rev;
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
 }
 
-
 /*
  * Fax Class 2 Modem results
  *
  */
-static void isdn_tty_fax_modem_result(int code, modem_info * info)
+
+static void
+isdn_tty_fax_modem_result(int code, modem_info * info)
 {
 	atemu *m = &info->emu;
 	T30_s *f = info->fax;
@@ -85,7 +92,7 @@
 	static char *msg[] =
 	{"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:",
 	 "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:",
-	 "+FCFR", "+FPTS:", "+FET:" };
+	 "+FCFR", "+FPTS:", "+FET:"};
 
 
 	isdn_tty_at_cout("\r\n", info);
@@ -115,12 +122,12 @@
 		case 3:	/* +FCSI */
 		case 8:	/* +FTSI */
 			sprintf(rs, "\"%s\"", f->r_id);
-			isdn_tty_at_cout(rs, info); 
+			isdn_tty_at_cout(rs, info);
 			break;
 		case 4:	/* +FDIS */
 			rs[0] = 0;
 			rp = &f->r_resolution;
-			for(i = 0; i < 8; i++) {
+			for (i = 0; i < 8; i++) {
 				sprintf(rss, "%c%s", rp[i] + 48,
 					(i < 7) ? "," : "");
 				strcat(rs, rss);
@@ -128,18 +135,18 @@
 			isdn_tty_at_cout(rs, info);
 #ifdef ISDN_TTY_FAX_CMD_DEBUG
 			printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n",
-				rs, info->line);
+			       rs, info->line);
 #endif
 			break;
 		case 5:	/* +FHNG */
 			sprintf(rs, "%d", f->code);
-			isdn_tty_at_cout(rs, info); 
+			isdn_tty_at_cout(rs, info);
 			info->faxonline = 0;
 			break;
 		case 6:	/* +FDCS */
 			rs[0] = 0;
 			rp = &f->r_resolution;
-			for(i = 0; i < 8; i++) {
+			for (i = 0; i < 8; i++) {
 				sprintf(rss, "%c%s", rp[i] + 48,
 					(i < 7) ? "," : "");
 				strcat(rs, rss);
@@ -147,127 +154,169 @@
 			isdn_tty_at_cout(rs, info);
 #ifdef ISDN_TTY_FAX_CMD_DEBUG
 			printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n",
-				rs, info->line);
+			       rs, info->line);
 #endif
 			break;
 		case 7:	/* CONNECT */
 			info->faxonline |= 2;
 			break;
-		case 9: /* FCFR */
+		case 9:	/* FCFR */
 			break;
-		case 10: /* FPTS */
+		case 10:	/* FPTS */
 			isdn_tty_at_cout("1", info);
 			break;
-		case 11: /* FET */
+		case 11:	/* FET */
 			sprintf(rs, "%d", f->fet);
-			isdn_tty_at_cout(rs, info); 
+			isdn_tty_at_cout(rs, info);
 			break;
 	}
 
 	isdn_tty_at_cout("\r\n", info);
 
 	switch (code) {
-		case 7: /* CONNECT */
+		case 7:	/* CONNECT */
 			info->online = 2;
 			if (info->faxonline & 1) {
 				sprintf(rs, "%c", XON);
-				isdn_tty_at_cout(rs, info); 
+				isdn_tty_at_cout(rs, info);
 			}
 			break;
 	}
 }
 
 int
-isdn_tty_fax_command(modem_info * info)
+isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c)
+{
+	static char *msg[] =
+	{"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"};
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd);
+#endif
+	if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) {
+		if (info->online)
+			info->online = 1;
+		isdn_tty_at_cout("\r\n", info);
+		isdn_tty_at_cout(msg[c->parm.aux.cmd], info);
+		isdn_tty_at_cout("\r\n", info);
+	}
+	switch (c->parm.aux.cmd) {
+		case ISDN_FAX_CLASS1_CONNECT:
+			info->online = 2;
+			break;
+		case ISDN_FAX_CLASS1_OK:
+		case ISDN_FAX_CLASS1_FCERROR:
+		case ISDN_FAX_CLASS1_ERROR:
+		case ISDN_FAX_CLASS1_NOCARR:
+			break;
+		case ISDN_FAX_CLASS1_QUERY:
+			isdn_tty_at_cout("\r\n", info);
+			if (!c->parm.aux.para[0]) {
+				isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info);
+				isdn_tty_at_cout("\r\n", info);
+			} else {
+				isdn_tty_at_cout(c->parm.aux.para, info);
+				isdn_tty_at_cout("\r\nOK\r\n", info);
+			}
+			break;
+	}
+	return (0);
+}
+
+int
+isdn_tty_fax_command(modem_info * info, isdn_ctrl * c)
 {
 	T30_s *f = info->fax;
 	char rs[10];
 
+	if (TTY_IS_FCLASS1(info))
+		return (isdn_tty_fax_command1(info, c));
+
 #ifdef ISDN_TTY_FAX_CMD_DEBUG
 	printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n",
-		f->r_code, info->line);
+	       f->r_code, info->line);
 #endif
-	switch(f->r_code) {
+	switch (f->r_code) {
 		case ISDN_TTY_FAX_FCON:
 			info->faxonline = 1;
-			isdn_tty_fax_modem_result(2, info); /* +FCON */
-			return(0);
+			isdn_tty_fax_modem_result(2, info);	/* +FCON */
+			return (0);
 		case ISDN_TTY_FAX_FCON_I:
 			info->faxonline = 16;
-			isdn_tty_fax_modem_result(2, info); /* +FCON */
-			return(0);
+			isdn_tty_fax_modem_result(2, info);	/* +FCON */
+			return (0);
 		case ISDN_TTY_FAX_RID:
 			if (info->faxonline & 1)
-				isdn_tty_fax_modem_result(3, info); /* +FCSI */
+				isdn_tty_fax_modem_result(3, info);	/* +FCSI */
 			if (info->faxonline & 16)
-				isdn_tty_fax_modem_result(8, info); /* +FTSI */
-			return(0);
+				isdn_tty_fax_modem_result(8, info);	/* +FTSI */
+			return (0);
 		case ISDN_TTY_FAX_DIS:
-			isdn_tty_fax_modem_result(4, info); /* +FDIS */
-			return(0);
+			isdn_tty_fax_modem_result(4, info);	/* +FDIS */
+			return (0);
 		case ISDN_TTY_FAX_HNG:
 			if (f->phase == ISDN_FAX_PHASE_C) {
 				if (f->direction == ISDN_TTY_FAX_CONN_IN) {
 					sprintf(rs, "%c%c", DLE, ETX);
-					isdn_tty_at_cout(rs, info); 
+					isdn_tty_at_cout(rs, info);
 				} else {
 					sprintf(rs, "%c", 0x18);
-					isdn_tty_at_cout(rs, info); 
+					isdn_tty_at_cout(rs, info);
 				}
-				info->faxonline &= ~2; /* leave data mode */
+				info->faxonline &= ~2;	/* leave data mode */
 				info->online = 1;
 			}
 			f->phase = ISDN_FAX_PHASE_E;
-			isdn_tty_fax_modem_result(5, info); /* +FHNG */
-			isdn_tty_fax_modem_result(0, info); /* OK */
-			return(0);
+			isdn_tty_fax_modem_result(5, info);	/* +FHNG */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
 		case ISDN_TTY_FAX_DCS:
-			isdn_tty_fax_modem_result(6, info); /* +FDCS */
-			isdn_tty_fax_modem_result(7, info); /* CONNECT */
+			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
+			isdn_tty_fax_modem_result(7, info);	/* CONNECT */
 			f->phase = ISDN_FAX_PHASE_C;
-			return(0);
+			return (0);
 		case ISDN_TTY_FAX_TRAIN_OK:
-			isdn_tty_fax_modem_result(6, info); /* +FDCS */
-			isdn_tty_fax_modem_result(0, info); /* OK */
-			return(0);
+			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
 		case ISDN_TTY_FAX_SENT:
-			isdn_tty_fax_modem_result(0, info); /* OK */
-			return(0);
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
 		case ISDN_TTY_FAX_CFR:
-			isdn_tty_fax_modem_result(9, info); /* +FCFR */
-			return(0);
+			isdn_tty_fax_modem_result(9, info);	/* +FCFR */
+			return (0);
 		case ISDN_TTY_FAX_ET:
 			sprintf(rs, "%c%c", DLE, ETX);
-			isdn_tty_at_cout(rs, info); 
-			isdn_tty_fax_modem_result(10, info); /* +FPTS */
-			isdn_tty_fax_modem_result(11, info); /* +FET */
-			isdn_tty_fax_modem_result(0, info); /* OK */
-			info->faxonline &= ~2; /* leave data mode */
+			isdn_tty_at_cout(rs, info);
+			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
+			isdn_tty_fax_modem_result(11, info);	/* +FET */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			info->faxonline &= ~2;	/* leave data mode */
 			info->online = 1;
 			f->phase = ISDN_FAX_PHASE_D;
-			return(0);
+			return (0);
 		case ISDN_TTY_FAX_PTS:
-			isdn_tty_fax_modem_result(10, info); /* +FPTS */
+			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
 			if (f->direction == ISDN_TTY_FAX_CONN_OUT) {
 				if (f->fet == 1)
 					f->phase = ISDN_FAX_PHASE_B;
 				if (f->fet == 0)
-					isdn_tty_fax_modem_result(0, info); /* OK */
+					isdn_tty_fax_modem_result(0, info);	/* OK */
 			}
-			return(0);
+			return (0);
 		case ISDN_TTY_FAX_EOP:
-			info->faxonline &= ~2; /* leave data mode */
+			info->faxonline &= ~2;	/* leave data mode */
 			info->online = 1;
 			f->phase = ISDN_FAX_PHASE_D;
-			return(0);
+			return (0);
 
 	}
-	return(-1);
+	return (-1);
 }
 
 
 void
-isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb)
+isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb)
 {
 	__u8 LeftMask;
 	__u8 RightMask;
@@ -276,13 +325,13 @@
 	int i;
 
 	if (!info->fax->bor) {
-		for(i = 0; i < skb->len; i++) {
+		for (i = 0; i < skb->len; i++) {
 			Data = skb->data[i];
 			for (
-				LeftMask = 0x80, RightMask = 0x01;
-				LeftMask > RightMask;
-				LeftMask >>= 1, RightMask <<= 1
-			) {
+				    LeftMask = 0x80, RightMask = 0x01;
+				    LeftMask > RightMask;
+				    LeftMask >>= 1, RightMask <<= 1
+			    ) {
 				fBit = (Data & LeftMask);
 				if (Data & RightMask)
 					Data |= LeftMask;
@@ -300,10 +349,103 @@
 }
 
 /*
+ * Parse AT+F.. FAX class 1 commands
+ */
+
+int
+isdn_tty_cmd_FCLASS1(char **p, modem_info * info)
+{
+	static char *cmd[] =
+	{"AE", "TS", "RS", "TM", "RM", "TH", "RH"};
+	isdn_ctrl c;
+	int par, i;
+	long flags;
+
+	for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++)
+		if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2))
+			break;
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd);
+#endif
+	if (c.parm.aux.cmd == 7)
+		PARSE_ERROR1;
+
+	p[0] += 2;
+	switch (*p[0]) {
+		case '?':
+			p[0]++;
+			c.parm.aux.subcmd = AT_QUERY;
+			break;
+		case '=':
+			p[0]++;
+			if (*p[0] == '?') {
+				p[0]++;
+				c.parm.aux.subcmd = AT_EQ_QUERY;
+			} else {
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				c.parm.aux.subcmd = AT_EQ_VALUE;
+				c.parm.aux.para[0] = par;
+			}
+			break;
+		case 0:
+			c.parm.aux.subcmd = AT_COMMAND;
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	c.command = ISDN_CMD_FAXCMD;
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n",
+	       c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]);
+#endif
+	if (info->isdn_driver < 0) {
+		save_flags(flags);
+		cli();
+		if ((c.parm.aux.subcmd == AT_EQ_VALUE) ||
+		    (c.parm.aux.subcmd == AT_COMMAND)) {
+			restore_flags(flags);
+			PARSE_ERROR1;
+		}
+		/* get a temporary connection to the first free fax driver */
+		i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX,
+					  ISDN_PROTO_L3_FCLASS1, -1, -1, "00");
+		if (i < 0) {
+			restore_flags(flags);
+			PARSE_ERROR1;
+		}
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		c.driver = info->isdn_driver;
+		c.arg = info->isdn_channel;
+		isdn_command(&c);
+		isdn_free_channel(info->isdn_driver, info->isdn_channel,
+				  ISDN_USAGE_FAX);
+		info->isdn_driver = -1;
+		info->isdn_channel = -1;
+		if (info->drv_index >= 0) {
+			dev->m_idx[info->drv_index] = -1;
+			info->drv_index = -1;
+		}
+		restore_flags(flags);
+	} else {
+		c.driver = info->isdn_driver;
+		c.arg = info->isdn_channel;
+		isdn_command(&c);
+	}
+	return 1;
+}
+
+/*
  * Parse AT+F.. FAX class 2 commands
  */
 
-int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
+int
+isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
 {
 	atemu *m = &info->emu;
 	T30_s *f = info->fax;
@@ -311,10 +453,11 @@
 	int par;
 	char rs[50];
 	char rss[50];
-	int maxdccval[]={1,5,2,2,3,2,0,7};
+	int maxdccval[] =
+	{1, 5, 2, 2, 3, 2, 0, 7};
 
 	/* FAA still unchanged */
-        if (!strncmp(p[0], "AA", 2)) { /* TODO */
+	if (!strncmp(p[0], "AA", 2)) {	/* TODO */
 		p[0] += 2;
 		switch (*p[0]) {
 			case '?':
@@ -332,399 +475,363 @@
 				PARSE_ERROR1;
 		}
 		return 0;
-       }
-
+	}
 	/* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */
-        if (!strncmp(p[0], "BADLIN", 6)) {
-            p[0] += 6;
+	if (!strncmp(p[0], "BADLIN", 6)) {
+		p[0] += 6;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d",f->badlin);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->badlin);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0-255");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 255))
-                                            PARSE_ERROR1;
-                                        f->badlin = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->badlin = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
 #endif
-                                    }
-                                break;
+				}
+				break;
 			default:
-			        PARSE_ERROR1;
+				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
-        if (!strncmp(p[0], "BADMUL", 6)){
-            p[0] +=6;
+	if (!strncmp(p[0], "BADMUL", 6)) {
+		p[0] += 6;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d", f->badmul);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->badmul);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0-255");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 255))
-                                            PARSE_ERROR1;
-                                        f->badmul = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->badmul = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* BOR=n - Phase C bit order, 0=direct, 1=reverse */
-        if (!strncmp(p[0], "BOR", 3)){
-            p[0] +=3;
+	if (!strncmp(p[0], "BOR", 3)) {
+		p[0] += 3;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d", f->bor);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->bor);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0,1");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 1))
-                                            PARSE_ERROR1;
-                                        f->bor = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
-#endif
-                                    }
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                                }
-                       return 0;
-        }
-
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->bor = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
 	/* NBC=n - No Best Capabilities */
-        if (!strncmp(p[0], "NBC", 3)){
-            p[0] +=3;
+	if (!strncmp(p[0], "NBC", 3)) {
+		p[0] += 3;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d", f->nbc);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->nbc);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0,1");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 1))
-                                            PARSE_ERROR1;
-                                        f->nbc = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
-#endif
-                                    }
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                                }
-                       return 0;
-        }
-
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->nbc = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
 	/* BUF? - Readonly buffersize readout  */
-        if (!strncmp(p[0], "BUF?", 4)) {
-                p[0] += 4;
+	if (!strncmp(p[0], "BUF?", 4)) {
+		p[0] += 4;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
+		printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
 #endif
-                                p[0]++;
-                                 sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
-                                isdn_tty_at_cout(rs, info);
-                return 0;
-        }
-        
+		p[0]++;
+		sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
+		isdn_tty_at_cout(rs, info);
+		return 0;
+	}
 	/* CIG=string - local fax station id string for polling rx */
-        if (!strncmp(p[0], "CIG", 3)) {
+	if (!strncmp(p[0], "CIG", 3)) {
 		int i, r;
-                p[0] += 3;
+		p[0] += 3;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
 				sprintf(rs, "\r\n\"%s\"", f->pollid);
-                                isdn_tty_at_cout(rs, info);
-                                break;
+				isdn_tty_at_cout(rs, info);
+				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n\"STRING\"");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        if (*p[0] =='"')
-                                            p[0]++;
-                                        for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++)
-                                        {
-                                            f->pollid[i] = *p[0]++;
-                                        }
-                                        if (*p[0] =='"')
-                                            p[0]++;
-                                        for(r=i; r < FAXIDLEN; r++)
-                                        {
-                                        	f->pollid[r] = 32;
-                                        }
-                                        f->pollid[FAXIDLEN-1] = 0;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n\"STRING\"");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					if (*p[0] == '"')
+						p[0]++;
+					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+						f->pollid[i] = *p[0]++;
+					}
+					if (*p[0] == '"')
+						p[0]++;
+					for (r = i; r < FAXIDLEN; r++) {
+						f->pollid[r] = 32;
+					}
+					f->pollid[FAXIDLEN - 1] = 0;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                       printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
+					printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
 #endif
-                                    }
-                                break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */
-        if (!strncmp(p[0], "CQ", 2)) {
-                p[0] += 2;
+	if (!strncmp(p[0], "CQ", 2)) {
+		p[0] += 2;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d", f->cq);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->cq);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0,1,2");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 2))
-                                            PARSE_ERROR1;
-                                        f->cq = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1,2");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 2))
+						PARSE_ERROR1;
+					f->cq = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */
-        if (!strncmp(p[0], "CR", 2)) {
-                p[0] += 2;
+	if (!strncmp(p[0], "CR", 2)) {
+		p[0] += 2;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->cr);	/* read actual value from struct and print */
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0,1"); /* display online help */
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 1))
-                                            PARSE_ERROR1;
-                                        f->cr = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");		/* display online help */
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->cr = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* CTCRTY=value - ECM retry count */
-        if (!strncmp(p[0], "CTCRTY", 6)){
-            p[0] +=6;
+	if (!strncmp(p[0], "CTCRTY", 6)) {
+		p[0] += 6;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d",f->ctcrty);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->ctcrty);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0-255");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 255))
-                                            PARSE_ERROR1;
-                                        f->ctcrty = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->ctcrty = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */
-        if (!strncmp(p[0], "DCC", 3)) {
+	if (!strncmp(p[0], "DCC", 3)) {
 		char *rp = &f->resolution;
 		int i;
 
-                p[0] += 3;
-		switch(*p[0]) {
+		p[0] += 3;
+		switch (*p[0]) {
 			case '?':
 				p[0]++;
-	                        strcpy(rs, "\r\n");
-	                        for(i = 0; i < 8; i++) {
-        	                        sprintf(rss, "%c%s", rp[i] + 48,
-                	                        (i < 7) ? "," : "");
-                        	        strcat(rs, rss);
-	                        }
-        	                isdn_tty_at_cout(rs, info);
+				strcpy(rs, "\r\n");
+				for (i = 0; i < 8; i++) {
+					sprintf(rss, "%c%s", rp[i] + 48,
+						(i < 7) ? "," : "");
+					strcat(rs, rss);
+				}
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-		                if (*p[0] == '?') {
-					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info);
-                                	p[0]++;
-		                } else {
-					for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) {
+				p[0]++;
+				if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+					p[0]++;
+				} else {
+					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
 						if (*p[0] != ',') {
 							if ((*p[0] - 48) > maxdccval[i]) {
 								PARSE_ERROR1;
 							}
 							rp[i] = *p[0] - 48;
 							p[0]++;
-							if (*p[0] == ',') 
+							if (*p[0] == ',')
 								p[0]++;
-						} else p[0]++;
+						} else
+							p[0]++;
 					}
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-				printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
-				       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+					printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
+					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
 #endif
-        		        }
+				}
 				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-       
+		return 0;
+	}
 	/* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */
-        if (!strncmp(p[0], "DIS", 3)) {
+	if (!strncmp(p[0], "DIS", 3)) {
 		char *rp = &f->resolution;
 		int i;
 
-                p[0] += 3;
-		switch(*p[0]) {
+		p[0] += 3;
+		switch (*p[0]) {
 			case '?':
 				p[0]++;
-	                        strcpy(rs, "\r\n");
-	                        for(i = 0; i < 8; i++) {
-        	                        sprintf(rss, "%c%s", rp[i] + 48,
-                	                        (i < 7) ? "," : "");
-                        	        strcat(rs, rss);
-	                        }
-        	                isdn_tty_at_cout(rs, info);
+				strcpy(rs, "\r\n");
+				for (i = 0; i < 8; i++) {
+					sprintf(rss, "%c%s", rp[i] + 48,
+						(i < 7) ? "," : "");
+					strcat(rs, rss);
+				}
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-		                if (*p[0] == '?') {
-					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info);
-                                	p[0]++;
-		                } else {
-					for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) {
+				p[0]++;
+				if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+					p[0]++;
+				} else {
+					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
 						if (*p[0] != ',') {
 							if ((*p[0] - 48) > maxdccval[i]) {
 								PARSE_ERROR1;
 							}
 							rp[i] = *p[0] - 48;
 							p[0]++;
-							if (*p[0] == ',') 
+							if (*p[0] == ',')
 								p[0]++;
-						} else p[0]++;
+						} else
+							p[0]++;
 					}
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-				printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
-				       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+					printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
+					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
 #endif
-        		        }
+				}
 				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-       
-	/* DR - Receive Phase C data command, initiates document reception */ 
-        if (!strncmp(p[0], "DR", 2)) {
-                p[0] += 2;
+		return 0;
+	}
+	/* DR - Receive Phase C data command, initiates document reception */
+	if (!strncmp(p[0], "DR", 2)) {
+		p[0] += 2;
 		if ((info->faxonline & 16) &&	/* incoming connection */
-   		   ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
+		    ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
 			printk(KERN_DEBUG "isdn_tty: Fax FDR\n");
 #endif
@@ -735,11 +842,11 @@
 			isdn_command(&cmd);
 			if (f->phase == ISDN_FAX_PHASE_B) {
 				f->phase = ISDN_FAX_PHASE_C;
-			} else if (f->phase == ISDN_FAX_PHASE_D) { 
-				switch(f->fet) {
+			} else if (f->phase == ISDN_FAX_PHASE_D) {
+				switch (f->fet) {
 					case 0:	/* next page will be received */
 						f->phase = ISDN_FAX_PHASE_C;
-						isdn_tty_fax_modem_result(7, info); /* CONNECT */
+						isdn_tty_fax_modem_result(7, info);	/* CONNECT */
 						break;
 					case 1:	/* next doc will be received */
 						f->phase = ISDN_FAX_PHASE_B;
@@ -747,35 +854,36 @@
 					case 2:	/* fax session is terminating */
 						f->phase = ISDN_FAX_PHASE_E;
 						break;
-					default:	
+					default:
 						PARSE_ERROR1;
 				}
 			}
 		} else {
 			PARSE_ERROR1;
 		}
-                return 1;
+		return 1;
 	}
- 
 	/* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */
-        if (!strncmp(p[0], "DT", 2)) {
-		int i, val[]={4,0,2,3};
+	if (!strncmp(p[0], "DT", 2)) {
+		int i, val[] =
+		{4, 0, 2, 3};
 		char *rp = &f->resolution;
 
-                p[0] += 2;
-		if (!info->faxonline & 1) 	/* not outgoing connection */
+		p[0] += 2;
+		if (!info->faxonline & 1)	/* not outgoing connection */
 			PARSE_ERROR1;
 
-		for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<4); i++) {
+		for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) {
 			if (*p[0] != ',') {
 				if ((*p[0] - 48) > maxdccval[val[i]]) {
 					PARSE_ERROR1;
 				}
 				rp[val[i]] = *p[0] - 48;
 				p[0]++;
-				if (*p[0] == ',') 
+				if (*p[0] == ',')
 					p[0]++;
-			} else p[0]++;
+			} else
+				p[0]++;
 		}
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
 		printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n",
@@ -789,48 +897,43 @@
 			isdn_command(&cmd);
 			if (f->phase == ISDN_FAX_PHASE_D) {
 				f->phase = ISDN_FAX_PHASE_C;
-				isdn_tty_fax_modem_result(7, info); /* CONNECT */
+				isdn_tty_fax_modem_result(7, info);	/* CONNECT */
 			}
 		} else {
 			PARSE_ERROR1;
 		}
-                return 1;
-        }
-
+		return 1;
+	}
 	/* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */
-        if (!strncmp(p[0], "ECM", 3)) {
-                p[0] += 3;
+	if (!strncmp(p[0], "ECM", 3)) {
+		p[0] += 3;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d",f->ecm);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->ecm);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0,2");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par != 0) && (par != 2))
-                                            PARSE_ERROR1;
-                                        f->ecm = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,2");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par != 0) && (par != 2))
+						PARSE_ERROR1;
+					f->ecm = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* ET=n - End of page or document */
 	if (!strncmp(p[0], "ET=", 3)) {
 		p[0] += 3;
@@ -857,7 +960,6 @@
 		}
 		return 0;
 	}
-
 	/* K - terminate */
 	if (!strncmp(p[0], "K", 1)) {
 		p[0] += 1;
@@ -866,205 +968,191 @@
 		isdn_tty_modem_hup(info, 1);
 		return 1;
 	}
-
 	/* LID=string - local fax ID */
-        if (!strncmp(p[0], "LID", 3)) {
+	if (!strncmp(p[0], "LID", 3)) {
 		int i, r;
-                p[0] += 3;
+		p[0] += 3;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
 				sprintf(rs, "\r\n\"%s\"", f->id);
-                                isdn_tty_at_cout(rs, info);
-                                break;
+				isdn_tty_at_cout(rs, info);
+				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n\"STRING\"");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        if (*p[0] =='"')
-                                            p[0]++;
-                                        for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++)
-                                        {
-                                            f->id[i] = *p[0]++;
-                                        }
-                                        if (*p[0] =='"')
-                                            p[0]++;
-                                        for(r=i; r < FAXIDLEN; r++)
-                                        {
-                                        	f->id[r] = 32;
-                                        }
-                                        f->id[FAXIDLEN-1] = 0;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n\"STRING\"");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					if (*p[0] == '"')
+						p[0]++;
+					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+						f->id[i] = *p[0]++;
+					}
+					if (*p[0] == '"')
+						p[0]++;
+					for (r = i; r < FAXIDLEN; r++) {
+						f->id[r] = 32;
+					}
+					f->id[FAXIDLEN - 1] = 0;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                       printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
+					printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
 #endif
-                                    }
-                                break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 
 	/* MDL? - DCE Model       */
-        if (!strncmp(p[0], "MDL?", 4)) {
-                p[0] += 4;
+	if (!strncmp(p[0], "MDL?", 4)) {
+		p[0] += 4;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                printk(KERN_DEBUG "isdn_tty: FMDL?\n");
+		printk(KERN_DEBUG "isdn_tty: FMDL?\n");
 #endif
-                isdn_tty_at_cout("\r\nisdn4linux", info);
-                return 0;
-        }
-
+		isdn_tty_at_cout("\r\nisdn4linux", info);
+		return 0;
+	}
 	/* MFR? - DCE Manufacturer */
-        if (!strncmp(p[0], "MFR?", 4)) {
-                p[0] += 4;
+	if (!strncmp(p[0], "MFR?", 4)) {
+		p[0] += 4;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                printk(KERN_DEBUG "isdn_tty: FMFR?\n");
+		printk(KERN_DEBUG "isdn_tty: FMFR?\n");
 #endif
-                isdn_tty_at_cout("\r\nisdn4linux", info);
-                return 0;
-        }
-
+		isdn_tty_at_cout("\r\nisdn4linux", info);
+		return 0;
+	}
 	/* MINSP=n - Minimum Speed for Phase C */
-        if (!strncmp(p[0], "MINSP", 5)) {
-                p[0] += 5;
+	if (!strncmp(p[0], "MINSP", 5)) {
+		p[0] += 5;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d",f->minsp);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->minsp);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0-5");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 5))
-                                            PARSE_ERROR1;
-                                        f->minsp = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-5");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 5))
+						PARSE_ERROR1;
+					f->minsp = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* PHCTO=value - DTE phase C timeout */
-        if (!strncmp(p[0], "PHCTO", 5)){
-            p[0] +=5;
+	if (!strncmp(p[0], "PHCTO", 5)) {
+		p[0] += 5;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d",f->phcto);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->phcto);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0-255");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 255))
-                                            PARSE_ERROR1;
-                                        f->phcto = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->phcto = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 
 	/* REL=n - Phase C received EOL alignment */
-        if (!strncmp(p[0], "REL", 3)) {
-                p[0] += 3;
+	if (!strncmp(p[0], "REL", 3)) {
+		p[0] += 3;
 		switch (*p[0]) {
 			case '?':
 				p[0]++;
-                                sprintf(rs, "\r\n%d",f->rel);
-                                isdn_tty_at_cout(rs, info);
+				sprintf(rs, "\r\n%d", f->rel);
+				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
-                                p[0]++;
-                                if (*p[0] == '?')
-                                    {
-                                     p[0]++;
-                                     sprintf(rs, "\r\n0,1");
-                       		     isdn_tty_at_cout(rs, info);
-                                    }
-                                    else
-                                    {
-                                        par = isdn_getnum(p);
-                                        if ((par < 0) || (par > 1))
-                                            PARSE_ERROR1;
-                                        f->rel = par;
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->rel = par;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                                        printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
+					printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
 #endif
-                                    }
-                                    break;
+				}
+				break;
 			default:
 				PARSE_ERROR1;
 		}
-                return 0;
-        }
-
+		return 0;
+	}
 	/* REV? - DCE Revision */
-        if (!strncmp(p[0], "REV?", 4)) {
-                p[0] += 4;
+	if (!strncmp(p[0], "REV?", 4)) {
+		p[0] += 4;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                printk(KERN_DEBUG "isdn_tty: FREV?\n");
+		printk(KERN_DEBUG "isdn_tty: FREV?\n");
 #endif
 		strcpy(rss, isdn_tty_fax_revision);
 		sprintf(rs, "\r\nRev: %s", isdn_getrev(rss));
-                isdn_tty_at_cout(rs, info);
-                return 0;
-        }
-
+		isdn_tty_at_cout(rs, info);
+		return 0;
+	}
 
 	/* Phase C Transmit Data Block Size */
-	if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */
+	if (!strncmp(p[0], "TBC=", 4)) {	/* dummy, not used */
 		p[0] += 4;
 #ifdef ISDN_TTY_FAX_STAT_DEBUG
-                printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
+		printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
 #endif
-                switch (*p[0]) {
-                        case '0':
-                                p[0]++;
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
+		switch (*p[0]) {
+			case '0':
+				p[0]++;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
 	}
-
-        printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+	printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
 	PARSE_ERROR1;
 }
 
+int
+isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
+{
+	if (TTY_IS_FCLASS2(info))
+		return (isdn_tty_cmd_FCLASS2(p, info));
+	else if (TTY_IS_FCLASS1(info))
+		return (isdn_tty_cmd_FCLASS1(p, info));
+	PARSE_ERROR1;
+}
diff -u --recursive --new-file v2.3.45/linux/drivers/macintosh/adb-iop.c linux/drivers/macintosh/adb-iop.c
--- v2.3.45/linux/drivers/macintosh/adb-iop.c	Sun Feb 13 19:29:03 2000
+++ linux/drivers/macintosh/adb-iop.c	Tue Feb 15 22:39:01 2000
@@ -12,7 +12,6 @@
  * o Implement SRQ handling.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.3.45/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- v2.3.45/linux/drivers/macintosh/adb.c	Thu Feb 10 17:11:09 2000
+++ linux/drivers/macintosh/adb.c	Wed Feb 16 15:42:05 2000
@@ -22,6 +22,7 @@
 #include <linux/malloc.h>
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/adb.h>
@@ -664,6 +665,11 @@
 		return;
 #endif
 
-	if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
+	if (devfs_register_chrdev(ADB_MAJOR, "adb", &adb_fops))
 		printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
+	else
+		devfs_register (NULL, "adb", 0, DEVFS_FL_NONE,
+				ADB_MAJOR, 0,
+				S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				&adb_fops, NULL);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/macintosh/via-pmu68k.c linux/drivers/macintosh/via-pmu68k.c
--- v2.3.45/linux/drivers/macintosh/via-pmu68k.c	Sun Feb 13 19:29:03 2000
+++ linux/drivers/macintosh/via-pmu68k.c	Tue Feb 15 22:39:01 2000
@@ -18,7 +18,6 @@
  */
 
 #include <stdarg.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.3.45/linux/drivers/net/3c505.c linux/drivers/net/3c505.c
--- v2.3.45/linux/drivers/net/3c505.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/3c505.c	Mon Feb 14 15:34:21 2000
@@ -184,23 +184,9 @@
 
 /* Dma Memory related stuff */
 
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
-	int order;
-
-	size = (size - 1) >> (PAGE_SHIFT - 1);
-	order = -1;
-	do {
-		size >>= 1;
-		order++;
-	} while (size);
-	return order;
-}
-
 static unsigned long dma_mem_alloc(int size)
 {
-	int order = __get_order(size);
+	int order = get_order(size);
 
 	return __get_dma_pages(GFP_KERNEL, order);
 }
@@ -1191,7 +1177,7 @@
 	free_irq(dev->irq, dev);
 
 	free_dma(dev->dma);
-	free_pages((unsigned long) adapter->dma_buffer, __get_order(DMA_BUFFER_SIZE));
+	free_pages((unsigned long) adapter->dma_buffer, get_order(DMA_BUFFER_SIZE));
 
 	MOD_DEC_USE_COUNT;
 
diff -u --recursive --new-file v2.3.45/linux/drivers/net/8139too.c linux/drivers/net/8139too.c
--- v2.3.45/linux/drivers/net/8139too.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/8139too.c	Tue Feb 15 13:43:05 2000
@@ -19,6 +19,11 @@
 		
 		David S. Miller - PCI DMA and softnet updates
 
+		Ernst Gill - fixes ported from BSD driver
+
+		Daniel Kobras - identified specific locations of
+			posted MMIO write bugginess
+
 -----------------------------------------------------------------------------
 
 				Theory of Operation
@@ -82,7 +87,7 @@
 #include <asm/io.h>
 
 
-#define RTL8139_VERSION "0.9.0"
+#define RTL8139_VERSION "0.9.2"
 #define RTL8139_MODULE_NAME "8139too"
 #define RTL8139_DRIVER_NAME   RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION
 #define PFX RTL8139_MODULE_NAME ": "
@@ -154,15 +159,17 @@
 #define RTL8139_CAPS  HAS_CHIP_XCVR|HAS_LNK_CHNG
 
 typedef enum {
-	UNKNOWN = 0,
 	RTL8139,
 	RTL8139_CB,
 	SMC1211TX,
-	MPX5030,
+	/*MPX5030,*/
 	SIS900,
 	SIS7016,
+	DELTA8139,
+	ADDTRON8139,
 } chip_t;
 
+
 static struct {
 	chip_t chip;
 	const char *name;
@@ -170,19 +177,24 @@
 	{ RTL8139, "RealTek RTL8139 Fast Ethernet"},
 	{ RTL8139_CB, "RealTek RTL8139B PCI/CardBus"},
 	{ SMC1211TX, "SMC1211TX EZCard 10/100 (RealTek RTL8139)"},
-	{ MPX5030, "Accton MPX5030 (RealTek RTL8139)"},
+/*	{ MPX5030, "Accton MPX5030 (RealTek RTL8139)"},*/
 	{ SIS900, "SiS 900 (RealTek RTL8139) Fast Ethernet"},
 	{ SIS7016, "SiS 7016 (RealTek RTL8139) Fast Ethernet"},
+	{ DELTA8139, "Delta Electronics 8139 10/100BaseTX"},
+	{ ADDTRON8139, "Addtron Technolgy 8139 10/100BaseTX"},
 	{0,},
 };
 
+
 static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = {
 	{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
 	{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139_CB },
 	{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX },
-	{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },
+/*	{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/
 	{0x1039, 0x0900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS900 },
 	{0x1039, 0x7016, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS7016 },
+	{0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 },
+	{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },
 	{0,},
 };
 MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
@@ -304,7 +316,7 @@
 #define PARA78_default	0x78fa8388
 #define PARA7c_default	0xcb38de43	/* param[0][3] */
 #define PARA7c_xxx		0xcb38de43
-unsigned long param[4][4] = {
+static const unsigned long param[4][4] = {
 	{0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
 	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
 	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
@@ -328,7 +340,6 @@
 	struct timer_list timer;	/* Media selection timer. */
 	unsigned char *rx_ring;
 	unsigned int cur_rx;	/* Index into the Rx buffer of next Rx pkt. */
-	unsigned int rx_config;
 	unsigned int cur_tx, dirty_tx, tx_flag;
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct ring_info tx_info[NUM_TX_DESC];
@@ -370,13 +381,43 @@
 static inline u32 ether_crc (int length, unsigned char *data);
 static void rtl8139_set_rx_mode (struct net_device *dev);
 
-/* read after write, to avoid rtl8139 bug w/ posted MMIO writes */
-#define RTL_W8(reg, val8)	do { writeb (val8, ioaddr + reg); readb (ioaddr + reg); } while (0)
-#define RTL_W16(reg, val16)	do { writew (val16, ioaddr + reg); readw (ioaddr + reg); } while (0)
-#define RTL_W32(reg, val32)	do { writel (val32, ioaddr + reg); readl (ioaddr + reg); } while (0)
-#define RTL_R8(reg)		readb (ioaddr + reg)
-#define RTL_R16(reg)		readw (ioaddr + reg)
-#define RTL_R32(reg)		readl (ioaddr + reg)
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8)	do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16)	do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32)	do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0)
+
+
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8)	writeb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16)	writew ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32)	writel ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8		RTL_W8_F
+#define RTL_W16		RTL_W16_F
+#define RTL_W32		RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg)		readb (ioaddr + (reg))
+#define RTL_R16(reg)		readw (ioaddr + (reg))
+#define RTL_R32(reg)		readl (ioaddr + (reg))
+
+
+static const u16 rtl8139_intr_mask = 
+	PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+	TxErr | TxOK | RxErr | RxOK;
+
+static const unsigned int rtl8139_rx_config =
+	    (RX_FIFO_THRESH << 13) |
+	    (RX_BUF_LEN_IDX << 11) |
+	    (RX_DMA_BURST << 8);
 
 
 static const char * __devinit rtl8139_name_from_chip (chip_t chip)
@@ -855,22 +896,18 @@
 	rtl8139_init_ring (dev);
 	tp->full_duplex = tp->duplex_lock;
 	tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
-	tp->rx_config =
-	    (RX_FIFO_THRESH << 13) |
-	    (RX_BUF_LEN_IDX << 11) |
-	    (RX_DMA_BURST << 8);
 
 	/* Check that the chip has finished the reset. */
 	for (i = 1000; i > 0; i--)
 		if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
 			break;
 
-	RTL_W32 (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
-	RTL_W32 (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+	RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+	RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
 
 	/* Must enable Tx/Rx before setting transfer thresholds! */
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-	RTL_W32 (RxConfig, tp->rx_config);
+	RTL_W32 (RxConfig, rtl8139_rx_config);
 	RTL_W32 (TxConfig, (TX_DMA_BURST << 8) | 0x00000000);
 
 	/* Reset N-Way to chipset defaults */
@@ -897,9 +934,7 @@
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
 
 	/* Enable all known interrupts by setting the interrupt mask. */
-	RTL_W16 (IntrMask,
-		 PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-		 | TxErr | TxOK | RxErr | RxOK);
+	RTL_W16 (IntrMask, rtl8139_intr_mask);
 
 	DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d"
 			" GP Pins %2.2x %s-duplex.\n",
@@ -938,8 +973,8 @@
 			break;
 
 	/* Restore our idea of the MAC address. */
-	RTL_W32 (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
-	RTL_W32 (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+	RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+	RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
 
 	/* Hmmm, do these belong here? */
 	RTL_W8 (Cfg9346, 0x00);
@@ -947,7 +982,7 @@
 
 	/* Must enable Tx/Rx before setting transfer thresholds! */
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-	RTL_W32 (RxConfig, tp->rx_config);
+	RTL_W32 (RxConfig, rtl8139_rx_config);
 	/* Check this value: the documentation contradicts ifself.  Is the
 	   IFG correct with bit 28:27 zero, or with |0x03000000 ? */
 	RTL_W32 (TxConfig, (TX_DMA_BURST << 8) | 0x00000000);
@@ -974,9 +1009,7 @@
 	rtl8139_set_rx_mode (dev);
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
 	/* Enable all known interrupts by setting the interrupt mask. */
-	RTL_W16 (IntrMask,
-		 PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-		 | TxErr | TxOK | RxErr | RxOK);
+	RTL_W16 (IntrMask, rtl8139_intr_mask);
 
 	netif_start_queue (dev);
 
@@ -1136,9 +1169,9 @@
 
 	DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x "
 		 "media %2.2x.\n", dev->name,
-		 readb (ioaddr + ChipCmd),
-		 readw (ioaddr + IntrStatus),
-		 readb (ioaddr + GPPinData));
+		 RTL_R8 (ChipCmd),
+		 RTL_R16 (IntrStatus),
+		 RTL_R8 (GPPinData));
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	RTL_W16 (IntrMask, 0x0000);
@@ -1148,7 +1181,7 @@
 		dev->name, tp->cur_tx, tp->dirty_tx);
 	for (i = 0; i < NUM_TX_DESC; i++)
 		printk (KERN_DEBUG "%s:  Tx descriptor %d is %8.8x.%s\n",
-			dev->name, i, readl (ioaddr + TxStatus0 + i * 4),
+			dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
 			i ==
 		      tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : "");
 	printk (KERN_DEBUG "%s: MII #%d registers are:", dev->name,
@@ -1263,7 +1296,7 @@
 
 	while (tp->cur_tx - dirty_tx > 0) {
 		int entry = dirty_tx % NUM_TX_DESC;
-		int txstatus = readl (ioaddr + TxStatus0 + entry * 4);
+		int txstatus = RTL_R32 (TxStatus0 + (entry * 4));
 
 		if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
 			break;	/* It still hasn't been Txed */
@@ -1338,11 +1371,11 @@
 
 	DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
 			" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-			readw (ioaddr + RxBufAddr),
-			readw (ioaddr + RxBufPtr),
-			readb (ioaddr + ChipCmd));
+			RTL_R16 (RxBufAddr),
+			RTL_R16 (RxBufPtr),
+			RTL_R8 (ChipCmd));
 
-	while ((readb (ioaddr + ChipCmd) & RxBufEmpty) == 0) {
+	while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
 		int ring_offset = cur_rx % RX_BUF_LEN;
 		u32 rx_status =
 		    le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
@@ -1359,6 +1392,18 @@
 		printk (".\n");
 #endif
 
+		/* E. Gill */
+		/* Note from BSD driver:
+		 * Here's a totally undocumented fact for you. When the
+		 * RealTek chip is in the process of copying a packet into
+		 * RAM for you, the length will be 0xfff0. If you spot a
+		 * packet header with this value, you need to stop. The
+		 * datasheet makes absolutely no mention of this and
+		 * RealTek should be shot for this.
+		 */
+		if (rx_size == 0xfff0)
+			break;
+
 		if (rx_status &
 		    (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr |
 		     RxBadAlign)) {
@@ -1437,13 +1482,13 @@
 		}
 
 		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
-		RTL_W16 (RxBufPtr, cur_rx - 16);
+		RTL_W16_F (RxBufPtr, cur_rx - 16);
 	}
 	DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
 		" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-		readw (ioaddr + RxBufAddr),
-		readw (ioaddr + RxBufPtr),
-		readb (ioaddr + ChipCmd));
+		RTL_R16 (RxBufAddr),
+		RTL_R16 (RxBufPtr),
+		RTL_R8 (ChipCmd));
 	tp->cur_rx = cur_rx;
 }
 
@@ -1468,14 +1513,13 @@
 	}
 
 	/* Update the error count. */
-	tp->stats.rx_missed_errors +=
-	    readl (ioaddr + RxMissed);
+	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
 	RTL_W32 (RxMissed, 0);
 
 	if ((status & RxUnderrun) && link_changed &&
 	    (tp->drv_flags & HAS_LNK_CHNG)) {
 		/* Really link-change on new chips. */
-		int lpar = readw (ioaddr + NWayLPAR);
+		int lpar = RTL_R16 (NWayLPAR);
 		int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040
 				|| tp->duplex_lock;
 		if (tp->full_duplex != duplex) {
@@ -1496,8 +1540,8 @@
 		tp->stats.rx_fifo_errors++;
 	if (status & RxOverflow) {
 		tp->stats.rx_over_errors++;
-		tp->cur_rx = readw (ioaddr + RxBufAddr) % RX_BUF_LEN;
-		RTL_W16 (RxBufPtr, tp->cur_rx - 16);
+		tp->cur_rx = RTL_R16 (RxBufAddr) % RX_BUF_LEN;
+		RTL_W16_F (RxBufPtr, tp->cur_rx - 16);
 	}
 	if (status & PCIErr) {
 		u16 pci_cmd_status;
@@ -1524,17 +1568,40 @@
 
 	spin_lock_irq (&tp->lock);
 
+	/* disable interrupt generation while handling this interrupt */
+	RTL_W16 (IntrMask, 0x0000);
+
 	do {
-		int status = readw (ioaddr + IntrStatus);
+		int status = RTL_R16 (IntrStatus);
 		/* Acknowledge all of the current interrupt sources ASAP, but
 		   an first get an additional status bit from CSCR. */
 		if (status & RxUnderrun)
-			link_changed = readw (ioaddr + CSCR) & CSCR_LinkChangeBit;
-		RTL_W16 (IntrStatus, status);
+			link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+		/* E. Gill */
+		/* In case of an RxFIFOOver we must also clear the RxOverflow
+		   bit to avoid dropping frames for ever. Believe me, I got a
+		   lot of troubles copying huge data (approximately 2 RxFIFOOver
+		   errors per 1GB data transfer).
+		   The following is written in the 'p-guide.pdf' file (RTL8139(A/B)
+		   Programming guide V0.1, from 1999/1/15) on page 9 from REALTEC.
+		   -----------------------------------------------------------
+		   2. RxFIFOOvw handling:
+		     When RxFIFOOvw occurs, all incoming packets are discarded.
+		     Clear ISR(RxFIFOOvw) doesn't dismiss RxFIFOOvw event. To
+		     dismiss RxFIFOOvw event, the ISR(RxBufOvw) must be written
+		     with a '1'.
+		   -----------------------------------------------------------
+		   Unfortunately I was not able to find any reason for the
+		   RxFIFOOver error (I got the feeling this depends on the
+		   CPU speed, lower CPU speed --> more errors).
+		   After clearing the RxOverflow bit the transfer of the
+		   packet was repeated and all data are error free transfered */
+		RTL_W16 (IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status);
 
 		DPRINTK ("%s: interrupt  status=%#4.4x new intstat=%#4.4x.\n",
 				dev->name, status,
-				readw (ioaddr + IntrStatus));
+				RTL_R16 (IntrStatus));
 
 		if ((status &
 		     (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
@@ -1565,11 +1632,14 @@
 		}
 	} while (1);
 
-	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
-			dev->name, readw (ioaddr + IntrStatus));
+	/* Enable all known interrupts by setting the interrupt mask. */
+	RTL_W16 (IntrMask, rtl8139_intr_mask);
 
 	spin_unlock_irq (&tp->lock);
-	return;
+
+	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+			dev->name, RTL_R16 (IntrStatus));
+
 }
 
 static int rtl8139_close (struct net_device *dev)
@@ -1583,7 +1653,7 @@
 	netif_stop_queue (dev);
 
 	DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n",
-			dev->name, readw (ioaddr + IntrStatus));
+			dev->name, RTL_R16 (IntrStatus));
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	RTL_W16 (IntrMask, 0x0000);
@@ -1710,7 +1780,7 @@
 	DPRINTK ("ENTER\n");
 
 	DPRINTK ("%s:   rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8x.\n",
-			dev->name, dev->flags, readl (ioaddr + RxConfig));
+			dev->name, dev->flags, RTL_R32 (RxConfig));
 
 	/* Note: do not reorder, GCC is clever about common statements. */
 	if (dev->flags & IFF_PROMISC) {
@@ -1739,9 +1809,9 @@
 					    mc_filter);
 	}
 	/* We can safely update without stopping the chip. */
-	RTL_W32 (RxConfig, tp->rx_config | rx_mode);
-	RTL_W32 (MAR0 + 0, mc_filter[0]);
-	RTL_W32 (MAR0 + 4, mc_filter[1]);
+	RTL_W32 (RxConfig, rtl8139_rx_config | rx_mode);
+	RTL_W32_F (MAR0 + 0, mc_filter[0]);
+	RTL_W32_F (MAR0 + 4, mc_filter[1]);
 
 	DPRINTK ("EXIT\n");
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.3.45/linux/drivers/net/eepro100.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/eepro100.c	Mon Feb 14 15:26:31 2000
@@ -274,34 +274,45 @@
 #define outl writel
 #endif
 
+
 /* How to wait for the command unit to accept a command.
    Typically this takes 0 ticks. */
-static inline void wait_for_cmd_done(long cmd_ioaddr)
+static inline void wait_for_cmd_done (long cmd_ioaddr)
 {
 	int wait = 100;
-	do   ;
-	while(inb(cmd_ioaddr) && --wait >= 0);
+	do;
+	while (inb (cmd_ioaddr) && --wait >= 0);
 }
 
+
 /* Offsets to the various registers.
    All accesses need not be longword aligned. */
 enum speedo_offsets {
 	SCBStatus = 0, SCBCmd = 2,	/* Rx/Command Unit command and status. */
-	SCBPointer = 4,				/* General purpose pointer. */
-	SCBPort = 8,				/* Misc. commands and operands.  */
-	SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
-	SCBCtrlMDI = 16,			/* MDI interface control. */
-	SCBEarlyRx = 20,			/* Early receive byte count. */
+	SCBPointer = 4,			/* General purpose pointer. */
+	SCBPort = 8,			/* Misc. commands and operands.  */
+	SCBflash = 12, SCBeeprom = 14, 	/* EEPROM and flash memory control. */
+	SCBCtrlMDI = 16,		/* MDI interface control. */
+	SCBEarlyRx = 20,		/* Early receive byte count. */
 };
+
+
 /* Commands that can be put in a command list entry. */
 enum commands {
-	CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000,
-	CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000,
-	CmdDump = 0x60000, CmdDiagnose = 0x70000,
+	CmdNOp = 0,
+	CmdIASetup = 0x10000,
+	CmdConfigure = 0x20000,
+	CmdMulticastList = 0x30000,
+	CmdTx = 0x40000,
+	CmdTDR = 0x50000,
+	CmdDump = 0x60000,
+	CmdDiagnose = 0x70000,
 	CmdSuspend = 0x40000000,	/* Suspend after completion. */
 	CmdIntr = 0x20000000,		/* Interrupt after completion. */
 	CmdTxFlex = 0x00080000,		/* Use "Flexible mode" for CmdTx command. */
 };
+
+
 /* Do atomically if possible. */
 #if defined(__i386__) || defined(__alpha__) || defined(__ia64__)
 #define clear_suspend(cmd)	clear_bit(30, &(cmd)->cmd_status)
@@ -1145,9 +1156,9 @@
 	sp->stats.tx_errors++;
 	dev->trans_start = jiffies;
 	netif_start_queue (dev);
-	return;
 }
 
+
 static int
 speedo_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
@@ -1764,6 +1775,8 @@
 
 	netif_stop_queue (dev);
 	outl(PortPartialReset, ioaddr + SCBPort);
+	
+	/* XXX call pci_set_power_state ()? */
 }
 
 
diff -u --recursive --new-file v2.3.45/linux/drivers/net/epic100.c linux/drivers/net/epic100.c
--- v2.3.45/linux/drivers/net/epic100.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/epic100.c	Mon Feb 14 15:26:31 2000
@@ -15,8 +15,42 @@
 
 	Information and updates available at
 	http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html
+
+
+
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the SMC "EPIC/100", the SMC
+single-chip Ethernet controllers for PCI.  This chip is used on
+the SMC EtherPower II boards.
+
+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 will assign the
+PCI INTA signal to a (preferably otherwise unused) system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+IVb. References
+
+http://www.smsc.com/main/datasheets/83c171.pdf
+http://www.smsc.com/main/datasheets/83c175.pdf
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.national.com/pf/DP/DP83840A.html
+
+IVc. Errata
+
 */
 
+
+
 static const char *version =
 "epic100.c:v1.04 8/23/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
 
@@ -56,7 +90,6 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/init.h>
-#define PCI_SUPPORT_VER2
 #include <linux/delay.h>
 
 #include <asm/processor.h>		/* Processor type for cache alignment. */
@@ -72,6 +105,9 @@
 
 #define RUN_AT(x) (jiffies + (x))
 
+#define EPIC100_MODULE_NAME "epic100"
+#define PFX EPIC100_MODULE_NAME ": "
+
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
 MODULE_PARM(debug, "i");
@@ -86,82 +122,88 @@
 #define epic_debug debug
 static int epic_debug = 1;
 
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the SMC "EPIC/100", the SMC
-single-chip Ethernet controllers for PCI.  This chip is used on
-the SMC EtherPower II boards.
-
-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 will assign the
-PCI INTA signal to a (preferably otherwise unused) system IRQ line.
-Note: Kernel versions earlier than 1.3.73 do not support shared PCI
-interrupt lines.
+/* The rest of these values should never change. */
 
-III. Driver operation
 
-IIIa. Ring buffers
+enum pci_flags_bit {
+	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,
+};
 
-IVb. References
 
-http://www.smsc.com/main/datasheets/83c171.pdf
-http://www.smsc.com/main/datasheets/83c175.pdf
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
-http://www.national.com/pf/DP/DP83840A.html
+typedef enum {
+	SMSC_83C170,
+	SMSC_83C175,
+} chip_t;
 
-IVc. Errata
 
-*/
-
-/* The rest of these values should never change. */
+struct epic100_chip_info {
+	const char *name;
+};
 
-static struct net_device *epic_probe1(struct pci_dev *pdev, long ioaddr, int irq,
-					  int chip_id, int card_idx);
 
-enum pci_flags_bit {
-	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,
+/* indexed by chip_t */
+static struct epic100_chip_info epic100_chip_info[] __devinitdata = {
+	{ "SMSC EPIC/100 83c170" },
+	{ "SMSC EPIC/C 83c175" },
 };
 
-struct chip_info {
-	const char *name;
-	u16	vendor_id, device_id, device_id_mask, pci_flags;
-	int io_size, min_latency;
-	struct net_device *(*probe1)(struct pci_dev *pdev,
-				     long ioaddr, int irq, int chip_idx,
-				     int fnd_cnt);
-} chip_tbl[] = {
-	{"SMSC EPIC/100 83c170", 0x10B8, 0x0005, 0x7fff,
-	 PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1},
-	{"SMSC EPIC/C 83c175", 0x10B8, 0x0006, 0x7fff,
-	 PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1},
-	{0,},
+
+static struct pci_device_id epic100_pci_tbl[] __devinitdata = {
+	{ 0x10B8, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C170, },
+	{ 0x10B8, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C175, },
+	{ 0,},
 };
+MODULE_DEVICE_TABLE (pci, epic100_pci_tbl);
 
+	
 /* Offsets to registers, using the (ugh) SMC names. */
 enum epic_registers {
-  COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14,
-  PCIBurstCnt=0x18,
-  TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28,	/* Rx error counters. */
-  MIICtrl=0x30, MIIData=0x34, MIICfg=0x38,
-  LAN0=64,						/* MAC address. */
-  MC0=80,						/* Multicast filter table. */
-  RxCtrl=96, TxCtrl=112, TxSTAT=0x74,
-  PRxCDAR=0x84, RxSTAT=0xA4, EarlyRx=0xB0, PTxCDAR=0xC4, TxThresh=0xDC,
+	COMMAND = 0,
+	INTSTAT = 4,
+	INTMASK = 8,
+	GENCTL = 0x0C,
+	NVCTL = 0x10,
+	EECTL = 0x14,
+	PCIBurstCnt = 0x18,
+	TEST1 = 0x1C,
+	CRCCNT = 0x20,
+	ALICNT = 0x24,
+	MPCNT = 0x28,		/* Rx error counters. */
+	MIICtrl = 0x30,
+	MIIData = 0x34,
+	MIICfg = 0x38,
+	LAN0 = 64,		/* MAC address. */
+	MC0 = 80,		/* Multicast filter table. */
+	RxCtrl = 96,
+	TxCtrl = 112,
+	TxSTAT = 0x74,
+	PRxCDAR = 0x84,
+	RxSTAT = 0xA4,
+	EarlyRx = 0xB0,
+	PTxCDAR = 0xC4,
+	TxThresh = 0xDC,
 };
 
+
 /* Interrupt register bits, using my own meaningful names. */
 enum IntrStatus {
-  TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000,
-  PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000,
-  RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100,
-  TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010,
-  RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001,
+	TxIdle = 0x40000,
+	RxIdle = 0x20000,
+	IntrSummary = 0x010000,
+	PCIBusErr170 = 0x7000,
+	PCIBusErr175 = 0x1000,
+	PhyEvent175 = 0x8000,
+	RxStarted = 0x0800,
+	RxEarlyWarn = 0x0400,
+	CntFull = 0x0200,
+	TxUnderrun = 0x0100,
+	TxEmpty = 0x0080,
+	TxDone = 0x0020, RxError = 0x0010,
+	RxOverflow = 0x0008,
+	RxFull = 0x0004,
+	RxHeader = 0x0002,
+	RxDone = 0x0001,
 };
 
 /* The EPIC100 Rx and Tx buffer descriptors. */
@@ -202,7 +244,7 @@
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 
-	u8 pci_bus, pci_dev_fn;				/* PCI bus location. */
+	struct pci_dev *pdev;
 	u16 chip_id;
 
 	struct net_device_stats stats;
@@ -240,196 +282,9 @@
 static struct net_device_stats *epic_get_stats(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
 
-
-/* A list of all installed EPIC devices, for removing the driver module. */
-static struct net_device *root_epic_dev = NULL;
-
-#ifndef CARDBUS
-static int __init epic100_probe(void)
-{
-	int cards_found = 0;
-	int chip_idx, irq;
-	u16 pci_command, new_command;
-	unsigned char pci_bus, pci_device_fn;
-	struct net_device *dev;
-
-	struct pci_dev *pcidev = NULL;
-	while ((pcidev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pcidev))
-			!= NULL) {
-		long pci_ioaddr = pcidev->resource[0].start;
-		int vendor = pcidev->vendor;
-		int device = pcidev->device;
-
-		for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++)
-			if (vendor == chip_tbl[chip_idx].vendor_id
-				&& (device & chip_tbl[chip_idx].device_id_mask) ==
-				chip_tbl[chip_idx].device_id)
-				break;
-		if (chip_tbl[chip_idx].vendor_id == 0 		/* Compiled out! */
-			||	check_region(pci_ioaddr, chip_tbl[chip_idx].io_size))
-			continue;
-		pci_bus = pcidev->bus->number;
-		pci_device_fn = pcidev->devfn;
-		irq = pcidev->irq;
-
-		/* EPIC-specific code: Soft-reset the chip ere setting as master. */
-		outl(0x0001, pci_ioaddr + GENCTL);
-
-		/* Activate the card: fix for brain-damaged Win98 BIOSes. */
-		pci_read_config_word(pcidev, 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 Ethernet"
-				   " device %4.4x-%4.4x."
-				   "  Updating PCI command %4.4x->%4.4x.\n",
-				   vendor, device, pci_command, new_command);
-			pci_write_config_word(pcidev, PCI_COMMAND, new_command);
-		}
-
-		dev = chip_tbl[chip_idx].probe1(pcidev, pci_ioaddr, irq,
-						chip_idx, cards_found);
-
-		/* Check the latency timer. */
-		if (dev) {
-			u8 pci_latency;
-			pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency);
-			if (pci_latency < chip_tbl[chip_idx].min_latency) {
-				printk(KERN_INFO "  PCI latency timer (CFLT) value of %d is "
-					   "unreasonably low, setting to %d.\n", pci_latency,
-					   chip_tbl[chip_idx].min_latency);
-				pci_write_config_byte(pcidev, PCI_LATENCY_TIMER,
-										  chip_tbl[chip_idx].min_latency);
-			}
-			dev = 0;
-			cards_found++;
-		}
-	}
-
-	return cards_found ? 0 : -ENODEV;
-}
-#endif  /* not CARDBUS */
-
-static struct net_device *epic_probe1(struct pci_dev *pdev, long ioaddr, int irq,
-				      int chip_idx, int card_idx)
-{
-	struct epic_private *ep;
-	int i, option = 0, duplex = 0;
-	struct net_device *dev;
-
-// FIXME	if (dev && dev->mem_start) {
-//		option = dev->mem_start;
-//		duplex = (dev->mem_start & 16) ? 1 : 0;
-//	}
-//	else 
-	if (card_idx >= 0  &&  card_idx < MAX_UNITS) {
-		if (options[card_idx] >= 0)
-			option = options[card_idx];
-		if (full_duplex[card_idx] >= 0)
-			duplex = full_duplex[card_idx];
-	}
-
-	dev = init_etherdev(NULL, 0);
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-	printk(KERN_INFO "%s: SMC EPIC/100 at %#lx, IRQ %d, ",
-		   dev->name, ioaddr, dev->irq);
-
-	/* Bring the chip out of low-power mode. */
-	outl(0x4200, ioaddr + GENCTL);
-	/* Magic?!  If we don't set this bit the MII interface won't work. */
-	outl(0x0008, ioaddr + TEST1);
-
-	/* Turn on the MII transceiver. */
-	outl(0x12, ioaddr + MIICfg);
-	if (chip_idx == 1)
-		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
-	outl(0x0200, ioaddr + GENCTL);
-
-	/* This could also be read from the EEPROM. */
-	for (i = 0; i < 3; i++)
-		((u16 *)dev->dev_addr)[i] = inw(ioaddr + LAN0 + i*4);
-
-	for (i = 0; i < 5; i++)
-		printk("%2.2x:", dev->dev_addr[i]);
-	printk("%2.2x.\n", dev->dev_addr[i]);
-
-	if (epic_debug > 1) {
-		printk(KERN_DEBUG "%s: EEPROM contents\n", dev->name);
-		for (i = 0; i < 64; i++)
-			printk(" %4.4x%s", read_eeprom(ioaddr, i),
-				   i % 16 == 15 ? "\n" : "");
-	}
-
-	/* We do a request_region() to register /proc/ioports info. */
-	request_region(ioaddr, EPIC_TOTAL_SIZE, dev->name);
-
-	/* The data structures must be quadword aligned. */
-	ep = kmalloc(sizeof(*ep), GFP_KERNEL | GFP_DMA);
-	memset(ep, 0, sizeof(*ep));
-	dev->priv = ep;
-
-	ep->next_module = root_epic_dev;
-	root_epic_dev = dev;
-
-	ep->lock = SPIN_LOCK_UNLOCKED;
-	ep->pci_bus = pdev->bus->number;
-	ep->pci_dev_fn = pdev->devfn;
-	ep->chip_id = pdev->device;
-
-	/* Find the connected MII xcvrs.
-	   Doing this in open() would allow detecting external xcvrs later, but
-	   takes too much time. */
-	{
-		int phy, phy_idx;
-		for (phy = 1, phy_idx = 0; phy < 32 && phy_idx < sizeof(ep->phys);
-			 phy++) {
-			int mii_status = mdio_read(ioaddr, phy, 1);
-			if (mii_status != 0xffff  && mii_status != 0x0000) {
-				ep->phys[phy_idx++] = phy;
-				printk(KERN_INFO "%s: MII transceiver #%d control "
-					   "%4.4x status %4.4x.\n"
-					   KERN_INFO "%s:  Autonegotiation advertising %4.4x "
-					   "link partner %4.4x.\n",
-					   dev->name, phy, mdio_read(ioaddr, phy, 0), mii_status,
-					   dev->name, mdio_read(ioaddr, phy, 4),
-					   mdio_read(ioaddr, phy, 5));
-			}
-		}
-		if (phy_idx == 0) {
-			printk(KERN_WARNING "%s: ***WARNING***: No MII transceiver found!\n",
-				   dev->name);
-			/* Use the known PHY address of the EPII. */
-			ep->phys[0] = 3;
-		}
-	}
-
-	/* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */
-	if (ep->chip_id == 6)
-		outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL);
-	outl(0x0008, ioaddr + GENCTL);
 
-	/* The lower four bits are the media type. */
-	ep->force_fd = duplex;
-	ep->default_port = option;
-	if (ep->default_port)
-		ep->medialock = 1;
 
-	/* The Epic-specific entries in the device structure. */
-	dev->open = &epic_open;
-	dev->hard_start_xmit = &epic_start_xmit;
-	dev->stop = &epic_close;
-	dev->get_stats = &epic_get_stats;
-	dev->set_multicast_list = &set_rx_mode;
-	dev->do_ioctl = &mii_ioctl;
-	dev->tx_timeout = epic_tx_timeout;
-	dev->watchdog_timeo = TX_TIMEOUT;
-	
-	netif_stop_queue (dev);
 
-	return dev;
-}
-
 /* Serial EEPROM section. */
 
 /*  EEPROM_Ctrl bits. */
@@ -533,7 +388,7 @@
 	/* Soft reset the chip. */
 	outl(0x4001, ioaddr + GENCTL);
 
-	if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, "SMC EPIC/100", dev)) {
+	if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, EPIC100_MODULE_NAME, dev)) {
 		MOD_DEC_USE_COUNT;
 		return -EBUSY;
 	}
@@ -609,6 +464,7 @@
 	return 0;
 }
 
+
 /* Reset the chip to recover from a PCI transaction error.
    This may occur at interrupt time. */
 static void epic_pause(struct net_device *dev)
@@ -978,6 +834,7 @@
 	spin_unlock (&ep->lock);
 }
 
+
 static int epic_rx(struct net_device *dev)
 {
 	struct epic_private *ep = (struct epic_private *)dev->priv;
@@ -1054,6 +911,7 @@
 	return work_done;
 }
 
+
 static int epic_close(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
@@ -1106,6 +964,7 @@
 	return 0;
 }
 
+
 static struct net_device_stats *epic_get_stats(struct net_device *dev)
 {
 	struct epic_private *ep = (struct epic_private *)dev->priv;
@@ -1121,6 +980,7 @@
 	return &ep->stats;
 }
 
+
 /* Set or clear the multicast filter for this adaptor.
    Note that we only use exclusion around actually queueing the
    new frame, not around filling ep->setup_frame.  This is non-deterministic
@@ -1187,6 +1047,7 @@
 	return;
 }
 
+
 static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	long ioaddr = dev->base_addr;
@@ -1229,132 +1090,213 @@
 	}
 }
 
-
-#ifdef CARDBUS
 
-#include <pcmcia/driver_ops.h>
-
-static dev_node_t *epic_attach(dev_locator_t *loc)
+static int __devinit epic100_init_one (struct pci_dev *pdev,
+				       const struct pci_device_id *ent)
 {
+	struct epic_private *ep;
+	int i, option = 0, duplex = 0, irq, chip_idx;
 	struct net_device *dev;
-	u16 dev_id;
-	u32 io;
-	u8 irq;
-	struct pci_dev *pdev;
-
-	if (loc->bus != LOC_PCI) return NULL;
-	pdev = pci_find_slot (loc->b.pci.bus, loc->b.pci.devfn);
-	if (!pdev) return NULL;
-	printk(KERN_DEBUG "epic_attach(bus %d, function %d)\n",
-	       pdev->bus->number, pdev->devfn);
-	io = pdev->resource[0].start;
+	long ioaddr;
+	static int card_idx = -1;
+	
+	chip_idx = ent->driver_data;
+	
+	ioaddr = pci_resource_start (pdev, 0);
+	if (!ioaddr)
+		return -ENODEV;
+	
 	irq = pdev->irq;
-	dev_id = pdev->device;
-	io &= ~3;
-	if (io == 0 || irq == 0) {
-		printk(KERN_ERR "The EPIC/C 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;
-	}
-	dev = epic_probe1(pdev, io, irq, 2, -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 epic_suspend(dev_node_t *node)
-{
-	struct net_device **devp, **next;
-	printk(KERN_INFO "epic_suspend(%s)\n", node->dev_name);
-	for (devp = &root_epic_dev; *devp; devp = next) {
-		next = &((struct epic_private *)(*devp)->priv)->next_module;
-		if (strcmp((*devp)->name, node->dev_name) == 0) break;
-	}
-	if (*devp) {
-		long ioaddr = (*devp)->base_addr;
-		epic_pause(*devp);
-		/* Put the chip into low-power mode. */
-		outl(0x0008, ioaddr + GENCTL);
-	}
-}
-static void epic_resume(dev_node_t *node)
-{
-	struct net_device **devp, **next;
-	printk(KERN_INFO "epic_resume(%s)\n", node->dev_name);
-	for (devp = &root_epic_dev; *devp; devp = next) {
-		next = &((struct epic_private *)(*devp)->priv)->next_module;
-		if (strcmp((*devp)->name, node->dev_name) == 0) break;
-	}
-	if (*devp) {
-		epic_restart(*devp);
-	}
-}
-static void epic_detach(dev_node_t *node)
-{
-	struct net_device **devp, **next;
-	printk(KERN_INFO "epic_detach(%s)\n", node->dev_name);
-	for (devp = &root_epic_dev; *devp; devp = next) {
-		next = &((struct epic_private *)(*devp)->priv)->next_module;
-		if (strcmp((*devp)->name, node->dev_name) == 0) break;
-	}
-	if (*devp) {
-		unregister_netdev(*devp);
-		kfree(*devp);
-		*devp = *next;
-		kfree(node);
-		MOD_DEC_USE_COUNT;
+	if (!irq)
+		return -ENODEV;
+
+	/* We do a request_region() to register /proc/ioports info. */
+	if (!request_region(ioaddr, EPIC_TOTAL_SIZE, EPIC100_MODULE_NAME))
+		return -EBUSY;
+
+	pci_enable_device (pdev);
+
+	/* EPIC-specific code: Soft-reset the chip ere setting as master. */
+	outl(0x0001, ioaddr + GENCTL);
+	
+	pci_set_master (pdev);
+
+// FIXME	if (dev && dev->mem_start) {
+//		option = dev->mem_start;
+//		duplex = (dev->mem_start & 16) ? 1 : 0;
+//	}
+//	else 
+
+	card_idx++;
+	if (card_idx >= 0  &&  card_idx < MAX_UNITS) {
+		if (options[card_idx] >= 0)
+			option = options[card_idx];
+		if (full_duplex[card_idx] >= 0)
+			duplex = full_duplex[card_idx];
 	}
+
+	dev = init_etherdev(NULL, sizeof (*ep));
+	if (!dev)
+		goto err_out_free_region;
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
+		   dev->name, epic100_chip_info[chip_idx].name,
+		   ioaddr, dev->irq);
+
+	/* Bring the chip out of low-power mode. */
+	outl(0x4200, ioaddr + GENCTL);
+	/* Magic?!  If we don't set this bit the MII interface won't work. */
+	outl(0x0008, ioaddr + TEST1);
+
+	/* Turn on the MII transceiver. */
+	outl(0x12, ioaddr + MIICfg);
+	if (chip_idx == 1)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+	outl(0x0200, ioaddr + GENCTL);
+
+	/* This could also be read from the EEPROM. */
+	for (i = 0; i < 3; i++)
+		((u16 *)dev->dev_addr)[i] = inw(ioaddr + LAN0 + i*4);
+
+	for (i = 0; i < 5; i++)
+		printk("%2.2x:", dev->dev_addr[i]);
+	printk("%2.2x.\n", dev->dev_addr[i]);
+
+	if (epic_debug > 1) {
+		printk(KERN_DEBUG "%s: EEPROM contents\n", dev->name);
+		for (i = 0; i < 64; i++)
+			printk(" %4.4x%s", read_eeprom(ioaddr, i),
+				   i % 16 == 15 ? "\n" : "");
+	}
+
+	/* The data structures must be quadword aligned,
+	 * init_etherdev ensures this */
+	ep = dev->priv;
+	memset(ep, 0, sizeof(*ep));
+
+	ep->lock = SPIN_LOCK_UNLOCKED;
+	ep->chip_id = pdev->device;
+	ep->pdev = pdev;
+	pdev->driver_data = dev;
+
+	/* Find the connected MII xcvrs.
+	   Doing this in open() would allow detecting external xcvrs later, but
+	   takes too much time. */
+	{
+		int phy, phy_idx;
+		for (phy = 1, phy_idx = 0; phy < 32 && phy_idx < sizeof(ep->phys);
+			 phy++) {
+			int mii_status = mdio_read(ioaddr, phy, 1);
+			if (mii_status != 0xffff  && mii_status != 0x0000) {
+				ep->phys[phy_idx++] = phy;
+				printk(KERN_INFO "%s: MII transceiver #%d control "
+					   "%4.4x status %4.4x.\n"
+					   KERN_INFO "%s:  Autonegotiation advertising %4.4x "
+					   "link partner %4.4x.\n",
+					   dev->name, phy, mdio_read(ioaddr, phy, 0), mii_status,
+					   dev->name, mdio_read(ioaddr, phy, 4),
+					   mdio_read(ioaddr, phy, 5));
+			}
+		}
+		if (phy_idx == 0) {
+			printk(KERN_WARNING "%s: ***WARNING***: No MII transceiver found!\n",
+				   dev->name);
+			/* Use the known PHY address of the EPII. */
+			ep->phys[0] = 3;
+		}
+	}
+
+	/* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */
+	if (ep->chip_id == 6)
+		outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL);
+	outl(0x0008, ioaddr + GENCTL);
+
+	/* The lower four bits are the media type. */
+	ep->force_fd = duplex;
+	ep->default_port = option;
+	if (ep->default_port)
+		ep->medialock = 1;
+
+	/* The Epic-specific entries in the device structure. */
+	dev->open = epic_open;
+	dev->hard_start_xmit = epic_start_xmit;
+	dev->stop = epic_close;
+	dev->get_stats = epic_get_stats;
+	dev->set_multicast_list = set_rx_mode;
+	dev->do_ioctl = mii_ioctl;
+	dev->tx_timeout = epic_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+	
+	netif_stop_queue (dev);
+
+	return 0;
+
+err_out_free_region:
+	release_region(ioaddr, EPIC_TOTAL_SIZE);
+	return -ENODEV;
 }
 
-struct driver_operations epic_ops = {
-	"epic_cb", epic_attach, epic_suspend, epic_resume, epic_detach
-};
 
-#endif  /* Cardbus support */
+static void __devexit epic100_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+
+	unregister_netdev(dev);
+	release_region(dev->base_addr, EPIC_TOTAL_SIZE);
+	kfree(dev);
+}
 
 
-static int __init epic100_init_module(void)
+static void epic100_suspend (struct pci_dev *pdev)
 {
-	if (epic_debug)
-		printk(KERN_INFO "%s", version);
+	struct net_device *dev = pdev->driver_data;
+	long ioaddr = dev->base_addr;
 
-#ifdef CARDBUS
-	register_driver(&epic_ops);
-	return 0;
-#else
-	return epic100_probe();
-#endif
+	epic_pause(dev);
+	/* Put the chip into low-power mode. */
+	outl(0x0008, ioaddr + GENCTL);
 }
 
-static void __exit epic100_cleanup_module(void)
+
+static void epic100_resume (struct pci_dev *pdev)
 {
-	struct net_device *next_dev;
+	struct net_device *dev = pdev->driver_data;
 
-#ifdef CARDBUS
-	unregister_driver(&epic_ops);
-#endif
+	epic_restart (dev);
+}
 
-	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-	while (root_epic_dev) {
-		struct epic_private *ep = (struct epic_private *)root_epic_dev->priv;
-		next_dev = ep->next_module;
-		unregister_netdev(root_epic_dev);
-		release_region(root_epic_dev->base_addr, EPIC_TOTAL_SIZE);
-		kfree(root_epic_dev);
-		root_epic_dev = next_dev;
-		kfree(ep);
-	}
+
+static struct pci_driver epic100_driver = {
+	name:		EPIC100_MODULE_NAME,
+	id_table:	epic100_pci_tbl,
+	probe:		epic100_init_one,
+	remove:		epic100_remove_one,
+	suspend:	epic100_suspend,
+	resume:		epic100_resume,
+};
+
+
+static int __init epic100_init (void)
+{
+	printk (KERN_INFO "%s", version);
+	
+	if (pci_register_driver (&epic100_driver) > 0)
+		return 0;
+	
+	return -ENODEV;
 }
 
-module_init(epic100_init_module);
-module_exit(epic100_cleanup_module);
+
+static void __exit epic100_cleanup (void)
+{
+	pci_unregister_driver (&epic100_driver);
+}
+
+
+module_init(epic100_init);
+module_exit(epic100_cleanup);
 
 
 /*
diff -u --recursive --new-file v2.3.45/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- v2.3.45/linux/drivers/net/ewrk3.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/ewrk3.c	Tue Feb 15 22:39:01 2000
@@ -138,6 +138,7 @@
 
 static const char *version = "ewrk3.c:v0.43 96/8/16 davies@maniac.ultranet.com\n";
 
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.3.45/linux/drivers/net/hamradio/6pack.c linux/drivers/net/hamradio/6pack.c
--- v2.3.45/linux/drivers/net/hamradio/6pack.c	Wed Aug 18 11:38:51 1999
+++ linux/drivers/net/hamradio/6pack.c	Tue Feb 15 17:19:07 2000
@@ -158,24 +158,6 @@
 }
 
 
-/* Set the "sending" flag. */
-static inline void
-sp_lock(struct sixpack *sp)
-{
-	if (test_and_set_bit(0, (void *) &sp->dev->tbusy))  
-		printk(KERN_WARNING "%s: trying to lock already locked device!\n", sp->dev->name);
-}
-
-
-/* Clear the "sending" flag. */
-static inline void
-sp_unlock(struct sixpack *sp)
-{
-	if (!test_and_clear_bit(0, (void *)&sp->dev->tbusy))  
-		printk(KERN_WARNING "%s: trying to unlock already unlocked device!\n", sp->dev->name);
-}
-
-
 /* Send one completely decapsulated IP datagram to the IP layer. */
 
 /* This is the routine that sends the received data to the kernel AX.25.
@@ -226,7 +208,7 @@
 		len = sp->mtu;
 		printk(KERN_DEBUG "%s: truncating oversized transmit packet!\n", sp->dev->name);
 		sp->tx_dropped++;
-		sp_unlock(sp);
+		netif_start_queue(sp->dev);
 		return;
 	}
 
@@ -235,21 +217,21 @@
 	if (p[0] > 5)
 	{
 		printk(KERN_DEBUG "%s: invalid KISS command -- dropped\n", sp->dev->name);
-		sp_unlock(sp);
+		netif_start_queue(sp->dev);
 		return;
 	}
 
 	if ((p[0] != 0) && (len > 2))
 	{
 		printk(KERN_DEBUG "%s: KISS control packet too long -- dropped\n", sp->dev->name);
-		sp_unlock(sp);
+		netif_start_queue(sp->dev);
 		return;
 	}
 
 	if ((p[0] == 0) && (len < 15))
 	{
 		printk(KERN_DEBUG "%s: bad AX.25 packet to transmit -- dropped\n", sp->dev->name);
-		sp_unlock(sp);
+		netif_start_queue(sp->dev);
 		sp->tx_dropped++;
 		return;
 	}
@@ -301,17 +283,17 @@
 	struct sixpack *sp = (struct sixpack *) tty->disc_data;
 
 	/* First make sure we're connected. */
-	if (!sp || sp->magic != SIXPACK_MAGIC || !sp->dev->start) {
+	if (!sp || sp->magic != SIXPACK_MAGIC || !test_bit(LINK_STATE_START, &sp->dev->state)) {
 		return;
 	}
+
 	if (sp->xleft <= 0)  {
 		/* Now serial buffer is almost free & we can start
 		 * transmission of another packet */
 		sp->tx_packets++;
 		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-		sp_unlock(sp);
 		sp->tx_enable = 0;
-		mark_bh(NET_BH);
+		netif_wake_queue(sp->dev);
 		return;
 	}
 
@@ -331,18 +313,9 @@
 {
 	struct sixpack *sp = (struct sixpack*)(dev->priv);
 
-	if (!dev->start)  
-	{
-		printk(KERN_WARNING "%s: xmit call when iface is down\n", dev->name);
-		return 1;
-	}
-
-	if (dev->tbusy)
-		return 1;
-
 	/* We were not busy, so we are now... :-) */
 	if (skb != NULL) {
-		sp_lock(sp);
+		netif_stop_queue(dev);
 		sp->tx_bytes+=skb->len; /*---2.1.x---*/
 		sp_encaps(sp, skb->data, skb->len);
 		dev_kfree_skb(skb);
@@ -458,8 +431,7 @@
 	sp->tnc_ok      = 0;
 	sp->tx_enable   = 0;
 	
-	dev->tbusy  = 0;
-	dev->start  = 1;
+	netif_start_queue(dev);
 
 	init_timer(&sp->tx_t);
 	init_timer(&sp->resync_t);
@@ -477,9 +449,8 @@
 		return -EBUSY;
 	}
 	sp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-	dev->tbusy = 1;
-	dev->start = 0;
 	
+	netif_stop_queue(dev);
 	return 0;
 }
 
@@ -506,7 +477,7 @@
 
 	struct sixpack *sp = (struct sixpack *) tty->disc_data;
 
-	if (!sp || sp->magic != SIXPACK_MAGIC || !sp->dev->start || !count)
+	if (!sp || sp->magic != SIXPACK_MAGIC || !test_bit(LINK_STATE_START, &sp->dev->state) || !count)
 		return;
 
 	save_flags(flags);
@@ -723,17 +694,13 @@
 
 /* Initialize 6pack control device -- register 6pack line discipline */
 
-#ifdef MODULE
-static int sixpack_init_ctrl_dev(void)
-#else	/* !MODULE */
-int __init sixpack_init_ctrl_dev(struct net_device *dummy)
-#endif	/* !MODULE */
+static int __init sixpack_init_ctrl_dev(void)
 {
 	int status;
 
 	if (sixpack_maxdev < 4) sixpack_maxdev = 4; /* Sanity */
 
-	printk(KERN_INFO "6pack: %s (dynamic channels, max=%d)\n",
+	printk(KERN_INFO "AX.25: 6pack driver, %s (dynamic channels, max=%d)\n",
 	       SIXPACK_VERSION, sixpack_maxdev);
 
 	sixpack_ctrls = (sixpack_ctrl_t **) kmalloc(sizeof(void*)*sixpack_maxdev, GFP_KERNEL);
@@ -766,16 +733,40 @@
 		printk(KERN_WARNING "6pack: can't register line discipline (err = %d)\n", status);
 	}
 
-#ifdef MODULE
 	return status;
-#else
-	/* Return "not found", so that dev_init() will unlink
-	 * the placeholder device entry for us.
-	 */
-	return ENODEV;
-#endif
 }
 
+static void __exit sixpack_cleanup_driver(void)
+{
+	int i;
+
+	if (sixpack_ctrls != NULL) 
+	{
+		for (i = 0; i < sixpack_maxdev; i++)  
+		{
+			if (sixpack_ctrls[i])
+			{
+				/*
+				 * VSV = if dev->start==0, then device
+				 * unregistered while close proc.
+				 */ 
+				if (test_bit(LINK_STATE_START, &sixpack_ctrls[i]->dev.state))
+					unregister_netdev(&(sixpack_ctrls[i]->dev));
+
+				kfree(sixpack_ctrls[i]);
+				sixpack_ctrls[i] = NULL;
+			}
+		}
+		kfree(sixpack_ctrls);
+		sixpack_ctrls = NULL;
+	}
+	if ((i = tty_register_ldisc(N_6PACK, NULL)))  
+	{
+		printk(KERN_WARNING "6pack: can't unregister line discipline (err = %d)\n", i);
+	}
+}
+
+
 /* Initialize the 6pack driver.  Called by DDI. */
 int
 sixpack_init(struct net_device *dev)
@@ -809,6 +800,7 @@
 	dev->type		= ARPHRD_AX25;
 	dev->tx_queue_len	= 10;
 	dev->rebuild_header	= sp_rebuild_header;
+	dev->tx_timeout		= NULL;
 
 	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);	/* Only activated in AX.25 mode */
 	memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);	/*    ""      ""       ""    "" */
@@ -821,45 +813,8 @@
 	return 0;
 }
 
-#ifdef MODULE
 
-int
-init_module(void)
-{
-	return sixpack_init_ctrl_dev();
-}
 
-void
-cleanup_module(void)
-{
-	int i;
-
-	if (sixpack_ctrls != NULL) 
-	{
-		for (i = 0; i < sixpack_maxdev; i++)  
-		{
-			if (sixpack_ctrls[i])
-			{
-				/*
-				 * VSV = if dev->start==0, then device
-				 * unregistered while close proc.
-				 */ 
-				if (sixpack_ctrls[i]->dev.start)
-					unregister_netdev(&(sixpack_ctrls[i]->dev));
-
-				kfree(sixpack_ctrls[i]);
-				sixpack_ctrls[i] = NULL;
-			}
-		}
-		kfree(sixpack_ctrls);
-		sixpack_ctrls = NULL;
-	}
-	if ((i = tty_register_ldisc(N_6PACK, NULL)))  
-	{
-		printk(KERN_WARNING "6pack: can't unregister line discipline (err = %d)\n", i);
-	}
-}
-#endif /* MODULE */
 
 /* ----> 6pack timer interrupt handler and friends. <---- */
 static void 
@@ -1127,3 +1082,9 @@
 		sp->rx_count = 0;
 	}
 }
+
+
+MODULE_AUTHOR("Andreas Könsgen <ajk@ccac.rwth-aachen.de>");
+MODULE_DESCRIPTION("6pack driver for AX.25");
+module_init(sixpack_init_ctrl_dev);
+module_exit(sixpack_cleanup_driver);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/hamradio/bpqether.c linux/drivers/net/hamradio/bpqether.c
--- v2.3.45/linux/drivers/net/hamradio/bpqether.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/hamradio/bpqether.c	Tue Feb 15 17:19:07 2000
@@ -222,7 +222,7 @@
 
 	dev = bpq_get_ax25_dev(dev);
 
-	if (dev == NULL || test_bit(LINK_STATE_START, &dev->state) == 0) {
+	if (dev == NULL || !test_bit(LINK_STATE_START, &dev->state)) {
 		kfree_skb(skb);
 		return 0;
 	}
@@ -275,7 +275,8 @@
 	 * Just to be *really* sure not to send anything if the interface
 	 * is down, the ethernet device may have gone.
 	 */
-	if (!test_bit(LINK_STATE_START, &dev->state)) {
+	if (!test_bit(LINK_STATE_START, &dev->state))
+	{
 		bpq_check_devices(dev);
 		kfree_skb(skb);
 		return -ENODEV;
@@ -324,7 +325,7 @@
 	bpq->stats.tx_bytes+=skb->len;
   
 	dev_queue_xmit(skb);
-
+	netif_wake_queue(dev);
 	return 0;
 }
 
@@ -407,7 +408,9 @@
 {
 	if (bpq_check_devices(dev))
 		return -ENODEV;		/* oops, it's gone */
+	
 	MOD_INC_USE_COUNT;
+
 	netif_start_queue(dev);
 	return 0;
 }
@@ -614,7 +617,7 @@
  * Initialize driver. To be called from af_ax25 if not compiled as a
  * module
  */
-int __init bpq_init(void)
+static int __init bpq_init_driver(void)
 {
 	struct net_device *dev;
 
@@ -623,7 +626,7 @@
 
 	register_netdevice_notifier(&bpq_dev_notifier);
 
-	printk(KERN_INFO "AX.25 ethernet driver version 0.01\n");
+	printk(KERN_INFO "AX.25: bpqether driver version 0.01\n");
 
 	proc_net_create ("bpqether", 0, bpq_get_info);
 
@@ -636,22 +639,10 @@
 		}
 	}
 	read_unlock_bh(&dev_base_lock);
-out:
 	return 0;
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@lykos.oche.de>");
-MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
-
-int init_module(void)
-{
-	return bpq_init();
-}
-
-void cleanup_module(void)
+static void __exit bpq_cleanup_driver(void)
 {
 	struct bpqdev *bpq;
 
@@ -664,4 +655,8 @@
 	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
 		unregister_netdev(&bpq->axdev);
 }
-#endif
+
+MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@poboxes.com>");
+MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
+module_init(bpq_init_driver);
+module_exit(bpq_cleanup_driver);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/hamradio/hdlcdrv.c linux/drivers/net/hamradio/hdlcdrv.c
--- v2.3.45/linux/drivers/net/hamradio/hdlcdrv.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/hamradio/hdlcdrv.c	Tue Feb 15 11:45:00 2000
@@ -381,11 +381,11 @@
 			if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
 				s->hdlctx.tx_state = 0;
 				s->hdlctx.numflags = 1;
-				dev_kfree_skb(skb);
+				dev_kfree_skb_irq(skb);
 				break;
 			}
 			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
-			dev_kfree_skb(skb);
+			dev_kfree_skb_irq(skb);
 			s->hdlctx.bp = s->hdlctx.buffer;
 			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
 			s->hdlctx.len = pkt_len+2; /* the appended CRC */
diff -u --recursive --new-file v2.3.45/linux/drivers/net/hamradio/scc.c linux/drivers/net/hamradio/scc.c
--- v2.3.45/linux/drivers/net/hamradio/scc.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/hamradio/scc.c	Tue Feb 15 17:19:07 2000
@@ -1,7 +1,7 @@
 #define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $"
 
 #define VERSION "3.0"
-#define BANNER  "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
+#define BANNER  "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n"
 
 /*
  * Please use z8530drv-utils-3.0 with this version.
@@ -19,7 +19,7 @@
 
    ********************************************************************
 
-	Copyright (c) 1993, 1998 Joerg Reuter DL1BKE
+	Copyright (c) 1993, 2000 Joerg Reuter DL1BKE
 
 	portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -104,6 +104,9 @@
    		flags that aren't... Restarting the DPLL does not help
    		either, it resynchronizes too slow and the first received
    		frame gets lost.
+   2000-02-13	Fixed for new network driver interface changes, still
+   		does TX timeouts itself since it uses its own queue
+   		scheme.
 
    Thanks to all who contributed to this driver with ideas and bug
    reports!
@@ -236,9 +239,6 @@
 static int Nchips = 0;
 static io_port Vector_Latch = 0;
 
-MODULE_AUTHOR("Joerg Reuter <jreuter@poboxes.com>");
-MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
-MODULE_SUPPORTED_DEVICE("scc");
 
 /* ******************************************************************** */
 /* *			Port Access Functions			      * */
@@ -314,17 +314,6 @@
 /* *			Some useful macros			      * */
 /* ******************************************************************** */
 
-
-static inline void scc_lock_dev(struct scc_channel *scc)
-{
-	netif_stop_queue(scc->dev);
-}
-
-static inline void scc_unlock_dev(struct scc_channel *scc)
-{
-	netif_wake_queue(scc->dev);
-}
-
 static inline void scc_discard_buffers(struct scc_channel *scc)
 {
 	unsigned long flags;
@@ -382,7 +371,7 @@
 	if(scc->rx_buff != NULL)		/* did we receive something? */
 	{
 		scc->stat.rxerrs++;  /* then count it as an error */
-		kfree_skb(scc->rx_buff);
+		dev_kfree_skb_irq(scc->rx_buff);
 		scc->rx_buff = NULL;
 	}
 }
@@ -411,7 +400,7 @@
 	{
 		skb = skb_dequeue(&scc->tx_queue);
 		scc->tx_buff = skb;
-		scc_unlock_dev(scc);
+		netif_wake_queue(scc->dev);
 
 		if (skb == NULL)
 		{
@@ -422,7 +411,7 @@
 		
 		if (skb->len == 0)		/* Paranoia... */
 		{
-			dev_kfree_skb(skb);
+			dev_kfree_skb_irq(skb);
 			scc->tx_buff = NULL;
 			scc_tx_done(scc);
 			Outb(scc->ctrl, RES_Tx_P);
@@ -448,7 +437,7 @@
 	{
 		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
 		cl(scc, R10, ABUNDER);		/* send CRC */
-		dev_kfree_skb(skb);
+		dev_kfree_skb_irq(skb);
 		scc->tx_buff = NULL;
 		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
 		return;
@@ -533,7 +522,7 @@
 
 		if (scc->tx_buff != NULL)
 		{
-			dev_kfree_skb(scc->tx_buff);
+			dev_kfree_skb_irq(scc->tx_buff);
 			scc->tx_buff = NULL;
 		}
 		
@@ -583,7 +572,7 @@
 #ifdef notdef
 		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
 #endif
-		kfree_skb(skb);
+		dev_kfree_skb_irq(skb);
 		scc->rx_buff = NULL;
 		Inb(scc->data);
 		or(scc, R3, ENT_HM);
@@ -613,7 +602,7 @@
 		or(scc,R3,ENT_HM);               /* enter hunt mode for next flag */
 		
 		if (skb != NULL) 
-			kfree_skb(skb);
+			dev_kfree_skb_irq(skb);
 		scc->rx_buff = NULL;
 	}
 
@@ -629,7 +618,7 @@
 			scc->rx_buff = NULL;
 			scc->stat.rxframes++;
 		} else {				/* a bad frame */
-			kfree_skb(skb);
+			dev_kfree_skb_irq(skb);
 			scc->rx_buff = NULL;
 			scc->stat.rxerrs++;
 		}
@@ -1112,7 +1101,7 @@
 			scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
 	}
 
-	scc_unlock_dev(scc);
+	netif_wake_queue(scc->dev);
 }
 
 
@@ -1163,7 +1152,7 @@
 		if (skb_queue_len(&scc->tx_queue) == 0)	/* nothing to send */
 		{
 			scc->stat.tx_state = TXS_IDLE;
-			scc_unlock_dev(scc);	/* t_maxkeyup locked it. */
+			netif_wake_queue(scc->dev);	/* t_maxkeyup locked it. */
 			return;
 		}
 
@@ -1243,7 +1232,7 @@
  	}
  	
  	scc->stat.tx_state = TXS_IDLE;
-	scc_unlock_dev(scc);
+	netif_wake_queue(scc->dev);
 }
 
 
@@ -1257,14 +1246,13 @@
 	struct scc_channel *scc = (struct scc_channel *) channel;
 
 	del_timer(&scc->tx_t);
-	scc_lock_dev(scc);
+	netif_stop_queue(scc->dev);	/* don't pile on the wabbit! */
 
 	scc_discard_buffers(scc);
-
 	scc->stat.txerrs++;
 	scc->stat.tx_state = TXS_IDLE;
-	
-	scc_unlock_dev(scc);
+
+	netif_wake_queue(scc->dev);	
 }
 
 /* MAXKEYUP timeout
@@ -1285,7 +1273,7 @@
 	 * accept new data.
 	 */
 
-	scc_lock_dev(scc);
+	netif_stop_queue(scc->dev);
 	scc_discard_buffers(scc);
 
 	del_timer(&scc->tx_t);
@@ -1460,8 +1448,7 @@
 	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
 	Outb(scc->ctrl,RES_EXT_INT);
 
-	scc_unlock_dev(scc);
-	
+	netif_wake_queue(scc->dev);
 	restore_flags(flags);
 }
 
@@ -1474,7 +1461,7 @@
 	save_flags(flags);
 	cli();
 
-	scc_lock_dev(scc);
+	netif_stop_queue(scc->dev);
 	scc_discard_buffers(scc);
 
 	del_timer(&scc->tx_wdog);
@@ -1497,7 +1484,6 @@
 	Outb(scc->ctrl,RES_EXT_INT);
 
 	scc_key_trx(scc, TX_ON);
-	
 	restore_flags(flags);
 }
 
@@ -1583,7 +1569,9 @@
 	dev = scc->dev;
 	memset(dev, 0, sizeof(struct net_device));
 
-	buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
+	if ((buf = (unsigned char *) kmalloc(10, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
 	strcpy(buf, name);
 
 	dev->priv = (void *) scc;
@@ -1627,6 +1615,7 @@
 	dev->set_mac_address = scc_net_set_mac_address;
 	dev->get_stats       = scc_net_get_stats;
 	dev->do_ioctl        = scc_net_ioctl;
+	dev->tx_timeout      = NULL;
 
 	memcpy(dev->broadcast, ax25_bcast,  AX25_ADDR_LEN);
 	memcpy(dev->dev_addr,  ax25_nocall, AX25_ADDR_LEN);
@@ -1647,14 +1636,11 @@
 {
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return -ENODEV;
-
  	if (!scc->init)
 		return -EINVAL;
 
 	MOD_INC_USE_COUNT;
-	
+
 	scc->tx_buff = NULL;
 	skb_queue_head_init(&scc->tx_queue);
  
@@ -1671,10 +1657,7 @@
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	unsigned long flags;
 
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return -ENODEV;
-
-	MOD_DEC_USE_COUNT;
+	netif_stop_queue(dev);
 
 	save_flags(flags); 
 	cli();
@@ -1690,7 +1673,7 @@
 	
 	scc_discard_buffers(scc);
 
-	netif_stop_queue(dev);
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1700,7 +1683,7 @@
 {
 	if (skb->len == 0)
 	{
-		kfree_skb(skb);
+		dev_kfree_skb_irq(skb);
 		return;
 	}
 		
@@ -1722,12 +1705,6 @@
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	unsigned long flags;
 	char kisscmd;
-	
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-	{
-		dev_kfree_skb(skb);
-		return 0;
-	}
 
 	if (skb->len > scc->stat.bufsize || skb->len < 2)
 	{
@@ -1749,18 +1726,18 @@
 		return 0;
 	}
 
-	save_flags(flags); 
+	save_flags(flags);
 	cli();
 	
 	if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len)
 	{
 		struct sk_buff *skb_del;
-		skb_del = __skb_dequeue(&scc->tx_queue);
+		skb_del = skb_dequeue(&scc->tx_queue);
 		dev_kfree_skb(skb_del);
 	}
-	__skb_queue_tail(&scc->tx_queue, skb);
-
+	skb_queue_tail(&scc->tx_queue, skb);
 	dev->trans_start = jiffies;
+	
 
 	/*
 	 * Start transmission if the trx state is idle or
@@ -1777,8 +1754,7 @@
 			scc_start_tx_timer(scc, t_dwait, 0);
 	}
 
-	restore_flags(flags);
-
+	restore_flags(flags);	
 	return 0;
 }
 
@@ -1807,9 +1783,6 @@
 	struct scc_channel *scc;
 	
 	scc = (struct scc_channel *) dev->priv;
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return -EINVAL;
-		
 	arg = (void *) ifr->ifr_data;
 	
 	if (!Driver_Initialized)
@@ -2048,9 +2021,6 @@
 {
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return NULL;
-		
 	scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
 	scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
 	scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
@@ -2182,7 +2152,7 @@
 /* * 			Init SCC driver 			      * */
 /* ******************************************************************** */
 
-int __init scc_init (void)
+static int __init scc_init_driver (void)
 {
 	int chip, chan, k, result;
 	char devname[10];
@@ -2218,25 +2188,7 @@
 	return 0;
 }
 
-/* ******************************************************************** */
-/* *			    Module support 			      * */
-/* ******************************************************************** */
-
-
-#ifdef MODULE
-int init_module(void)
-{
-	int result = 0;
-	
-	result = scc_init();
-
-	if (result == 0)
-		printk(KERN_INFO "Copyright 1993,1998 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n");
-		
-	return result;
-}
-
-void cleanup_module(void)
+static void __exit scc_cleanup_driver(void)
 {
 	long flags;
 	io_port ctrl;
@@ -2279,4 +2231,9 @@
 
 	scc_net_procfs_remove();
 }
-#endif
+
+MODULE_AUTHOR("Joerg Reuter <jreuter@poboxes.com>");
+MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards");
+MODULE_SUPPORTED_DEVICE("scc");
+module_init(scc_init_driver);
+module_exit(scc_cleanup_driver);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/ltpc.c linux/drivers/net/ltpc.c
--- v2.3.45/linux/drivers/net/ltpc.c	Thu Feb 10 17:11:10 2000
+++ linux/drivers/net/ltpc.c	Mon Feb 14 15:34:21 2000
@@ -246,23 +246,9 @@
 
 /* Dma Memory related stuff, cribbed directly from 3c505.c */
 
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
-        int order;
-
-        size = (size - 1) >> (PAGE_SHIFT - 1);
-        order = -1;
-        do {
-                size >>= 1;
-                order++;
-        } while (size);
-        return order;
-}
-
 static unsigned long dma_mem_alloc(int size)
 {
-        int order = __get_order(size);
+        int order = get_order(size);
 
         return __get_dma_pages(GFP_KERNEL, order);
 }
@@ -1364,7 +1350,7 @@
 
 	if(debug&DEBUG_VERBOSE) printk("free_pages\n");
 
-	free_pages( (unsigned long) ltdmabuf, __get_order(1000));
+	free_pages( (unsigned long) ltdmabuf, get_order(1000));
 	ltdmabuf=NULL;
 	ltdmacbuf=NULL;
 
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/3c574_cs.c linux/drivers/net/pcmcia/3c574_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/3c574_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/3c574_cs.c	Tue Feb 15 08:53:46 2000
@@ -105,11 +105,6 @@
 MODULE_PARM(irq_list, "1-4i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(full_duplex, "i");
-#ifdef BROKEN_FEATURES
-MODULE_PARM(use_fifo_buffer, "i");
-MODULE_PARM(use_memory_ops, "i");
-MODULE_PARM(no_wait, "i");
-#endif
 
 /* Now-standard PC card module parameters. */
 static u_int irq_mask = 0xdeb8;			/* IRQ3,4,5,7,9,10,11,12,14,15 */
@@ -124,16 +119,6 @@
 /* Force full duplex modes? */
 static int full_duplex = 0;
 
-#ifdef BROKEN_FEATURES
-/* Performance features: best left disabled. */
-/* Set to buffer all Tx/RxFIFO accesses. */
-static int use_fifo_buffer = 0;
-/* Set iff memory ops are faster than I/O ops. */
-static int use_memory_ops = 0;
-/* Set iff disabling the WAIT signal is reliable and faster. */
-static int no_wait = 0;
-#endif
-
 /* To minimize the size of the driver source and make the driver more
    readable not all constants are symbolically defined.
    You'll need the manual if you want to understand driver details anyway. */
@@ -484,27 +469,6 @@
 	CS_CHECK(RequestIRQ, link->handle, &link->irq);
 	CS_CHECK(RequestConfiguration, link->handle, &link->conf);
 
-	dev->mem_start = 0;
-#ifdef BROKEN_FEATURES
-	if (use_memory_ops) {
-		win_req_t req;
-		memreq_t mem;
-		req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM |
-			WIN_ENABLE | WIN_USE_WAIT;
-		req.Base = 0;
-		req.Size = 0x1000;
-		req.AccessSpeed = 0;
-		link->win = (window_handle_t)link->handle;
-		i = CardServices(RequestWindow, &link->win, &req);
-		if (i == CS_SUCCESS) {
-			mem.Page = mem.CardOffset = 0;
-			CardServices(MapMemPage, link->win, &mem);
-			dev->mem_start = (long)(ioremap(req.Base, 0x1000)) + 0x800;
-		} else
-			cs_error(link->handle, RequestWindow, i);
-	}
-#endif
-
 	dev->irq = link->irq.AssignedIRQ;
 	dev->base_addr = link->io.BasePort1;
 
@@ -551,10 +515,6 @@
 	for (i = 0; i < 6; i++)
 		printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n"));
 
-	if (dev->mem_start)
-		printk(KERN_INFO"  Acceleration window at memory base %#lx.\n",
-			   dev->mem_start);
-
 	{
 		u_char mcr, *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
 		union wn3_config config;
@@ -629,8 +589,6 @@
 static void tc574_release(u_long arg)
 {
 	dev_link_t *link = (dev_link_t *)arg;
-	struct el3_private *lp = link->priv;
-	struct net_device *dev = &lp->dev;
 
 	DEBUG(0, "3c574_release(0x%p)\n", link);
 
@@ -644,10 +602,6 @@
 	CardServices(ReleaseConfiguration, link->handle);
 	CardServices(ReleaseIO, link->handle, &link->io);
 	CardServices(ReleaseIRQ, link->handle, &link->irq);
-	if (link->win) {
-		iounmap((void *)(dev->mem_start - 0x800));
-		CardServices(ReleaseWindow, link->win);
-	}
 
 	link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
 
@@ -674,6 +628,7 @@
 		link->state &= ~DEV_PRESENT;
 		if (link->state & DEV_CONFIG) {
 			netif_stop_queue (dev);
+			clear_bit(LINK_STATE_START, &dev->state);
 			link->release.expires = jiffies + HZ/20;
 			add_timer(&link->release);
 		}
@@ -689,6 +644,7 @@
 		if (link->state & DEV_CONFIG) {
 			if (link->open) {
 				netif_stop_queue (dev);
+				clear_bit(LINK_STATE_START, &dev->state);
 			}
 			CardServices(ReleaseConfiguration, link->handle);
 		}
@@ -701,6 +657,7 @@
 			CardServices(RequestConfiguration, link->handle, &link->conf);
 			if (link->open) {
 				tc574_reset(dev);
+				set_bit(LINK_STATE_START, &dev->state);
 				netif_start_queue (dev);
 			}
 		}
@@ -837,19 +794,6 @@
 
 	wait_for_completion(dev, TotalReset|0x10);
 
-#ifdef BROKEN_FEATURES
-	/* Set the PIO ctrl bits in the PC card LAN COR using Runner window 1. */
-	if (dev->mem_start || no_wait) {
-		u8 lan_cor;
-		outw(1<<11, ioaddr + RunnerRdCtrl);
-		lan_cor = inw(ioaddr) & ~0x30;
-		if (dev->mem_start)		/* Iff use_memory_ops worked! */
-			lan_cor |= 0x10;
-		if (no_wait)
-			lan_cor |= 0x20;
-		outw(lan_cor, ioaddr);
-	}
-#endif
 	/* Clear any transactions in progress. */
 	outw(0, ioaddr + RunnerWrCtrl);
 	outw(0, ioaddr + RunnerRdCtrl);
@@ -883,10 +827,10 @@
 		inb(ioaddr + i);
 	inw(ioaddr + 10);
 	inw(ioaddr + 12);
-
 	EL3WINDOW(4);
-	/* New: On the Vortex/Odie we must also clear the BadSSD counter.. */
 	inb(ioaddr + 12);
+	inb(ioaddr + 13);
+
 	/* .. enable any extra statistics bits.. */
 	outw(0x0040, ioaddr + Wn4_NetDiag);
 	/* .. re-sync MII and re-fill what NWay is advertising. */
@@ -974,9 +918,6 @@
 static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	ioaddr_t ioaddr = dev->base_addr;
-#ifdef BROKEN_FEATURES
-	long flags = 0;
-#endif
 
 	DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
 		  "status %4.4x.\n", dev->name, (long)skb->len,
@@ -984,38 +925,9 @@
 
 	netif_stop_queue (dev);
 	
-#ifdef BROKEN_FEATURES
-	if (use_fifo_buffer) {
-		/* Avoid other accesses to the chip while RunnerWrCtrl is non-zero. */
-		spin_lock_irqsave(&lp->lock, flags);
-		outw((((skb->len + 7)>>2)<<1), ioaddr + RunnerWrCtrl);
-		DEBUG(0, "TxFree %x, tx length %x, RunnerWrCtrl is %4.4x.\n",
-			  inw(ioaddr+TxFree), skb->len, inw(ioaddr+RunnerWrCtrl));
-	}
-
-	/* Put out the doubleword header... */
-	/* ... and the packet rounded to a doubleword. */
-	if (dev->mem_start) {
-		writew(skb->len, (void *)dev->mem_start);
-		writew(0, (void *)dev->mem_start);
-		copy_to_pc((void*)dev->mem_start, skb->data, (skb->len+3)&~3);
-	} else {
-		outw(skb->len, ioaddr + TX_FIFO);
-		outw(0, ioaddr + TX_FIFO);
-		outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
-	}
-
-	if (use_fifo_buffer) {
-		DEBUG(0, " RunnerWr/RdCtrl is %4.4x/%4.4x, TxFree %x.\n",
-			  inw(ioaddr + RunnerWrCtrl), inw(ioaddr + RunnerRdCtrl),
-			  inw(ioaddr + TxFree));
-		spin_unlock_irqrestore (&lp->lock, flags);
-	}
-#else
 	outw(skb->len, ioaddr + TX_FIFO);
 	outw(0, ioaddr + TX_FIFO);
 	outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
-#endif
 
 	dev->trans_start = jiffies;
 
@@ -1041,27 +953,24 @@
 	ioaddr_t ioaddr, status;
 	int work_budget = max_interrupt_work;
 
-	if (lp == NULL)
+    if (!test_bit(LINK_STATE_START, &dev->state))
 		return;
 
 	spin_lock (&lp->lock);
 
 	ioaddr = dev->base_addr;
 
-#ifdef PCMCIA_DEBUG
 	DEBUG(3, "%s: interrupt, status %4.4x.\n",
 		  dev->name, inw(ioaddr + EL3_STATUS));
-#endif
 
 	while ((status = inw(ioaddr + EL3_STATUS)) &
 		   (IntLatch | RxComplete | RxEarly | StatsFull)) {
 
-#if 0
-		if ((dev->start == 0) || ((status & 0xe000) != 0x2000)) {
+		if (!test_bit(LINK_STATE_START, &dev->state) ||
+			((status & 0xe000) != 0x2000)) {
 			DEBUG(1, "%s: Interrupt from dead card\n", dev->name);
 			break;
 		}
-#endif
 
 		if (status & RxComplete)
 			work_budget = el3_rx(dev, work_budget);
@@ -1119,10 +1028,8 @@
 		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
 	}
 
-#ifdef PCMCIA_DEBUG
 	DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
 		  dev->name, inw(ioaddr + EL3_STATUS));
-#endif
 
 	spin_unlock (&lp->lock);
 }
@@ -1140,9 +1047,8 @@
     u_long flags;
 	u_short /* cable, */ media, partner;
 	
-#if 0
-    if (dev->start == 0) goto reschedule;
-#endif
+    if (!test_bit(LINK_STATE_START, &dev->state))
+		goto reschedule;
 	
     /* Check for pending interrupt with expired latency timer: with
        this, we can limp along even if the interrupt is blocked */
@@ -1253,6 +1159,12 @@
 	EL3WINDOW(4);
 	inb(ioaddr + 12);
 
+	{
+		u8 up = inb(ioaddr + 13);
+		lp->stats.rx_bytes += (up & 0x0f) << 16;
+		lp->stats.tx_bytes += (up & 0xf0) << 12;
+	}
+
 	EL3WINDOW(1);
 }
 
@@ -1289,30 +1201,8 @@
 				skb->dev = dev;
 				skb_reserve(skb, 2);
 
-#ifdef BROKEN_FEATURES
-				if (use_fifo_buffer) {
-					outw(((pkt_len+3)>>2)<<1, ioaddr + RunnerRdCtrl);
-					DEBUG(0,"Start Rx %x -- RunnerRdCtrl is %4.4x.\n",
-						  pkt_len, inw(ioaddr + RunnerRdCtrl));
-				}
-				if (dev->mem_start) {
-					copy_from_pc(skb_put(skb, pkt_len),
-								 (void*)dev->mem_start, (pkt_len+3)&~3);
-				} else {
-					insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
-							((pkt_len+3)>>2));
-				}
-				if (use_fifo_buffer) {
-					DEBUG(0," RunnerRdCtrl is now %4.4x.\n",
-						  inw(ioaddr + RunnerRdCtrl));
-					outw(0, ioaddr + RunnerRdCtrl);
-					DEBUG(0, " Rx packet with data %2.2x:%2.2x:%2.2x\n",
-						  skb->head[0], skb->head[1], skb->head[2]);
-				}
-#else
 				insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
 						((pkt_len+3)>>2));
-#endif
 
 				skb->protocol = eth_type_trans(skb, dev);
 				netif_rx(skb);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/3c589_cs.c linux/drivers/net/pcmcia/3c589_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/3c589_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/3c589_cs.c	Tue Feb 15 15:31:07 2000
@@ -4,7 +4,7 @@
     
     Copyright (C) 1999 David A. Hinds -- dhinds@pcmcia.sourceforge.org
 
-    3c589_cs.c 1.143 1999/12/30 21:28:10
+    3c589_cs.c 1.145 2000/02/11 03:11:51
 
     The network driver code is based on Donald Becker's 3c589 code:
     
@@ -117,7 +117,7 @@
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"3c589_cs.c 1.143 1999/12/30 21:28:10 (David Hinds)";
+"3c589_cs.c 1.145 2000/02/11 03:11:51 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -513,6 +513,7 @@
 	link->state &= ~DEV_PRESENT;
 	if (link->state & DEV_CONFIG) {
 	    netif_stop_queue (dev);
+	    clear_bit(LINK_STATE_START, &dev->state);
 	    link->release.expires = jiffies + HZ/20;
 	    add_timer(&link->release);
 	}
@@ -528,6 +529,7 @@
 	if (link->state & DEV_CONFIG) {
 	    if (link->open) {
 		netif_stop_queue (dev);
+		clear_bit(LINK_STATE_START, &dev->state);
 	    }
 	    CardServices(ReleaseConfiguration, link->handle);
 	}
@@ -540,6 +542,7 @@
 	    CardServices(RequestConfiguration, link->handle, &link->conf);
 	    if (link->open) {
 		tc589_reset(dev);
+		set_bit(LINK_STATE_START, &dev->state);
 		netif_start_queue (dev);
 	    }
 	}
@@ -748,22 +751,22 @@
 	  inw(ioaddr + EL3_STATUS));
 
     netif_stop_queue (dev);
-    if (1) {
+    {
 	struct el3_private *lp = (struct el3_private *)dev->priv;
 	lp->stats.tx_bytes += skb->len;
-	/* Put out the doubleword header... */
-	outw(skb->len, ioaddr + TX_FIFO);
-	outw(0x00, ioaddr + TX_FIFO);
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-	
-	dev->trans_start = jiffies;
-	if (inw(ioaddr + TX_FREE) > 1536) {
-	    netif_start_queue (dev);
-	} else
-	    /* Interrupt us when the FIFO has room for max-sized packet. */
-	    outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
     }
+    /* Put out the doubleword header... */
+    outw(skb->len, ioaddr + TX_FIFO);
+    outw(0x00, ioaddr + TX_FIFO);
+    /* ... and the packet rounded to a doubleword. */
+    outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+    dev->trans_start = jiffies;
+    if (inw(ioaddr + TX_FREE) > 1536) {
+	netif_start_queue (dev);
+    } else
+	/* Interrupt us when the FIFO has room for max-sized packet. */
+	outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
 
     dev_kfree_skb(skb);
     pop_tx_status(dev);
@@ -779,29 +782,20 @@
     ioaddr_t ioaddr, status;
     int i = 0;
     
-    if (lp == NULL)
+    if (!test_bit(LINK_STATE_START, &dev->state))
 	return;
     ioaddr = dev->base_addr;
 
-#ifdef PCMCIA_DEBUG
-    if (dev->interrupt) {
-	printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n",
-	       dev->name);
-	return;
-    }
-    dev->interrupt = 1;
     DEBUG(3, "%s: interrupt, status %4.4x.\n",
 	  dev->name, inw(ioaddr + EL3_STATUS));
-#endif
     
     while ((status = inw(ioaddr + EL3_STATUS)) &
 	(IntLatch | RxComplete | StatsFull)) {
-#if 0
-	if ((dev->start == 0) || ((status & 0xe000) != 0x2000)) {
+	if (!test_bit(LINK_STATE_START, &dev->state) ||
+	    ((status & 0xe000) != 0x2000)) {
 	    DEBUG(1, "%s: interrupt from dead card\n", dev->name);
 	    break;
 	}
-#endif
 	
 	if (status & RxComplete)
 	    el3_rx(dev);
@@ -811,8 +805,6 @@
 	    /* There's room in the FIFO for a full-sized packet. */
 	    outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
 	    netif_wake_queue (dev);
-	} else {
-	    netif_stop_queue (dev);
 	}
 	
 	if (status & TxComplete)
@@ -860,11 +852,8 @@
     }
 
     lp->last_irq = jiffies;
-#ifdef PCMCIA_DEBUG
     DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
 	  dev->name, inw(ioaddr + EL3_STATUS));
-    dev->interrupt = 0;
-#endif
     return;
 }
 
@@ -876,9 +865,8 @@
     u_short media, errs;
     u_long flags;
 
-#if 0
-    if (dev->start == 0) goto reschedule;
-#endif
+    if (!test_bit(LINK_STATE_START, &dev->state))
+	goto reschedule;
 
     EL3WINDOW(1);
     /* Check for pending interrupt with expired latency timer: with
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in
--- v2.3.45/linux/drivers/net/pcmcia/Config.in	Fri Jan 28 15:09:07 2000
+++ linux/drivers/net/pcmcia/Config.in	Mon Feb 14 15:26:31 2000
@@ -20,7 +20,6 @@
    if [ "$CONFIG_CARDBUS" = "y" ]; then
       dep_tristate '  3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m
       dep_tristate '  DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP m
-      dep_tristate '  SMC EPIC CardBus support' CONFIG_PCMCIA_EPIC100 m
    fi
 
    bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile
--- v2.3.45/linux/drivers/net/pcmcia/Makefile	Tue Jan  4 13:57:17 2000
+++ linux/drivers/net/pcmcia/Makefile	Mon Feb 14 15:26:31 2000
@@ -41,7 +41,6 @@
 # Cardbus client drivers
 obj-$(CONFIG_PCMCIA_3C575)	+= 3c575_cb.o
 obj-$(CONFIG_PCMCIA_TULIP)	+= tulip_cb.o
-obj-$(CONFIG_PCMCIA_EPIC100)	+= epic100_cb.o
 
 O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
 OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
@@ -49,6 +48,3 @@
 MX_OBJS		:= $(filter     $(export-objs), $(obj-m))
 
 include $(TOPDIR)/Rules.make
-
-epic100_cb.o: ../epic100.c
-	$(CC) $(CFLAGS) -DMODULE -DCARDBUS -c -o $@ ../epic100.c
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/fmvj18x_cs.c linux/drivers/net/pcmcia/fmvj18x_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/fmvj18x_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/fmvj18x_cs.c	Tue Feb 15 08:53:46 2000
@@ -594,6 +594,7 @@
 	link->state &= ~DEV_PRESENT;
 	if (link->state & DEV_CONFIG) {
 	    netif_stop_queue (dev);
+	    clear_bit(LINK_STATE_START, &dev->state);
 	    link->release.expires = jiffies + HZ/20;
 	    add_timer(&link->release);
 	}
@@ -609,6 +610,7 @@
 	if (link->state & DEV_CONFIG) {
 	    if (link->open) {
 	    	netif_stop_queue (dev);
+		clear_bit(LINK_STATE_START, &dev->state);
 	    }
 	    CardServices(ReleaseConfiguration, link->handle);
 	}
@@ -621,6 +623,7 @@
 	    CardServices(RequestConfiguration, link->handle, &link->conf);
 	    if (link->open) {
 		fjn_reset(dev);
+		set_bit(LINK_STATE_START, &dev->state);
 		netif_start_queue (dev);
 	    }
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/netwave_cs.c linux/drivers/net/pcmcia/netwave_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/netwave_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/netwave_cs.c	Tue Feb 15 08:53:46 2000
@@ -1020,6 +1020,7 @@
 	link->state &= ~DEV_PRESENT;
 	if (link->state & DEV_CONFIG) {
 	    netif_stop_queue (dev);
+	    clear_bit(LINK_STATE_START, &dev->state);
 	    link->release.expires = jiffies + 5;
 	    add_timer(&link->release);
 	}
@@ -1035,6 +1036,7 @@
 	if (link->state & DEV_CONFIG) {
 	    if (link->open) {
 		netif_stop_queue (dev);
+		clear_bit(LINK_STATE_START, &dev->state);
 	    }
 	    CardServices(ReleaseConfiguration, link->handle);
 	}
@@ -1047,6 +1049,7 @@
 	    CardServices(RequestConfiguration, link->handle, &link->conf);
 	    if (link->open) {
 		netwave_reset(dev);
+		set_bit(LINK_STATE_START, &dev->state);
 		netif_start_queue (dev);
 	    }
 	}
@@ -1296,7 +1299,7 @@
     dev_link_t *link = &priv->link;
     int i;
     
-    if (dev == NULL)
+    if ((dev == NULL) || !test_bit(LINK_STATE_START, &dev->state))
 	return;
     
     spin_lock (&priv->lock);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/nmclan_cs.c linux/drivers/net/pcmcia/nmclan_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/nmclan_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/nmclan_cs.c	Tue Feb 15 08:53:46 2000
@@ -1153,6 +1153,11 @@
     return;
   }
 
+  if (!test_bit(LINK_STATE_START, &dev->state)) {
+    DEBUG(2, "%s: interrupt from dead card\n", dev->name);
+    goto exception;
+  }
+
   do {
     /* WARNING: MACE_IR is a READ/CLEAR port! */
     status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/pcnet_cs.c linux/drivers/net/pcmcia/pcnet_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/pcnet_cs.c	Thu Feb 10 17:11:10 2000
+++ linux/drivers/net/pcmcia/pcnet_cs.c	Tue Feb 15 08:53:46 2000
@@ -11,7 +11,7 @@
 
     Copyright (C) 1999 David A. Hinds -- dhinds@pcmcia.sourceforge.org
 
-    pcnet_cs.c 1.110 1999/12/06 21:39:18
+    pcnet_cs.c 1.112 2000/02/11 01:24:44
     
     The network driver code is based on Donald Becker's NE2000 code:
 
@@ -72,7 +72,7 @@
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"pcnet_cs.c 1.110 1999/12/06 21:39:18 (David Hinds)";
+"pcnet_cs.c 1.112 2000/02/11 01:24:44 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -211,7 +211,8 @@
       DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
     { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
     { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
-    { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 }
+    { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
+    { /* PCMCIA Technology OEM */ 0x01c8, 0xa0, 0x0c, 0 }
 };
 
 #define NR_INFO		(sizeof(hw_info)/sizeof(hw_info_t))
@@ -306,7 +307,6 @@
 	for (i = 0; i < 4; i++)
 	    link->irq.IRQInfo2 |= 1 << irq_list[i];
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
     ethdev_init(dev);
@@ -596,6 +596,7 @@
     int i, last_ret, last_fn, start_pg, stop_pg, cm_offset;
     int manfid = 0, prodid = 0, has_shmem = 0;
     u_short buf[64];
+    config_info_t conf;
     hw_info_t *hw_info;
 
     DEBUG(0, "pcnet_config(0x%p)\n", link);
@@ -614,6 +615,10 @@
     /* Configure card */
     link->state |= DEV_CONFIG;
 
+    /* Look up current Vcc */
+    CS_CHECK(GetConfigurationInfo, handle, &conf);
+    link->conf.Vcc = conf.Vcc;
+
     tuple.DesiredTuple = CISTPL_MANFID;
     tuple.Attributes = TUPLE_RETURN_COMMON;
     if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) &&
@@ -908,7 +913,6 @@
     free_irq(dev->irq, dev);
     
     link->open--;
-    clear_bit(LINK_STATE_START, &dev->state);
     del_timer(&info->watchdog);
     if (link->state & DEV_STALE_CONFIG) {
 	link->release.expires = jiffies + HZ/20;
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/smc91c92_cs.c linux/drivers/net/pcmcia/smc91c92_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/smc91c92_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/smc91c92_cs.c	Tue Feb 15 08:53:46 2000
@@ -1113,6 +1113,7 @@
 	link->state &= ~DEV_PRESENT;
 	if (link->state & DEV_CONFIG) {
 	    netif_stop_queue (dev);
+	    clear_bit(LINK_STATE_START, &dev->state);
 	    link->release.expires = jiffies + HZ/20;
 	    link->state |= DEV_RELEASE_PENDING;
 	    add_timer(&link->release);
@@ -1129,6 +1130,7 @@
 	if (link->state & DEV_CONFIG) {
 	    if (link->open) {
 		netif_stop_queue (dev);
+		clear_bit(LINK_STATE_START, &dev->state);
 	    }
 	    CardServices(ReleaseConfiguration, link->handle);
 	}
@@ -1152,6 +1154,7 @@
 	    }
 	    if (link->open) {
 		smc_reset(dev);
+		set_bit(LINK_STATE_START, &dev->state);
 		netif_start_queue (dev);
 	    }
 	}
@@ -1503,16 +1506,14 @@
     u_short saved_bank, saved_pointer, mask, status;
     char bogus_cnt = INTR_WORK;		/* Work we are willing to do. */
 
-    if (smc == NULL)
+    if ((smc == NULL) || !test_bit(LINK_STATE_START, &dev->state))
 	return;
     ioaddr = dev->base_addr;
     
     spin_lock (&smc->lock);
 
-#ifdef PCMCIA_DEBUG
     DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
 	  irq, ioaddr);
-#endif
     
     smc->watchdog = 0;
     saved_bank = inw(ioaddr + BANK_SELECT);
@@ -1523,7 +1524,6 @@
 	if (dev->start)
 	    DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent"
 		  "/ejected device.\n", dev->name, irq);
-	dev->interrupt = 0;
 #endif
 	goto irq_done;
     }
@@ -1588,9 +1588,7 @@
 
     spin_unlock (&smc->lock);
 
-#ifdef PCMCIA_DEBUG
     DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
-#endif
 
 irq_done:
     
@@ -1912,9 +1910,8 @@
     ioaddr_t ioaddr = dev->base_addr;
     u_short i, media, saved_bank;
 
-#if 0
-    if (dev->start == 0) goto reschedule;
-#endif
+    if (!test_bit(LINK_STATE_START, &dev->state))
+	goto reschedule;
 
     saved_bank = inw(ioaddr + BANK_SELECT);
     SMC_SELECT_BANK(2);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/wavelan_cs.c linux/drivers/net/pcmcia/wavelan_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/wavelan_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/wavelan_cs.c	Tue Feb 15 08:53:46 2000
@@ -4663,6 +4663,7 @@
 	  {
 	    /* Accept no more transmissions */
       	    netif_stop_queue (dev);
+	    clear_bit(LINK_STATE_START, &dev->state);
 
 	    /* Release the card */
 	    wv_pcmcia_release((u_long) link);
@@ -4701,6 +4702,7 @@
       	    if(link->open)
 	      {
 	      	netif_stop_queue (dev);
+		clear_bit(LINK_STATE_START, &dev->state);
 	      }
       	    CardServices(ReleaseConfiguration, link->handle);
 	  }
@@ -4716,6 +4718,7 @@
       	    if(link->open)	/* If RESET -> True, If RESUME -> False ??? */
 	      {
 		wv_hw_reset(dev);
+		set_bit(LINK_STATE_START, &dev->state);
 		netif_start_queue (dev);
 	      }
 	  }
diff -u --recursive --new-file v2.3.45/linux/drivers/net/pcmcia/xirc2ps_cs.c linux/drivers/net/pcmcia/xirc2ps_cs.c
--- v2.3.45/linux/drivers/net/pcmcia/xirc2ps_cs.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/pcmcia/xirc2ps_cs.c	Tue Feb 15 08:53:46 2000
@@ -723,7 +723,7 @@
     dev->stop = &do_stop;
     dev->tx_timeout = xirc_tx_timeout;
     dev->watchdog_timeo = TX_TIMEOUT;
-    netif_start_queue (dev);
+    netif_start_queue(dev);
 
     /* Register with Card Services */
     link->next = dev_list;
@@ -1239,7 +1239,7 @@
     /* we can now register the device with the net subsystem */
     dev->irq = link->irq.AssignedIRQ;
     dev->base_addr = link->io.BasePort1;
-    netif_start_queue (dev);
+    netif_start_queue(dev);
     if ((err=register_netdev(dev))) {
 	printk(KNOT_XIRC "register_netdev() failed\n");
 	goto config_error;
@@ -1340,7 +1340,8 @@
       case CS_EVENT_CARD_REMOVAL:
 	  link->state &= ~DEV_PRESENT;
 	  if (link->state & DEV_CONFIG) {
-	      netif_stop_queue (dev);
+	      netif_stop_queue(dev);
+	      clear_bit(LINK_STATE_START, &dev->state);
 	      link->release.expires = jiffies + HZ / 20;
 	      add_timer(&link->release);
 	  }
@@ -1355,7 +1356,8 @@
       case CS_EVENT_RESET_PHYSICAL:
 	  if (link->state & DEV_CONFIG) {
 	      if (link->open) {
-		  netif_stop_queue (dev);
+		  netif_stop_queue(dev);
+		  clear_bit(LINK_STATE_START, &dev->state);
 		  lp->suspended=1;
 		  do_powerdown(dev);
 	      }
@@ -1371,7 +1373,8 @@
 	     if (link->open) {
 		 do_reset(dev,1);
 		 lp->suspended=0;
-		 netif_start_queue (dev);
+		 set_bit(LINK_STATE_START, &dev->state);
+		 netif_start_queue(dev);
 	     }
 	  }
 	  break;
@@ -1400,6 +1403,8 @@
 				  */
 
     spin_lock (&lp->lock);
+    if (!test_bit(LINK_STATE_START, &dev->state))
+	return;
 
     ioaddr = dev->base_addr;
     if (lp->mohawk) { /* must disable the interrupt */
@@ -1555,7 +1560,7 @@
 	    DEBUG(0, "PTR not changed?\n");
 	} else
 	    lp->stats.tx_packets += lp->last_ptr_value - n;
-	netif_wake_queue (dev);
+	netif_wake_queue(dev);
     }
     if (tx_status & 0x0002) {	/* Execessive collissions */
 	DEBUG(0, "tx restarted due to execssive collissions\n");
@@ -1613,7 +1618,7 @@
 	if (lp->suspended) {
 	    dev->trans_start = jiffies;
 	    lp->stats.tx_dropped++;
-	    netif_start_queue (dev);
+	    netif_start_queue(dev);
 	    return;
 	}
 
@@ -1622,7 +1627,7 @@
 	/* reset the card */
 	do_reset(dev,1);
 	dev->trans_start = jiffies;
-	netif_start_queue (dev);
+	netif_start_queue(dev);
 }
 
 
@@ -1638,7 +1643,7 @@
     DEBUG(1, "do_start_xmit(skb=%p, dev=%p) len=%u\n",
 	  skb, dev, pktlen);
 
-    netif_stop_queue (dev);
+    netif_stop_queue(dev);
 
     /* adjust the packet length to min. required
      * and hope that the buffer is large enough
@@ -1673,7 +1678,7 @@
 
     dev_kfree_skb (skb);
     dev->trans_start = jiffies;
-    netif_start_queue (dev);
+    netif_start_queue(dev);
     lp->stats.tx_bytes += pktlen;
     return 0;
 }
@@ -1818,7 +1823,7 @@
     link->open++;
     MOD_INC_USE_COUNT;
 
-    netif_start_queue (dev);
+    netif_start_queue(dev);
     lp->suspended = 0;
     do_reset(dev,1);
 
@@ -2136,7 +2141,7 @@
     if (!link)
 	return -ENODEV;
 
-    netif_stop_queue (dev);
+    netif_stop_queue(dev);
 
     SelectPage(0);
     PutByte(XIRCREG_CR, 0);  /* disable interrupts */
diff -u --recursive --new-file v2.3.45/linux/drivers/net/ppp_generic.c linux/drivers/net/ppp_generic.c
--- v2.3.45/linux/drivers/net/ppp_generic.c	Thu Feb 10 17:11:11 2000
+++ linux/drivers/net/ppp_generic.c	Wed Feb 16 15:42:05 2000
@@ -30,6 +30,7 @@
 #include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/netdevice.h>
 #include <linux/poll.h>
 #include <linux/ppp_defs.h>
@@ -549,6 +550,8 @@
 
 #define PPP_MAJOR	108
 
+static devfs_handle_t devfs_handle = NULL;
+
 /* Called at boot time if ppp is compiled into the kernel,
    or at module load time (from init_module) if compiled as a module. */
 int __init ppp_init(void)
@@ -561,9 +564,13 @@
 #endif
 
 	printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
-	err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
+	err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
 	if (err)
 		printk(KERN_ERR "failed to register PPP device (%d)\n", err);
+	devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE,
+				       PPP_MAJOR, 0,
+				       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				       &ppp_device_fops, NULL);
 #ifndef MODULE
 #ifdef CONFIG_PPP_ASYNC
 	ppp_async_init();
@@ -1613,7 +1620,8 @@
 	/* should never happen */
 	if (!list_empty(&all_ppp_units))
 		printk(KERN_ERR "PPP: removing module but units remain!\n");
-	if (unregister_chrdev(PPP_MAJOR, "ppp") != 0)
+	if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0)
 		printk(KERN_ERR "PPP: failed to unregister PPP device\n");
+	devfs_unregister (devfs_handle);
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.3.45/linux/drivers/net/rtl8129.c linux/drivers/net/rtl8129.c
--- v2.3.45/linux/drivers/net/rtl8129.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/rtl8129.c	Tue Feb 15 13:43:05 2000
@@ -240,7 +240,7 @@
 	CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
 	CSCR_LinkDownCmd=0x0f3c0,
 };
-unsigned long param[4][4]={
+static const unsigned long param[4][4]={
 	{0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43},
 	{0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
 	{0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
diff -u --recursive --new-file v2.3.45/linux/drivers/net/setup.c linux/drivers/net/setup.c
--- v2.3.45/linux/drivers/net/setup.c	Thu Feb 10 17:11:11 2000
+++ linux/drivers/net/setup.c	Tue Feb 15 17:19:07 2000
@@ -15,9 +15,7 @@
 extern int x25_asy_init_ctrl_dev(void);
 extern int slhc_install(void);
   
-extern int bpq_init(void);
 extern int dmascc_init(void);
-extern int scc_init(void);
 extern int yam_init(void);
 
 extern int awc4500_pci_probe(void);
@@ -59,15 +57,9 @@
 	 *	Early setup devices
 	 */
 
-#if defined(CONFIG_SCC)
-	{scc_init, 0},
-#endif
 #if defined(CONFIG_DMASCC)
 	{dmascc_init, 0},
 #endif	
-#if defined(CONFIG_BPQETHER)
-	{bpq_init, 0},
-#endif
 #if defined(CONFIG_DLCI)
 	{dlci_setup, 0},
 #endif
diff -u --recursive --new-file v2.3.45/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.3.45/linux/drivers/net/sunlance.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/sunlance.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.95 2000/02/10 21:14:24 davem Exp $
+/* $Id: sunlance.c,v 1.97 2000/02/14 09:02:32 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -602,7 +602,7 @@
 					lp->init_ring(dev);
 					load_csrs(lp);
 					init_restart_lance(lp);
-					return;
+					goto out;
 				}
 			}
 
@@ -618,7 +618,7 @@
 				lp->init_ring(dev);
 				load_csrs(lp);
 				init_restart_lance(lp);
-				return;
+				goto out;
 			}
 		} else if ((bits & LE_T1_POK) == LE_T1_POK) {
 			/*
@@ -644,7 +644,7 @@
 	if (test_bit(LINK_STATE_XOFF, &dev->state) &&
 	    TX_BUFFS_AVAIL > 0)
 		netif_wake_queue(dev);
-
+out:
 	spin_unlock(&lp->lock);
 }
 
@@ -773,7 +773,7 @@
 					lp->init_ring(dev);
 					load_csrs(lp);
 					init_restart_lance(lp);
-					return;
+					goto out;
 				}
 			}
 
@@ -789,7 +789,7 @@
 				lp->init_ring(dev);
 				load_csrs(lp);
 				init_restart_lance(lp);
-				return;
+				goto out;
 			}
 		} else if ((bits & LE_T1_POK) == LE_T1_POK) {
 			/*
@@ -815,7 +815,7 @@
 	if (test_bit(LINK_STATE_XOFF, &dev->state) &&
 	    TX_BUFFS_AVAIL > 0)
 		netif_wake_queue(dev);
-
+out:
 	spin_unlock(&lp->lock);
 }
 
@@ -1014,7 +1014,7 @@
 	return status;
 }
 
-static void lance_piocopy_from_skb(volatile void *dest, char *src, int len)
+static void lance_piocopy_from_skb(volatile void *dest, unsigned char *src, int len)
 {
 	unsigned long piobuf = (unsigned long) dest;
 	u32 *p32;
@@ -1128,10 +1128,10 @@
 
 	len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;
 
-	lp->stats.tx_bytes += len;
-
 	spin_lock_irq(&lp->lock);
 
+	lp->stats.tx_bytes += len;
+
 	entry = lp->tx_new & TX_RING_MOD_MASK;
 	if (lp->pio_buffer) {
 		sbus_writew((-len) | 0xf000, &ib->btx_ring[entry].length);
@@ -1154,19 +1154,20 @@
 	if (TX_BUFFS_AVAIL <= 0)
 		netif_stop_queue(dev);
 
-	spin_unlock_irq(&lp->lock);
-
 	/* Kick the lance: transmit now */
 	sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP);
-	dev->trans_start = jiffies;
-	dev_kfree_skb(skb);
-    
+
 	/* Read back CSR to invalidate the E-Cache.
 	 * This is needed, because DMA_DSBL_WR_INV is set.
 	 */
 	if (lp->dregs)
 		sbus_readw(lp->lregs + RDP);
 
+	spin_unlock_irq(&lp->lock);
+
+	dev->trans_start = jiffies;
+	dev_kfree_skb(skb);
+    
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.3.45/linux/drivers/net/tulip.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/tulip.c	Tue Feb 15 22:39:01 2000
@@ -1,140 +1,53 @@
 /* tulip.c: A DEC 21040-family ethernet driver for Linux. */
 /*
-	Written 1994-1998 by Donald Becker.
+	Copyright 2000  The Linux Kernel Team
+	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.
+	This driver is for the Digital "Tulip" Ethernet adapter interface.
 	It should work with most DEC 21*4*-based chips/ethercards, as well as
-	PNIC and MXIC chips.
+	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
+	Additional information available at
 	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
-*/
-
-#define SMP_CHECK
-static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\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",
-};
-
-/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
-#ifdef REVERSE_PROBE_ORDER
-static int reverse_probe = 1;
-#else
-static int reverse_probe = 0;
-#endif
-
-/* 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
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (4*HZ)
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-
-#include <asm/processor.h>		/* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-/* Kernel compatibility defines, common to David Hind's PCMCIA package.
-   This is only in the support-all-kernels source code. */
-#include <linux/version.h>		/* Evil, but neccessary */
-
-#define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
-
-#define NEW_MULTICAST
-#include <linux/delay.h>
-
-#ifdef SA_SHIRQ
-#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
-#else
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
-#endif
-
-/* The total size is unusually large: The 21040 aligns each of its 16
-   longword-wide registers on a quadword boundary. */
-#define TULIP_TOTAL_SIZE 0x80
-
-#ifdef HAVE_DEVLIST
-struct netdev_entry tulip_drv =
-{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
-#endif
+	
+	For this specific driver variant please use linux-kernel for 
+	bug reports.
 
-#ifdef TULIP_DEBUG
-int tulip_debug = TULIP_DEBUG;
-#else
-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.  These chips are used on
-many PCI boards including the SMC EtherPower series.
-
+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.
-Note: Kernel versions earlier than 1.3.73 do not support shared PCI
-interrupt lines.
+
+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
 
@@ -182,111 +95,261 @@
 
 IV. Notes
 
-Thanks to Duke Kamstra of SMC for providing an EtherPower board.
+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/DP83840.html
+http://www.national.com/pf/DP/DP83840A.html
+http://www.asix.com.tw/pmac.htm
+http://www.admtek.com.tw/
 
 IVc. Errata
 
-The DEC databook doesn't document which Rx filter settings accept broadcast
-packets.  Nor does it document how to configure the part to configure the
-serial subsystem for normal (vs. loopback) operation or how to have it
-autoswitch between internal 10baseT, SIA and AUI transceivers.
-
+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?  */
+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.
 
-/* A few values that may be tweaked. */
-#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+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.
+
+*/
+
+static const char version[] = "Linux Tulip driver version 0.9.2 (Feb 15, 2000)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.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/init.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+
+
+
+/* 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",
+};
+
+/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
+#ifdef REVERSE_PROBE_ORDER
+static int reverse_probe = 1;
+#else
+static int reverse_probe = 0;
+#endif
+
+/* 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(__i386__) || defined(__powerpc__) || defined(__sparc__)
+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
 
-#ifndef PCI_VENDOR_ID_DEC		/* Now defined in linux/pci.h */
-#define PCI_VENDOR_ID_DEC			0x1011
-#define PCI_DEVICE_ID_TULIP			0x0002		/* 21040. */
-#define PCI_DEVICE_ID_TULIP_FAST	0x0009		/* 21140. */
-#endif
 
-#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
-#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014		/* 21041. */
-#endif
-#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
-#define PCI_DEVICE_ID_DEC_TULIP_21142	0x0019
-#endif
+/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
 
-#ifndef PCI_VENDOR_ID_LITEON
-#define PCI_VENDOR_ID_LITEON	0x11AD
-#endif
+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");
 
-#ifndef PCI_VENDOR_ID_MXIC
-#define	PCI_VENDOR_ID_MXIC		0x10d9
-#define PCI_DEVICE_ID_MX98713	0x0512
-#define PCI_DEVICE_ID_MX98715	0x0531
-#define PCI_DEVICE_ID_MX98725	0x0531
+#define TULIP_MODULE_NAME "tulip"
+#define PFX TULIP_MODULE_NAME ": "
+
+#define RUN_AT(x) (jiffies + (x))
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
+
+#define tulip_debug debug
+#ifdef TULIP_DEBUG
+static int tulip_debug = TULIP_DEBUG;
+#else
+static int tulip_debug = 1;
 #endif
 
-/* The rest of these values should never change. */
+
+
+/* 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);
 
-/* A table describing the chip types. */
-enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,};
+enum tbl_flag {
+	HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
+	HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
+	HAS_PNICNWAY=0x80, HAS_NWAY143=0x40,	/* Uses internal NWay xcvr. */
+	HAS_8023X=0x100,
+};
 static struct tulip_chip_table {
-	int vendor_id, device_id;
 	char *chip_name;
 	int io_size;
 	int valid_intrs;			/* CSR7 interrupt enable settings */
 	int flags;
 	void (*media_timer)(unsigned long data);
 } tulip_tbl[] = {
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP,
-	"Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS,
-	"Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST,
-	"Digital DS21140 Tulip", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM,
-	tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142,
-	"Digital DS21142/3 Tulip", 128, 0x0801fbff,
-	HAS_MII | HAS_MEDIA_TABLE, t21142_timer },
-  { PCI_VENDOR_ID_LITEON, 0x0002,
-	"Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713,
-	"Macronix 98713 PMAC", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715,
-	"Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725,
-	"Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
-  { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff,
+  { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
+  { "Digital DC21041 Tulip", 128, 0x0001ebff, HAS_MEDIA_TABLE, tulip_timer },
+  { "Digital DS21140 Tulip", 128, 0x0001ebef,
 	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
-  {0, 0, 0, 0},
+  { "Digital DS21143 Tulip", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+	t21142_timer },
+  { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
+	HAS_MII | HAS_PNICNWAY, 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, 0x0801fbff,
+	HAS_MII | HAS_NWAY143 | 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_PWRDWN | HAS_NWAY143,
+	t21142_timer },
+  { "Xircom tulip work-alike", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+	t21142_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,
+	I21145,
+	XIRCLONE,
+};
+
+
+static struct pci_device_id tulip_pci_tbl[] __devinitdata = {
+	{ 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, DC21143 },
+	{ 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 },
+	{ 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 },
+	{ 0x115d, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, XIRCLONE },
+	{0},
 };
-/* This matches the table above. */
-enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
-			 LC82C168, MX98713, MX98715, MX98725};
+MODULE_DEVICE_TABLE(pci,tulip_pci_tbl);
+
 
 /* A full-duplex map for media types. */
-enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
-		  MediaIs100=16};
+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_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
 static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
 static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
@@ -302,16 +365,12 @@
 
 /* The bits in the CSR5 status registers, mostly interrupt sources. */
 enum status_bits {
-	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+	TimerInt=0x800, SytemError=0x2000, 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,
 };
 
-enum desc_status_bits {
-	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
-};
-        
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
 	s32 status;
@@ -325,6 +384,22 @@
 	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
+
+#define EEPROM_SIZE 128 	/* 2 << EEPROM_ADDRLEN */
+
 struct medialeaf {
 	u8 type;
 	u8 media;
@@ -334,7 +409,8 @@
 struct mediatable {
 	u16 defaultmedia;
 	u8 leafcount, csr12dir;				/* General purpose pin directions. */
-	unsigned has_mii:1, has_nonmii:1;
+	unsigned has_mii:1, has_nonmii:1, has_reset:6;
+	u32 csr15dir, csr15val;				/* 21143 NWay setting. */
 	struct medialeaf mleaf[0];
 };
 
@@ -356,12 +432,13 @@
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	char *rx_buffs;				/* Address of temporary Rx buffers. */
-	u32 setup_frame[48];		/* Pseudo-Tx frame to init address table. */
+	u16 setup_frame[96];		/* Pseudo-Tx frame to init address table. */
 	int chip_id;
 	int revision;
+	int flags;
 	struct net_device_stats stats;
 	struct timer_list timer;	/* Media selection timer. */
-	spinlock_t tx_lock;       
+	spinlock_t tx_lock;
 	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. */
@@ -372,469 +449,111 @@
 	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 csr0;					/* CSR0 setting. */
 	unsigned int csr6;					/* Current CSR6 control settings. */
-	unsigned char eeprom[128];			/* Serial EEPROM contents. */
+	unsigned char eeprom[EEPROM_SIZE];	/* Serial EEPROM contents. */
+	void (*link_change)(struct net_device *dev, int csr5);
 	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. */
-	unsigned char pci_bus, pci_dev_fn;
+	int saved_if_port;
+	struct pci_dev *pdev;
+	int ttimer;
+	int susp_rx;
+	unsigned long nir;
 	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-static struct net_device *tulip_probe1(int pci_bus, int pci_devfn,
-								   int chip_id, int options);
 static void parse_eeprom(struct net_device *dev);
-static int read_eeprom(long ioaddr, int location);
+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 int tulip_open(struct net_device *dev);
-static void tulip_timer(unsigned long data);
+/* Chip-specific media selection (timer functions prototyped above). */
+static void t21142_lnk_change(struct net_device *dev, int csr5);
+static void t21142_start_nway(struct net_device *dev);
+static void pnic_lnk_change(struct net_device *dev, int csr5);
+static void pnic_do_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_refill_rx(struct net_device *dev);
 static int tulip_rx(struct net_device *dev);
-static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct net_device *dev);
-static struct enet_statistics *tulip_get_stats(struct net_device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
+static struct net_device_stats *tulip_get_stats(struct net_device *dev);
 static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#endif
-#ifdef NEW_MULTICAST
 static void set_rx_mode(struct net_device *dev);
-#else
-static void set_rx_mode(struct net_device *dev, int num_addrs, void *addrs);
-#endif
-
-
-
-/* A list of all installed Tulip devices, for removing the driver module. */
-static struct net_device *root_tulip_dev = NULL;
 
-/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
-   but now receives directly into full-sized skbuffs that are allocated
-   at open() time.
-   This allows the probe routine to use the old driver initialization
-   interface. */
-
-static int __init tulip_probe(void)
-{
-	int cards_found = 0;
-	static int pci_index = 0;	/* Static, for multiple probe calls. */
-	unsigned char pci_bus, pci_device_fn;
-
-	/* 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 with the current structure.  So instead we detect just the
-	   Tulip cards in slot order. */
-
-	if (! pci_present())
-		return -ENODEV;
-	for (;pci_index < 0xff; pci_index++) {
-		u16 vendor, device, pci_command, new_command;
-		unsigned long pci_ioaddr = 0;
-		int chip_idx = 0;
-		struct pci_dev *pdev;
-
-		if (pcibios_find_class
-			(PCI_CLASS_NETWORK_ETHERNET << 8,
-			 reverse_probe ? 0xfe - pci_index : pci_index,
-			 &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
-		{
-			if (reverse_probe)
-				continue;
-			else
-				break;
-		}
-		
-		pdev = pci_find_slot (pci_bus, pci_device_fn);
-		vendor = pdev->vendor;
-		device = pdev->device;
-
-		for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
-			if (vendor == tulip_tbl[chip_idx].vendor_id  &&
-				device == tulip_tbl[chip_idx].device_id)
-				break;
-		if (tulip_tbl[chip_idx].chip_name == 0) {
-			if (vendor == PCI_VENDOR_ID_DEC  ||
-				vendor == PCI_VENDOR_ID_LITEON)
-				printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type"
-					   " %4.4x %4.4x"" detected: not configured.\n",
-					   vendor, device);
-			continue;
-		}
-		pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->resource[0].start;
-		/* Remove I/O space marker in bit 0. */
-		pci_ioaddr &= ~3;
 
-		if (tulip_debug > 2)
-			printk(KERN_DEBUG "Found %s at I/O %#lx.\n",
-				   tulip_tbl[chip_idx].chip_name, pci_ioaddr);
+/* 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.
+   */
 
-		if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
-			continue;
+/* 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, 0x041f,
+							   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 */ }},
+  {"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 */}},
+  {"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 */
+   }},
+  {0, 0, 0, 0, {}}};
 
-		pci_read_config_word(pdev, 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 this"
-				   " device!  Updating PCI command %4.4x->%4.4x.\n",
-				   pci_command, new_command);
-			pci_write_config_word(pdev, PCI_COMMAND, new_command);
-		}
-
-		if(tulip_probe1(pci_bus, pci_device_fn, chip_idx, cards_found))
-		{
-			/* Get and check the bus-master and latency values. */
-			unsigned char pci_latency;
-			pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
-			if (pci_latency < 10) {
-				printk(KERN_INFO "  PCI latency timer (CFLT) is "
-					   "unreasonably low at %d.  Setting to 64 clocks.\n",
-					   pci_latency);
-				pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
-			} else if (tulip_debug > 1)
-				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x, "
-					   " PCI command is %4.4x.\n",
-					   pci_latency, new_command);
-			/* Bring the 21143 out power-down mode. */
-			if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
-				pci_write_config_dword(pdev, 0x40, 0x40000000);
-			cards_found++;
-		}
-	}
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
 
-	return cards_found ? 0 : -ENODEV;
-}
+#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 struct net_device *tulip_probe1(int pci_bus, int pci_device_fn,
-								   int chip_id, int board_idx)
-{
-	struct net_device *dev;
-	static int did_version = 0;			/* Already printed version info. */
-	struct tulip_private *tp;
-	long ioaddr;
-	int irq;
-	/* 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 */
-	int i;
-	unsigned short sum;
-
-	if (tulip_debug > 0  &&  did_version++ == 0)
-		printk(KERN_INFO "%s", version);
-
-	dev = init_etherdev(NULL, 0);
-
-	irq = pci_find_slot(pci_bus, pci_device_fn)->irq;
-	ioaddr = pci_find_slot(pci_bus, pci_device_fn)->resource[0].start;
-
-	printk(KERN_INFO "%s: %s at %#3lx,",
-		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
-
-	/* Stop the chip's Tx and Rx processes. */
-	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
-	/* Clear the missed-packet counter. */
-	(volatile int)inl(ioaddr + CSR8);
-
-	if (chip_id == DC21041) {
-		if (inl(ioaddr + CSR9) & 0x8000) {
-			printk(" 21040 compatible mode,");
-			chip_id = 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_id == 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_id == 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);
-			((u16*)dev->dev_addr)[i] = value;
-			sum += value & 0xffff;
-		}
-	} 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[128];
-		int sa_offset = 0;
-
-		for (i = 0; i < sizeof(ee_data)/2; i++)
-			((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
-
-		/* 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[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__)		/* This BIOS bug doesn't exist on Alphas. */
-		irq = last_irq;
-#endif
-	}
-
-	for (i = 0; i < 6; i++)
-		printk(" %2.2x", 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_id].chip_name, but... */
-	request_region(ioaddr, TULIP_TOTAL_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->next_module = root_tulip_dev;
-	root_tulip_dev = dev;
-
-	tp->pci_bus = pci_bus;
-	tp->pci_dev_fn = pci_device_fn;
-	tp->chip_id = chip_id;
-
-#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_id].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 ((tp->mtable  &&  tp->mtable->has_mii) ||
-		( ! tp->mtable  &&  (tulip_tbl[tp->chip_id].flags & HAS_MII))) {
-		int phy, phy_idx;
-		/* 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 != 0xffff  &&  mii_status != 0x0000) {
-				int mii_reg0 = mdio_read(dev, phy, 0);
-				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 found at MDIO address "
-					   "%d, config %4.4x status %4.4x.\n",
-					   dev->name, phy, mii_reg0, mii_status);
-				if (1 || (media_cap[tp->default_port] & MediaIsMII)) {
-					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
-						   " previously advertising %4.4x.\n",
-						   dev->name, reg4, phy, mdio_read(dev, phy, 4));
-					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->tx_timeout = &tulip_tx_timeout;
-	dev->watchdog_timeo = TX_TIMEOUT;
-	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
-
-	/* Reset the xcvr interface and turn on heartbeat. */
-	switch (chip_id) {
-	case DC21041:
-		outl(0x00000000, ioaddr + CSR13);
-		outl(0xFFFFFFFF, ioaddr + CSR14);
-		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
-		outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
-		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:
-		outl(0x82420200, ioaddr + CSR6);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x0003FFFF, ioaddr + CSR14);
-		outl(0x0008, ioaddr + CSR15);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x1301, ioaddr + CSR12); /* Start NWay. */
-		break;
-	case LC82C168:
-		if ( ! tp->mii_cnt) {
-			outl(0x00420000, ioaddr + CSR6);
-			outl(0x30, ioaddr + CSR12);
-			outl(0x0001F078, ioaddr + 0xB8);
-			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
-		}
-		break;
-	case MX98713: case MX98715: case MX98725:
-		outl(0x00000000, ioaddr + CSR6);
-		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
-		outl(0x00000001, ioaddr + CSR13);
-		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"};
-
-#define EEPROM_SIZE 128
-#if defined(__i386__)
-#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)
+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;
 
 	tp->mtable = 0;
-	for (i = 0; i < EEPROM_SIZE/2; i++)
-		((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
-
 	/* Detect an old-style (SA only) EEPROM layout:
 	   memcmp(eedata, eedata+16, 8). */
 	for (i = 0; i < 8; i ++)
@@ -871,20 +590,13 @@
 		}
 	  }
 	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
-		printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n",
+		  printk(KERN_INFO "%s: Old style EEPROM with no media selection "
+				 "information.\n",
 			   dev->name);
 		return;
 	  }
 	}
-	if (tulip_debug > 1) {
-	  printk(KERN_DEBUG "read_eeprom:");
-	  for (i = 0; i < 64; i++) {
-		printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ",
-			   read_eeprom(ioaddr, i));
-	  }
-	  printk("\n");
-	}
-	
+
 	controller_index = 0;
 	if (ee_data[19] > 1) {		/* Multiport board. */
 		last_ee_data = ee_data;
@@ -894,42 +606,29 @@
 	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++;
+		int media = get_u16(p);
+		int count = p[2];
+		p += 3;
 
-		printk(KERN_INFO "%s:21041 Media information at %d, default media "
-			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
+		printk(KERN_INFO "%s: 21041 Media table, default media %4.4x (%s).\n",
+			   dev->name, 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]);
+			if (media_code & 0x40)
+				p += 6;
+			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;
+		int count, new_advertise = 0;
 		struct mediatable *mtable;
 		u16 media = get_u16(p);
 
 		p += 2;
-		if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
+		if (tp->flags & CSR12_IN_SROM)
 			csr12dir = *p++;
 		count = *p++;
 		mtable = (struct mediatable *)
@@ -941,13 +640,14 @@
 		mtable->defaultmedia = media;
 		mtable->leafcount = count;
 		mtable->csr12dir = csr12dir;
-		mtable->has_nonmii = mtable->has_mii = 0;
+		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;
@@ -957,12 +657,34 @@
 				p += 4;
 			} else {
 				leaf->type = p[1];
-				if (p[1] & 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;
+					switch (leaf->media) {
+					case 0: new_advertise |= 0x0020; break;
+					case 4: new_advertise |= 0x0040; break;
+					case 3: new_advertise |= 0x0080; break;
+					case 5: new_advertise |= 0x0100; break;
+					case 6: new_advertise |= 0x0200; break;
+					}
+					if (p[1] == 2  &&  leaf->media == 0) {
+						if (p[2] & 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;
+						} else {
+							mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
+							mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
+						}
+					}
 				}
 				leaf->leafdata = p + 2;
 				p += (p[0] & 0x3f) + 1;
@@ -971,7 +693,7 @@
 				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],
+					   dev->name, bp[0], bp[1], bp[2 + 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 "
@@ -979,6 +701,8 @@
 				   dev->name, i, medianame[leaf->media], leaf->media,
 				   block_name[leaf->type], leaf->type);
 		}
+		if (new_advertise)
+			tp->to_advertise = new_advertise;
 	}
 }
 /* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
@@ -986,54 +710,48 @@
 /*  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_DATA_WRITE	0x04	/* Data from the Tulip to EEPROM. */
 #define EE_WRITE_0		0x01
 #define EE_WRITE_1		0x05
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_DATA_READ	0x08	/* Data from the EEPROM chip. */
 #define EE_ENB			(0x4800 | EE_CS)
 
 /* Delay between EEPROM clock transitions.
-   The 1.2 code is a "nasty" timing loop, but PC compatible machines are
-   *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro.  */
-#ifdef _LINUX_DELAY_H
-#define eeprom_delay(nanosec)	udelay((nanosec + 999)/1000)
-#else
-#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-#endif
+   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 << 6)
-#define EE_READ_CMD		(6 << 6)
-#define EE_ERASE_CMD	(7 << 6)
+#define EE_READ_CMD		(6)
 
-static int read_eeprom(long ioaddr, int location)
+/* Note: this routine returns extra data bits for size detection. */
+static int read_eeprom(long ioaddr, int location, int addr_len)
 {
 	int i;
-	unsigned short retval = 0;
+	unsigned retval = 0;
 	long ee_addr = ioaddr + CSR9;
-	int read_cmd = location | EE_READ_CMD;
-	
+	int read_cmd = location | (EE_READ_CMD << addr_len);
+
 	outl(EE_ENB & ~EE_CS, ee_addr);
 	outl(EE_ENB, ee_addr);
-	
+
 	/* Shift the read command bits out. */
-	for (i = 10; i >= 0; i--) {
+	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(100);
+		eeprom_delay();
 		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(150);
-		outl(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
-		eeprom_delay(250);
+		eeprom_delay();
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
 	}
 	outl(EE_ENB, ee_addr);
-	
+
 	for (i = 16; i > 0; i--) {
 		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
 		outl(EE_ENB, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 	}
 
 	/* Terminate the EEPROM access. */
@@ -1067,19 +785,33 @@
 	int i;
 	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	int retval = 0;
-	long mdio_addr = dev->base_addr + CSR9;
+	long ioaddr = dev->base_addr;
+	long mdio_addr = ioaddr + CSR9;
 
 	if (tp->chip_id == LC82C168) {
-		long ioaddr = dev->base_addr;
 		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;
 	}
 
-	/* Establish sync by sending at least 32 logic ones. */ 
+	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();
@@ -1111,10 +843,10 @@
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
-	long mdio_addr = dev->base_addr + CSR9;
+	long ioaddr = dev->base_addr;
+	long mdio_addr = ioaddr + CSR9;
 
 	if (tp->chip_id == LC82C168) {
-		long ioaddr = dev->base_addr;
 		int i = 1000;
 		outl(cmd, ioaddr + 0xA0);
 		do
@@ -1124,7 +856,19 @@
 		return;
 	}
 
-	/* Establish sync by sending 32 logic ones. */ 
+	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();
@@ -1155,7 +899,12 @@
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int i = 0;
+	int next_tick = 3*HZ;
+	int i;
+
+	/* Wake the chip from sleep/snooze mode. */
+	if (tp->flags & HAS_PWRDWN)
+		pci_write_config_dword(tp->pdev, 0x40, 0);
 
 	/* On some chip revs we must set the MII/SYM port before the reset!? */
 	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
@@ -1163,85 +912,63 @@
 
 	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
 	outl(0x00000001, ioaddr + CSR0);
-#ifdef _LINUX_DELAY_H
-	udelay(2);
-#else
-	SLOW_DOWN_IO;
-#endif
+
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
 	/* Deassert reset.
-	   486: Set 8 longword cache alignment, 8 longword burst.
-	   586: 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
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
-#if defined(__alpha__)
-	/* ToDo: Alpha setting could be better. */
-	outl(0x01A00000 | 0xE000, ioaddr + CSR0);
-#elif defined(__powerpc__)
-	outl(0x01A00080 | 0x8000, ioaddr + CSR0);
-#elif defined(__i386__)
-#if defined(MODULE)
-	/* When a module we don't have 'x86' to check. */
-	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
-#else
-#define x86 boot_cpu_data.x86
-	outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
-	if (x86 <= 4)
-	  printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
-			 "alignment to %x.\n", dev->name,
-			 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000));
-#endif
-#else
-	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
-#warning Processor architecture undefined!
-#endif
-
-#ifdef SA_SHIRQ
-	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) {
-		return -EAGAIN;
-	}
-#else
-	if (irq2dev_map[dev->irq] != NULL
-		|| (irq2dev_map[dev->irq] = dev) == NULL
-		|| dev->irq == 0
-		|| request_irq(dev->irq, &tulip_interrupt, 0,
-					   tulip_tbl[tp->chip_id].chip_name)) {
-		return -EAGAIN;
-	}
-#endif
+	outl(tp->csr0, ioaddr + CSR0);
 
 	if (tulip_debug > 1)
 		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
 
 	MOD_INC_USE_COUNT;
 
-    spin_lock_init(&tp->tx_lock);
+	spin_lock_init(&tp->tx_lock);
 	tulip_init_ring(dev);
 
-	/* This is set_rx_mode(), but without starting the transmitter. */
-	/* Fill the whole address filter table with our physical address. */
-	{
+#if 0
+	if (tp->chip_id == PNIC2) {
+		u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
+		u32 addr_high = cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4)));
+		addr_high = (dev->dev_addr[4]<<8) + (dev->dev_addr[5]<<0);
+		outl((dev->dev_addr[0]<<8) + dev->dev_addr[1] +
+			 (dev->dev_addr[2]<<24) + (dev->dev_addr[3]<<16),
+			 ioaddr + 0xB0);
+		outl(addr_high + (addr_high<<16), ioaddr + 0xB8);
+	}
+#endif
+	if (tp->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 {
+		/* This is set_rx_mode(), but without starting the transmitter. */
 		u16 *eaddrs = (u16 *)dev->dev_addr;
-		u32 *setup_frm = tp->setup_frame, i;
+		u16 *setup_frm = &tp->setup_frame[15*6];
 
-		/* You must add the broadcast address when doing perfect filtering! */
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
-		/* Fill the rest of the accept table with our physical address. */
-		for (i = 1; i < 16; i++) {
-			*setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2];
-		}
+		/* 21140 bug: you must add the broadcast address. */
+		memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame));
+		/* 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 = 0x80000000;
+		tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192);
+		tp->tx_ring[0].buffer1 = virt_to_le32desc(tp->setup_frame);
+		tp->tx_ring[0].status = cpu_to_le32(DescOwned);
 
 		tp->cur_tx++;
 	}
@@ -1249,13 +976,12 @@
 	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) {
@@ -1268,31 +994,80 @@
 				goto media_picked;
 			}
 	}
-	if ((tp->mtable->defaultmedia & 0x0800) == 0)
+	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 == (tp->mtable->defaultmedia & 15)) {
-			printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
-				   dev->name, medianame[tp->mtable->mleaf[i].media]);
-			goto media_picked;
-		  }
+			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;
+	tp->nwayset = 0;
+	if (dev->if_port == 0  && tp->chip_id == DC21041) {
+		tp->nway = 1;
+	}
 	if (dev->if_port == 0  &&  tp->chip_id == DC21142) {
-		tp->csr6 = 0x82420200;
-		outl(0x0003FFFF, ioaddr + CSR14);
+		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(0x82020000, ioaddr + CSR6);
+			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 == PNIC2) {
+		t21142_start_nway(dev);
+	} else if (tp->chip_id == LC82C168  &&  ! tp->medialock) {
+		if (tp->mii_cnt) {
+			dev->if_port = 11;
+			tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
+			outl(0x0001, ioaddr + CSR15);
+		} else if (inl(ioaddr + CSR5) & TPLnkPass)
+			pnic_do_nway(dev);
+		else {
+			/* Start with 10mbps to do autonegotiation. */
+			outl(0x32, ioaddr + CSR12);
+			tp->csr6 = 0x00420000;
+			outl(0x0001B078, ioaddr + 0xB8);
+			outl(0x0201B078, ioaddr + 0xB8);
+			next_tick = 1*HZ;
+		}
+	} 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 = 0x01a80200;
+		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);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x1301, ioaddr + CSR12);
-	} else if (tp->chip_id == LC82C168  &&  tp->mii_cnt && ! tp->medialock) {
-		dev->if_port = 11;
-		tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0);
-		outl(0x0001, ioaddr + CSR15);
+	} else if (tp->chip_id == COMET) {
+		dev->if_port = 0;
+		tp->csr6 = 0x00040000;
+	} else if (tp->chip_id == AX88140) {
+		tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100;
 	} else
 		select_media(dev, 1);
 
@@ -1314,7 +1089,7 @@
 	/* 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.expires = RUN_AT(next_tick);
 	tp->timer.data = (unsigned long)dev;
 	tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
 	add_timer(&tp->timer);
@@ -1331,7 +1106,7 @@
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	struct mediatable *mtable = tp->mtable;
 	u32 new_csr6;
-	int check_mii =0, i;
+	int i;
 
 	if (mtable) {
 		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
@@ -1349,33 +1124,56 @@
 			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
 			break;
 		case 2: case 4: {
-			u16 setup[3];
-			for (i = 0; i < 3; i++)
+			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: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+				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(setup[1], ioaddr + CSR14);
-				outl(setup[2], ioaddr + CSR15);
-				outl(setup[0], ioaddr + CSR13);
-				for (i = 0; i < 3; i++)			/* Re-fill setup[]  */
-					setup[i] = get_u16(&p[i*2 + 7]);
-			} else if (dev->if_port <= 4) {
-				outl(0, ioaddr + CSR13);
-				outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
-				outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
-				outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+				outl(csr14val, ioaddr + CSR14);
+				outl(csr15dir, ioaddr + CSR15);	/* Direction */
+				outl(csr15val, ioaddr + CSR15);	/* Data */
+				outl(csr13val, ioaddr + CSR13);
 			} else {
-				outl(0, ioaddr + CSR14);
-				outl(8, ioaddr + CSR15);
-				outl(0, ioaddr + CSR13);
+				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);
 			}
-			outl(setup[0]<<16, ioaddr + CSR15);	/* Direction */
-			outl(setup[1]<<16, ioaddr + CSR15);	/* Data */
+			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
@@ -1389,7 +1187,6 @@
 			u16 to_advertise;
 
 			dev->if_port = 11;
-			check_mii = 1;
 			new_csr6 = 0x020E0000;
 			if (mleaf->type == 3) {	/* 21142 */
 				u16 *init_sequence = (u16*)(p+2);
@@ -1416,7 +1213,7 @@
 			}
 			to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
 			tp->advertising[phy_num] = to_advertise;
-			if (tulip_debug > 1 || 1)
+			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?  */
@@ -1424,32 +1221,33 @@
 			break;
 		}
 		default:
-		  new_csr6 = 0x020E0000;
+			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) {
+		int port = dev->if_port <= 4 ? dev->if_port : 0;
 		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);
+				   dev->name, medianame[port == 3 ? 12: port],
+				   inl(ioaddr + CSR12));
 		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);
+		outl(t21041_csr14[port], ioaddr + CSR14);
+		outl(t21041_csr15[port], ioaddr + CSR15);
+		outl(t21041_csr13[port], ioaddr + CSR13);
 		new_csr6 = 0x80020000;
 	} else if (tp->chip_id == LC82C168) {
 		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]);
+			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, media %s.\n",
+				   dev->name, inl(ioaddr + 0xB8), medianame[dev->if_port]);
 		if (tp->mii_cnt) {
-			new_csr6 = 0x812C0000;
+			new_csr6 = 0x810C0000;
 			outl(0x0001, ioaddr + CSR15);
 			outl(0x0201B07A, ioaddr + 0xB8);
 		} else if (startup) {
@@ -1461,10 +1259,8 @@
 		} 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);
+			/* Trigger autonegotiation. */
+			outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
 		} else {
 			outl(0x32, ioaddr + CSR12);
 			new_csr6 = 0x00420000;
@@ -1475,20 +1271,24 @@
 		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, dev->if_port ? "AUI" : "10baseT", csr12);
-		new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
+				   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 */
-		outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+		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 {					/* Unknown chip type with no media table. */
 		if (tp->default_port == 0)
-		{
-			if (tp->mii_cnt)
-				dev->if_port = 11;
-			else
-				dev->if_port = 3;
-		}
+			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) {
@@ -1506,28 +1306,80 @@
 	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 (negotiated & 0x038)	/* 100mbps. */
+			tp->csr6 &= ~0x00400000;
+		if (tp->full_duplex) tp->csr6 |= 0x0200;
+		else				 tp->csr6 &= ~0x0200;
+		outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		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 1;
+	}
+	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 = 0;
+	int next_tick = 2*HZ;
 
-	if (tulip_debug > 3) {
-		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),
+	if (tulip_debug > 2) {
+		printk(KERN_DEBUG "%s: Media selection tick, %s, status %8.8x mode"
+			   " %8.8x SIA %8.8x %8.8x %8.8x %8.8x.\n",
+			   dev->name, medianame[dev->if_port], inl(ioaddr + CSR5),
+			   inl(ioaddr + CSR6), csr12, inl(ioaddr + CSR13),
 			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
 	}
 	switch (tp->chip_id) {
 	case DC21040:
-		if (csr12 & 0x0002) { /* Network error */
-			printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n",
-				   dev->name, dev->if_port ? "10baseT" : "AUI");
-			dev->if_port ^= 1;
-			outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+		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;
@@ -1535,6 +1387,7 @@
 		if (tulip_debug > 2)
 			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
 				   dev->name, csr12);
+		if (tp->medialock) break;
 		switch (dev->if_port) {
 		case 0: case 3: case 4:
 		  if (csr12 & 0x0004) { /*LnkFail */
@@ -1556,25 +1409,26 @@
 		  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;
+			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: 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. */
@@ -1643,53 +1497,11 @@
 			next_tick = (24*HZ)/10;
 			break;
 		}
-		case 1:  case 3: {		/* 21140, 21142 MII */
-			int mii_reg1, mii_reg5;
+		case 1:  case 3:		/* 21140, 21142 MII */
 		actually_mii:
-			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, CSR12 %2.2x, %cD.\n",
-					   dev->name, mii_reg1, mii_reg5, csr12,
-					   tp->full_duplex ? 'F' : 'H');
-			if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0) {
-				int new_reg1 = mdio_read(dev, tp->phys[0], 1);
-				if ((new_reg1 & 0x0004) == 0) {
-					printk(KERN_INFO "%s: No link beat on the MII interface,"
-						   " status then %4.4x now %4.4x.\n",
-						   dev->name, mii_reg1, new_reg1);
-					if (tp->mtable  &&  tp->mtable->has_nonmii)
-						goto select_next_media;
-				}
-			}
-			if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000)
-				;				/* No MII device or no link partner report */
-			else if (tp->full_duplex_lock)
-				;
-			else {
-				int negotiated = mii_reg5 & tp->advertising[0];
-				int duplex = ((negotiated & 0x0100) != 0
-							  || (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(tp->csr6 | 0x0002, ioaddr + CSR6);
-					outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-					if (tulip_debug > 0) /* Gurppp, should be >1 */
-						printk(KERN_INFO "%s: Setting %s-duplex based on MII"
-							   " Xcvr #%d parter capability of %4.4x.\n",
-							   dev->name, tp->full_duplex ? "full" : "half",
-							   tp->phys[0], mii_reg5);
-				}
-			}
+			check_duplex(dev);
 			next_tick = 60*HZ;
 			break;
-		}
 		case 2:					/* 21142 serial block has no link beat. */
 		default:
 			break;
@@ -1697,10 +1509,8 @@
 	}
 	break;
 	}
-	if (next_tick) {
-		tp->timer.expires = RUN_AT(next_tick);
-		add_timer(&tp->timer);
-	}
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
 }
 
 /* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
@@ -1714,49 +1524,50 @@
 	int next_tick = 60*HZ;
 	int new_csr6 = 0;
 
-	if (tulip_debug > 1)
-		printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n",
+	if (tulip_debug > 2)
+		printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
 			   dev->name, csr12, medianame[dev->if_port]);
-	if (dev->if_port == 3) {
-		if (csr12 & 2) {		/* No 100mbps link beat, revert to 10mbps. */
-			new_csr6 = 0x82420200;
-			outl(new_csr6, ioaddr + CSR6);
-			outl(0x0000, ioaddr + CSR13);
-			outl(0x0003FFFF, ioaddr + CSR14);
-			outl(0x0008, ioaddr + CSR15);
-			outl(0x0001, ioaddr + CSR13);
-			outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+	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) {
 		/* Negotiation failed.  Search media types. */
 		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n",
+			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);
-			outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+			outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
 			outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
-		} else if (csr12 & 0x100) {
-			new_csr6 = 0x82420200;
-			dev->if_port = 2;
-			outl(0, ioaddr + CSR13);
-			outl(0x0003FFFF, ioaddr + CSR14);
-			outl(0x0008, ioaddr + CSR15);
-			outl(0x0001, 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);
-			outl(8, ioaddr + CSR15);
+			outw(8, ioaddr + CSR15);
 			outl(1, ioaddr + CSR13);
 		}
 		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: Testing new 21142 media %s.\n",
+			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;
@@ -1765,51 +1576,141 @@
 			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
 			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 		}
+		next_tick = 3*HZ;
 	}
+
 	tp->timer.expires = RUN_AT(next_tick);
 	add_timer(&tp->timer);
 }
 
-static void t21142_lnk_change( struct net_device *dev)
+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 & 0x0780) << 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(tp->csr6, ioaddr + CSR6);
+	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: 21142 link status interrupt %8.8x, CSR5 %x.\n",
-			   dev->name, csr12, inl(ioaddr + CSR5));
+		printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
+			   "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
 
-	if ((csr12 & 0x7000) == 0x5000) {
-		if (csr12 & 0x01800000) {
-			/* Switch to 100mbps mode. */
-			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-			if (csr12 & 0x01000000) {
-				dev->if_port = 5;
-				tp->csr6 = 0x83860200;
-			} else {
+	/* If NWay finished and we have a negotiated partner capability. */
+	if (tp->nway  &&  !tp->nwayset  &&  (csr12 & 0x7000) == 0x5000) {
+		int setup_done = 0;
+		int negotiated = tp->to_advertise & (csr12 >> 16);
+		tp->lpar = csr12 >> 16;
+		tp->nwayset = 1;
+		if (negotiated & 0x0100)		dev->if_port = 5;
+		else if (negotiated & 0x0080)	dev->if_port = 3;
+		else if (negotiated & 0x0040)	dev->if_port = 4;
+		else if (negotiated & 0x0020)	dev->if_port = 0;
+		else {
+			tp->nwayset = 0;
+			if ((csr12 & 2) == 0  &&  (tp->to_advertise & 0x0180))
 				dev->if_port = 3;
-				tp->csr6 = 0x83860000;
-			}
-			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-		} /* Else 10baseT-FD is handled automatically. */
-	} else if (dev->if_port == 3) {
-		if (!(csr12 & 2))
-			printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n",
-				   dev->name);
-		else
-			dev->if_port = 0;
-	} else if (dev->if_port == 0) {
-		if (!(csr12 & 4))
-			printk(KERN_INFO"%s: 21142 10baseT link beat good.\n",
+		}
+		tp->full_duplex = (media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0;
+
+		if (tulip_debug > 1) {
+			if (tp->nwayset)
+				printk(KERN_INFO "%s: Switching to %s based on link "
+					   "negotiation %4.4x & %4.4x = %4.4x.\n",
+					   dev->name, medianame[dev->if_port], tp->to_advertise,
+					   tp->lpar, negotiated);
+			else
+				printk(KERN_INFO "%s: Autonegotiation failed, using %s,"
+					   " link beat status %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;
+			outl(1, ioaddr + CSR13);
+		}
+#if 0							/* Restart shouldn't be needed. */
+		outl(tp->csr6 | 0x0000, ioaddr + CSR6);
+		if (debug > 2)
+			printk(KERN_DEBUG "%s:  Restarting Tx and Rx, CSR5 is %8.8x.\n",
+				   dev->name, inl(ioaddr + CSR5));
+#endif
+		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		if (debug > 2)
+			printk(KERN_DEBUG "%s:  Setting CSR6 %8.8x/%x CSR12 %8.8x.\n",
+				   dev->name, tp->csr6, inl(ioaddr + CSR6),
+				   inl(ioaddr + CSR12));
+	} 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. */
-			printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n",
+		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 = 0;
-	} else  {		/* 100mbps link beat good. */
-		printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n",
-			   dev->name);
 		dev->if_port = 3;
 		tp->csr6 = 0x83860000;
 		outl(0x0003FF7F, ioaddr + CSR14);
@@ -1818,7 +1719,6 @@
 		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 	}
 }
-	
 
 static void mxic_timer(unsigned long data)
 {
@@ -1837,57 +1737,98 @@
 	}
 }
 
+static void pnic_do_nway(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u32 phy_reg = inl(ioaddr + 0xB8);
+	u32 new_csr6 = tp->csr6 & ~0x40C40200;
+
+	if (phy_reg & 0x78000000) { /* Ignore baseT4 */
+		if (phy_reg & 0x20000000)		dev->if_port = 5;
+		else if (phy_reg & 0x40000000)	dev->if_port = 3;
+		else if (phy_reg & 0x10000000)	dev->if_port = 4;
+		else if (phy_reg & 0x08000000)	dev->if_port = 0;
+		tp->nwayset = 1;
+		new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000;
+		outl(0x32 | (dev->if_port & 1), ioaddr + CSR12);
+		if (dev->if_port & 1)
+			outl(0x1F868, ioaddr + 0xB8);
+		if (phy_reg & 0x30000000) {
+			tp->full_duplex = 1;
+			new_csr6 |= 0x00000200;
+		}
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: PNIC autonegotiated status %8.8x, %s.\n",
+				   dev->name, phy_reg, medianame[dev->if_port]);
+		if (tp->csr6 != new_csr6) {
+			tp->csr6 = new_csr6;
+			outl(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
+			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			dev->trans_start = jiffies;
+		}
+	}
+}
+static void pnic_lnk_change(struct net_device *dev, int csr5)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int phy_reg = inl(ioaddr + 0xB8);
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: PNIC link changed state %8.8x, CSR5 %8.8x.\n",
+			   dev->name, phy_reg, csr5);
+	if (inl(ioaddr + CSR5) & TPLnkFail) {
+		outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7);
+		if (! tp->nwayset  ||  jiffies - dev->trans_start > 1*HZ) {
+			tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff);
+			outl(tp->csr6, ioaddr + CSR6);
+			outl(0x30, ioaddr + CSR12);
+			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+			dev->trans_start = jiffies;
+		}
+	} else if (inl(ioaddr + CSR5) & TPLnkPass) {
+		pnic_do_nway(dev);
+		outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7);
+	}
+}
 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: LC82C168 negotiated capability %8.8x, "
-				   "CSR5 %8.8x.\n",
-				   dev->name, negotiated, inl(ioaddr + CSR5));
-
-		if (negotiated & 0x0380) 				/* 10 vs 100mbps */
-			new_csr6 |= 0x812E0000;
-		else
-			new_csr6 |= 0x816E0000;
-		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: LC82C168 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));
+		if (check_duplex(dev) > 0)
+			next_tick = 3*HZ;
 	} else {
+		int csr12 = inl(ioaddr + CSR12);
+		int new_csr6 = tp->csr6 & ~0x40C40200;
 		int phy_reg = inl(ioaddr + 0xB8);
 		int csr5 = inl(ioaddr + CSR5);
 
 		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
-				   dev->name, phy_reg, csr5);
-
+			printk(KERN_DEBUG "%s: PNIC timer PHY status %8.8x, %s "
+				   "CSR5 %8.8x.\n",
+				   dev->name, phy_reg, medianame[dev->if_port], csr5);
 		if (phy_reg & 0x04000000) {	/* Remote link fault */
-			/*outl(0x0201F078, ioaddr + 0xB8);*/
-			next_tick = 3*HZ;
-		}
-		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+			outl(0x0201F078, ioaddr + 0xB8);
+			next_tick = 1*HZ;
+			tp->nwayset = 0;
+		} else if (phy_reg & 0x78000000) { /* Ignore baseT4 */
+			pnic_do_nway(dev);
+			next_tick = 60*HZ;
+		} else if (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));
+			next_tick = 3*HZ;
 			if (tp->medialock) {
+			} else if (tp->nwayset  &&  (dev->if_port & 1)) {
+				next_tick = 1*HZ;
 			} else if (dev->if_port == 0) {
 				dev->if_port = 3;
 				outl(0x33, ioaddr + CSR12);
@@ -1899,162 +1840,187 @@
 				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(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+				dev->trans_start = jiffies;
+				if (tulip_debug > 1)
+					printk(KERN_INFO "%s: Changing PNIC configuration to %s "
+						   "%s-duplex, CSR6 %8.8x.\n",
+						   dev->name, medianame[dev->if_port],
+						   tp->full_duplex ? "full" : "half", new_csr6);
+			}
 		}
 	}
-	if (tp->csr6 != new_csr6) {
-		tp->csr6 = new_csr6;
-		outl(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
-		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-		dev->trans_start = jiffies;
-		if (tulip_debug > 0) /* Gurppp, should be >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 tulip_tx_timeout(struct net_device *dev)
+static void comet_timer(unsigned long data)
 {
-  struct tulip_private *tp = (struct tulip_private *)dev->priv;
-  long ioaddr = dev->base_addr;
-
-  printk("%s: transmit timed out\n", dev->name);
-
-  if (media_cap[dev->if_port] & MediaIsMII) {
-	  /* Do nothing -- the media monitor should handle this. */
-#if 0
-	  if (tulip_debug > 1)
-#endif
-		  printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
-				 dev->name);
-	  dev->trans_start = jiffies;
-	  return;
-  } else if (tp->chip_id == DC21040) {
-	  if (inl(ioaddr + CSR12) & 0x0002) {
-		  printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n",
-				 dev->name, dev->if_port ? "10baseT" : "AUI");
-		  dev->if_port ^= 1;
-		  outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
-	  }
-	  dev->trans_start = jiffies;
-	  return;
-  } else if (tp->chip_id == DC21041) {
-	u32 csr12 = inl(ioaddr + CSR12);
+	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;
 
-	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 (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);
-	tp->stats.tx_errors++;
-	dev->trans_start = jiffies;
-	return;
-  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
-			 || tp->chip_id == MX98713) {
-	  /* Stop the transmit process. */
-	  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-	  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->mtable) {
-		  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, dev->if_port ? "100baseTx" : "10baseT");
-	  }
-	  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-	  tp->stats.tx_errors++;
-	  dev->trans_start = jiffies;
-	  return;
-  } else
-	printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
-		   " resetting...\n",
-		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
-#ifdef way_too_many_messages
-  printk("  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  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
-
-  /* Perhaps we should reinitialize the hardware here. */
-  dev->if_port = 0;
-  /* Stop and restart the chip's Tx processes . */
-  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-  /* Trigger an immediate transmit demand. */
-  outl(0, ioaddr + CSR1);
-
-  dev->trans_start = jiffies;
-  tp->stats.tx_errors++;
-  return;
+	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);
 }
 
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-tulip_init_ring(struct net_device *dev)
+static void tulip_tx_timeout(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;
+	long ioaddr = dev->base_addr;
 
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = 0x80000000;	/* Owned by Tulip chip */
-		tp->rx_ring[i].length = PKT_BUF_SZ;
-		{
-			/* 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;
-			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
-			tp->rx_skbuff[i] = skb;
-			if (skb == NULL)
-				break;			/* Bad news!  */
-			skb->dev = dev;			/* Mark as being used by this device. */
-			tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+	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);
 		}
-		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
-	}
-	/* Mark the last entry as wrapping the ring. */
-	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
-	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[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(tp->csr6 | 0x0002, ioaddr + CSR6);
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	/* Trigger an immediate transmit demand. */
+	outl(0, ioaddr + CSR1);
+
+	dev->trans_start = jiffies;
+	tp->stats.tx_errors++;
+	return;
+}
+
+
+/* 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;
+	tp->susp_rx = 0;
+	tp->ttimer = 0;
+	tp->nir = 0;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		tp->rx_ring[i].status = 0x00000000;
+		tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ);
+		tp->rx_ring[i].buffer2 = virt_to_le32desc(&tp->rx_ring[i+1]);
+		tp->rx_skbuff[i] = NULL;
+	}
+	/* Mark the last entry as wrapping the ring. */
+	tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP);
+	tp->rx_ring[i-1].buffer2 = virt_to_le32desc(&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 = cpu_to_le32(DescOwned);	/* Owned by Tulip chip */
+		tp->rx_ring[i].buffer1 = virt_to_le32desc(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]);
+		tp->tx_ring[i].buffer2 = virt_to_le32desc(&tp->tx_ring[i+1]);
 	}
-	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
+	tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]);
 }
 
 static int
@@ -2065,8 +2031,8 @@
 	u32 flag;
 	unsigned long cpuflags;
 
-	/* Caution: the write order is important here, set the base address
-	   with the "ownership" bits last. */
+	/* Caution: the write order is important here, set the field
+	   with the ownership bits last. */
 
 	spin_lock_irqsave(&tp->tx_lock, cpuflags);
 
@@ -2074,25 +2040,24 @@
 	entry = tp->cur_tx % TX_RING_SIZE;
 
 	tp->tx_skbuff[entry] = skb;
-	tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+	tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data);
 
 	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
-	  flag = 0x60000000; /* No interrupt */
+		flag = 0x60000000; /* No interrupt */
 	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
-	  flag = 0xe0000000; /* Tx-done intr. */
+		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;
-	  netif_stop_queue(dev);
+		flag = 0x60000000; /* No Tx-done intr. */
+	} else {		/* Leave room for set_rx_mode() to fill entries. */
+		tp->tx_full = 1;
+		flag = 0xe0000000; /* Tx-done intr. */
+		netif_stop_queue(dev);
 	}
 	if (entry == TX_RING_SIZE-1)
-		flag |= 0xe2000000;
+		flag = 0xe0000000 | DESC_RING_WRAP;
 
-	tp->tx_ring[entry].length = skb->len | flag;
-	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
+	tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag);
+	tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
 	tp->cur_tx++;
 	spin_unlock_irqrestore(&tp->tx_lock, cpuflags);
 
@@ -2106,26 +2071,22 @@
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
 	struct net_device *dev = (struct net_device *)dev_instance;
-#else
-	struct net_device *dev = (struct net_device *)(irq2dev_map[irq]);
-#endif
-
-	struct tulip_private *tp;
-	long ioaddr;
-	int csr5, work_budget = max_interrupt_work;
-
-	if (dev == NULL) {
-		printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
-				irq);
-		return;
-	}
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr5;
+	int entry;
+	int missed;
+	int rx = 0;
+	int tx = 0;
+	int oi = 0;
+	int maxrx = RX_RING_SIZE;
+	int maxtx = TX_RING_SIZE;
+	int maxoi = TX_RING_SIZE;
 
-	ioaddr = dev->base_addr;
-	tp = (struct tulip_private *)dev->priv;
+	tp->nir++;
 
 	do {
 		csr5 = inl(ioaddr + CSR5);
@@ -2139,10 +2100,12 @@
 		if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
 			break;
 
-		if (csr5 & (RxIntr | RxNoBuf))
-			work_budget -= tulip_rx(dev);
+		if (csr5 & (RxIntr | RxNoBuf)) {
+			rx += tulip_rx(dev);
+			tulip_refill_rx(dev);
+		}
 
-		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
+		if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) {
 			unsigned int dirty_tx;
 
 			spin_lock(&tp->tx_lock);
@@ -2150,14 +2113,14 @@
 			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;
+				int status = le32_to_cpu(tp->tx_ring[entry].status);
 
 				if (status < 0)
-					break;			/* It still hasn't been Txed */
+					break;			/* It still has not 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
@@ -2179,7 +2142,7 @@
 #ifdef ETHER_STATS
 					if (status & 0x0001) tp->stats.tx_deferred++;
 #endif
-					tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
+					tp->stats.tx_bytes += tp->tx_skbuff[entry]->len;
 					tp->stats.collisions += (status >> 3) & 15;
 					tp->stats.tx_packets++;
 				}
@@ -2187,6 +2150,7 @@
 				/* Free the original skb. */
 				dev_kfree_skb_irq(tp->tx_skbuff[entry]);
 				tp->tx_skbuff[entry] = 0;
+				tx++;
 			}
 
 #ifndef final_version
@@ -2204,12 +2168,11 @@
 			}
 
 			tp->dirty_tx = dirty_tx;
-
 			if (csr5 & TxDied) {
-				if (tulip_debug > 1)
-					printk(KERN_WARNING "%s: The transmitter stopped!"
-						   "  CSR5 is %x, CSR6 %x.\n",
-						   dev->name, csr5, inl(ioaddr + CSR6));
+				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(tp->csr6 | 0x0002, ioaddr + CSR6);
 				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 			}
@@ -2218,6 +2181,8 @@
 
 		/* 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)
@@ -2227,117 +2192,177 @@
 				/* Restart the transmit process. */
 				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
 				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+				outl(0, ioaddr + CSR1);
 			}
 			if (csr5 & RxDied) {		/* Missed a Rx frame. */
 				tp->stats.rx_errors++;
 				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 			}
-			if (csr5 & TimerInt) {
-				printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n",
-					   dev->name, csr5);
-				/* Hmmmmm, it's not clear what to do here. */
+			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
+				if (tp->link_change)
+					(tp->link_change)(dev, csr5);
 			}
-			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)
-				&& tp->chip_id == DC21142) {
-				if (tulip_debug > 1)
-					printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n",
-						   dev->name, csr5);
-				t21142_lnk_change(dev);
+			if (csr5 & SytemError) {
+				printk(KERN_ERR "%s: (%lu) System Error occured\n", dev->name, tp->nir);
 			}
 			/* Clear all error sources, included undocumented ones! */
 			outl(0x0800f7ba, ioaddr + CSR5);
+			oi++;
+		}
+		if (csr5 & TimerInt) {
+#if 0
+			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);
+#endif
+			tp->ttimer = 0;
+			oi++;
 		}
-		if (--work_budget < 0) {
+		if (tx > maxtx || rx > maxrx || oi > maxoi) {
 			if (tulip_debug > 1)
-				printk(KERN_WARNING "%s: Too much work at interrupt, "
-					   "csr5=0x%8.8x.\n", dev->name, csr5);
+				printk(KERN_WARNING "%s: Too much work during an interrupt, "
+					   "csr5=0x%8.8x. (%lu) (%d,%d,%d)\n", dev->name, csr5, tp->nir, tx, rx, oi);
 			/* Acknowledge all interrupt sources. */
-			outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
-			/* Clear all but standard interrupt sources. */
-			outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
+#if 0
+			/* Clear all interrupting sources, set timer to re-enable. */
+			outl(((~csr5) & 0x0001ebef) | NormalIntr | AbnormalIntr | TimerInt,
+				 ioaddr + CSR7);
+			outl(12, ioaddr + CSR11);
+			tp->ttimer = 1;
 #endif
 			break;
 		}
 	} while (1);
 
-	if (tulip_debug > 3)
+	tulip_refill_rx(dev);
+
+	/* check if we card is in suspend mode */
+	entry = tp->dirty_rx % RX_RING_SIZE;
+	if (tp->rx_skbuff[entry] == NULL) {
+		if (tulip_debug > 1)
+			printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx);
+		if (tp->ttimer == 0 || (inl(ioaddr + CSR11) & 0xffff) == 0) {
+			if (tulip_debug > 1)
+				printk(KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n", dev->name, tp->nir);
+			outl(tulip_tbl[tp->chip_id].valid_intrs | TimerInt,
+				ioaddr + CSR7);
+			outl(TimerInt, ioaddr + CSR5);
+			outl(12, ioaddr + CSR11);
+			tp->ttimer = 1;
+		}
+	}
+
+	if ((missed = inl(ioaddr + CSR8) & 0x1ffff)) {
+		tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed;
+	}
+
+	if (tulip_debug > 4)
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
-	return;
 }
 
-static int
-tulip_rx(struct net_device *dev)
+static int tulip_refill_rx(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int entry;
+	int refilled = 0;
+
+	/* 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_le32desc(skb->tail);
+			refilled++;
+		}
+		tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
+	}
+	return refilled;
+}
+
+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;
+	int received = 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 we own the next entry, it is a new packet. Send it up. */
+	while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) {
+		s32 status = le32_to_cpu(tp->rx_ring[entry].status);
+
+		if (tulip_debug > 5)
+			printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n",
+				   dev->name, entry, status);
 		if (--rx_work_limit < 0)
 			break;
-		if ((status & 0x0300) != 0x0300) {
-			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
-				if (tulip_debug > 1)
-					printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
-						   "multiple buffers, status %8.8x!\n",
+		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_length_errors++;
+				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 if (status & 0x8000) {
-			/* 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) - 4;
+			short pkt_len = ((status >> 16) & 0x7ff) - 4;
 			struct sk_buff *skb;
 
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
+#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_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-#if LINUX_VERSION_CODE < 0x20200  || defined(__alpha__)
-				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#else
-				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
-								 pkt_len, 0);
+#if ! defined(__alpha__)
+				eth_copy_and_sum(skb, tp->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
+#else
+				memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail,
+					   pkt_len);
 #endif
-				work_done++;
 			} else { 	/* Pass up the skb already on the Rx ring. */
-				skb = tp->rx_skbuff[entry];
+				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
 				tp->rx_skbuff[entry] = NULL;
 #ifndef final_version
-				{
-					void *temp = skb_put(skb, pkt_len);
-					if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
-						printk(KERN_ERR "%s: Internal consistency error! 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);
-				}
-#else
-				skb_put(skb, pkt_len);
+				if (le32desc_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,
+						   le32desc_to_virt(tp->rx_ring[entry].buffer1),
+						   skb->head, temp);
 #endif
 			}
 			skb->protocol = eth_type_trans(skb, dev);
@@ -2346,25 +2371,11 @@
 			tp->stats.rx_packets++;
 			tp->stats.rx_bytes += pkt_len;
 		}
+		received++;
 		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 = 0x80000000;
-	}
-
-	return work_done;
+	return received;
 }
 
 static int
@@ -2382,22 +2393,20 @@
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	outl(0x00000000, ioaddr + CSR7);
-	/* Stop the chip's Tx and Rx processes. */
+	/* Stop the Tx and Rx processes. */
 	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
 	/* 21040 -- Leave the card in 10baseT state. */
 	if (tp->chip_id == DC21040)
 		outl(0x00000004, ioaddr + CSR13);
 
-	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+	if (inl(ioaddr + CSR6) != 0xffffffff)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
 	del_timer(&tp->timer);
 
-#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
-#else
-	free_irq(dev->irq);
-	irq2dev_map[dev->irq] = 0;
-#endif
+
+	dev->if_port = tp->saved_if_port;
 
 	/* Free all the skbuffs in the Rx queue. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
@@ -2416,6 +2425,9 @@
 		tp->tx_skbuff[i] = 0;
 	}
 
+	/* Leave the driver in snooze, not sleep, mode. */
+	if (tp->flags & HAS_PWRDWN)
+		pci_write_config_dword(tp->pdev, 0x40, 0x40000000);
 
 	MOD_DEC_USE_COUNT;
 
@@ -2434,7 +2446,7 @@
 	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)
 {
@@ -2446,31 +2458,32 @@
 
 	switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
-		if (tp->mtable  &&  tp->mtable->has_mii)
+		if (tp->mii_cnt)
 			data[0] = phy;
-		else if (tp->chip_id == DC21142)
+		else if (tp->flags & HAS_NWAY143)
 			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) {  /* 21142 pseudo-MII */
+		if (data[0] == 32  &&  (tp->flags & HAS_NWAY143)) {
 			int csr12 = inl(ioaddr + CSR12);
 			int csr14 = inl(ioaddr + CSR14);
 			switch (data[1]) {
 			case 0: {
-				data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000);
+				data[3] = (csr14<<5) & 0x1000;
 				break; }
 			case 1:
 				data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
 					+ (csr12&0x06 ? 0x04 : 0);
 				break;
 			case 4: {
-				int csr14 = inl(ioaddr + CSR14);
-				data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1;
+				data[3] = ((csr14>>9)&0x07C0) +
+					((inl(ioaddr + CSR6)>>3)&0x0040) + ((csr14>>1)&0x20) + 1;
 				break;
 			}
-			case 5: data[3] = inl(ioaddr + CSR12) >> 16; break;
+			case 5: data[3] = csr12 >> 16; break;
 			default: data[3] = 0; break;
 			}
 		} else {
@@ -2481,9 +2494,11 @@
 		}
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
-		if (data[0] == 32) {  /* 21142 pseudo-MII */
+		if (data[0] == 32  &&  (tp->flags & HAS_NWAY143)) {
+			if (data[1] == 5)
+				tp->to_advertise = data[2];
 		} else {
 			save_flags(flags);
 			cli();
@@ -2497,7 +2512,7 @@
 
 	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
@@ -2508,9 +2523,9 @@
    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 unsigned ether_crc_le(int length, unsigned char *data)
+static inline u32 ether_crc_le(int length, unsigned char *data)
 {
-	unsigned int crc = 0xffffffff;	/* Initial value. */
+	u32 crc = 0xffffffff;	/* Initial value. */
 	while(--length >= 0) {
 		unsigned char current_octet = *data++;
 		int bit;
@@ -2524,223 +2539,554 @@
 	}
 	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;
+}
 
-#ifdef NEW_MULTICAST
 static void set_rx_mode(struct net_device *dev)
-#else
-static void set_rx_mode(struct net_device *dev, int num_addrs, void *addrs)
-#endif
 {
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	unsigned long cpuflags;
 
 	tp->csr6 &= ~0x00D5;
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
-		outl(csr6 | 0x00C0, ioaddr + CSR6);
+		tp->csr6 |= 0x00C0;
+		csr6 |= 0x00C0;
 		/* Unconditionally log net taps. */
 		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
-		tp->csr6 |= 0xC0;
 	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
-		/* Too many to filter perfectly -- accept all multicasts. */
-		outl(csr6 | 0x0080, ioaddr + CSR6);
-		tp->csr6 |= 0x80;
+		/* Too many to filter well -- accept all multicasts. */
+		tp->csr6 |= 0x0080;
+		csr6 |= 0x0080;
+	} else	if (tp->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 {
-		u32 *setup_frm = tp->setup_frame;
+		u16 *eaddrs, *setup_frm = tp->setup_frame;
 		struct dev_mc_list *mclist;
-		u16 *eaddrs;
-		u32 tx_flags;
+		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) { /* Must use a multicast hash table. */
-		  u16 hash_table[32];
-		  memset(hash_table, 0, sizeof(hash_table));
-		  /* 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);
-		  /* Copy the hash table to the setup frame.
-			 NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */
-		  for (i = 0; i < 32; i++)
-			*setup_frm++ = hash_table[i];
-		  setup_frm += 7;
-		  tx_flags = 0x08400000 | 192;
-		  /* Too clever: i > 15 for fall-though. */
+			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++ = *setup_frm++ = hash_table[i];
+			setup_frm = &tp->setup_frame[13*6];
 		} else {
-		  /* We have <= 15 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) {
-			/* Note that only the low shortword of setup_frame[] is valid!
-			   This code may require tweaking for non-x86 architectures! */
-			eaddrs = (u16 *)mclist->dmi_addr;
-			*setup_frm++ = *eaddrs++;
-			*setup_frm++ = *eaddrs++;
-			*setup_frm++ = *eaddrs++;
-		  }
-		  /* Fill the rest of the table with our physical address.
-			 Once again, only the low shortword or setup_frame[] is valid! */
-		  *setup_frm++ = 0xffff;
-		  *setup_frm++ = 0xffff;
-		  *setup_frm++ = 0xffff;
-		  tx_flags = 0x08000000 | 192;
+			/* 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++ = *setup_frm++ = *eaddrs++;
+				*setup_frm++ = *setup_frm++ = *eaddrs++;
+				*setup_frm++ = *setup_frm++ = *eaddrs++;
+			}
+			/* 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;
-		do {
-			*setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2];
-		} while (++i < 15);
+		*setup_frm++ = *setup_frm++ = eaddrs[0];
+		*setup_frm++ = *setup_frm++ = eaddrs[1];
+		*setup_frm++ = *setup_frm++ = eaddrs[2];
 		/* Now add this frame to the Tx list. */
 		spin_lock_irqsave(&tp->tx_lock, cpuflags);
 		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
 			/* Same setup recently queued, we need not add it. */
 		} else {
-			unsigned int entry, dummy = 0;
+			unsigned long flags;
+			unsigned int entry;
 
+			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) ? 0x02000000 : 0;
+					(entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
 				tp->tx_ring[entry].buffer1 = 0;
-				/* race with chip, set DescOwned later */
-				dummy = entry;
+				tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
 				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 |= 0x02000000;		/* Wrap ring. */
-			tp->tx_ring[entry].length = tx_flags;
-			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
-			tp->tx_ring[entry].status = 0x80000000;
+				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */
+			tp->tx_ring[entry].length = cpu_to_le32(tx_flags);
+			tp->tx_ring[entry].buffer1 = virt_to_le32desc(tp->setup_frame);
+			tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
 			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
 				netif_stop_queue(dev);
 				tp->tx_full = 1;
 			}
-			if (dummy >= 0)
-				tp->tx_ring[dummy].status = DescOwned;
 			spin_unlock_irqrestore(&tp->tx_lock, cpuflags);
 			/* Trigger an immediate transmit demand. */
 			outl(0, ioaddr + CSR1);
 		}
-		outl(csr6 | 0x0000, ioaddr + CSR6);
 	}
+	outl(csr6 | 0x0000, ioaddr + CSR6);
 }
-
-#ifdef CARDBUS
 
-#include <pcmcia/driver_ops.h>
 
-static dev_node_t *tulip_attach(dev_locator_t *loc)
+static int __devinit tulip_init_one (struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
 {
-	u16 dev_id;
-	u32 io;
-	u8 bus, devfn;
+	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, irq;
+	unsigned short sum;
+	u8 ee_data[EEPROM_SIZE];
 	struct net_device *dev;
-	struct pci_dev *pdev;
+	long ioaddr;
+	static int board_idx = -1;
+	int chip_idx = ent->driver_data;
+	
+	board_idx++;
 
-	if (loc->bus != LOC_PCI) return NULL;
-	pdev = pci_find_slot (loc->b.pci.bus, loc->b.pci.devfn);
-	if (!pdev) return NULL;
-	printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn);
-	io = pdev->resource[0].start;
-	dev_id = pdev->device;
-	io &= ~3;
-	dev = tulip_probe1(bus, devfn, DC21142, -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;
+	if (tulip_debug > 0  &&  did_version++ == 0)
+		printk(KERN_INFO "%s", version);
+
+        if( pdev->subsystem_vendor == 0x1376 ){
+		printk(KERN_ERR "tulip: skipping LMC card.\n");
+		return -ENODEV;
 	}
-	return NULL;
-}
+	
+	ioaddr = pci_resource_start (pdev, 0);
+	irq = pdev->irq;
 
-static void tulip_detach(dev_node_t *node)
-{
-	struct net_device **devp, **next;
-	printk(KERN_INFO "tulip_detach(%s)\n", node->dev_name);
-	for (devp = &root_tulip_dev; *devp; devp = next) {
-		next = &((struct tulip_private *)(*devp)->priv)->next_module;
-		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	/* Make certain the data structures are quadword aligned. */
+	dev = init_etherdev (NULL, sizeof (*tp));
+	if (!dev)
+		return -ENOMEM;
+
+	/* We do a request_region() only to register /proc/ioports info. */
+	/* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
+	if (!request_region (ioaddr, tulip_tbl[chip_idx].io_size, dev->name))
+		goto err_out_free_netdev;
+
+	pci_enable_device (pdev);
+
+	tp = dev->priv;
+	memset(tp, 0, sizeof(*tp));
+
+	pci_read_config_byte (pdev, PCI_REVISION_ID, &chip_rev);
+
+	/* Bring the 21041/21143 out of sleep mode.
+	   Caution: Snooze mode does not work with some boards! */
+	if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
+		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(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* 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 (*devp) {
-		unregister_netdev(*devp);
-		kfree(*devp);
-		*devp = *next;
-		kfree(node);
-		MOD_DEC_USE_COUNT;
+
+	/* 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 {
+		/* A serial EEPROM interface, we read now and sort it out later. */
+		int sa_offset = 0;
+		int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6;
+
+		for (i = 0; i < sizeof(ee_data)/2; i++)
+			((u16 *)ee_data)[i] =
+				le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size));
+
+		/* DEC now has a specification (see Notes) but early board makers
+		   just put the address in the first EEPROM locations. */
+		/* This does  memcmp(eedata, eedata+16, 8) */
+		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
 	}
-}
 
-struct driver_operations tulip_ops = {
-	"tulip_cb", tulip_attach, NULL, NULL, tulip_detach
-};
+	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;
 
-#endif  /* Cardbus support */
+	pdev->driver_data = dev;
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
 
-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(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+	tp->chip_id = chip_idx;
+	tp->revision = chip_rev;
+	tp->flags = tulip_tbl[chip_idx].flags;
+	tp->csr0 = csr0;
+	tp->pdev = pdev;
+
+	/* 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)
+		tp->csr0 &= ~0x01000000;
+	else if (chip_idx == AX88140)
+		tp->csr0 |= 0x2000;
 
-/* An additional parameter that may be passed in... */
-static int debug = -1;
+#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
 
-static int __init tulip_init_module (void)
-{
-	if (debug >= 0)
-		tulip_debug = debug;
+	/* 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;
+
+	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 if (tp->flags & HAS_8023X)
+		tp->to_advertise = 0x05e1;
+	else 
+		tp->to_advertise = 0x01e1;
+
+	/* This is logically part of probe1(), but too complex to write inline. */
+	if (tp->flags & HAS_MEDIA_TABLE) {
+		memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
+		parse_eeprom(dev);
+	}
+
+	if ((tp->flags & ALWAYS_CHECK_MII) ||
+		(tp->mtable  &&  tp->mtable->has_mii) ||
+		( ! tp->mtable  &&  (tp->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);
+					printk(KERN_DEBUG "%s:  Advertising %4.4x (to advertise"
+						   " is %4.4x).\n",
+						   dev->name, reg4, tp->to_advertise);
+					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->tx_timeout = tulip_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->stop = tulip_close;
+	dev->get_stats = tulip_get_stats;
+	dev->do_ioctl = private_ioctl;
+	dev->set_multicast_list = set_rx_mode;
+
+	if ((tp->flags & HAS_NWAY143)  || tp->chip_id == DC21041)
+		tp->link_change = t21142_lnk_change;
+	else if (tp->flags & HAS_PNICNWAY)
+		tp->link_change = pnic_lnk_change;
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	switch (chip_idx) {
+	case DC21041:
+		tp->to_advertise = 0x0061;
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+		outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
+		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(0x82020000, ioaddr + CSR6);
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+			outl(0x820E0000, ioaddr + CSR6);
+		} else
+			t21142_start_nway(dev);
+		break;
+	case LC82C168:
+		if ( ! tp->mii_cnt) {
+			tp->nway = 1;
+			tp->nwayset = 0;
+			outl(0x00420000, ioaddr + CSR6);
+			outl(0x30, ioaddr + CSR12);
+			outl(0x0001F078, ioaddr + 0xB8);
+			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+		}
+		break;
+	case MX98713: case COMPEX9881:
+		outl(0x00000000, ioaddr + CSR6);
+		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+		outl(0x00000001, ioaddr + CSR13);
+		break;
+	case MX98715: case MX98725:
+		outl(0x01a80000, ioaddr + CSR6);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00001000, ioaddr + CSR12);
+		break;
+	case COMET:
+		/* No initialization necessary. */
+		break;
+	}
+
+	if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
+		pci_write_config_dword(pdev, 0x40, 0x40000000);
 
-#ifdef CARDBUS
-	register_driver(&tulip_ops);
 	return 0;
-#else
-	return tulip_probe();
-#endif
+
+err_out_free_netdev:
+	unregister_netdev (dev);
+	kfree (dev);
+	return -ENODEV;
 }
 
-static void __exit tulip_cleanup_module (void)
+
+static void tulip_suspend (struct pci_dev *pdev)
 {
-	struct net_device *next_dev;
+	struct net_device *dev = pdev->driver_data;
+
+	if (dev) {
+		long ioaddr = dev->base_addr;
+		struct tulip_private *tp = (struct tulip_private *)dev->priv;
+		int csr6 = inl(ioaddr + CSR6);
+		/* Disable interrupts, stop the chip, gather stats. */
+		if (csr6 != 0xffffffff) {
+			outl(0x00000000, ioaddr + CSR7);
+			outl(csr6 & ~0x2002, ioaddr + CSR6);
+			tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+		}
+		tulip_close(dev);
+		/* Put the 21143 into sleep mode. */
+		pci_write_config_dword(pdev, 0x40,0x80000000);
+	}
+}
 
-#ifdef CARDBUS
-	unregister_driver(&tulip_ops);
-#endif
 
-	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-	while (root_tulip_dev) {
-		next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
-		unregister_netdev(root_tulip_dev);
-		release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
-		kfree(root_tulip_dev);
-		root_tulip_dev = next_dev;
+static void tulip_resume (struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
+
+	if (dev) {
+		pci_write_config_dword(pdev, 0x40, 0x0000);
+		tulip_open(dev);
 	}
 }
 
-module_init(tulip_init_module);
-module_exit(tulip_cleanup_module);
 
+static void __devexit tulip_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pdev->driver_data;
 
-/*
- * Local variables:
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- * End:
- */
+	if (dev) {
+		struct tulip_private *tp = (struct tulip_private *)dev->priv;
+		unregister_netdev(dev);
+		release_region(dev->base_addr,
+			       tulip_tbl[tp->chip_id].io_size);
+		kfree(dev);
+	}
+}
+
+
+static struct pci_driver tulip_driver = {
+	name:		TULIP_MODULE_NAME,
+	id_table:	tulip_pci_tbl,
+	probe:		tulip_init_one,
+	remove:		tulip_remove_one,
+	suspend:	tulip_suspend,
+	resume:		tulip_resume,
+};
+
+
+static int __init tulip_init (void)
+{
+	return pci_register_driver (&tulip_driver) > 0 ? 0 : -ENODEV;
+}
+
+
+static void __exit tulip_cleanup (void)
+{
+	pci_unregister_driver (&tulip_driver);
+}
+
+
+module_init(tulip_init);
+module_exit(tulip_cleanup);
diff -u --recursive --new-file v2.3.45/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- v2.3.45/linux/drivers/net/via-rhine.c	Thu Feb 10 17:11:11 2000
+++ linux/drivers/net/via-rhine.c	Mon Feb 14 15:31:14 2000
@@ -865,7 +865,7 @@
 				np->stats.tx_packets++;
 			}
 			/* Free the original skb. */
-			kfree_skb(np->tx_skbuff[entry]);
+			dev_kfree_skb_irq(np->tx_skbuff[entry]);
 			np->tx_skbuff[entry] = 0;
 		}
 		if (np->tx_full &&
@@ -1147,13 +1147,13 @@
 		np->rx_ring[i].rx_length = 0;
 		np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
 		if (np->rx_skbuff[i]) {
-			kfree_skb(np->rx_skbuff[i]);
+			dev_kfree_skb(np->rx_skbuff[i]);
 		}
 		np->rx_skbuff[i] = 0;
 	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (np->tx_skbuff[i])
-			kfree_skb(np->tx_skbuff[i]);
+			dev_kfree_skb(np->tx_skbuff[i]);
 		np->tx_skbuff[i] = 0;
 	}
 
diff -u --recursive --new-file v2.3.45/linux/drivers/net/wan/cosa.c linux/drivers/net/wan/cosa.c
--- v2.3.45/linux/drivers/net/wan/cosa.c	Thu Feb 10 17:11:11 2000
+++ linux/drivers/net/wan/cosa.c	Wed Feb 16 15:42:05 2000
@@ -84,6 +84,7 @@
 #include <linux/malloc.h>
 #include <linux/poll.h>
 #include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -359,6 +360,8 @@
 
 /* ---------- Initialization stuff ---------- */
 
+static devfs_handle_t devfs_handle = NULL;
+
 #ifdef MODULE
 int init_module(void)
 #else
@@ -366,18 +369,19 @@
 #endif
 {
 	int i;
+
 	printk(KERN_INFO "cosa v1.06 (c) 1997-8 Jan Kasprzak <kas@fi.muni.cz>\n");
 #ifdef __SMP__
 	printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n");
 #endif
 	if (cosa_major > 0) {
-		if (register_chrdev(cosa_major, "cosa", &cosa_fops)) {
+		if (devfs_register_chrdev(cosa_major, "cosa", &cosa_fops)) {
 			printk(KERN_WARNING "cosa: unable to get major %d\n",
 				cosa_major);
 			return -EIO;
 		}
 	} else {
-		if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) {
+		if (!(cosa_major=devfs_register_chrdev(0, "cosa", &cosa_fops))) {
 			printk(KERN_WARNING "cosa: unable to register chardev\n");
 			return -EIO;
 		}
@@ -386,9 +390,14 @@
 		cosa_cards[i].num = -1;
 	for (i=0; io[i] != 0 && i < MAX_CARDS; i++)
 		cosa_probe(io[i], irq[i], dma[i]);
+	devfs_handle = devfs_mk_dir (NULL, "cosa", 4, NULL);
+	devfs_register_series (devfs_handle, "%u", nr_cards, DEVFS_FL_DEFAULT,
+			       cosa_major, 0,
+			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &cosa_fops, NULL);
 	if (!nr_cards) {
 		printk(KERN_WARNING "cosa: no devices found.\n");
-		unregister_chrdev(cosa_major, "cosa");
+		devfs_unregister_chrdev(cosa_major, "cosa");
 		return -ENODEV;
 	}
 	return 0;
@@ -397,9 +406,11 @@
 #ifdef MODULE
 void cleanup_module (void)
 {
+	int i;
 	struct cosa_data *cosa;
 	printk(KERN_INFO "Unloading the cosa module\n");
 
+	devfs_unregister (devfs_handle);
 	for (cosa=cosa_cards; nr_cards--; cosa++) {
 		int i;
 		/* Clean up the per-channel data */
@@ -414,7 +425,7 @@
 		free_dma(cosa->dma);
 		release_region(cosa->datareg,is_8bit(cosa)?2:4);
 	}
-	unregister_chrdev(cosa_major, "cosa");
+	devfs_unregister_chrdev(cosa_major, "cosa");
 }
 #endif
 
diff -u --recursive --new-file v2.3.45/linux/drivers/net/yellowfin.c linux/drivers/net/yellowfin.c
--- v2.3.45/linux/drivers/net/yellowfin.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/net/yellowfin.c	Tue Feb 15 13:47:36 2000
@@ -1,6 +1,6 @@
 /* yellowfin.c: A Packet Engines G-NIC ethernet driver for linux. */
 /*
-	Written 1997-1998 by Donald Becker.
+	Written 1997-1999 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
@@ -17,13 +17,13 @@
 */
 
 static const char *version =
-"yellowfin.c:v1.02 7/26/98  Written by Donald Becker, becker@cesdis.edu\n"
+"yellowfin.c:v1.03a 7/30/99  Written by Donald Becker, becker@cesdis.edu\n"
 " http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html\n";
 
 /* A few user-configurable values. */
 
+static int debug = 1;
 static int max_interrupt_work = 20;
-static int min_pci_latency = 64;
 static int mtu = 0;
 #ifdef YF_PROTOTYPE			/* Support for prototype hardware errata. */
 /* System-wide count of bogus-rx frames. */
@@ -50,25 +50,38 @@
 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};
 
+/* Do ugly workaround for GX server chipset errata. */
+static int gx_fix = 0;
+
 /* Operational parameters that are set at compile time. */
 
 /* Keep the ring sizes a power of two for efficiency.
-   Making the Tx ring too large decreases the effectiveness of channel
+   Making the Tx queue too long 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
+#define TX_QUEUE_SIZE	12		/* Must be > 4 && <= TX_RING_SIZE */
+#define RX_RING_SIZE	64
 
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((2000*HZ)/1000)
+#define TX_TIMEOUT  (2*HZ)
+
+#define yellowfin_debug debug
 
+#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/config.h>
+#include <linux/version.h>
 #include <linux/module.h>
+#include <linux/modversions.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
@@ -76,20 +89,20 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
-#include <asm/bitops.h>
 #include <asm/unaligned.h>
+#include <asm/bitops.h>
 #include <asm/io.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
-#define RUN_AT(x) (jiffies + (x))
+/* Condensed operations for readability.
+   Compatibility defines are now in drv_compat.h */
 
-/* The PCI I/O space extent. */
-#define YELLOWFIN_TOTAL_SIZE 0x100
+#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
 
-int yellowfin_debug = 1;
 
 /*
 				Theory of Operation
@@ -172,31 +185,49 @@
 /* A few values that may be tweaked. */
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-#ifndef PCI_VENDOR_ID_PKT_ENG		/* To be defined in linux/pci.h */
-#define PCI_VENDOR_ID_PKT_ENG			0x1000 /* Hmm, likely number.. */
-#define PCI_DEVICE_ID_SYM58C885			0x0701
-#define PCI_DEVICE_ID_YELLOWFIN			0x0702
-#endif
-
 /* The rest of these values should never change. */
 
-static void yellowfin_timer(unsigned long data);
+enum capability_flags {
+	HasMII=1, FullTxStatus=2, IsGigabit=4, HasMulticastBug=8, FullRxStatus=16,
+	HasMACAddrBug=32,			/* Really only on early revs.  */
+};
+
+
+/* The PCI I/O space extent. */
+#define YELLOWFIN_SIZE 0x100
+
+#define YELLOWFIN_MODULE_NAME "yellowfin"
+#define PFX YELLOWFIN_MODULE_NAME ": "
+
+
+typedef enum {
+	YELLOWFIN_GNIC,
+	SYM83C885,
+} chip_t;
 
-enum capability_flags {HasMII=1, FullTxStatus=2};
-static struct chip_info {
-	u16	vendor_id, device_id, device_id_mask, pci_flags;
+
+struct chip_info {
 	const char *name;
-	void (*media_timer)(unsigned long data);
-	u32 chip_rev;				/* As read from ChipRev, not PCI dev ID. */
 	int flags;
-} chip_tbl[] = {
-	{0x1000, 0x0702, 0xffff, 0, "Yellowfin G-NIC Gbit Ethernet",
-	 yellowfin_timer, 0x0702, FullTxStatus},
-	{0x1000, 0x0701, 0xffff, 0, "Symbios SYM83C885",
-	 yellowfin_timer, 0x0701, HasMII},
-	{0,},
 };
 
+
+/* index by chip_t */
+static struct chip_info chip_info[] = {
+	{"Yellowfin G-NIC Gigabit Ethernet",
+	 FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug},
+	{"Symbios SYM83C885", HasMII },
+};
+
+
+static struct pci_device_id yellowfin_pci_tbl[] __devinitdata = {
+	{ 0x1000, 0x0702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, YELLOWFIN_GNIC },
+	{ 0x1000, 0x0701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SYM83C885 },
+	{ 0, },
+};
+MODULE_DEVICE_TABLE (pci, yellowfin_pci_tbl);
+
+
 /* Offsets to the Yellowfin registers.  Various sizes and alignments. */
 enum yellowfin_offsets {
 	TxCtrl=0x00, TxStatus=0x04, TxPtr=0x0C,
@@ -204,7 +235,8 @@
 	RxCtrl=0x40, RxStatus=0x44, RxPtr=0x4C,
 	RxIntrSel=0x50, RxBranchSel=0x54, RxWaitSel=0x58,
 	EventStatus=0x80, IntrEnb=0x82, IntrClear=0x84, IntrStatus=0x86,
-	ChipRev=0x8C, DMACtrl=0x90, Cnfg=0xA0, FrameGap0=0xA2, FrameGap1=0xA4,
+	ChipRev=0x8C, DMACtrl=0x90, TxThreshold=0x94,
+	Cnfg=0xA0, FrameGap0=0xA2, FrameGap1=0xA4,
 	MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC,
 	MII_Status=0xAE,
 	RxDepth=0xB8, FlowCtrl=0xBC,
@@ -213,29 +245,35 @@
 	EEFeature=0xF5,
 };
 
-/* The Yellowfin Rx and Tx buffer descriptors. */
+/* The Yellowfin Rx and Tx buffer descriptors.
+   Elements are written as 32 bit for endian portability. */
 struct yellowfin_desc {
-	u16 request_cnt;
-	u16 cmd;
+	u32 dbdma_cmd;
 	u32 addr;
 	u32 branch_addr;
-	u16 result_cnt;
-	u16 status;
+	u32 result_status;
 };
 
 struct tx_status_words {
+#if defined(__powerpc__)
+	u16 tx_errs;
+	u16 tx_cnt;
+	u16 paused;
+	u16 total_tx_cnt;
+#else  /* Little endian chips. */
 	u16 tx_cnt;
 	u16 tx_errs;
 	u16 total_tx_cnt;
 	u16 paused;
+#endif
 };
 
 /* Bits in yellowfin_desc.cmd */
 enum desc_cmd_bits {
-	CMD_TX_PKT=0x1000, CMD_RX_BUF=0x2000, CMD_TXSTATUS=0x3000,
-	CMD_NOP=0x6000, CMD_STOP=0x7000,
-	BRANCH_ALWAYS=0x0C, INTR_ALWAYS=0x30, WAIT_ALWAYS=0x03,
-	BRANCH_IFTRUE=0x04,
+	CMD_TX_PKT=0x10000000, CMD_RX_BUF=0x20000000, CMD_TXSTATUS=0x30000000,
+	CMD_NOP=0x60000000, CMD_STOP=0x70000000,
+	BRANCH_ALWAYS=0x0C0000, INTR_ALWAYS=0x300000, WAIT_ALWAYS=0x030000,
+	BRANCH_IFTRUE=0x040000,
 };
 
 /* Bits in yellowfin_desc.status */
@@ -247,6 +285,7 @@
 	IntrTxDone=0x10, IntrTxInvalid=0x20, IntrTxPCIFault=0x40,IntrTxPCIErr=0x80,
 	IntrEarlyRx=0x100, IntrWakeup=0x200, };
 
+#define PRIV_ALIGN	31 	/* Required alignment mask */
 struct yellowfin_private {
 	/* Descriptor rings first for alignment.  Tx requires a second descriptor
 	   for status. */
@@ -254,21 +293,23 @@
 	struct yellowfin_desc tx_ring[TX_RING_SIZE*2];
 	const char *product_name;
 	struct net_device *next_module;
+	void *priv_addr;					/* Unaligned address for kfree */
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
 	struct tx_status_words tx_status[TX_RING_SIZE];
 	struct timer_list timer;	/* Media selection timer. */
-	struct enet_statistics stats;
-	spinlock_t lock;
+	struct net_device_stats stats;
 	/* Frequently used and paired value: keep adjacent for cache effect. */
-	int chip_id;
+	struct pci_dev *pci_dev;
+	int chip_id, flags;
 	struct yellowfin_desc *rx_head_desc;
-	struct tx_status_words *tx_tail_desc;
 	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. */
+	struct tx_status_words *tx_tail_desc;
+	unsigned int cur_tx, dirty_tx;
+	int tx_threshold;
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
 	unsigned int duplex_lock:1;
@@ -279,19 +320,21 @@
 	u16 advertising;					/* NWay media advertisement */
 	unsigned char phys[2];				/* MII device addresses. */
 	u32 pad[4];							/* Used for 32-byte alignment */
+	spinlock_t lock;
 };
 
+
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("Packet Engines Yellowfin G-NIC Gigabit 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");
+MODULE_PARM(gx_fix, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 
-static struct net_device *yellowfin_probe1(long ioaddr, int irq, int chip_id, int options);
+
 static int read_eeprom(long ioaddr, int location);
 static int mdio_read(long ioaddr, int phy_id, int location);
 static void mdio_write(long ioaddr, int phy_id, int location, int value);
@@ -307,197 +350,17 @@
 static int yellowfin_rx(struct net_device *dev);
 static void yellowfin_error(struct net_device *dev, int intr_status);
 static int yellowfin_close(struct net_device *dev);
-static struct enet_statistics *yellowfin_get_stats(struct net_device *dev);
+static struct net_device_stats *yellowfin_get_stats(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
 
-
-
-/* A list of all installed Yellowfin devices, for removing the driver module. */
-static struct net_device *root_yellowfin_dev = NULL;
-
-static int __init yellowfin_probe(void)
-{
-	int cards_found = 0;
-	int pci_index = 0;
-	unsigned char pci_bus, pci_device_fn;
-
-	if ( ! pci_present())
-		return -ENODEV;
-
-	for (;pci_index < 0xff; pci_index++) {
-		u8 pci_latency;
-		u16 pci_command, new_command, vendor, device;
-		int chip_idx;
-		int irq;
-		long ioaddr;
-		struct pci_dev *pdev;
-
-		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) break;
-		vendor = pdev->vendor;
-		device = pdev->device;
-
-		for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++)
-			if (vendor == chip_tbl[chip_idx].vendor_id
-				&& (device & chip_tbl[chip_idx].device_id_mask) ==
-				chip_tbl[chip_idx].device_id)
-				break;
-		if (chip_tbl[chip_idx].vendor_id == 0) 		/* Compiled out! */
-			continue;
-
-		{
-			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
-			ioaddr = pdev->resource[0].start;
-			irq = pdev->irq;
-		}
-
-		if (yellowfin_debug > 2)
-			printk(KERN_INFO "Found %s at I/O %#lx, IRQ %d.\n",
-				   chip_tbl[chip_idx].name, ioaddr, irq);
-
-		if (check_region(ioaddr, YELLOWFIN_TOTAL_SIZE))
-			continue;
-
-		pci_read_config_word(pdev, 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);
-			pci_write_config_word(pdev, PCI_COMMAND, new_command);
-		}
-
-		if(yellowfin_probe1(ioaddr, irq, chip_idx, cards_found))
-		{
-			/* Get and check the bus-master and latency values. */
-			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);
-			} else if (yellowfin_debug > 1)
-				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x.\n",
-					   pci_latency);
-			cards_found++;
-		}
-	}
-
-	return cards_found ? 0 : -ENODEV;
-}
-
-static struct net_device *yellowfin_probe1(long ioaddr, int irq, int chip_id, int card_idx)
-{
-	static int did_version = 0;			/* Already printed version info. */
-	struct yellowfin_private *yp;
-	int option, i;
-	struct net_device *dev;
-
-	if (yellowfin_debug > 0  &&  did_version++ == 0)
-		printk(version);
-
-	dev = init_etherdev(NULL, sizeof(struct yellowfin_private));
-
-	printk(KERN_INFO "%s: %s type %8x at 0x%lx, ",
-		   dev->name, chip_tbl[chip_id].name, inl(ioaddr + ChipRev), ioaddr);
-
-	if (inw(ioaddr + ChipRev) == 0x0702)
-		for (i = 0; i < 6; i++)
-			dev->dev_addr[i] = inb(ioaddr + StnAddr + i);
-	else {
-		int ee_offset = (read_eeprom(ioaddr, 6) == 0xff ? 0x100 : 0);
-		for (i = 0; i < 6; i++)
-			dev->dev_addr[i] = read_eeprom(ioaddr, ee_offset + i);
-	}
-	for (i = 0; i < 5; i++)
-			printk("%2.2x:", dev->dev_addr[i]);
-	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
-
-	/* Reset the chip. */
-	outl(0x80000000, ioaddr + DMACtrl);
-
-	/* We do a request_region() only to register /proc/ioports info. */
-	request_region(ioaddr, YELLOWFIN_TOTAL_SIZE, dev->name);
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-
-	/* Make certain the descriptor lists are aligned. */
-	yp = (void *)(((long)kmalloc(sizeof(*yp), GFP_KERNEL) + 31) & ~31);
-	memset(yp, 0, sizeof(*yp));
-	dev->priv = yp;
-
-	yp->next_module = root_yellowfin_dev;
-	root_yellowfin_dev = dev;
-
-	yp->chip_id = chip_id;
-	yp->lock = SPIN_LOCK_UNLOCKED;
-
-	option = card_idx < MAX_UNITS ? options[card_idx] : 0;
-	if (dev->mem_start)
-		option = dev->mem_start;
-
-	/* The lower four bits are the media type. */
-	if (option > 0) {
-		if (option & 0x200)
-			yp->full_duplex = 1;
-		yp->default_port = option & 15;
-		if (yp->default_port)
-			yp->medialock = 1;
-	}
-	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
-		yp->full_duplex = 1;
-
-	if (yp->full_duplex)
-		yp->duplex_lock = 1;
-
-	/* The Yellowfin-specific entries in the device structure. */
-	dev->open = &yellowfin_open;
-	dev->hard_start_xmit = &yellowfin_start_xmit;
-	dev->stop = &yellowfin_close;
-	dev->get_stats = &yellowfin_get_stats;
-	dev->set_multicast_list = &set_rx_mode;
-#ifdef HAVE_PRIVATE_IOCTL
-	dev->do_ioctl = &mii_ioctl;
-#endif
-	dev->tx_timeout = yellowfin_tx_timeout;
-	dev->watchdog_timeo = TX_TIMEOUT;
-
-	if (mtu)
-		dev->mtu = mtu;
-
-	if (chip_tbl[yp->chip_id].flags & HasMII) {
-		int phy, phy_idx = 0;
-		for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
-			int mii_status = mdio_read(ioaddr, phy, 1);
-			if (mii_status != 0xffff  &&
-				mii_status != 0x0000) {
-				yp->phys[phy_idx++] = phy;
-				yp->advertising = mdio_read(ioaddr, phy, 4);
-				printk(KERN_INFO "%s: MII PHY found at address %d, status "
-					   "0x%4.4x advertising %4.4x.\n",
-					   dev->name, phy, mii_status, yp->advertising);
-			}
-		}
-		yp->mii_cnt = phy_idx;
-	}
-
-	return dev;
-}
 
-static int read_eeprom(long ioaddr, int location)
+static int __devinit read_eeprom(long ioaddr, int location)
 {
-	int bogus_cnt = 1000;
+	int bogus_cnt = 10000;		/* Typical 33Mhz: 1050 ticks */
 
 	outb(location, ioaddr + EEAddr);
 	outb(0x30 | ((location >> 8) & 7), ioaddr + EECtrl);
-	while ((inb(ioaddr + EEStatus) & 0x80)  && --bogus_cnt > 0)
+	while ((inb(ioaddr + EEStatus) & 0x80)  &&  --bogus_cnt > 0)
 		;
 	return inb(ioaddr + EERead);
 }
@@ -575,13 +438,16 @@
 	/* Enable automatic generation of flow control frames, period 0xffff. */
 	outl(0x0030FFFF, ioaddr + FlowCtrl);
 
+	yp->tx_threshold = 32;
+	outl(yp->tx_threshold, ioaddr + TxThreshold);
+
 	if (dev->if_port == 0)
 		dev->if_port = yp->default_port;
 
-	netif_start_queue(dev);
+	netif_start_queue (dev);
 
 	/* Setting the Rx mode will start the Rx process. */
-	if (yp->chip_id == 0) {
+	if (yp->flags & IsGigabit) {
 		/* We are always in full-duplex mode with gigabit! */
 		yp->full_duplex = 1;
 		outw(0x01CF, ioaddr + Cnfg);
@@ -604,7 +470,7 @@
 	}
 	/* Set the timer to check for link beat. */
 	init_timer(&yp->timer);
-	yp->timer.expires = RUN_AT((24*HZ)/10);			/* 2.4 sec. */
+	yp->timer.expires = jiffies + 3*HZ;
 	yp->timer.data = (unsigned long)dev;
 	yp->timer.function = &yellowfin_timer;				/* timer handler */
 	add_timer(&yp->timer);
@@ -617,7 +483,7 @@
 	struct net_device *dev = (struct net_device *)data;
 	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int next_tick = 0;
+	int next_tick = 60*HZ;
 
 	if (yellowfin_debug > 3) {
 		printk(KERN_DEBUG "%s: Yellowfin timer tick, status %8.8x.\n",
@@ -646,10 +512,8 @@
 			next_tick = 3*HZ;
 	}
 
-	if (next_tick) {
-		yp->timer.expires = RUN_AT(next_tick);
-		add_timer(&yp->timer);
-	}
+	yp->timer.expires = jiffies + next_tick;
+	add_timer(&yp->timer);
 }
 
 static void yellowfin_tx_timeout(struct net_device *dev)
@@ -657,33 +521,37 @@
 	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 
-	printk(KERN_WARNING "%s: Yellowfin transmit timed out, status %8.8x, resetting...\n",
-		   dev->name, inl(ioaddr));
+	printk(KERN_WARNING "%s: Yellowfin transmit timed out at %d/%d Tx "
+		   "status %4.4x, Rx status %4.4x, resetting...\n",
+		   dev->name, yp->cur_tx, yp->dirty_tx,
+		   inl(ioaddr + TxStatus), inl(ioaddr + RxStatus));
 
-#ifndef __alpha__
-	{
+	/* Note: these should be KERN_DEBUG. */
+	if (yellowfin_debug) {
 		int i;
-		printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)yp->rx_ring);
+		printk(KERN_WARNING "  Rx ring %p: ", yp->rx_ring);
 		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)yp->rx_ring[i].status);
-		printk("\n"KERN_DEBUG"  Tx ring %8.8x: ", (int)yp->tx_ring);
+			printk(" %8.8x", yp->rx_ring[i].result_status);
+		printk("\n"KERN_WARNING"  Tx ring %p: ", yp->tx_ring);
 		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(" %4.4x /%4.4x", yp->tx_status[i].tx_errs, yp->tx_ring[i].status);
+			printk(" %4.4x /%8.8x", yp->tx_status[i].tx_errs,
+				   yp->tx_ring[i].result_status);
 		printk("\n");
 	}
-#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. */
-
-  dev->trans_start = jiffies;
-  yp->stats.tx_errors++;
-  return;
-}
+	/* If the hardware is found to hang regularly, we will update the code
+	   to reinitialize the chip here. */
+	dev->if_port = 0;
 
+	/* Wake the potentially-idle transmit channel. */
+	outl(0x10001000, dev->base_addr + TxCtrl);
+	if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE)
+		netif_wake_queue (dev);		/* Typical path */
+
+	dev->trans_start = jiffies;
+	yp->stats.tx_errors++;
+	return;
+}
 
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void yellowfin_init_ring(struct net_device *dev)
@@ -693,62 +561,66 @@
 
 	yp->tx_full = 0;
 	yp->cur_rx = yp->cur_tx = 0;
-	yp->dirty_rx = yp->dirty_tx = 0;
+	yp->dirty_tx = 0;
 
 	yp->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
 	yp->rx_head_desc = &yp->rx_ring[0];
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb;
-
-		yp->rx_ring[i].request_cnt = yp->rx_buf_sz;
-		yp->rx_ring[i].cmd = CMD_RX_BUF | INTR_ALWAYS;
+		yp->rx_ring[i].dbdma_cmd =
+			cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz);
+		yp->rx_ring[i].branch_addr = virt_to_le32desc(&yp->rx_ring[i+1]);
+	}
+	/* Mark the last entry as wrapping the ring. */
+	yp->rx_ring[i-1].branch_addr = virt_to_le32desc(&yp->rx_ring[0]);
 
-		skb = dev_alloc_skb(yp->rx_buf_sz);
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = dev_alloc_skb(yp->rx_buf_sz);
 		yp->rx_skbuff[i] = skb;
-		if (skb) {
-			skb->dev = dev;			/* Mark as being used by this device. */
-			skb_reserve(skb, 2);	/* 16 byte align the IP header. */
-			yp->rx_ring[i].addr = virt_to_bus(skb->tail);
-		} else if (yp->dirty_rx == 0)
-			yp->dirty_rx = (unsigned int)(0 - RX_RING_SIZE);
-		yp->rx_ring[i].branch_addr = virt_to_bus(&yp->rx_ring[i+1]);
+		if (skb == NULL)
+			break;
+		skb->dev = dev;			/* Mark as being used by this device. */
+		skb_reserve(skb, 2);	/* 16 byte align the IP header. */
+		yp->rx_ring[i].addr = virt_to_le32desc(skb->tail);
 	}
-	/* Mark the last entry as wrapping the ring. */
-	yp->rx_ring[i-1].cmd = CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS;
-	yp->rx_ring[i-1].branch_addr = virt_to_bus(&yp->rx_ring[0]);
+	yp->rx_ring[i-1].dbdma_cmd = cpu_to_le32(CMD_STOP);
+	yp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 
-/*#define NO_TXSTATS*/
+#define NO_TXSTATS
 #ifdef NO_TXSTATS
 	/* In this mode the Tx ring needs only a single descriptor. */
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		yp->tx_skbuff[i] = 0;
-		yp->tx_ring[i].cmd = CMD_STOP;
-		yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]);
+		yp->tx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP);
+		yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[i+1]);
 	}
-	yp->tx_ring[--i].cmd = CMD_STOP | BRANCH_ALWAYS; /* Wrap ring */
-	yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[0]);
+	/* Wrap ring */
+	yp->tx_ring[--i].dbdma_cmd = cpu_to_le32(CMD_STOP | BRANCH_ALWAYS);
+	yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[0]);
 #else
 	/* Tx ring needs a pair of descriptors, the second for the status. */
 	for (i = 0; i < TX_RING_SIZE*2; i++) {
 		yp->tx_skbuff[i/2] = 0;
-		yp->tx_ring[i].cmd = CMD_STOP; /* Branch on Tx error. */
-		yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]);
+		/* Branch on Tx error. */
+		yp->tx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP);
+		yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[i+1]);
 		i++;
-		if (chip_tbl[yp->chip_id].flags & FullTxStatus) {
-			yp->tx_ring[i].cmd = CMD_TXSTATUS;
+		if (yp->flags & FullTxStatus) {
+			yp->tx_ring[i].dbdma_cmd =
+				cpu_to_le32(CMD_TXSTATUS | sizeof(yp->tx_status[i]));
 			yp->tx_ring[i].request_cnt = sizeof(yp->tx_status[i]);
-			yp->tx_ring[i].addr = virt_to_bus(&yp->tx_status[i/2]);
+			yp->tx_ring[i].addr = virt_to_le32desc(&yp->tx_status[i/2]);
 		} else {				/* Symbios chips write only tx_errs word. */
-			yp->tx_ring[i].cmd = CMD_TXSTATUS | INTR_ALWAYS;
+			yp->tx_ring[i].dbdma_cmd =
+				cpu_to_le32(CMD_TXSTATUS | INTR_ALWAYS | 2);
 			yp->tx_ring[i].request_cnt = 2;
-			yp->tx_ring[i].addr = virt_to_bus(&yp->tx_status[i/2].tx_errs);
+			yp->tx_ring[i].addr = virt_to_le32desc(&yp->tx_status[i/2].tx_errs);
 		}
-		yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]);
+		yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[i+1]);
 	}
 	/* Wrap ring */
-	yp->tx_ring[--i].cmd = CMD_TXSTATUS | BRANCH_ALWAYS | INTR_ALWAYS;
-	yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[0]);
+	yp->tx_ring[--i].dbdma_cmd |= cpu_to_le32(BRANCH_ALWAYS | INTR_ALWAYS);
+	yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[0]);
 #endif
 	yp->tx_tail_desc = &yp->tx_status[0];
 	return;
@@ -759,6 +631,8 @@
 	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv;
 	unsigned entry;
 
+	netif_stop_queue (dev);
+
 	/* Caution: the write order is important here, set the base address
 	   with the "ownership" bits last. */
 
@@ -767,34 +641,42 @@
 
 	yp->tx_skbuff[entry] = skb;
 
+	if (gx_fix) {		/* Note: only works for paddable protocols e.g. IP. */
+		int cacheline_end = (virt_to_bus(skb->data) + skb->len) % 32;
+		/* Fix GX chipset errata. */
+		if (cacheline_end > 24  || cacheline_end == 0)
+			skb->len += 32 - cacheline_end + 1;
+	}
 #ifdef NO_TXSTATS
-	yp->tx_ring[entry].request_cnt = skb->len;
-	yp->tx_ring[entry].addr = virt_to_bus(skb->data);
-	yp->tx_ring[entry].status = 0;
+	yp->tx_ring[entry].addr = virt_to_le32desc(skb->data);
+	yp->tx_ring[entry].result_status = 0;
 	if (entry >= TX_RING_SIZE-1) {
-		yp->tx_ring[0].cmd = CMD_STOP; /* New stop command. */
-		yp->tx_ring[TX_RING_SIZE-1].cmd = CMD_TX_PKT | BRANCH_ALWAYS;
+		/* New stop command. */
+		yp->tx_ring[0].dbdma_cmd = cpu_to_le32(CMD_STOP);
+		yp->tx_ring[TX_RING_SIZE-1].dbdma_cmd =
+			cpu_to_le32(CMD_TX_PKT|BRANCH_ALWAYS | skb->len);
 	} else {
-		yp->tx_ring[entry+1].cmd = CMD_STOP; /* New stop command. */
-		yp->tx_ring[entry].cmd = CMD_TX_PKT | BRANCH_IFTRUE;
+		yp->tx_ring[entry+1].dbdma_cmd = cpu_to_le32(CMD_STOP);
+		yp->tx_ring[entry].dbdma_cmd =
+			cpu_to_le32(CMD_TX_PKT | BRANCH_IFTRUE | skb->len);
 	}
 	yp->cur_tx++;
 #else
 	yp->tx_ring[entry<<1].request_cnt = skb->len;
-	yp->tx_ring[entry<<1].addr = virt_to_bus(skb->data);
+	yp->tx_ring[entry<<1].addr = virt_to_le32desc(skb->data);
 	/* The input_last (status-write) command is constant, but we must rewrite
 	   the subsequent 'stop' command. */
 
 	yp->cur_tx++;
 	{
 		unsigned next_entry = yp->cur_tx % TX_RING_SIZE;
-		yp->tx_ring[next_entry<<1].cmd = CMD_STOP;
+		yp->tx_ring[next_entry<<1].dbdma_cmd = cpu_to_le32(CMD_STOP);
 	}
 	/* Final step -- overwrite the old 'stop' command. */
 
-	yp->tx_ring[entry<<1].cmd =
-		(entry % 6) == 0 ? CMD_TX_PKT | INTR_ALWAYS | BRANCH_IFTRUE :
-		CMD_TX_PKT | BRANCH_IFTRUE;
+	yp->tx_ring[entry<<1].dbdma_cmd =
+		cpu_to_le32( ((entry % 6) == 0 ? CMD_TX_PKT|INTR_ALWAYS|BRANCH_IFTRUE :
+					  CMD_TX_PKT | BRANCH_IFTRUE) | skb->len);
 #endif
 
 	/* Non-x86 Todo: explicitly flush cache lines here. */
@@ -802,8 +684,8 @@
 	/* Wake the potentially-idle transmit channel. */
 	outl(0x10001000, dev->base_addr + TxCtrl);
 
-	if (yp->cur_tx - yp->dirty_tx < TX_RING_SIZE - 1)
-		netif_start_queue(dev);		/* Typical path */
+	if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE)
+		netif_start_queue (dev);		/* Typical path */
 	else
 		yp->tx_full = 1;
 	dev->trans_start = jiffies;
@@ -853,20 +735,23 @@
 #ifdef NO_TXSTATS
 		for (; yp->cur_tx - yp->dirty_tx > 0; yp->dirty_tx++) {
 			int entry = yp->dirty_tx % TX_RING_SIZE;
-			if (yp->tx_ring[entry].status == 0)
+			if (yp->tx_ring[entry].result_status == 0)
 				break;
+			yp->stats.tx_bytes += yp->tx_skbuff[entry]->len;
+			yp->stats.tx_packets++;
 			/* Free the original skb. */
 			dev_kfree_skb_irq(yp->tx_skbuff[entry]);
 			yp->tx_skbuff[entry] = 0;
-			yp->stats.tx_packets++;
 		}
-		if (yp->tx_full &&
-		    test_bit(LINK_STATE_XOFF, &dev->flags) &&
-		    yp->cur_tx - yp->dirty_tx < TX_RING_SIZE - 4) {
+		if (yp->tx_full
+			&& yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) {
 			/* The ring is no longer full, clear tbusy. */
 			yp->tx_full = 0;
-			netif_wake_queue (dev);
 		}
+		if (yp->tx_full)
+			netif_stop_queue(dev);
+		else
+			netif_wake_queue(dev);
 #else
 		if (intr_status & IntrTxDone
 			|| yp->tx_tail_desc->tx_errs) {
@@ -890,7 +775,7 @@
 #endif
 				if (tx_errs == 0)
 					break;			/* It still hasn't been Txed */
-				if (tx_errs & 0xF8100000) {
+				if (tx_errs & 0xF810) {
 					/* There was an major error, log it. */
 #ifndef final_version
 					if (yellowfin_debug > 1)
@@ -914,10 +799,10 @@
 #ifdef ETHER_STATS
 					if (tx_errs & 0x0400) yp->stats.tx_deferred++;
 #endif
+					yp->stats.tx_bytes += yp->tx_skbuff[entry]->len;
 					yp->stats.collisions += tx_errs & 15;
 					yp->stats.tx_packets++;
 				}
-
 				/* Free the original skb. */
 				dev_kfree_skb_irq(yp->tx_skbuff[entry]);
 				yp->tx_skbuff[entry] = 0;
@@ -933,13 +818,15 @@
 			}
 #endif
 
-			if (yp->tx_full &&
-			    test_bit(LINK_STATE_XOFF, &dev->flags) &&
-			    yp->cur_tx - dirty_tx < TX_RING_SIZE - 2) {
+			if (yp->tx_full
+				&& yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) {
 				/* The ring is no longer full, clear tbusy. */
 				yp->tx_full = 0;
-				netif_wake_queue (dev);
 			}
+			if (yp->tx_full)
+				netif_stop_queue(dev);
+			else
+				netif_wake_queue(dev);
 
 			yp->dirty_tx = dirty_tx;
 			yp->tx_tail_desc = &yp->tx_status[dirty_tx % TX_RING_SIZE];
@@ -964,15 +851,14 @@
 	/* Code that should never be run!  Perhaps remove after testing.. */
 	{
 		static int stopit = 10;
-		if ((!test_bit(LINK_STATE_START, &dev->state))  &&  --stopit < 0) {
+		if ((!(test_bit(LINK_STATE_START, &dev->state)))  &&  --stopit < 0) {
 			printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n",
 				   dev->name);
 			free_irq(irq, dev);
 		}
 	}
 
-	spin_lock (&yp->lock);
-	return;
+	spin_unlock (&yp->lock);
 }
 
 /* This routine is logically part of the interrupt handler, but separated
@@ -984,21 +870,22 @@
 	int boguscnt = 20;
 
 	if (yellowfin_debug > 4) {
-		printk(KERN_DEBUG " In yellowfin_rx(), entry %d status %4.4x.\n",
-			   entry, yp->rx_ring[entry].status);
-		printk(KERN_DEBUG "   #%d desc. %4.4x %4.4x %8.8x %4.4x %4.4x.\n",
-			   entry, yp->rx_ring[entry].cmd,
-			   yp->rx_ring[entry].request_cnt, yp->rx_ring[entry].addr,
-			   yp->rx_ring[entry].result_cnt, yp->rx_ring[entry].status);
+		printk(KERN_DEBUG " In yellowfin_rx(), entry %d status %8.8x.\n",
+			   entry, yp->rx_ring[entry].result_status);
+		printk(KERN_DEBUG "   #%d desc. %8.8x %8.8x %8.8x.\n",
+			   entry, yp->rx_ring[entry].dbdma_cmd, yp->rx_ring[entry].addr,
+			   yp->rx_ring[entry].result_status);
 	}
 
 	/* If EOP is set on the next entry, it's a new packet. Send it up. */
-	while (yp->rx_head_desc->status) {
+	while (yp->rx_head_desc->result_status) {
 		struct yellowfin_desc *desc = yp->rx_head_desc;
-		u16 desc_status = desc->status;
-		int data_size = desc->request_cnt - desc->result_cnt;
-		u8 *buf_addr = bus_to_virt(desc->addr);
-		s16 frame_status = get_unaligned((s16*)(buf_addr+data_size-2));
+		u16 desc_status = le32_to_cpu(desc->result_status) >> 16;
+		int data_size =
+			(le32_to_cpu(desc->dbdma_cmd) - le32_to_cpu(desc->result_status))
+			& 0xffff;
+		u8 *buf_addr = le32desc_to_virt(desc->addr);
+		s16 frame_status = get_unaligned((s16*)&(buf_addr[data_size - 2]));
 
 		if (yellowfin_debug > 4)
 			printk(KERN_DEBUG "  yellowfin_rx() status was %4.4x.\n",
@@ -1009,7 +896,7 @@
 			printk(KERN_WARNING "%s: Oversized Ethernet frame spanned multiple buffers,"
 				   " status %4.4x!\n", dev->name, desc_status);
 			yp->stats.rx_length_errors++;
-		} else if (yp->chip_id == 0  &&  (frame_status & 0x0038)) {
+		} else if ((yp->flags & IsGigabit)  &&  (frame_status & 0x0038)) {
 			/* There was a error. */
 			if (yellowfin_debug > 3)
 				printk(KERN_DEBUG "  yellowfin_rx() Rx error was %4.4x.\n",
@@ -1019,7 +906,7 @@
 			if (frame_status & 0x0008) yp->stats.rx_frame_errors++;
 			if (frame_status & 0x0010) yp->stats.rx_crc_errors++;
 			if (frame_status < 0) yp->stats.rx_dropped++;
-		} else if (yp->chip_id != 0  &&
+		} else if ( !(yp->flags & IsGigabit)  &&
 				   ((buf_addr[data_size-1] & 0x85) || buf_addr[data_size-2] & 0xC0)) {
 			u8 status1 = buf_addr[data_size-2];
 			u8 status2 = buf_addr[data_size-1];
@@ -1029,14 +916,16 @@
 			if (status2 & 0x04) yp->stats.rx_crc_errors++;
 			if (status2 & 0x80) yp->stats.rx_dropped++;
 #ifdef YF_PROTOTYPE			/* Support for prototype hardware errata. */
-		} else if (memcmp(bus_to_virt(yp->rx_ring[entry].addr),
+		} else if ((yp->flags & HasMACAddrBug)  &&
+				   memcmp(le32desc_to_virt(yp->rx_ring[entry].addr),
 						  dev->dev_addr, 6) != 0
-				   && memcmp(bus_to_virt(yp->rx_ring[entry].addr),
+				   && memcmp(le32desc_to_virt(yp->rx_ring[entry].addr),
 							 "\377\377\377\377\377\377", 6) != 0) {
-			printk(KERN_WARNING "%s: Bad frame to %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
-				   dev->name, buf_addr[0], buf_addr[1], buf_addr[2],
-				   buf_addr[3], buf_addr[4], buf_addr[5]);
-			bogus_rx++;
+			if (bogus_rx++ == 0)
+				printk(KERN_WARNING "%s: Bad frame to %2.2x:%2.2x:%2.2x:%2.2x:"
+					   "%2.2x:%2.2x.\n",
+					   dev->name, buf_addr[0], buf_addr[1], buf_addr[2],
+					   buf_addr[3], buf_addr[4], buf_addr[5]);
 #endif
 		} else {
 			struct sk_buff *skb;
@@ -1055,10 +944,10 @@
 			if (pkt_len > rx_copybreak) {
 				char *temp = skb_put(skb = yp->rx_skbuff[entry], pkt_len);
 #ifndef final_verison				/* Remove after testing. */
-				if (bus_to_virt(yp->rx_ring[entry].addr) != temp)
+				if (le32desc_to_virt(yp->rx_ring[entry].addr) != temp)
 					printk(KERN_WARNING "%s: Warning -- the skbuff addresses "
 						   "do not match in yellowfin_rx: %p vs. %p / %p.\n",
-						   dev->name, bus_to_virt(yp->rx_ring[entry].addr),
+						   dev->name, le32desc_to_virt(yp->rx_ring[entry].addr),
 						   skb->head, temp);
 #endif
 				yp->rx_skbuff[entry] = NULL;
@@ -1068,19 +957,19 @@
 					break;
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the data fields */
-#if 1
-				eth_copy_and_sum(skb, bus_to_virt(yp->rx_ring[entry].addr),
-								 pkt_len, 0);
+#if 1 || USE_IP_CSUM
+				eth_copy_and_sum(skb, yp->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
 #else
-				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(yp->rx_ring[entry].addr), pkt_len);
+				memcpy(skb_put(skb, pkt_len), yp->rx_skbuff[entry]->tail,
+					   pkt_len);
 #endif
 			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			yp->stats.rx_packets++;
+			yp->stats.rx_bytes += pkt_len;
 		}
 		entry = (++yp->cur_rx) % RX_RING_SIZE;
 		yp->rx_head_desc = &yp->rx_ring[entry];
@@ -1088,24 +977,25 @@
 
 	/* Refill the Rx ring buffers. */
 	for (; yp->cur_rx - yp->dirty_rx > 0; yp->dirty_rx++) {
-		struct sk_buff *skb;
 		entry = yp->dirty_rx % RX_RING_SIZE;
 		if (yp->rx_skbuff[entry] == NULL) {
-			skb = dev_alloc_skb(yp->rx_buf_sz);
+			struct sk_buff *skb = dev_alloc_skb(yp->rx_buf_sz);
 			if (skb == NULL)
 				break;			/* Better luck next round. */
+			yp->rx_skbuff[entry] = skb;
 			skb->dev = dev;			/* Mark as being used by this device. */
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			yp->rx_ring[entry].addr = virt_to_bus(skb->tail);
-			yp->rx_skbuff[entry] = skb;
+			yp->rx_ring[entry].addr = virt_to_le32desc(skb->tail);
 		}
-		yp->rx_ring[entry].cmd = CMD_STOP;
-		yp->rx_ring[entry].status = 0;	/* Clear complete bit. */
+		yp->rx_ring[entry].dbdma_cmd = cpu_to_le32(CMD_STOP);
+		yp->rx_ring[entry].result_status = 0;	/* Clear complete bit. */
 		if (entry != 0)
-			yp->rx_ring[entry - 1].cmd = CMD_RX_BUF | INTR_ALWAYS;
+			yp->rx_ring[entry - 1].dbdma_cmd =
+				cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz);
 		else
-			yp->rx_ring[RX_RING_SIZE - 1].cmd =
-				CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS;
+			yp->rx_ring[RX_RING_SIZE - 1].dbdma_cmd =
+				cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS
+							| yp->rx_buf_sz);
 	}
 
 	return 0;
@@ -1130,7 +1020,7 @@
 	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv;
 	int i;
 
-	netif_stop_queue(dev);
+	netif_stop_queue (dev);
 
 	if (yellowfin_debug > 1) {
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n",
@@ -1149,16 +1039,14 @@
 
 	del_timer(&yp->timer);
 
-#ifdef __i386__
+#if !defined(final_version) && defined(__i386__)
 	if (yellowfin_debug > 2) {
 		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", (int)virt_to_bus(yp->tx_ring));
 		for (i = 0; i < TX_RING_SIZE*2; i++)
-			printk(" %c #%d desc. %4.4x %4.4x %8.8x %8.8x %4.4x %4.4x.\n",
+			printk(" %c #%d desc. %8.8x %8.8x %8.8x %8.8x.\n",
 				   inl(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ',
-				   i, yp->tx_ring[i].cmd,
-				   yp->tx_ring[i].request_cnt, yp->tx_ring[i].addr,
-				   yp->tx_ring[i].branch_addr,
-				   yp->tx_ring[i].result_cnt, yp->tx_ring[i].status);
+				   i, yp->tx_ring[i].dbdma_cmd, yp->tx_ring[i].addr,
+				   yp->tx_ring[i].branch_addr, yp->tx_ring[i].result_status);
 		printk(KERN_DEBUG "  Tx status %p:\n", yp->tx_status);
 		for (i = 0; i < TX_RING_SIZE; i++)
 			printk("   #%d status %4.4x %4.4x %4.4x %4.4x.\n",
@@ -1167,16 +1055,16 @@
 
 		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", (int)virt_to_bus(yp->rx_ring));
 		for (i = 0; i < RX_RING_SIZE; i++) {
-			printk(KERN_DEBUG " %c #%d desc. %4.4x %4.4x %8.8x %4.4x %4.4x\n",
+			printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x %8.8x\n",
 				   inl(ioaddr + RxPtr) == (long)&yp->rx_ring[i] ? '>' : ' ',
-				   i, yp->rx_ring[i].cmd,
-				   yp->rx_ring[i].request_cnt, yp->rx_ring[i].addr,
-				   yp->rx_ring[i].result_cnt, yp->rx_ring[i].status);
+				   i, yp->rx_ring[i].dbdma_cmd, yp->rx_ring[i].addr,
+				   yp->rx_ring[i].result_status);
 			if (yellowfin_debug > 6) {
-				if (*(u8*)yp->rx_ring[i].addr != 0x69) {
+				if (get_unaligned((u8*)yp->rx_ring[i].addr) != 0x69) {
 					int j;
 					for (j = 0; j < 0x50; j++)
-						printk(" %4.4x", ((u16*)yp->rx_ring[i].addr)[j]);
+						printk(" %4.4x",
+							   get_unaligned(((u16*)yp->rx_ring[i].addr) + j));
 					printk("\n");
 				}
 			}
@@ -1188,7 +1076,7 @@
 
 	/* Free all the skbuffs in the Rx queue. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		yp->rx_ring[i].cmd = CMD_STOP;
+		yp->rx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP);
 		yp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
 		if (yp->rx_skbuff[i]) {
 			dev_kfree_skb(yp->rx_skbuff[i]);
@@ -1212,7 +1100,7 @@
 	return 0;
 }
 
-static struct enet_statistics *yellowfin_get_stats(struct net_device *dev)
+static struct net_device_stats *yellowfin_get_stats(struct net_device *dev)
 {
 	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv;
 	return &yp->stats;
@@ -1267,7 +1155,7 @@
 			 i++, mclist = mclist->next) {
 			/* Due to a bug in the early chip versions, multiple filter
 			   slots must be set for each address. */
-			if (yp->chip_id == 0) {
+			if (yp->flags & HasMulticastBug) {
 				set_bit((ether_crc_le(3, mclist->dmi_addr) >> 3) & 0x3f,
 						hash_table);
 				set_bit((ether_crc_le(4, mclist->dmi_addr) >> 3) & 0x3f,
@@ -1303,7 +1191,7 @@
 		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
 		return 0;
@@ -1314,40 +1202,201 @@
 #endif  /* HAVE_PRIVATE_IOCTL */
 
 
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-
-static int __init yellowfin_init_module(void)
+static int __devinit yellowfin_init_one(struct pci_dev *pdev,
+					const struct pci_device_id *ent)
 {
-	if (debug >= 0)
-		yellowfin_debug = debug;
+	struct net_device *dev;
+	struct yellowfin_private *yp;
+	int option, i, irq;
+	int flags, chip_idx;
+	static int find_cnt = 0;
+	long ioaddr;
+	
+	chip_idx = ent->driver_data;
+	flags = chip_info[chip_idx].flags;
 
-	return yellowfin_probe();
+	dev = init_etherdev(NULL, 0);
+	if (!dev) {
+		printk (KERN_ERR PFX "cannot allocate ethernet device\n");
+		return -ENOMEM;
+	}
+
+	dev->priv = kmalloc(sizeof(*yp) + PRIV_ALIGN, GFP_KERNEL);
+	if (!dev->priv)
+		goto err_out_free_netdev;
+	yp = (void *)(((long)dev->priv + PRIV_ALIGN) & ~PRIV_ALIGN);
+	memset(yp, 0, sizeof(*yp));
+	yp->priv_addr = dev->priv;	/* store real addr for kfree */
+	dev->priv = yp;			/* use aligned addr */
+
+	if (!request_region (pci_resource_start (pdev, 0),
+			     YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) {
+		printk (KERN_ERR PFX "cannot obtain I/O port region\n");
+		goto err_out_free_priv;
+	}
+	if (!request_mem_region (pci_resource_start (pdev, 1),
+			         YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) {
+		printk (KERN_ERR PFX "cannot obtain MMIO region\n");
+		goto err_out_free_pio_region;
+	}
+	
+	pci_enable_device (pdev);
+	pci_set_master (pdev);
+
+#ifdef USE_IO_OPS
+	ioaddr = pci_resource_start (pdev, 0);
+#else
+	ioaddr = pci_resource_start (pdev, 1);
+#endif
+	irq = pdev->irq;
+
+	printk(KERN_INFO "%s: %s type %8x at 0x%lx, ",
+		   dev->name, chip_info[chip_idx].name, inl(ioaddr + ChipRev), ioaddr);
+
+	if (flags & IsGigabit)
+		for (i = 0; i < 6; i++)
+			dev->dev_addr[i] = inb(ioaddr + StnAddr + i);
+	else {
+		int ee_offset = (read_eeprom(ioaddr, 6) == 0xff ? 0x100 : 0);
+		for (i = 0; i < 6; i++)
+			dev->dev_addr[i] = read_eeprom(ioaddr, ee_offset + i);
+	}
+	for (i = 0; i < 5; i++)
+			printk("%2.2x:", dev->dev_addr[i]);
+	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+	/* Reset the chip. */
+	outl(0x80000000, ioaddr + DMACtrl);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	pdev->driver_data = dev;
+	yp->chip_id = chip_idx;
+	yp->flags = flags;
+	yp->lock = SPIN_LOCK_UNLOCKED;
+
+	option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
+	if (dev->mem_start)
+		option = dev->mem_start;
+
+	/* The lower four bits are the media type. */
+	if (option > 0) {
+		if (option & 0x200)
+			yp->full_duplex = 1;
+		yp->default_port = option & 15;
+		if (yp->default_port)
+			yp->medialock = 1;
+	}
+	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
+		yp->full_duplex = 1;
+
+	if (yp->full_duplex)
+		yp->duplex_lock = 1;
+
+	/* The Yellowfin-specific entries in the device structure. */
+	dev->open = &yellowfin_open;
+	dev->hard_start_xmit = &yellowfin_start_xmit;
+	dev->stop = &yellowfin_close;
+	dev->get_stats = &yellowfin_get_stats;
+	dev->set_multicast_list = &set_rx_mode;
+#ifdef HAVE_PRIVATE_IOCTL
+	dev->do_ioctl = &mii_ioctl;
+#endif
+	dev->tx_timeout = yellowfin_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+
+	if (mtu)
+		dev->mtu = mtu;
+
+	if (yp->flags & HasMII) {
+		int phy, phy_idx = 0;
+		for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
+			int mii_status = mdio_read(ioaddr, phy, 1);
+			if (mii_status != 0xffff  &&
+				mii_status != 0x0000) {
+				yp->phys[phy_idx++] = phy;
+				yp->advertising = mdio_read(ioaddr, phy, 4);
+				printk(KERN_INFO "%s: MII PHY found at address %d, status "
+					   "0x%4.4x advertising %4.4x.\n",
+					   dev->name, phy, mii_status, yp->advertising);
+			}
+		}
+		yp->mii_cnt = phy_idx;
+	}
+
+	find_cnt++;
+	
+	return 0;
+
+err_out_free_pio_region:
+	release_region (pci_resource_start (pdev, 0), YELLOWFIN_SIZE);
+err_out_free_priv:
+	kfree (dev->priv);
+err_out_free_netdev:
+	unregister_netdev (dev);
+	kfree (dev);
+	return -ENODEV;
 }
 
-static void __exit yellowfin_cleanup_module (void)
+static void __devexit yellowfin_remove_one (struct pci_dev *pdev)
 {
-	struct net_device *next_dev;
+	struct net_device *dev = pdev->driver_data;
+	struct yellowfin_private *np;
 
-	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-	while (root_yellowfin_dev) {
-		next_dev = ((struct yellowfin_private *)root_yellowfin_dev->priv)->next_module;
-		unregister_netdev(root_yellowfin_dev);
-		release_region(root_yellowfin_dev->base_addr, YELLOWFIN_TOTAL_SIZE);
-		kfree(root_yellowfin_dev);
-		root_yellowfin_dev = next_dev;
+	if (!dev) {
+		printk (KERN_ERR "remove non-existent device\n");
+		return;
 	}
+	np = (struct yellowfin_private *) dev->priv;
+
+	unregister_netdev (dev);
+
+#ifdef USE_IO_OPS
+	release_region (dev->base_addr, YELLOWFIN_SIZE);
+#else
+	iounmap ((void *) dev->base_addr);
+	release_mem_region (dev->base_addr, YELLOWFIN_SIZE);
+#endif
+
+	if (np->priv_addr)
+		kfree (np->priv_addr);
+
+	kfree (dev);
+}
+
+
+static struct pci_driver yellowfin_driver = {
+	name:		YELLOWFIN_MODULE_NAME,
+	id_table:	yellowfin_pci_tbl,
+	probe:		yellowfin_init_one,
+	remove:		yellowfin_remove_one,
+};
+
+
+static int __init yellowfin_init (void)
+{
+	if (debug)					/* Emit version even if no cards detected. */
+		printk(KERN_INFO "%s", version);
+	return pci_register_driver (&yellowfin_driver) > 0 ? 0 : -ENODEV;
+}
+
+
+static void __exit yellowfin_cleanup (void)
+{
+	pci_unregister_driver (&yellowfin_driver);
 }
 
-module_init(yellowfin_init_module);
-module_exit(yellowfin_cleanup_module);
+
+module_init(yellowfin_init);
+module_exit(yellowfin_exit);
 
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  compile-command-alphaLX: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`  -fomit-frame-pointer -fno-strength-reduce -mno-fp-regs -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED"
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__  -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  compile-command-alphaLX: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`  -fomit-frame-pointer -fno-strength-reduce -mno-fp-regs -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED"
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --recursive --new-file v2.3.45/linux/drivers/pci/pcisyms.c linux/drivers/pci/pcisyms.c
--- v2.3.45/linux/drivers/pci/pcisyms.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/pci/pcisyms.c	Tue Feb 15 11:40:43 2000
@@ -20,9 +20,11 @@
 EXPORT_SYMBOL(pci_devices);
 EXPORT_SYMBOL(pci_root_buses);
 EXPORT_SYMBOL(pci_enable_device);
+EXPORT_SYMBOL(pci_find_capability);
 EXPORT_SYMBOL(pci_find_class);
 EXPORT_SYMBOL(pci_find_device);
 EXPORT_SYMBOL(pci_find_slot);
+EXPORT_SYMBOL(pci_find_subsys);
 EXPORT_SYMBOL(pci_set_master);
 EXPORT_SYMBOL(pci_set_power_state);
 EXPORT_SYMBOL(pci_assign_resource);
diff -u --recursive --new-file v2.3.45/linux/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c
--- v2.3.45/linux/drivers/pcmcia/cs.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/pcmcia/cs.c	Tue Feb 15 08:53:46 2000
@@ -2,7 +2,7 @@
 
     PCMCIA Card Services -- core services
 
-    cs.c 1.247 2000/01/15 04:30:35
+    cs.c 1.249 2000/02/10 23:26:11
     
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -69,7 +69,7 @@
 int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 static const char *version =
-"cs.c 1.247 2000/01/15 04:30:35 (David Hinds)";
+"cs.c 1.249 2000/02/10 23:26:11 (David Hinds)";
 #endif
 
 #ifdef CONFIG_PCI
@@ -341,6 +341,7 @@
 	s->sock = ns;
 	s->setup.data = sockets;
 	s->setup.function = &setup_socket;
+	s->setup_timeout = 0;
 	s->shutdown.data = sockets;
 	s->shutdown.function = &shutdown_socket;
 	/* base address = 0, map = 0 */
@@ -486,7 +487,17 @@
     socket_info_t *s = socket_table[i];
 
     get_socket_status(s, &val);
-    if (val & SS_DETECT) {
+    if (val & SS_PENDING) {
+	/* Does the socket need more time? */
+	DEBUG(2, "cs: setup_socket(%ld): status pending\n", i);
+	if (++s->setup_timeout > 100) {
+	    printk(KERN_NOTICE "cs: socket %ld voltage interrogation"
+		   " timed out\n", i);
+	} else {
+	    s->setup.expires = jiffies + HZ/10;
+	    add_timer(&s->setup);
+	}
+    } else if (val & SS_DETECT) {
 	DEBUG(1, "cs: setup_socket(%ld): applying power\n", i);
 	s->state |= SOCKET_PRESENT;
 	s->socket.flags = 0;
@@ -532,7 +543,7 @@
     udelay((long)reset_time);
     s->socket.flags &= ~SS_RESET;
     set_socket(s, &s->socket);
-    s->unreset_timeout = 0;
+    s->setup_timeout = 0;
     s->setup.expires = jiffies + unreset_delay;
     s->setup.function = &unreset_socket;
     add_timer(&s->setup);
@@ -571,12 +582,11 @@
 	}
     } else {
 	DEBUG(2, "cs: socket %ld not ready yet\n", i);
-	if (s->unreset_timeout > unreset_limit) {
+	if (++s->setup_timeout > unreset_limit) {
 	    printk(KERN_NOTICE "cs: socket %ld timed out during"
 		   " reset\n", i);
 	    s->state &= ~EVENT_MASK;
 	} else {
-	    s->unreset_timeout++;
 	    s->setup.expires = jiffies + unreset_check;
 	    add_timer(&s->setup);
 	}
@@ -1156,6 +1166,8 @@
 	return s->cap.cb_dev->subordinate;
 }
 
+EXPORT_SYMBOL(pcmcia_lookup_bus);
+
 #endif
 
 /*======================================================================
@@ -2188,7 +2200,7 @@
     {
 	memory_handle_t m;
         int ret = pcmcia_open_memory(a1, a2, &m);
-        (memory_handle_t *)a1 = m;
+        *(memory_handle_t *)a1 = m;
 	return  ret;
     }
         break;
@@ -2202,7 +2214,7 @@
     {
 	eraseq_handle_t w;
         int ret = pcmcia_register_erase_queue(a1, a2, &w);
-        (eraseq_handle_t *)a1 = w;
+        *(eraseq_handle_t *)a1 = w;
 	return  ret;
     }
         break;
@@ -2227,7 +2239,7 @@
     {
 	window_handle_t w;
         int ret = pcmcia_request_window(a1, a2, &w);
-        (window_handle_t *)a1 = w;
+        *(window_handle_t *)a1 = w;
 	return  ret;
     }
         break;
@@ -2297,7 +2309,6 @@
 EXPORT_SYMBOL(pcmcia_get_status);
 EXPORT_SYMBOL(pcmcia_get_tuple_data);
 EXPORT_SYMBOL(pcmcia_insert_card);
-EXPORT_SYMBOL(pcmcia_lookup_bus);
 EXPORT_SYMBOL(pcmcia_map_mem_page);
 EXPORT_SYMBOL(pcmcia_modify_configuration);
 EXPORT_SYMBOL(pcmcia_modify_window);
diff -u --recursive --new-file v2.3.45/linux/drivers/pcmcia/cs_internal.h linux/drivers/pcmcia/cs_internal.h
--- v2.3.45/linux/drivers/pcmcia/cs_internal.h	Tue Jan  4 13:57:17 2000
+++ linux/drivers/pcmcia/cs_internal.h	Wed Feb 16 12:55:29 2000
@@ -131,7 +131,7 @@
     u_int			real_clients;
     client_handle_t		reset_handle;
     struct timer_list		setup, shutdown;
-    u_long			unreset_timeout;
+    u_long			setup_timeout;
     pccard_mem_map		cis_mem;
     u_char			*cis_virt;
     config_t			*config;
diff -u --recursive --new-file v2.3.45/linux/drivers/pcmcia/i82365.c linux/drivers/pcmcia/i82365.c
--- v2.3.45/linux/drivers/pcmcia/i82365.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/pcmcia/i82365.c	Tue Feb 15 08:53:46 2000
@@ -82,9 +82,9 @@
 static void irq_count(int, void *, struct pt_regs *);
 static inline int _check_irq(int irq, int flags)
 {
-    if (request_irq(irq, irq_count, flags, "x", NULL) != 0)
+    if (request_irq(irq, irq_count, flags, "x", irq_count) != 0)
 	return -1;
-    free_irq(irq, NULL);
+    free_irq(irq, irq_count);
     return 0;
 }
 
@@ -570,28 +570,26 @@
     DEBUG(2, "-> hit on irq %d\n", irq);
 }
 
-static u_int __init test_irq(u_short sock, int irq, int pci)
+static u_int __init test_irq(u_short sock, int irq)
 {
-    u_char csc = (pci) ? 0 : irq;
-    DEBUG(2, "  testing %s irq %d\n", pci ? "PCI" : "ISA", irq);
-    
-    if (request_irq(irq, irq_count, (pci?SA_SHIRQ:0), "scan", NULL) != 0)
+    DEBUG(2, "  testing ISA irq %d\n", irq);
+    if (request_irq(irq, irq_count, 0, "scan", irq_count) != 0)
 	return 1;
     irq_hits = 0; irq_sock = sock;
     __set_current_state(TASK_UNINTERRUPTIBLE);
     schedule_timeout(HZ/100);
     if (irq_hits) {
-	free_irq(irq, NULL);
+	free_irq(irq, irq_count);
 	DEBUG(2, "    spurious hit!\n");
 	return 1;
     }
 
     /* Generate one interrupt */
-    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (csc << 4));
+    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4));
     i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
     udelay(1000);
 
-    free_irq(irq, NULL);
+    free_irq(irq, irq_count);
 
     /* mask all interrupts */
     i365_set(sock, I365_CSCINT, 0);
@@ -617,10 +615,10 @@
 	set_bridge_state(sock);
 	i365_set(sock, I365_CSCINT, 0);
 	for (i = 0; i < 16; i++)
-	    if ((mask0 & (1 << i)) && (test_irq(sock, i, 0) == 0))
+	    if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0))
 		mask1 |= (1 << i);
 	for (i = 0; i < 16; i++)
-	    if ((mask1 & (1 << i)) && (test_irq(sock, i, 0) != 0))
+	    if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0))
 		mask1 ^= (1 << i);
     }
     
@@ -1543,7 +1541,7 @@
     /* Set up interrupt handler(s) */
 #ifdef CONFIG_ISA
     if (grab_irq != 0)
-	request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL);
+	request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
 #endif
     
     if (register_ss_entry(sockets, &pcic_operations) != 0)
@@ -1573,7 +1571,7 @@
 	del_timer(&poll_timer);
 #ifdef CONFIG_ISA
     if (grab_irq != 0)
-	free_irq(cs_irq, NULL);
+	free_irq(cs_irq, pcic_interrupt);
 #endif
     for (i = 0; i < sockets; i++) {
 	/* Turn off all interrupt sources! */
diff -u --recursive --new-file v2.3.45/linux/drivers/pcmcia/tcic.c linux/drivers/pcmcia/tcic.c
--- v2.3.45/linux/drivers/pcmcia/tcic.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/pcmcia/tcic.c	Tue Feb 15 08:53:46 2000
@@ -2,7 +2,7 @@
 
     Device driver for Databook TCIC-2 PCMCIA controller
 
-    tcic.c 1.108 1999/12/09 20:17:29
+    tcic.c 1.111 2000/02/15 04:13:12
 
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -60,7 +60,7 @@
 static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 static const char *version =
-"tcic.c 1.108 1999/12/09 20:17:29 (David Hinds)";
+"tcic.c 1.111 2000/02/15 04:13:12 (David Hinds)";
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 #else
 #define DEBUG(n, args...)
@@ -243,11 +243,11 @@
     u_short cfg;
 
     irq_hits = 0;
-    if (request_irq(irq, irq_count, 0, "irq scan", NULL) != 0)
+    if (request_irq(irq, irq_count, 0, "irq scan", irq_count) != 0)
 	return -1;
     mdelay(10);
     if (irq_hits) {
-	free_irq(irq, NULL);
+	free_irq(irq, irq_count);
 	return -1;
     }
 
@@ -258,7 +258,7 @@
     tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
 
     udelay(1000);
-    free_irq(irq, NULL);
+    free_irq(irq, irq_count);
 
     /* Turn off interrupts */
     tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
@@ -299,9 +299,9 @@
 	/* Fallback: just find interrupts that aren't in use */
 	for (i = 0; i < 16; i++)
 	    if ((mask0 & (1 << i)) &&
-		(request_irq(i, irq_count, 0, "x", NULL) == 0)) {
+		(request_irq(i, irq_count, 0, "x", irq_count) == 0)) {
 		mask1 |= (1 << i);
-		free_irq(i, NULL);
+		free_irq(i, irq_count);
 	    }
 	printk("default");
     }
@@ -475,7 +475,8 @@
 	u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
 	for (i = 15; i > 0; i--)
 	    if ((cs_mask & (1 << i)) &&
-		(request_irq(i, tcic_interrupt, 0, "tcic", NULL) == 0))
+		(request_irq(i, tcic_interrupt, 0, "tcic",
+			     tcic_interrupt) == 0))
 		break;
 	cs_irq = i;
 	if (cs_irq == 0) poll_interval = HZ;
@@ -501,7 +502,7 @@
 	printk(KERN_NOTICE "tcic: register_ss_entry() failed\n");
 	release_region(tcic_base, 16);
 	if (cs_irq != 0)
-	    free_irq(cs_irq, NULL);
+	    free_irq(cs_irq, tcic_interrupt);
 	return -ENODEV;
     }
 
@@ -519,7 +520,7 @@
     cli();
     if (cs_irq != 0) {
 	tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
-	free_irq(cs_irq, NULL);
+	free_irq(cs_irq, tcic_interrupt);
     }
     if (tcic_timer_pending)
 	del_timer(&poll_timer);
diff -u --recursive --new-file v2.3.45/linux/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c
--- v2.3.45/linux/drivers/sbus/audio/audio.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/audio/audio.c	Wed Feb 16 15:42:05 2000
@@ -32,6 +32,7 @@
 #include <linux/init.h>
 #include <linux/soundcard.h>
 #include <linux/version.h>
+#include <linux/devfs_fs_kernel.h>
 #include <asm/delay.h>
 #include <asm/pgtable.h>
 
@@ -2198,9 +2199,8 @@
 #endif
 
 	/* Register our character device driver with the VFS. */
-	if (register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
+	if (devfs_register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
 		return -EIO;
-
 	
 #ifdef CONFIG_SPARCAUDIO_AMD7930
 	amd7930_init();
@@ -2221,7 +2221,7 @@
 #ifdef MODULE
 void cleanup_module(void)
 {
-	unregister_chrdev(SOUND_MAJOR, "sparcaudio");
+	devfs_unregister_chrdev(SOUND_MAJOR, "sparcaudio");
 }
 #endif
 
diff -u --recursive --new-file v2.3.45/linux/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c
--- v2.3.45/linux/drivers/sbus/char/bpp.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/bpp.c	Wed Feb 16 15:42:05 2000
@@ -19,6 +19,7 @@
 #include <linux/timer.h>
 #include <linux/ioport.h>
 #include <linux/major.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -1004,6 +1005,8 @@
 
 #endif
 
+static devfs_handle_t devfs_handle = NULL;
+
 #ifdef MODULE
 int init_module(void)
 #else
@@ -1017,14 +1020,19 @@
 	if (rc == 0)
 		return -ENODEV;
 
-	rc = register_chrdev(BPP_MAJOR, dev_name, &bpp_fops);
+	rc = devfs_register_chrdev(BPP_MAJOR, dev_name, &bpp_fops);
 	if (rc < 0)
 		return rc;
 
 	for (idx = 0; idx < BPP_NO; idx += 1) {
 		instances[idx].opened = 0;
 		probeLptPort(idx);
+		sprintf(devname, "%s%i", dev_name, idx);
 	}
+	devfs_handle = devfs_mk_dir (NULL, "bpp", 3, NULL);
+	devfs_register_series (devfs_handle, "%u", BPP_NO, DEVFS_FL_DEFAULT,
+			       BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &bpp_fops, NULL);
 
 	return 0;
 }
@@ -1034,7 +1042,8 @@
 {
 	unsigned idx;
 
-	unregister_chrdev(BPP_MAJOR, dev_name);
+	devfs_unregister (devfs_handle);
+	devfs_unregister_chrdev(BPP_MAJOR, dev_name);
 
 	for (idx = 0 ;  idx < BPP_NO ;  idx += 1) {
 		if (instances[idx].present)
diff -u --recursive --new-file v2.3.45/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.3.45/linux/drivers/sbus/char/sunkbd.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/sunkbd.c	Wed Feb 16 15:42:05 2000
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/sysrq.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/kbio.h>
 #include <asm/vuid_event.h>
@@ -507,7 +508,7 @@
 	}
 	
 	do_poke_blanked_console = 1;
-	mark_bh(CONSOLE_BH);
+	tasklet_schedule(&console_tasklet);
 	add_keyboard_randomness(keycode);
 
 	tty = ttytab? ttytab[fg_console]: NULL;
@@ -616,7 +617,7 @@
 	wake_up(&keypress_wait);
 	if (tty) {
 		tty_insert_flip_char(tty, ch, 0);
-		tty_schedule_flip(tty);
+		con_schedule_flip(tty);
 	}
 }
 
@@ -630,7 +631,7 @@
 		tty_insert_flip_char(tty, *cp, 0);
 		cp++;
 	}
-	tty_schedule_flip(tty);
+	con_schedule_flip(tty);
 }
 
 static void applkey(int key, char mode)
@@ -742,7 +743,7 @@
 	if (!tty)
 		return;
 	tty_insert_flip_char(tty, 0, TTY_BREAK);
-	tty_schedule_flip(tty);
+	con_schedule_flip(tty);
 }
 
 static void scroll_forw(void)
@@ -1549,7 +1550,11 @@
 	send_cmd(SKBDCMD_SETLED); send_cmd(0x0); /* All off */
 
 	/* Register the /dev/kbd interface */
-	if (register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){
+	devfs_register (NULL, "kbd", 0, DEVFS_FL_NONE,
+			KBD_MAJOR, 0,
+			S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0,
+			&kbd_fops, NULL);
+	if (devfs_register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){
 		printk ("Could not register /dev/kbd device\n");
 		return;
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/sbus/char/vfc.h linux/drivers/sbus/char/vfc.h
--- v2.3.45/linux/drivers/sbus/char/vfc.h	Wed May 12 08:41:15 1999
+++ linux/drivers/sbus/char/vfc.h	Wed Feb 16 15:42:05 2000
@@ -1,6 +1,8 @@
 #ifndef _LINUX_VFC_H_
 #define _LINUX_VFC_H_
 
+#include <linux/devfs_fs_kernel.h>
+
 /*
  * The control register for the vfc is at offset 0x4000
  * The first field ram bank is located at offset 0x5000
@@ -126,6 +128,7 @@
 	volatile struct vfc_regs *regs;
 	struct vfc_regs *phys_regs;
 	unsigned int control_reg;
+	devfs_handle_t de;
 	struct semaphore device_lock_sem;
 	struct timer_list poll_timer;
 	wait_queue_head_t poll_wait;
diff -u --recursive --new-file v2.3.45/linux/drivers/sbus/char/vfc_dev.c linux/drivers/sbus/char/vfc_dev.c
--- v2.3.45/linux/drivers/sbus/char/vfc_dev.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/vfc_dev.c	Wed Feb 16 15:42:05 2000
@@ -41,6 +41,7 @@
 #include "vfc.h"
 #include <asm/vfc_ioctls.h>
 
+static devfs_handle_t devfs_handle = NULL;  /*  For the directory  */
 struct vfc_dev **vfc_dev_lst;
 static char vfcstr[]="vfc";
 static unsigned char saa9051_init_array[VFC_SAA9051_NR] = {
@@ -140,6 +141,8 @@
 
 int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance)
 {
+	char devname[8];
+
 	if(dev == NULL) {
 		printk(KERN_ERR "VFC: Bogus pointer passed\n");
 		return -ENOMEM;
@@ -162,6 +165,11 @@
 	if (init_vfc_hw(dev))
 		return -EIO;
 
+	sprintf (devname, "%d", instance);
+	dev->de = devfs_register (devfs_handle, devname, 0, DEVFS_FL_DEFAULT,
+				  VFC_MAJOR, instance,
+				  S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				  &vfc_fops, NULL);
 	return 0;
 }
 
@@ -659,12 +667,13 @@
 	memset(vfc_dev_lst, 0, sizeof(struct vfc_dev *) * (cards + 1));
 	vfc_dev_lst[cards] = NULL;
 
-	ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops);
+	ret = devfs_register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops);
 	if(ret) {
 		printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR);
 		kfree(vfc_dev_lst);
 		return -EIO;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "vfc", 3, NULL);
 
 	instance = 0;
 	for_all_sbusdev(sdev, sbus) {
@@ -705,6 +714,7 @@
 {
 	if(dev == NULL)
 		return;
+	devfs_unregister (dev->de);
 	sbus_iounmap((unsigned long)dev->regs, sizeof(struct vfc_regs));
 	kfree(dev);
 }
@@ -713,11 +723,12 @@
 {
 	struct vfc_dev **devp;
 
-	unregister_chrdev(VFC_MAJOR,vfcstr);
+	devfs_unregister_chrdev(VFC_MAJOR,vfcstr);
 
 	for (devp = vfc_dev_lst; *devp; devp++)
 		deinit_vfc_device(*devp);
 
+	devfs_unregister (devfs_handle);
 	kfree(vfc_dev_lst);
 	return;
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.3.45/linux/drivers/scsi/Config.in	Thu Feb 10 17:11:12 2000
+++ linux/drivers/scsi/Config.in	Wed Feb 16 08:54:48 2000
@@ -8,7 +8,7 @@
 
 dep_tristate '  SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
 
-if [ "$CONFIG_BLK_DEV_ST" != "n" ]; then
+if [ "$CONFIG_CHR_DEV_ST" != "n" ]; then
    int  'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2
 fi
 
@@ -183,3 +183,8 @@
 fi
 
 endmenu
+
+if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
+   source drivers/scsi/pcmcia/Config.in
+fi
+
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.3.45/linux/drivers/scsi/Makefile	Thu Feb 10 17:11:12 2000
+++ linux/drivers/scsi/Makefile	Tue Feb 15 08:53:46 2000
@@ -13,6 +13,16 @@
 MOD_LIST_NAME := SCSI_MODULES
 SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c))
 
+ALL_SUB_DIRS := pcmcia
+ifeq ($(CONFIG_PCMCIA),y)
+  SUB_DIRS += pcmcia
+  MOD_IN_SUB_DIRS += pcmcia
+else
+  ifeq ($(CONFIG_PCMCIA),m)
+    MOD_IN_SUB_DIRS += pcmcia
+  endif
+endif
+
 CFLAGS_aha152x.o =   -DAHA152X_STAT -DAUTOCONF
 CFLAGS_gdth.o    = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
 CFLAGS_seagate.o =   -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v2.3.45/linux/drivers/scsi/aha152x.c	Tue Jan  4 13:57:17 2000
+++ linux/drivers/scsi/aha152x.c	Tue Feb 15 08:53:46 2000
@@ -200,13 +200,9 @@
 
  **************************************************************************/
 
-#if defined(PCMCIA)
-#define MODULE
-#endif
-
 #include <linux/module.h>
 
-#if defined(PCMCIA)
+#ifdef PCMCIA
 #undef MODULE
 #endif
 
@@ -877,9 +873,9 @@
 
 static int checksetup(struct aha152x_setup *setup)
 {
-	int i;
 
 #if !defined(PCMCIA)
+	int i;
 	for (i = 0; i < PORT_COUNT && (setup->io_port != ports[i]); i++)
 		;
 
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/esp.c linux/drivers/scsi/esp.c
--- v2.3.45/linux/drivers/scsi/esp.c	Tue Feb  1 01:35:44 2000
+++ linux/drivers/scsi/esp.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: esp.c,v 1.90 2000/01/28 13:42:56 jj Exp $
+/* $Id: esp.c,v 1.91 2000/02/14 08:46:24 jj Exp $
  * esp.c:  EnhancedScsiProcessor Sun SCSI driver code.
  *
  * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
@@ -1412,9 +1412,13 @@
 		sp->SCp.this_residual = sp->request_bufflen;
 		sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
 		sp->SCp.buffers_residual = 0;
-		sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer,
-						       sp->SCp.this_residual);
-		sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in);
+		if (sp->request_bufflen) {
+			sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer,
+							       sp->SCp.this_residual);
+			sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in);
+		} else {
+			sp->SCp.ptr = NULL;
+		}
 	} else {
 		sp->SCp.buffer = (struct scatterlist *) sp->buffer;
 		sp->SCp.buffers_residual = sbus_map_sg(esp->sdev,
@@ -1427,12 +1431,12 @@
 
 static void esp_release_dmabufs(struct esp *esp, Scsi_Cmnd *sp)
 {
-	if (sp->use_sg == 0) {
+	if (sp->use_sg) {
+		sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg);
+	} else if (sp->request_bufflen) {
 		sbus_unmap_single(esp->sdev,
 				  sp->SCp.have_data_in,
 				  sp->request_bufflen);
-	} else {
-		sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg);
 	}
 }
 
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v2.3.45/linux/drivers/scsi/fdomain.c	Fri Jan 21 18:19:16 2000
+++ linux/drivers/scsi/fdomain.c	Tue Feb 15 08:53:46 2000
@@ -270,10 +270,6 @@
 
  **************************************************************************/
 
-#ifdef PCMCIA
-#define MODULE
-#endif
-
 #include <linux/module.h>
 
 #ifdef PCMCIA
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.3.45/linux/drivers/scsi/hosts.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/hosts.c	Wed Feb 16 15:42:05 2000
@@ -665,6 +665,7 @@
  *      MAX_SCSI_HOSTS here.
  */
 
+Scsi_Host_Name * scsi_host_no_list = NULL;
 struct Scsi_Host * scsi_hostlist = NULL;
 struct Scsi_Device_Template * scsi_devicelist = NULL;
 
@@ -674,7 +675,8 @@
 void
 scsi_unregister(struct Scsi_Host * sh){
     struct Scsi_Host * shpnt;
-    
+    Scsi_Host_Name *shn;
+        
     if(scsi_hostlist == sh)
 	scsi_hostlist = sh->next;
     else {
@@ -682,6 +684,16 @@
 	while(shpnt->next != sh) shpnt = shpnt->next;
 	shpnt->next = shpnt->next->next;
     }
+
+    /*
+     * We have to unregister the host from the scsi_host_no_list as well.
+     * Decide by the host_no not by the name because most host drivers are
+     * able to handle more than one adapters from the same kind (or family).
+     */
+    for ( shn=scsi_host_no_list; shn && (sh->host_no != shn->host_no);
+	  shn=shn->next);
+    if (shn) shn->host_registered = 0;
+    /* else {} : This should not happen, we should panic here... */
     
     /* If we are removing the last host registered, it is safe to reuse
      * its host number (this avoids "holes" at boot time) (DB) 
@@ -708,16 +720,50 @@
 
 struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
     struct Scsi_Host * retval, *shpnt;
+    Scsi_Host_Name *shn, *shn2;
+    int new = 1;
     retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j,
 					 (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
     memset(retval, 0, sizeof(struct Scsi_Host) + j);
+
+    /* trying to find a reserved entry (host_no) */
+    for (shn = scsi_host_no_list;shn;shn = shn->next)
+	if (!(shn->host_registered) && shn->loaded_as_module && tpnt->proc_dir &&
+		tpnt->proc_dir->name && !strncmp(tpnt->proc_dir->name, shn->name, strlen(tpnt->proc_dir->name))) {
+	    new = 0;
+	    retval->host_no = shn->host_no;
+	    shn->host_registered = 1;
+	    shn->loaded_as_module = scsi_loadable_module_flag;
+	    break;
+	}
     atomic_set(&retval->host_active,0);
     retval->host_busy = 0;
     retval->host_failed = 0;
     if(j > 0xffff) panic("Too many extra bytes requested\n");
     retval->extra_bytes = j;
     retval->loaded_as_module = scsi_loadable_module_flag;
-    retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
+    if (new) {
+	int len = 0;
+	shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC);
+	if (tpnt->proc_dir)
+	    len = strlen(tpnt->proc_dir->name);
+	shn->name = kmalloc(len+1, GFP_ATOMIC);
+	if (tpnt->proc_dir)
+	    strncpy(shn->name, tpnt->proc_dir->name, len);
+	shn->name[len] = 0;
+	shn->host_no = max_scsi_hosts++;
+	shn->host_registered = 1;
+	shn->loaded_as_module = scsi_loadable_module_flag;
+	shn->next = NULL;
+	if (scsi_host_no_list) {
+	    for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
+		;
+	    shn2->next = shn;
+	}
+	else
+	    scsi_host_no_list = shn;
+	retval->host_no = shn->host_no;
+    }
     next_scsi_host++;
     retval->host_queue = NULL;
     init_waitqueue_head(&retval->host_wait);
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v2.3.45/linux/drivers/scsi/hosts.h	Sun Feb 13 19:29:04 2000
+++ linux/drivers/scsi/hosts.h	Wed Feb 16 15:42:05 2000
@@ -413,6 +413,16 @@
 extern void scsi_free_host_dev(Scsi_Device * SDpnt);
 extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt);
 
+typedef struct SHN
+    {
+    struct SHN * next;
+    char * name;
+    unsigned short host_no;
+    unsigned short host_registered;
+    unsigned loaded_as_module;
+    } Scsi_Host_Name;
+	
+extern Scsi_Host_Name * scsi_host_no_list;
 extern struct Scsi_Host * scsi_hostlist;
 extern struct Scsi_Device_Template * scsi_devicelist;
 
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/mesh.c linux/drivers/scsi/mesh.c
--- v2.3.45/linux/drivers/scsi/mesh.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/mesh.c	Mon Feb 14 13:37:25 2000
@@ -253,7 +253,7 @@
 			continue;
 		}
 		mesh_host->unique_id = nmeshes;
-#if !defined(MODULE) && (defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC))
+#if !defined(MODULE)
 		note_scsi_host(mesh, mesh_host);
 #endif
 
@@ -305,9 +305,7 @@
 		if (mesh_sync_period < minper)
 			mesh_sync_period = minper;
 
-#if defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC)
 		feature_set(mesh, FEATURE_MESH_enable);
-#endif
 		mdelay(200);
 
 		mesh_init(ms);
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/pcmcia/Config.in linux/drivers/scsi/pcmcia/Config.in
--- v2.3.45/linux/drivers/scsi/pcmcia/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pcmcia/Config.in	Tue Feb 15 08:53:46 2000
@@ -0,0 +1,23 @@
+#
+# PCMCIA SCSI adapter configuration
+#
+
+mainmenu_option next_comment
+comment 'PCMCIA SCSI adapter support'
+
+bool 'PCMCIA SCSI adapter support' CONFIG_SCSI_PCMCIA
+if [ "$CONFIG_SCSI_PCMCIA" = "y" ]; then
+   dep_tristate '  Adaptec AHA152X PCMCIA support' CONFIG_PCMCIA_AHA152X m
+   dep_tristate '  Qlogic PCMCIA support' CONFIG_PCMCIA_QLOGIC m
+   dep_tristate '  Future Domain PCMCIA support' CONFIG_PCMCIA_FDOMAIN m
+   if [ "$CONFIG_CARDBUS" = "y" ]; then
+      dep_tristate '  Adaptec APA1480 CardBus support' CONFIG_PCMCIA_APA1480 m
+   fi
+fi
+
+if [ "$CONFIG_PCMCIA_QLOGIC" = "y" -o "$CONFIG_PCMCIA_AHA152X" = "y" -o \
+     "$CONFIG_PCMCIA_FDOMAIN" = "y" -o "$CONFIG_PCMCIA_APA1480" = "y" ]; then
+   define_bool CONFIG_PCMCIA_SCSICARD y
+fi
+
+endmenu
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/pcmcia/Makefile linux/drivers/scsi/pcmcia/Makefile
--- v2.3.45/linux/drivers/scsi/pcmcia/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pcmcia/Makefile	Tue Feb 15 08:53:46 2000
@@ -0,0 +1,67 @@
+#
+# drivers/scsi/pcmcia/Makefile
+#
+# Makefile for the Linux PCMCIA SCSI drivers.
+#
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+MOD_LIST_NAME := PCMCIA_SCSI_MODULES
+
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+vpath %c ..
+
+CFLAGS_aha152x.o = -DPCMCIA -D__NO_VERSION__ -DAHA152X_STAT
+CFLAGS_aic7xxx.o = -DPCMCIA -D__NO_VERSION__
+CFLAGS_fdomain.o = -DPCMCIA -D__NO_VERSION__
+CFLAGS_qlogicfas.o = -DPCMCIA -D__NO_VERSION__
+
+# 16-bit client drivers
+obj-$(CONFIG_PCMCIA_QLOGIC)	+= qlogic_cs.o
+obj-$(CONFIG_PCMCIA_FDOMAIN)	+= fdomain_cs.o
+obj-$(CONFIG_PCMCIA_AHA152X)	+= aha152x_cs.o
+
+# Cardbus client drivers
+obj-$(CONFIG_PCMCIA_APA1480)	+= apa1480_cb.o
+
+list-multi	:= qlogic_cs.o fdomain_cs.o aha152x_cs.o apa1480_cb.o
+aha152x-objs	:= aha152x_stub.o aha152x.o
+apa1480-objs	:= apa1480_stub.o aic7xxx.o
+fdomain-objs	:= fdomain_stub.o fdomain.o
+qlogic-objs	:= qlogic_stub.o qlogicfas.o
+
+# Extract lists of the multi-part drivers.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(filter-out $(export-objs), $(obj-m))
+MX_OBJS		:= $(filter     $(export-objs), $(obj-m))
+MI_OBJS		:= $(filter-out $(export-objs), $(int-m))
+MIX_OBJS	:= $(filter     $(export-objs), $(int-m))
+
+include $(TOPDIR)/Rules.make
+
+aha152x_cs.o: $(aha152x-objs)
+	$(LD) -r -o $@ $(aha152x-objs)
+
+apa1480_cb.o: $(apa1480-objs)
+	$(LD) -r -o $@ $(apa1480-objs)
+
+fdomain_cs.o: $(fdomain-objs)
+	$(LD) -r -o $@ $(fdomain-objs)
+
+qlogic_cs.o: $(qlogic-objs)
+	$(LD) -r -o $@ $(qlogic-objs)
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/pcmcia/aha152x_stub.c linux/drivers/scsi/pcmcia/aha152x_stub.c
--- v2.3.45/linux/drivers/scsi/pcmcia/aha152x_stub.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pcmcia/aha152x_stub.c	Tue Feb 15 08:53:46 2000
@@ -0,0 +1,437 @@
+/*======================================================================
+
+    A driver for Adaptec AHA152X-compatible PCMCIA SCSI cards.
+
+    This driver supports the Adaptec AHA-1460, the New Media Bus
+    Toaster, and the New Media Toast & Jam.
+    
+    aha152x_cs.c 1.52 2000/01/11 01:04:31
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+
+#include <../drivers/scsi/scsi.h>
+#include <../drivers/scsi/hosts.h>
+#include <scsi/scsi_ioctl.h>
+#include <../drivers/scsi/aha152x.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"aha152x_cs.c 1.52 2000/01/11 01:04:31 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+/* SCSI bus setup options */
+static int host_id = 7;
+static int reconnect = 1;
+static int parity = 1;
+static int synchronous = 0;
+static int reset_delay = 100;
+static int ext_trans = 0;
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(host_id, "i");
+MODULE_PARM(reconnect, "i");
+MODULE_PARM(parity, "i");
+MODULE_PARM(synchronous, "i");
+MODULE_PARM(reset_delay, "i");
+MODULE_PARM(ext_trans, "i");
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+    dev_link_t		link;
+    struct Scsi_Host	*host;
+    int			ndev;
+    dev_node_t		node[8];
+} scsi_info_t;
+
+extern void aha152x_setup(char *str, int *ints);
+
+static void aha152x_release_cs(u_long arg);
+static int aha152x_event(event_t event, int priority,
+			 event_callback_args_t *args);
+
+static dev_link_t *aha152x_attach(void);
+static void aha152x_detach(dev_link_t *);
+
+static Scsi_Host_Template driver_template = AHA152X;
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "aha152x_cs";
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static dev_link_t *aha152x_attach(void)
+{
+    scsi_info_t *info;
+    client_reg_t client_reg;
+    dev_link_t *link;
+    int i, ret;
+    
+    DEBUG(0, "aha152x_attach()\n");
+
+    /* Create new SCSI device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL);
+    if (!info) return NULL;
+    memset(info, 0, sizeof(*info));
+    link = &info->link; link->priv = info;
+    link->release.function = &aha152x_release_cs;
+    link->release.data = (u_long)link;
+
+    link->io.NumPorts1 = 0x20;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 10;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+	link->irq.IRQInfo2 = irq_mask;
+    else
+	for (i = 0; i < 4; i++)
+	    link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* 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.event_handler = &aha152x_event;
+    client_reg.EventMask =
+	CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    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);
+	aha152x_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* aha152x_attach */
+
+/*====================================================================*/
+
+static void aha152x_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "aha152x_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->state & DEV_CONFIG) {
+	aha152x_release_cs((u_long)link);
+	if (link->state & DEV_STALE_CONFIG) {
+	    link->state |= DEV_STALE_LINK;
+	    return;
+	}
+    }
+
+    if (link->handle)
+	CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    kfree(link->priv);
+    
+} /* aha152x_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+#define CFG_CHECK(fn, args...) \
+if (CardServices(fn, args) != 0) goto next_entry
+
+static void aha152x_config_cs(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    scsi_info_t *info = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, last_ret, last_fn, ints[8];
+    u_char tuple_data[64];
+    Scsi_Device *dev;
+    dev_node_t *node, **tail;
+    struct Scsi_Host *host;
+    
+    DEBUG(0, "aha152x_config(0x%p)\n", link);
+
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = tuple_data;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    link->conf.ConfigBase = parse.config.base;
+
+    /* Configure card */
+    driver_template.module = &__this_module;
+    link->state |= DEV_CONFIG;
+
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    while (1) {
+	CFG_CHECK(GetTupleData, handle, &tuple);
+	CFG_CHECK(ParseTuple, handle, &tuple, &parse);
+	/* For New Media T&J, look for a SCSI window */
+	if (parse.cftable_entry.io.win[0].len >= 0x20)
+	    link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+	else if ((parse.cftable_entry.io.nwin > 1) &&
+		 (parse.cftable_entry.io.win[1].len >= 0x20))
+	    link->io.BasePort1 = parse.cftable_entry.io.win[1].base;
+	if ((parse.cftable_entry.io.nwin > 0) &&
+	    (link->io.BasePort1 < 0xffff)) {
+	    link->conf.ConfigIndex = parse.cftable_entry.index;
+	    i = CardServices(RequestIO, handle, &link->io);
+	    if (i == CS_SUCCESS) break;
+	}
+    next_entry:
+	CS_CHECK(GetNextTuple, handle, &tuple);
+    }
+    
+    CS_CHECK(RequestIRQ, handle, &link->irq);
+    CS_CHECK(RequestConfiguration, handle, &link->conf);
+    
+    /* A bad hack... */
+    release_region(link->io.BasePort1, link->io.NumPorts1);
+    
+    /* Set configuration options for the aha152x driver */
+    ints[0] = 7;
+    ints[1] = link->io.BasePort1;
+    ints[2] = link->irq.AssignedIRQ;
+    ints[3] = host_id;
+    ints[4] = reconnect;
+    ints[5] = parity;
+    ints[6] = synchronous;
+    ints[7] = reset_delay;
+    if (ext_trans) {
+	ints[8] = ext_trans; ints[0] = 8;
+    }
+    aha152x_setup("PCMCIA setup", ints);
+    
+    scsi_register_module(MODULE_SCSI_HA, &driver_template);
+
+    tail = &link->dev;
+    info->ndev = 0;
+    for (host = scsi_hostlist; host; host = host->next)
+	if (host->hostt == &driver_template)
+	    for (dev = host->host_queue; dev; dev = dev->next) {
+	    u_long arg[2], id;
+	    kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
+	    id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
+		((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
+	    node = &info->node[info->ndev];
+	    node->minor = 0;
+	    switch (dev->type) {
+	    case TYPE_TAPE:
+		node->major = SCSI_TAPE_MAJOR;
+		sprintf(node->dev_name, "st#%04lx", id);
+		break;
+	    case TYPE_DISK:
+	    case TYPE_MOD:
+		node->major = SCSI_DISK0_MAJOR;
+		sprintf(node->dev_name, "sd#%04lx", id);
+		break;
+	    case TYPE_ROM:
+	    case TYPE_WORM:
+		node->major = SCSI_CDROM_MAJOR;
+		sprintf(node->dev_name, "sr#%04lx", id);
+		break;
+	    default:
+		node->major = SCSI_GENERIC_MAJOR;
+		sprintf(node->dev_name, "sg#%04lx", id);
+		break;
+	    }
+	    *tail = node; tail = &node->next;
+	    info->ndev++;
+	    info->host = dev->host;
+	}
+    *tail = NULL;
+    if (info->ndev == 0)
+	printk(KERN_INFO "aha152x_cs: no SCSI devices found\n");
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+    
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    aha152x_release_cs((u_long)link);
+    return;
+    
+} /* aha152x_config_cs */
+
+/*====================================================================*/
+
+static void aha152x_release_cs(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+
+    DEBUG(0, "aha152x_release_cs(0x%p)\n", link);
+
+    if (GET_USE_COUNT(driver_template.module) != 0) {
+	DEBUG(1, "aha152x_cs: release postponed, "
+	      "device still open\n");
+	link->state |= DEV_STALE_CONFIG;
+	return;
+    }
+
+    scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+    link->dev = NULL;
+    
+    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)
+	aha152x_detach(link);
+    
+} /* aha152x_release_cs */
+
+/*====================================================================*/
+
+static int aha152x_event(event_t event, int priority,
+			 event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    scsi_info_t *info = link->priv;
+    
+    DEBUG(0, "aha152x_event(0x%06x)\n", event);
+    
+    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;
+	aha152x_config_cs(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) {
+	    Scsi_Cmnd tmp;
+	    CardServices(RequestConfiguration, link->handle, &link->conf);
+	    tmp.host = info->host;
+	    aha152x_host_reset(&tmp);
+	}
+	break;
+    }
+    return 0;
+} /* aha152x_event */
+
+/*====================================================================*/
+
+static int __init init_aha152x_cs(void) {
+    servinfo_t serv;
+    DEBUG(0, "%s\n", version);
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	printk(KERN_NOTICE "aha152x_cs: Card Services release "
+	       "does not match!\n");
+	return -1;
+    }
+    register_pccard_driver(&dev_info, &aha152x_attach, &aha152x_detach);
+    return 0;
+}
+
+static void __exit exit_aha152x_cs(void) {
+    DEBUG(0, "aha152x_cs: unloading\n");
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL)
+	aha152x_detach(dev_list);
+}
+
+module_init(init_aha152x_cs);
+module_exit(exit_aha152x_cs);
+ 
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/pcmcia/apa1480_stub.c linux/drivers/scsi/pcmcia/apa1480_stub.c
--- v2.3.45/linux/drivers/scsi/pcmcia/apa1480_stub.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pcmcia/apa1480_stub.c	Tue Feb 15 08:53:46 2000
@@ -0,0 +1,174 @@
+/*======================================================================
+
+    A driver for the Adaptec APA1480 CardBus SCSI Host Adapter
+
+    apa1480_cb.c 1.19 2000/02/14 22:39:25
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+
+#include <../drivers/scsi/scsi.h>
+#include <../drivers/scsi/hosts.h>
+#include <scsi/scsi_ioctl.h>
+#include <../drivers/scsi/aic7xxx.h>
+
+#include <pcmcia/driver_ops.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"apa1480_cb.c 1.19 2000/02/14 22:39:25 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int reset = 1;
+static int ultra = 0;
+
+MODULE_PARM(reset, "i");
+MODULE_PARM(ultra, "i");
+
+/*====================================================================*/
+
+static Scsi_Host_Template driver_template = AIC7XXX;
+
+extern void aic7xxx_setup(char *, int *);
+
+static dev_node_t *apa1480_attach(dev_locator_t *loc);
+static void apa1480_detach(dev_node_t *node);
+
+struct driver_operations apa1480_ops = {
+    "apa1480_cb", apa1480_attach, NULL, NULL, apa1480_detach
+};
+
+/*====================================================================*/
+
+static dev_node_t *apa1480_attach(dev_locator_t *loc)
+{
+    u_char bus, devfn;
+    Scsi_Device *dev;
+    dev_node_t *node;
+    char s[60];
+    int n = 0;
+    struct Scsi_Host *host;
+    
+    if (loc->bus != LOC_PCI) return NULL;
+    bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
+    printk(KERN_INFO "apa1480_attach(bus %d, function %d)\n",
+	   bus, devfn);
+
+    driver_template.module = &__this_module;
+
+    sprintf(s, "no_probe:1,no_reset:%d,ultra:%d",
+	    (reset==0), (ultra!=0));
+    aic7xxx_setup(s, NULL);
+    scsi_register_module(MODULE_SCSI_HA, &driver_template);
+
+    node = kmalloc(7 * sizeof(dev_node_t), GFP_KERNEL);
+    for (host = scsi_hostlist; host; host = host->next)
+	if (host->hostt == &driver_template)
+	    for (dev = host->host_queue; dev; dev = dev->next) {
+	    u_long arg[2], id;
+	    kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
+	    id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
+		((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
+	    node[n].minor = 0;
+	    switch (dev->type) {
+	    case TYPE_TAPE:
+		node[n].major = SCSI_TAPE_MAJOR;
+		sprintf(node[n].dev_name, "st#%04lx", id);
+		break;
+	    case TYPE_DISK:
+	    case TYPE_MOD:
+		node[n].major = SCSI_DISK0_MAJOR;
+		sprintf(node[n].dev_name, "sd#%04lx", id);
+		break;
+	    case TYPE_ROM:
+	    case TYPE_WORM:
+		node[n].major = SCSI_CDROM_MAJOR;
+		sprintf(node[n].dev_name, "sr#%04lx", id);
+		break;
+	    default:
+		node[n].major = SCSI_GENERIC_MAJOR;
+		sprintf(node[n].dev_name, "sg#%04lx", id);
+		break;
+	    }
+	    if (n) node[n-1].next = &node[n];
+	    n++;
+	}
+    if (n == 0) {
+	printk(KERN_INFO "apa1480_cs: no SCSI devices found\n");
+	scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+	kfree(node);
+	return NULL;
+    } else
+	node[n-1].next = NULL;
+    
+    MOD_INC_USE_COUNT;
+    return node;
+}
+
+static void apa1480_detach(dev_node_t *node)
+{
+    MOD_DEC_USE_COUNT;
+    scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+    kfree(node);
+}
+
+/*====================================================================*/
+
+static int __init init_apa1480_cb(void) {
+    DEBUG(0, "%s\n", version);
+    register_driver(&apa1480_ops);
+    return 0;
+}
+
+static void __exit exit_apa1480_cb(void) {
+    DEBUG(0, "apa1480_cs: unloading\n");
+    unregister_driver(&apa1480_ops);
+}
+
+module_init(init_apa1480_cb);
+module_exit(exit_apa1480_cb);
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/pcmcia/fdomain_stub.c linux/drivers/scsi/pcmcia/fdomain_stub.c
--- v2.3.45/linux/drivers/scsi/pcmcia/fdomain_stub.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pcmcia/fdomain_stub.c	Tue Feb 15 08:53:46 2000
@@ -0,0 +1,398 @@
+/*======================================================================
+
+    A driver for Future Domain-compatible PCMCIA SCSI cards
+
+    fdomain_cs.c 1.41 1999/11/15 06:05:48
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+
+#include <../drivers/scsi/scsi.h>
+#include <../drivers/scsi/hosts.h>
+#include <scsi/scsi_ioctl.h>
+#include <../drivers/scsi/fdomain.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"fdomain_cs.c 1.41 1999/11/15 06:05:48 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+    dev_link_t		link;
+    int			ndev;
+    dev_node_t		node[8];
+} scsi_info_t;
+
+extern void fdomain_setup(char *str, int *ints);
+
+static void fdomain_release(u_long arg);
+static int fdomain_event(event_t event, int priority,
+			event_callback_args_t *args);
+
+static dev_link_t *fdomain_attach(void);
+static void fdomain_detach(dev_link_t *);
+
+static Scsi_Host_Template driver_template = FDOMAIN_16X0;
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "fdomain_cs";
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static dev_link_t *fdomain_attach(void)
+{
+    scsi_info_t *info;
+    client_reg_t client_reg;
+    dev_link_t *link;
+    int i, ret;
+    
+    DEBUG(0, "fdomain_attach()\n");
+
+    /* Create new SCSI device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL);
+    if (!info) return NULL;
+    memset(info, 0, sizeof(*info));
+    link = &info->link; link->priv = info;
+    link->release.function = &fdomain_release;
+    link->release.data = (u_long)link;
+
+    link->io.NumPorts1 = 0x10;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 10;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+	link->irq.IRQInfo2 = irq_mask;
+    else
+	for (i = 0; i < 4; i++)
+	    link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* 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.event_handler = &fdomain_event;
+    client_reg.EventMask =
+	CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    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);
+	fdomain_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* fdomain_attach */
+
+/*====================================================================*/
+
+static void fdomain_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "fdomain_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->state & DEV_CONFIG) {
+	fdomain_release((u_long)link);
+	if (link->state & DEV_STALE_CONFIG) {
+	    link->state |= DEV_STALE_LINK;
+	    return;
+	}
+    }
+
+    if (link->handle)
+	CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    kfree(link->priv);
+    
+} /* fdomain_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+#define CFG_CHECK(fn, args...) \
+if (CardServices(fn, args) != 0) goto next_entry
+
+static void fdomain_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    scsi_info_t *info = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, last_ret, last_fn, ints[3];
+    u_char tuple_data[64];
+    Scsi_Device *dev;
+    dev_node_t *node, **tail;
+    struct Scsi_Host *host;
+
+    DEBUG(0, "fdomain_config(0x%p)\n", link);
+
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = tuple_data;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    link->conf.ConfigBase = parse.config.base;
+
+    /* Configure card */
+    driver_template.module = &__this_module;
+    link->state |= DEV_CONFIG;
+    
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    while (1) {
+	CFG_CHECK(GetTupleData, handle, &tuple);
+	CFG_CHECK(ParseTuple, handle, &tuple, &parse);
+	link->conf.ConfigIndex = parse.cftable_entry.index;
+	link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+	i = CardServices(RequestIO, handle, &link->io);
+	if (i == CS_SUCCESS) break;
+    next_entry:
+	CS_CHECK(GetNextTuple, handle, &tuple);
+    }
+
+    CS_CHECK(RequestIRQ, handle, &link->irq);
+    CS_CHECK(RequestConfiguration, handle, &link->conf);
+    
+    /* A bad hack... */
+    release_region(link->io.BasePort1, link->io.NumPorts1);
+
+    /* Set configuration options for the fdomain driver */
+    ints[0] = 2;
+    ints[1] = link->io.BasePort1;
+    ints[2] = link->irq.AssignedIRQ;
+    fdomain_setup("PCMCIA setup", ints);
+    
+    scsi_register_module(MODULE_SCSI_HA, &driver_template);
+
+    tail = &link->dev;
+    info->ndev = 0;
+    for (host = scsi_hostlist; host; host = host->next)
+	if (host->hostt == &driver_template)
+	    for (dev = host->host_queue; dev; dev = dev->next) {
+	    u_long arg[2], id;
+	    kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
+	    id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
+		((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
+	    node = &info->node[info->ndev];
+	    node->minor = 0;
+	    switch (dev->type) {
+	    case TYPE_TAPE:
+		node->major = SCSI_TAPE_MAJOR;
+		sprintf(node->dev_name, "st#%04lx", id);
+		break;
+	    case TYPE_DISK:
+	    case TYPE_MOD:
+		node->major = SCSI_DISK0_MAJOR;
+		sprintf(node->dev_name, "sd#%04lx", id);
+		break;
+	    case TYPE_ROM:
+	    case TYPE_WORM:
+		node->major = SCSI_CDROM_MAJOR;
+		sprintf(node->dev_name, "sr#%04lx", id);
+		break;
+	    default:
+		node->major = SCSI_GENERIC_MAJOR;
+		sprintf(node->dev_name, "sg#%04lx", id);
+		break;
+	    }
+	    *tail = node; tail = &node->next;
+	    info->ndev++;
+	}
+    *tail = NULL;
+    if (info->ndev == 0)
+	printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+    
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    fdomain_release((u_long)link);
+    return;
+    
+} /* fdomain_config */
+
+/*====================================================================*/
+
+static void fdomain_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+
+    DEBUG(0, "fdomain_release(0x%p)\n", link);
+
+    if (GET_USE_COUNT(&__this_module) != 0) {
+	DEBUG(1, "fdomain_cs: release postponed, "
+	      "device still open\n");
+	link->state |= DEV_STALE_CONFIG;
+	return;
+    }
+
+    scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+    link->dev = NULL;
+    
+    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)
+	fdomain_detach(link);
+    
+} /* fdomain_release */
+
+/*====================================================================*/
+
+static int fdomain_event(event_t event, int priority,
+			event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    DEBUG(1, "fdomain_event(0x%06x)\n", event);
+    
+    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;
+	fdomain_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);
+	    fdomain_16x0_reset(NULL, 0);
+	}
+	break;
+    }
+    return 0;
+} /* fdomain_event */
+
+/*====================================================================*/
+
+static int __init init_fdomain_cs(void) {
+    servinfo_t serv;
+    DEBUG(0, "%s\n", version);
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	printk(KERN_NOTICE "fdomain_cs: Card Services release "
+	       "does not match!\n");
+	return -1;
+    }
+    register_pccard_driver(&dev_info, &fdomain_attach, &fdomain_detach);
+    return 0;
+}
+
+static void __exit exit_fdomain_cs(void) {
+    DEBUG(0, "fdomain_cs: unloading\n");
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL)
+	fdomain_detach(dev_list);
+}
+
+module_init(init_fdomain_cs);
+module_exit(exit_fdomain_cs);
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/pcmcia/qlogic_stub.c linux/drivers/scsi/pcmcia/qlogic_stub.c
--- v2.3.45/linux/drivers/scsi/pcmcia/qlogic_stub.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pcmcia/qlogic_stub.c	Tue Feb 15 08:53:46 2000
@@ -0,0 +1,428 @@
+/*======================================================================
+
+    A driver for the Qlogic SCSI card
+
+    qlogic_cs.c 1.77 2000/02/01 19:08:09
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+
+#include <../drivers/scsi/scsi.h>
+#include <../drivers/scsi/hosts.h>
+#include <scsi/scsi_ioctl.h>
+
+#include <../drivers/scsi/qlogicfas.h>
+
+#define qlogic_reset(h) qlogicfas_reset(h, 0)
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+extern void qlogicfas_preset(int port, int irq);
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"qlogic_cs.c 1.77 2000/02/01 19:08:09 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+    dev_link_t		link;
+    u_short		manf_id;
+    int			ndev;
+    dev_node_t		node[8];
+} scsi_info_t;
+
+static void qlogic_release(u_long arg);
+static int qlogic_event(event_t event, int priority,
+			event_callback_args_t *args);
+
+static dev_link_t *qlogic_attach(void);
+static void qlogic_detach(dev_link_t *);
+
+static Scsi_Host_Template driver_template = QLOGICFAS;
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "qlogic_cs";
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static dev_link_t *qlogic_attach(void)
+{
+    scsi_info_t *info;
+    client_reg_t client_reg;
+    dev_link_t *link;
+    int i, ret;
+    
+    DEBUG(0, "qlogic_attach()\n");
+
+    /* Create new SCSI device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL);
+    if (!info) return NULL;
+    memset(info, 0, sizeof(*info));
+    link = &info->link; link->priv = info;
+    link->release.function = &qlogic_release;
+    link->release.data = (u_long)link;
+
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 10;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+	link->irq.IRQInfo2 = irq_mask;
+    else
+	for (i = 0; i < 4; i++)
+	    link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* 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.event_handler = &qlogic_event;
+    client_reg.EventMask =
+	CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    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);
+	qlogic_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* qlogic_attach */
+
+/*====================================================================*/
+
+static void qlogic_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "qlogic_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->state & DEV_CONFIG) {
+	qlogic_release((u_long)link);
+	if (link->state & DEV_STALE_CONFIG) {
+	    link->state |= DEV_STALE_LINK;
+	    return;
+	}
+    }
+
+    if (link->handle)
+	CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    kfree(link->priv);
+    
+} /* qlogic_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+#define CFG_CHECK(fn, args...) \
+if (CardServices(fn, args) != 0) goto next_entry
+
+static void qlogic_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    scsi_info_t *info = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, last_ret, last_fn;
+    u_short tuple_data[32];
+    Scsi_Device *dev;
+    dev_node_t **tail, *node;
+    struct Scsi_Host *host;
+
+    DEBUG(0, "qlogic_config(0x%p)\n", link);
+
+    tuple.TupleData = (cisdata_t *)tuple_data;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    link->conf.ConfigBase = parse.config.base;
+    
+    tuple.DesiredTuple = CISTPL_MANFID;
+    if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) &&
+	(CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS))
+	info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+    /* Configure card */
+    driver_template.module = &__this_module;
+    link->state |= DEV_CONFIG;
+
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    while (1) {
+	CFG_CHECK(GetTupleData, handle, &tuple);
+	CFG_CHECK(ParseTuple, handle, &tuple, &parse);
+	link->conf.ConfigIndex = parse.cftable_entry.index;
+	link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+	link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+	if (link->io.BasePort1 != 0) {
+	    i = CardServices(RequestIO, handle, &link->io);
+	    if (i == CS_SUCCESS) break;
+	}
+    next_entry:
+	CS_CHECK(GetNextTuple, handle, &tuple);
+    }
+    
+    CS_CHECK(RequestIRQ, handle, &link->irq);
+    CS_CHECK(RequestConfiguration, handle, &link->conf);
+
+    if ((info->manf_id == MANFID_MACNICA) ||
+	(info->manf_id == MANFID_PIONEER) ||
+	(info->manf_id == 0x0098)) {
+	/* set ATAcmd */
+	outb(0xb4, link->io.BasePort1+0xd);
+	outb(0x24, link->io.BasePort1+0x9);
+	outb(0x04, link->io.BasePort1+0xd);
+    }
+
+    /* A bad hack... */
+    release_region(link->io.BasePort1, link->io.NumPorts1);
+
+    /* The KXL-810AN has a bigger IO port window */
+    if (link->io.NumPorts1 == 32)
+	qlogicfas_preset(link->io.BasePort1+16, link->irq.AssignedIRQ);
+    else
+	qlogicfas_preset(link->io.BasePort1, link->irq.AssignedIRQ);
+    
+    scsi_register_module(MODULE_SCSI_HA, &driver_template);
+
+    tail = &link->dev;
+    info->ndev = 0;
+    for (host = scsi_hostlist; host; host = host->next)
+	if (host->hostt == &driver_template)
+	    for (dev = host->host_queue; dev; dev = dev->next) {
+	    u_long arg[2], id;
+	    kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
+	    id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
+		((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
+	    node = &info->node[info->ndev];
+	    node->minor = 0;
+	    switch (dev->type) {
+	    case TYPE_TAPE:
+		node->major = SCSI_TAPE_MAJOR;
+		sprintf(node->dev_name, "st#%04lx", id);
+		break;
+	    case TYPE_DISK:
+	    case TYPE_MOD:
+		node->major = SCSI_DISK0_MAJOR;
+		sprintf(node->dev_name, "sd#%04lx", id);
+		break;
+	    case TYPE_ROM:
+	    case TYPE_WORM:
+		node->major = SCSI_CDROM_MAJOR;
+		sprintf(node->dev_name, "sr#%04lx", id);
+		break;
+	    default:
+		node->major = SCSI_GENERIC_MAJOR;
+		sprintf(node->dev_name, "sg#%04lx", id);
+		break;
+	    }
+	    *tail = node; tail = &node->next;
+	    info->ndev++;
+	}
+    *tail = NULL;
+    if (info->ndev == 0)
+	printk(KERN_INFO "qlogic_cs: no SCSI devices found\n");
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    qlogic_release((u_long)link);
+    return;
+
+} /* qlogic_config */
+
+/*====================================================================*/
+
+static void qlogic_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+
+    DEBUG(0, "qlogic_release(0x%p)\n", link);
+
+    if (GET_USE_COUNT(&__this_module) != 0) {
+	DEBUG(0, "qlogic_cs: release postponed, device still open\n");
+	link->state |= DEV_STALE_CONFIG;
+	return;
+    }
+
+    scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+    link->dev = NULL;
+    
+    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)
+	qlogic_detach(link);
+    
+} /* qlogic_release */
+
+/*====================================================================*/
+
+static int qlogic_event(event_t event, int priority,
+			event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    DEBUG(1, "qlogic_event(0x%06x)\n", event);
+    
+    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;
+	qlogic_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) {
+	    scsi_info_t *info = link->priv;
+	    CardServices(RequestConfiguration, link->handle, &link->conf);
+	    if ((info->manf_id == MANFID_MACNICA) ||
+		(info->manf_id == MANFID_PIONEER) ||
+		(info->manf_id == 0x0098)) {
+		outb( 0x80, link->io.BasePort1+0xd);
+		outb( 0x24, link->io.BasePort1+0x9);
+		outb( 0x04, link->io.BasePort1+0xd);
+	    }
+	    qlogic_reset(NULL);
+	}
+	break;
+    }
+    return 0;
+} /* qlogic_event */
+
+/*====================================================================*/
+
+static int __init init_qlogic_cs(void) {
+    servinfo_t serv;
+    DEBUG(0, "%s\n", version);
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	printk(KERN_NOTICE "qlogic_cs: Card Services release "
+	       "does not match!\n");
+	return -1;
+    }
+    register_pccard_driver(&dev_info, &qlogic_attach, &qlogic_detach);
+    return 0;
+}
+
+static void __exit exit_qlogic_cs(void) {
+    DEBUG(0, "qlogic_cs: unloading\n");
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL)
+	qlogic_detach(dev_list);
+}
+
+module_init(init_qlogic_cs);
+module_exit(exit_qlogic_cs);
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/qlogicfas.c linux/drivers/scsi/qlogicfas.c
--- v2.3.45/linux/drivers/scsi/qlogicfas.c	Thu Nov 11 20:11:48 1999
+++ linux/drivers/scsi/qlogicfas.c	Tue Feb 15 08:53:46 2000
@@ -21,7 +21,7 @@
    Version 0.46 1/30/97 - kernel 1.2.0+
 
    Functions as standalone, loadable, and PCMCIA driver, the latter from
-   Dave Hind's PCMCIA package.
+   Dave Hinds' PCMCIA package.
 
    Redistributable under terms of the GNU Public License
 
@@ -107,7 +107,6 @@
 #ifdef PCMCIA
 #undef QL_INT_ACTIVE_HIGH
 #define QL_INT_ACTIVE_HIGH 0
-#define MODULE
 #endif 
 
 #include <linux/module.h>
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/qlogicpti.c linux/drivers/scsi/qlogicpti.c
--- v2.3.45/linux/drivers/scsi/qlogicpti.c	Fri Jan 28 15:09:08 2000
+++ linux/drivers/scsi/qlogicpti.c	Mon Feb 14 15:31:14 2000
@@ -1074,7 +1074,7 @@
 			}
 			sg_count -= n;
 		}
-	} else {
+	} else if (Cmnd->request_bufflen) {
 		Cmnd->SCp.ptr = (char *)(unsigned long)
 			sbus_map_single(qpti->sdev,
 					Cmnd->request_buffer,
@@ -1083,6 +1083,10 @@
 		cmd->dataseg[0].d_base = (u32) ((unsigned long)Cmnd->SCp.ptr);
 		cmd->dataseg[0].d_count = Cmnd->request_bufflen;
 		cmd->segment_cnt = 1;
+	} else {
+		cmd->dataseg[0].d_base = 0;
+		cmd->dataseg[0].d_count = 0;
+		cmd->segment_cnt = 1; /* Shouldn't this be 0? */
 	}
 
 	/* Committed, record Scsi_Cmd so we can find it later. */
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.3.45/linux/drivers/scsi/scsi.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/scsi.c	Wed Feb 16 15:42:05 2000
@@ -636,6 +636,8 @@
 	return rtn;
 }
 
+devfs_handle_t scsi_devfs_handle = NULL;
+
 /*
  * scsi_do_cmd sends all the commands out to the low-level driver.  It
  * handles the specifics required for each low level driver - ie queued
@@ -1168,7 +1170,39 @@
 static int proc_scsi_gen_write(struct file * file, const char * buf,
                               unsigned long length, void *data);
 
+void __init scsi_host_no_insert(char *str, int n)
+{
+    Scsi_Host_Name *shn, *shn2;
+    int len;
+    
+    len = strlen(str);
+    if (len && (shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC))) {
+	if ((shn->name = kmalloc(len+1, GFP_ATOMIC))) {
+	    strncpy(shn->name, str, len);
+	    shn->name[len] = 0;
+	    shn->host_no = n;
+	    shn->host_registered = 0;
+	    shn->loaded_as_module = 1; /* numbers shouldn't be freed in any case */
+	    shn->next = NULL;
+	    if (scsi_host_no_list) {
+		for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
+		    ;
+		shn2->next = shn;
+	    }
+	    else
+		scsi_host_no_list = shn;
+	    max_scsi_hosts = n+1;
+	}
+	else
+	    kfree((char *) shn);
+    }
+}
+
 #ifndef MODULE			/* { */
+
+char scsi_host_no_table[20][10] __initdata = {};
+int scsi_host_no_set __initdata = 0;
+
 /*
  * scsi_dev_init() is our initialization routine, which in turn calls host
  * initialization, bus scanning, and sd/st initialization routines.
@@ -1184,8 +1218,16 @@
 	return;
 #endif
 
+        /* Initialize list of host_no if kernel parameter set */
+        if (scsi_host_no_set) {
+		int i;
+                for (i = 0;i < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0]);i++)
+			scsi_host_no_insert(scsi_host_no_table[i], i);
+        }
+
 	/* Yes we're here... */
 
+        scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL);
 	/*
 	 * This makes /proc/scsi and /proc/scsi/scsi visible.
 	 */
@@ -1535,6 +1577,7 @@
 			 * Nobody is using this device any more.
 			 * Free all of the command structures.
 			 */
+			devfs_unregister (scd->de);
 			scsi_release_commandblocks(scd);
 
 			/* Now we can remove the device structure */
@@ -1834,6 +1877,7 @@
 				printk("Attached usage count = %d\n", SDpnt->attached);
 				return;
 			}
+			devfs_unregister (SDpnt->de);
 		}
 	}
 
@@ -2193,19 +2237,24 @@
 			/* Now dump the request lists for each block device */
 			printk("Dump of pending block device requests\n");
 			for (i = 0; i < MAX_BLKDEV; i++) {
-				if (blk_dev[i].request_queue.current_request) {
+				struct list_head * queue_head;
+
+				queue_head = &blk_dev[i].request_queue.queue_head;
+				if (!list_empty(queue_head)) {
 					struct request *req;
+					struct list_head * entry;
+
 					printk("%d: ", i);
-					req = blk_dev[i].request_queue.current_request;
-					while (req) {
+					entry = queue_head->next;
+					do {
+						req = blkdev_entry_to_request(entry);
 						printk("(%s %d %ld %ld %ld) ",
 						   kdevname(req->rq_dev),
 						       req->cmd,
 						       req->sector,
 						       req->nr_sectors,
 						req->current_nr_sectors);
-						req = req->next;
-					}
+					} while ((entry = entry->next) != queue_head);
 					printk("\n");
 				}
 			}
@@ -2216,12 +2265,52 @@
 }
 #endif				/* CONFIG_PROC_FS */
 
+static int scsi_host_no_init (char *str)
+{
+    static int next_no = 0;
+    char *temp;
+
+#ifndef MODULE
+    int len;
+    scsi_host_no_set = 1;
+    memset(scsi_host_no_table, 0, sizeof(scsi_host_no_table));
+#endif /* MODULE */
+
+    while (str) {
+	temp = str;
+	while (*temp && (*temp != ':') && (*temp != ','))
+	    temp++;
+	if (!*temp)
+	    temp = NULL;
+	else
+	    *temp++ = 0;
+#ifdef MODULE
+	scsi_host_no_insert(str, next_no);
+#else
+	if (next_no < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0])) {
+	    if ((len = strlen(str)) >= sizeof(scsi_host_no_table[0]))
+		len = sizeof(scsi_host_no_table[0])-1;
+	    strncpy(scsi_host_no_table[next_no], str, len);
+	    scsi_host_no_table[next_no][len] = 0;
+	}
+#endif /* MODULE */
+	str = temp;
+	next_no++;
+    }
+    return 1;
+}
+
+#ifndef MODULE
+__setup("scsihosts=", scsi_host_no_init);
+#endif
+
 #ifdef MODULE
+static char *scsihosts;
+
+MODULE_PARM(scsihosts, "s");
 
 int init_module(void)
 {
-	unsigned long size;
-	int has_space = 0;
 	struct proc_dir_entry *generic;
 
         if( scsi_init_minimal_dma_pool() != 0 )
@@ -2249,6 +2338,8 @@
 
 	scsi_loadable_module_flag = 1;
 
+        scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL);
+        scsi_host_no_init (scsihosts);
 	/*
 	 * This is where the processing takes place for most everything
 	 * when commands are completed.
@@ -2260,7 +2351,20 @@
 
 void cleanup_module(void)
 {
+	Scsi_Host_Name *shn, *shn2 = NULL;
+
 	remove_bh(SCSI_BH);
+
+        devfs_unregister (scsi_devfs_handle);
+        for (shn = scsi_host_no_list;shn;shn = shn->next) {
+		if (shn->name)
+			kfree(shn->name);
+                if (shn2)
+			kfree (shn2);
+                shn2 = shn;
+        }
+        if (shn2)
+		kfree (shn2);
 
 #ifdef CONFIG_PROC_FS
 	/* No, we're not here anymore. Don't show the /proc/scsi files. */
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.3.45/linux/drivers/scsi/scsi.h	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/scsi.h	Wed Feb 16 15:42:05 2000
@@ -16,6 +16,7 @@
 #define _SCSI_H
 
 #include <linux/config.h>	/* for CONFIG_SCSI_LOGGING */
+#include <linux/devfs_fs_kernel.h>
 #include <linux/proc_fs.h>
 
 /*
@@ -512,6 +513,7 @@
 	int access_count;	/* Count of open channels/mounts */
 
 	void *hostdata;		/* available to low-level driver */
+	devfs_handle_t de;      /* directory for the device      */
 	char type;
 	char scsi_level;
 	char vendor[8], model[16], rev[4];
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c
--- v2.3.45/linux/drivers/scsi/scsi_lib.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/scsi_lib.c	Wed Feb 16 10:56:45 2000
@@ -86,6 +86,7 @@
 	q = &SCpnt->device->request_queue;
 	SCpnt->request.cmd = SPECIAL;
 	SCpnt->request.special = (void *) SCpnt;
+	SCpnt->request.q = NULL;
 
 	/*
 	 * We have the option of inserting the head or the tail of the queue.
@@ -96,8 +97,7 @@
 	spin_lock_irqsave(&io_request_lock, flags);
 
 	if (at_head) {
-		SCpnt->request.next = q->current_request;
-		q->current_request = &SCpnt->request;
+		list_add(&SCpnt->request.queue, &q->queue_head);
 	} else {
 		/*
 		 * FIXME(eric) - we always insert at the tail of the
@@ -107,19 +107,7 @@
 		 * request might not float high enough in the queue
 		 * to be scheduled.
 		 */
-		SCpnt->request.next = NULL;
-		if (q->current_request == NULL) {
-			q->current_request = &SCpnt->request;
-		} else {
-			struct request *req;
-
-			for (req = q->current_request; req; req = req->next) {
-				if (req->next == NULL) {
-					req->next = &SCpnt->request;
-					break;
-				}
-			}
-		}
+		list_add_tail(&SCpnt->request.queue, &q->queue_head);
 	}
 
 	/*
@@ -239,9 +227,8 @@
 		 * in which case we need to request the blocks that come after
 		 * the bad sector.
 		 */
-		SCpnt->request.next = q->current_request;
-		q->current_request = &SCpnt->request;
 		SCpnt->request.special = (void *) SCpnt;
+		list_add(&SCpnt->request.queue, &q->queue_head);
 	}
 
 	/*
@@ -260,7 +247,7 @@
 	 * use function pointers to pick the right one.
 	 */
 	if (SDpnt->single_lun
-	    && q->current_request == NULL
+	    && list_empty(&q->queue_head)
 	    && SDpnt->device_busy == 0) {
 		request_queue_t *q;
 
@@ -850,18 +837,18 @@
 		}
 
 		/*
-		 * Loop through all of the requests in this queue, and find
-		 * one that is queueable.
-		 */
-		req = q->current_request;
-
-		/*
 		 * If we couldn't find a request that could be queued, then we
 		 * can also quit.
 		 */
-		if (!req) {
+		if (list_empty(&q->queue_head))
 			break;
-		}
+
+		/*
+		 * Loop through all of the requests in this queue, and find
+		 * one that is queueable.
+		 */
+		req = blkdev_entry_next_request(&q->queue_head);
+
 		/*
 		 * Find the actual device driver associated with this command.
 		 * The SPECIAL requests are things like character device or
@@ -922,8 +909,7 @@
 		 * reason to search the list, because all of the commands
 		 * in this queue are for the same device.
 		 */
-		q->current_request = req->next;
-		SCpnt->request.next = NULL;
+		blkdev_dequeue_request(req);
 
 		if (req != &SCpnt->request) {
 			memcpy(&SCpnt->request, req, sizeof(struct request));
@@ -932,7 +918,6 @@
 			 * We have copied the data out of the request block - it is now in
 			 * a field in SCpnt.  Release the request block.
 			 */
-			req->next = NULL;
 			req->rq_status = RQ_INACTIVE;
 			wake_up(&wait_for_request);
 		}
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/scsi_merge.c linux/drivers/scsi/scsi_merge.c
--- v2.3.45/linux/drivers/scsi/scsi_merge.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/scsi_merge.c	Wed Feb 16 10:56:45 2000
@@ -307,6 +307,69 @@
 (((((long)(X)->b_data+(X)->b_size)|((long)(Y)->b_data)) & \
   (DMA_CHUNK_SIZE - 1)) == 0)
 
+#ifdef DMA_CHUNK_SIZE
+static inline int scsi_new_mergeable(request_queue_t * q,
+				     struct request * req,
+				     struct Scsi_Host *SHpnt,
+				     int max_segments)
+{
+	/*
+	 * pci_map_sg will be able to merge these two
+	 * into a single hardware sg entry, check if
+	 * we'll have enough memory for the sg list.
+	 * scsi.c allocates for this purpose
+	 * min(64,sg_tablesize) entries.
+	 */
+	if (req->nr_segments >= max_segments &&
+	    req->nr_segments >= SHpnt->sg_tablesize)
+		return 0;
+	req->nr_segments++;
+	q->nr_segments++;
+	return 1;
+}
+
+static inline int scsi_new_segment(request_queue_t * q,
+				   struct request * req,
+				   struct Scsi_Host *SHpnt,
+				   int max_segments)
+{
+	/*
+	 * pci_map_sg won't be able to map these two
+	 * into a single hardware sg entry, so we have to
+	 * check if things fit into sg_tablesize.
+	 */
+	if (req->nr_hw_segments >= SHpnt->sg_tablesize ||
+	    (req->nr_segments >= max_segments &&
+	     req->nr_segments >= SHpnt->sg_tablesize))
+		return 0;
+	if (req->nr_segments >= max_segments)
+		return 0;
+	req->nr_hw_segments++;
+	req->nr_segments++;
+	q->nr_segments++;
+	return 1;
+}
+#else
+static inline int scsi_new_segment(request_queue_t * q,
+				   struct request * req,
+				   struct Scsi_Host *SHpnt,
+				   int max_segments)
+{
+	if (req->nr_segments < SHpnt->sg_tablesize &&
+	    req->nr_segments < max_segments) {
+		/*
+		 * This will form the start of a new segment.  Bump the 
+		 * counter.
+		 */
+		req->nr_segments++;
+		q->nr_segments++;
+		return 1;
+	} else {
+		return 0;
+	}
+}
+#endif
+
 /*
  * Function:    __scsi_merge_fn()
  *
@@ -340,13 +403,14 @@
  *              than to have 4 separate functions all doing roughly the
  *              same thing.
  */
-__inline static int __scsi_merge_fn(request_queue_t * q,
-				    struct request *req,
-				    struct buffer_head *bh,
-				    int use_clustering,
-				    int dma_host)
+__inline static int __scsi_back_merge_fn(request_queue_t * q,
+					 struct request *req,
+					 struct buffer_head *bh,
+					 int max_segments,
+					 int use_clustering,
+					 int dma_host)
 {
-	unsigned int sector, count;
+	unsigned int count;
 	unsigned int segment_size = 0;
 	Scsi_Device *SDpnt;
 	struct Scsi_Host *SHpnt;
@@ -354,130 +418,97 @@
 	SDpnt = (Scsi_Device *) q->queuedata;
 	SHpnt = SDpnt->host;
 
-	count = bh->b_size >> 9;
-	sector = bh->b_rsector;
+	if (max_segments > 64)
+		max_segments = 64;
 
-	/*
-	 * We come in here in one of two cases.   The first is that we
-	 * are checking to see if we can add the buffer to the end of the
-	 * request, the other is to see if we should add the request to the
-	 * start.
-	 */
-	if (req->sector + req->nr_sectors == sector) {
-		if (use_clustering) {
-			/* 
-			 * See if we can do this without creating another
-			 * scatter-gather segment.  In the event that this is a
-			 * DMA capable host, make sure that a segment doesn't span
-			 * the DMA threshold boundary.  
-			 */
-			if (dma_host &&
-			    virt_to_phys(req->bhtail->b_data) - 1 == ISA_DMA_THRESHOLD) {
-				goto new_end_segment;
-			}
-			if (CONTIGUOUS_BUFFERS(req->bhtail, bh)) {
+	if (use_clustering) {
+		/* 
+		 * See if we can do this without creating another
+		 * scatter-gather segment.  In the event that this is a
+		 * DMA capable host, make sure that a segment doesn't span
+		 * the DMA threshold boundary.  
+		 */
+		if (dma_host &&
+		    virt_to_phys(req->bhtail->b_data) - 1 == ISA_DMA_THRESHOLD) {
+			goto new_end_segment;
+		}
+		if (CONTIGUOUS_BUFFERS(req->bhtail, bh)) {
 #ifdef DMA_SEGMENT_SIZE_LIMITED
-				if( dma_host
-				    && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) {
-					segment_size = 0;
-					count = __count_segments(req, use_clustering, dma_host, &segment_size);
-					if( segment_size + bh->b_size > PAGE_SIZE ) {
-						goto new_end_segment;
-					}
+			if( dma_host
+			    && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) {
+				segment_size = 0;
+				count = __count_segments(req, use_clustering, dma_host, &segment_size);
+				if( segment_size + bh->b_size > PAGE_SIZE ) {
+					goto new_end_segment;
 				}
-#endif
-				/*
-				 * This one is OK.  Let it go.
-				 */
-				return 1;
 			}
+#endif
+			/*
+			 * This one is OK.  Let it go.
+			 */
+			return 1;
 		}
-	      new_end_segment:
+	}
+ new_end_segment:
 #ifdef DMA_CHUNK_SIZE
-		if (MERGEABLE_BUFFERS(req->bhtail, bh))
-			goto new_mergeable;
+	if (MERGEABLE_BUFFERS(req->bhtail, bh))
+		return scsi_new_mergeable(q, req, SHpnt, max_segments);
 #endif
-		goto new_segment;
-	} else {
-		if (req->sector - count != sector) {
-			/* Attempt to merge sector that doesn't belong */
-			BUG();
-		}
-		if (use_clustering) {
-			/* 
-			 * See if we can do this without creating another
-			 * scatter-gather segment.  In the event that this is a
-			 * DMA capable host, make sure that a segment doesn't span
-			 * the DMA threshold boundary. 
-			 */
-			if (dma_host &&
-			    virt_to_phys(bh->b_data) - 1 == ISA_DMA_THRESHOLD) {
-				goto new_start_segment;
-			}
-			if (CONTIGUOUS_BUFFERS(bh, req->bh)) {
+	return scsi_new_segment(q, req, SHpnt, max_segments);
+}
+
+__inline static int __scsi_front_merge_fn(request_queue_t * q,
+					  struct request *req,
+					  struct buffer_head *bh,
+					  int max_segments,
+					  int use_clustering,
+					  int dma_host)
+{
+	unsigned int count;
+	unsigned int segment_size = 0;
+	Scsi_Device *SDpnt;
+	struct Scsi_Host *SHpnt;
+
+	SDpnt = (Scsi_Device *) q->queuedata;
+	SHpnt = SDpnt->host;
+
+	if (max_segments > 64)
+		max_segments = 64;
+
+	if (use_clustering) {
+		/* 
+		 * See if we can do this without creating another
+		 * scatter-gather segment.  In the event that this is a
+		 * DMA capable host, make sure that a segment doesn't span
+		 * the DMA threshold boundary. 
+		 */
+		if (dma_host &&
+		    virt_to_phys(bh->b_data) - 1 == ISA_DMA_THRESHOLD) {
+			goto new_start_segment;
+		}
+		if (CONTIGUOUS_BUFFERS(bh, req->bh)) {
 #ifdef DMA_SEGMENT_SIZE_LIMITED
-				if( dma_host
-				    && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) {
-					segment_size = bh->b_size;
-					count = __count_segments(req, use_clustering, dma_host, &segment_size);
-					if( count != req->nr_segments ) {
-						goto new_start_segment;
-					}
+			if( dma_host
+			    && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) {
+				segment_size = bh->b_size;
+				count = __count_segments(req, use_clustering, dma_host, &segment_size);
+				if( count != req->nr_segments ) {
+					goto new_start_segment;
 				}
-#endif
-				/*
-				 * This one is OK.  Let it go.
-				 */
-				return 1;
 			}
-		}
-	      new_start_segment:
-#ifdef DMA_CHUNK_SIZE
-		if (MERGEABLE_BUFFERS(bh, req->bh))
-			goto new_mergeable;
 #endif
-		goto new_segment;
+			/*
+			 * This one is OK.  Let it go.
+			 */
+			return 1;
+		}
 	}
+ new_start_segment:
 #ifdef DMA_CHUNK_SIZE
-      new_mergeable:
-	/*
-	 * pci_map_sg will be able to merge these two
-	 * into a single hardware sg entry, check if
-	 * we'll have enough memory for the sg list.
-	 * scsi.c allocates for this purpose
-	 * min(64,sg_tablesize) entries.
-	 */
-	if (req->nr_segments >= 64 &&
-	    req->nr_segments >= SHpnt->sg_tablesize)
-		return 0;
-	req->nr_segments++;
-	return 1;
-      new_segment:
-	/*
-	 * pci_map_sg won't be able to map these two
-	 * into a single hardware sg entry, so we have to
-	 * check if things fit into sg_tablesize.
-	 */
-	if (req->nr_hw_segments >= SHpnt->sg_tablesize ||
-	    (req->nr_segments >= 64 &&
-	     req->nr_segments >= SHpnt->sg_tablesize))
-		return 0;
-	req->nr_hw_segments++;
-	req->nr_segments++;
-	return 1;
-#else
-      new_segment:
-	if (req->nr_segments < SHpnt->sg_tablesize) {
-		/*
-		 * This will form the start of a new segment.  Bump the 
-		 * counter.
-		 */
-		req->nr_segments++;
-		return 1;
-	} else {
-		return 0;
-	}
+	if (MERGEABLE_BUFFERS(bh, req->bh))
+		return scsi_new_mergeable(q, req, SHpnt, max_segments);
 #endif
+	return scsi_new_segment(q, req, SHpnt, max_segments);
 }
 
 /*
@@ -497,23 +528,34 @@
  * Notes:       Optimized for different cases depending upon whether
  *              ISA DMA is in use and whether clustering should be used.
  */
-#define MERGEFCT(_FUNCTION, _CLUSTER, _DMA)		\
-static int _FUNCTION(request_queue_t * q,		\
-	       struct request * req,			\
-	       struct buffer_head * bh)			\
-{							\
-    int ret;						\
-    SANITY_CHECK(req, _CLUSTER, _DMA);			\
-    ret =  __scsi_merge_fn(q, req, bh, _CLUSTER, _DMA); \
-    return ret;						\
+#define MERGEFCT(_FUNCTION, _BACK_FRONT, _CLUSTER, _DMA)		\
+static int _FUNCTION(request_queue_t * q,				\
+		     struct request * req,				\
+		     struct buffer_head * bh,				\
+		     int max_segments)					\
+{									\
+    int ret;								\
+    SANITY_CHECK(req, _CLUSTER, _DMA);					\
+    ret =  __scsi_ ## _BACK_FRONT ## _merge_fn(q,			\
+					       req,			\
+					       bh,			\
+					       max_segments,		\
+					       _CLUSTER,		\
+					       _DMA);			\
+    return ret;								\
 }
 
 /* Version with use_clustering 0 and dma_host 1 is not necessary,
  * since the only use of dma_host above is protected by use_clustering.
  */
-MERGEFCT(scsi_merge_fn_, 0, 0)
-MERGEFCT(scsi_merge_fn_c, 1, 0)
-MERGEFCT(scsi_merge_fn_dc, 1, 1)
+MERGEFCT(scsi_back_merge_fn_, back, 0, 0)
+MERGEFCT(scsi_back_merge_fn_c, back, 1, 0)
+MERGEFCT(scsi_back_merge_fn_dc, back, 1, 1)
+
+MERGEFCT(scsi_front_merge_fn_, front, 0, 0)
+MERGEFCT(scsi_front_merge_fn_c, front, 1, 0)
+MERGEFCT(scsi_front_merge_fn_dc, front, 1, 1)
+
 /*
  * Function:    __scsi_merge_requests_fn()
  *
@@ -550,6 +592,7 @@
 __inline static int __scsi_merge_requests_fn(request_queue_t * q,
 					     struct request *req,
 					     struct request *next,
+					     int max_segments,
 					     int use_clustering,
 					     int dma_host)
 {
@@ -559,11 +602,14 @@
 	SDpnt = (Scsi_Device *) q->queuedata;
 	SHpnt = SDpnt->host;
 
+	if (max_segments > 64)
+		max_segments = 64;
+
 #ifdef DMA_CHUNK_SIZE
 	/* If it would not fit into prepared memory space for sg chain,
 	 * then don't allow the merge.
 	 */
-	if (req->nr_segments + next->nr_segments - 1 > 64 &&
+	if (req->nr_segments + next->nr_segments - 1 > max_segments &&
 	    req->nr_segments + next->nr_segments - 1 > SHpnt->sg_tablesize) {
 		return 0;
 	}
@@ -619,6 +665,7 @@
 			 * This one is OK.  Let it go.
 			 */
 			req->nr_segments += next->nr_segments - 1;
+			q->nr_segments--;
 #ifdef DMA_CHUNK_SIZE
 			req->nr_hw_segments += next->nr_hw_segments - 1;
 #endif
@@ -627,7 +674,7 @@
 	}
       dont_combine:
 #ifdef DMA_CHUNK_SIZE
-	if (req->nr_segments + next->nr_segments > 64 &&
+	if (req->nr_segments + next->nr_segments > max_segments &&
 	    req->nr_segments + next->nr_segments > SHpnt->sg_tablesize) {
 		return 0;
 	}
@@ -650,7 +697,8 @@
 	 * Make sure we can fix something that is the sum of the two.
 	 * A slightly stricter test than we had above.
 	 */
-	if (req->nr_segments + next->nr_segments > SHpnt->sg_tablesize) {
+	if (req->nr_segments + next->nr_segments > max_segments &&
+	    req->nr_segments + next->nr_segments > SHpnt->sg_tablesize) {
 		return 0;
 	} else {
 		/*
@@ -683,11 +731,12 @@
 #define MERGEREQFCT(_FUNCTION, _CLUSTER, _DMA)		\
 static int _FUNCTION(request_queue_t * q,		\
 		     struct request * req,		\
-		     struct request * next)		\
+		     struct request * next,		\
+		     int max_segments)			\
 {							\
     int ret;						\
     SANITY_CHECK(req, _CLUSTER, _DMA);			\
-    ret =  __scsi_merge_requests_fn(q, req, next, _CLUSTER, _DMA); \
+    ret =  __scsi_merge_requests_fn(q, req, next, max_segments, _CLUSTER, _DMA); \
     return ret;						\
 }
 
@@ -1068,7 +1117,7 @@
 	 * pick a new one.
 	 */
 #if 0
-	if (q->merge_fn != NULL)
+	if (q->back_merge_fn && q->front_merge_fn)
 		return;
 #endif
 	/*
@@ -1083,19 +1132,23 @@
 	 * rather than rely upon the default behavior of ll_rw_blk.
 	 */
 	if (!CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma == 0) {
-		q->merge_fn = scsi_merge_fn_;
+		q->back_merge_fn = scsi_back_merge_fn_;
+		q->front_merge_fn = scsi_front_merge_fn_;
 		q->merge_requests_fn = scsi_merge_requests_fn_;
 		SDpnt->scsi_init_io_fn = scsi_init_io_v;
 	} else if (!CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma != 0) {
-		q->merge_fn = scsi_merge_fn_;
+		q->back_merge_fn = scsi_back_merge_fn_;
+		q->front_merge_fn = scsi_front_merge_fn_;
 		q->merge_requests_fn = scsi_merge_requests_fn_;
 		SDpnt->scsi_init_io_fn = scsi_init_io_vd;
 	} else if (CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma == 0) {
-		q->merge_fn = scsi_merge_fn_c;
+		q->back_merge_fn = scsi_back_merge_fn_c;
+		q->front_merge_fn = scsi_front_merge_fn_c;
 		q->merge_requests_fn = scsi_merge_requests_fn_c;
 		SDpnt->scsi_init_io_fn = scsi_init_io_vc;
 	} else if (CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma != 0) {
-		q->merge_fn = scsi_merge_fn_dc;
+		q->back_merge_fn = scsi_back_merge_fn_dc;
+		q->front_merge_fn = scsi_front_merge_fn_dc;
 		q->merge_requests_fn = scsi_merge_requests_fn_dc;
 		SDpnt->scsi_init_io_fn = scsi_init_io_vdc;
 	}
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/scsi_scan.c linux/drivers/scsi/scsi_scan.c
--- v2.3.45/linux/drivers/scsi/scsi_scan.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/scsi_scan.c	Wed Feb 16 15:42:05 2000
@@ -455,6 +455,7 @@
 	       int *sparse_lun, Scsi_Device ** SDpnt2, 
 		      struct Scsi_Host *shpnt, char *scsi_result)
 {
+	char devname[64];
 	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
 	struct Scsi_Device_Template *sdtpnt;
 	Scsi_Device *SDtail, *SDpnt = *SDpnt2;
@@ -462,6 +463,7 @@
 	int bflags, type = -1;
 	static int ghost_channel=-1, ghost_dev=-1;
 	int org_lun = lun;
+	extern devfs_handle_t scsi_devfs_handle;
 
 	SDpnt->host = shpnt;
 	SDpnt->id = dev;
@@ -636,6 +638,11 @@
 	SDpnt->type = (type & 0x1f);
 
 	print_inquiry(scsi_result);
+
+        sprintf (devname, "host%d/bus%d/target%d/lun%d",
+                 SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun);
+        if (SDpnt->de) printk ("DEBUG: dir: \"%s\" already exists\n", devname);
+        else SDpnt->de = devfs_mk_dir (scsi_devfs_handle, devname, 0, NULL);
 
 	for (sdtpnt = scsi_devicelist; sdtpnt;
 	     sdtpnt = sdtpnt->next)
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.3.45/linux/drivers/scsi/sd.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/scsi/sd.c	Wed Feb 16 15:42:05 2000
@@ -17,6 +17,8 @@
  *
  *       Modified by Jirka Hanika geo@ff.cuni.cz to support more
  *       scsi disks using eight major numbers.
+ *
+ *       Modified by Richard Gooch rgooch@atnf.csiro.au to support devfs.
  */
 
 #include <linux/config.h>
@@ -489,7 +491,8 @@
 	NULL,			/* block sizes */
 	0,			/* number */
 	NULL,			/* internal */
-	NULL			/* next */
+	NULL,			/* next */
+        &sd_fops,		/* file operations */
 };
 
 static struct gendisk *sd_gendisks = &sd_gendisk;
@@ -948,7 +951,7 @@
 
 	if (!sd_registered) {
 		for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
-			if (register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) {
+			if (devfs_register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) {
 				printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i));
 				return 1;
 			}
@@ -988,6 +991,15 @@
 		sd_gendisks = (struct gendisk *)
 		    kmalloc(N_USED_SD_MAJORS * sizeof(struct gendisk), GFP_ATOMIC);
 	for (i = 0; i < N_USED_SD_MAJORS; i++) {
+		sd_gendisks[i] = sd_gendisk;
+		sd_gendisks[i].de_arr = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr,
+                                                 GFP_ATOMIC);
+                memset (sd_gendisks[i].de_arr, 0,
+                        SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr);
+		sd_gendisks[i].flags = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags,
+                                                GFP_ATOMIC);
+                memset (sd_gendisks[i].flags, 0,
+                        SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags);
 		sd_gendisks[i].major = SD_MAJOR(i);
 		sd_gendisks[i].major_name = "sd";
 		sd_gendisks[i].minor_shift = 4;
@@ -1061,8 +1073,10 @@
 
 	return 1;
 }
+
 static int sd_attach(Scsi_Device * SDp)
 {
+        unsigned int devnum;
 	Scsi_Disk *dpnt;
 	int i;
 
@@ -1084,6 +1098,10 @@
 	rscsi_disks[i].has_part_table = 0;
 	sd_template.nr_dev++;
 	SD_GENDISK(i).nr_real++;
+        devnum = i % SCSI_DISKS_PER_MAJOR;
+        SD_GENDISK(i).de_arr[devnum] = SDp->de;
+        if (SDp->removable)
+		SD_GENDISK(i).flags[devnum] |= GENHD_FL_REMOVABLE;
 	return 0;
 }
 
@@ -1182,6 +1200,8 @@
 				sd_gendisks->part[index].nr_sects = 0;
 				sd_sizes[index] = 0;
 			}
+                        devfs_register_partitions (&SD_GENDISK (i),
+                                                   SD_MINOR_NUMBER (start), 1);
 			/* unregister_disk() */
 			dpnt->has_part_table = 0;
 			dpnt->device = NULL;
@@ -1212,7 +1232,7 @@
 	scsi_unregister_module(MODULE_SCSI_DEV, &sd_template);
 
 	for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++)
-		unregister_blkdev(SD_MAJOR(i), "sd");
+		devfs_unregister_blkdev(SD_MAJOR(i), "sd");
 
 	sd_registered--;
 	if (rscsi_disks != NULL) {
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v2.3.45/linux/drivers/scsi/sg.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/sg.c	Wed Feb 16 15:42:05 2000
@@ -9,6 +9,8 @@
  * Version 2 and 3 extensions to driver:
  *        Copyright (C) 1998, 1999 Douglas Gilbert
  *
+ *  Modified  19-JAN-1998  Richard Gooch <rgooch@atnf.csiro.au>  Devfs support
+ *
  * 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)
@@ -50,6 +52,7 @@
 #include <linux/fcntl.h>
 #include <linux/init.h>
 #include <linux/poll.h>
+
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -179,6 +182,7 @@
     wait_queue_head_t o_excl_wait;   /* queue open() when O_EXCL in use */
     int sg_tablesize;   /* adapter's max scatter-gather table size */
     Sg_fd * headfp;     /* first open fd belonging to this device */
+    devfs_handle_t de;
     kdev_t i_rdev;      /* holds device major+minor number */
     char exclude;       /* opened for exclusive access */
     char sgdebug;       /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
@@ -1075,7 +1079,7 @@
     if (sg_template.dev_noticed == 0) return 0;
 
     if(!sg_registered) {
-        if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops))
+        if (devfs_register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops))
         {
             printk("Unable to get major %d for generic SCSI device\n",
                    SCSI_GENERIC_MAJOR);
@@ -1150,6 +1154,10 @@
     sdp->sgdebug = 0;
     sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0;
     sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k);
+    sdp->de = devfs_register (scsidp->de, "generic", 7, DEVFS_FL_NONE,
+			      SCSI_GENERIC_MAJOR, k,
+			      S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
+			      &sg_fops, NULL);
     sg_template.nr_dev++;
     return 0;
 }
@@ -1194,6 +1202,8 @@
             SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k));
 	    sdp->device = NULL;
         }
+	devfs_unregister (sdp->de);
+	sdp->de = NULL;
         scsidp->attached--;
         sg_template.nr_dev--;
 /* avoid associated device /dev/sg? being incremented
@@ -1219,7 +1229,7 @@
 void cleanup_module( void)
 {
     scsi_unregister_module(MODULE_SCSI_DEV, &sg_template);
-    unregister_chrdev(SCSI_GENERIC_MAJOR, "sg");
+    devfs_unregister_chrdev(SCSI_GENERIC_MAJOR, "sg");
 
 #ifdef CONFIG_PROC_FS
     sg_proc_cleanup();
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.3.45/linux/drivers/scsi/sr.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/scsi/sr.c	Wed Feb 16 15:42:05 2000
@@ -23,6 +23,8 @@
  *       Modified by Jens Axboe <axboe@image.dk> - Uniform sr_packet()
  *       interface, capabilities probe additions, ioctl cleanups, etc.
  *
+ *       Modified by Richard Gooch <rgooch@atnf.csiro.au> to support devfs
+ *
  */
 
 #include <linux/module.h>
@@ -693,7 +695,7 @@
 		return 0;
 
 	if (!sr_registered) {
-		if (register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) {
+		if (devfs_register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) {
 			printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR);
 			return 1;
 		}
@@ -767,6 +769,11 @@
 
 		sprintf(name, "sr%d", i);
 		strcpy(scsi_CDs[i].cdi.name, name);
+                scsi_CDs[i].cdi.de =
+                    devfs_register (scsi_CDs[i].device->de, "cd", 2,
+                                    DEVFS_FL_DEFAULT, MAJOR_NR, i,
+                                    S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
+                                    &cdrom_fops, NULL);
 		register_cdrom(&scsi_CDs[i].cdi);
 	}
 
@@ -828,7 +835,7 @@
 void cleanup_module(void)
 {
 	scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
-	unregister_blkdev(MAJOR_NR, "sr");
+	devfs_unregister_blkdev(MAJOR_NR, "sr");
 	sr_registered--;
 	if (scsi_CDs != NULL) {
 		kfree((char *) scsi_CDs);
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.3.45/linux/drivers/scsi/st.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/scsi/st.c	Wed Feb 16 15:42:05 2000
@@ -13,6 +13,8 @@
 
    Last modified: Fri Feb 11 19:43:57 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
  */
 
 #include <linux/module.h>
@@ -3221,7 +3223,7 @@
 	Scsi_Tape *tpnt;
 	ST_mode *STm;
 	ST_partstat *STps;
-	int i;
+	int i, mode;
 
 	if (SDp->type != TYPE_TAPE)
 		return 1;
@@ -3238,6 +3240,26 @@
 	if (i >= st_template.dev_max)
 		panic("scsi_devices corrupt (st)");
 
+	for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+	    char name[8];
+	    static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"};
+
+	    /*  Rewind entry  */
+	    sprintf (name, "mt%s", formats[mode]);
+	    tpnt->de_r[mode] =
+		devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
+				MAJOR_NR, i + (mode << 5),
+				S_IFCHR | S_IRUGO | S_IWUGO,
+				0, 0, &st_fops, NULL);
+	    /*  No-rewind entry  */
+	    sprintf (name, "mt%sn", formats[mode]);
+	    tpnt->de_n[mode] =
+		devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
+				MAJOR_NR, i + (mode << 5) + 128,
+				S_IFCHR | S_IRUGO | S_IWUGO,
+				0, 0, &st_fops, NULL);
+	}
+	devfs_register_tape (tpnt->de_r[0]);
 	scsi_tapes[i].device = SDp;
 	if (SDp->scsi_level <= 2)
 		scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
@@ -3327,7 +3349,7 @@
 	       st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
 
 	if (!st_registered) {
-		if (register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) {
+		if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) {
 			printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
                                MAJOR_NR);
 			return 1;
@@ -3346,7 +3368,7 @@
 				      GFP_ATOMIC);
 	if (scsi_tapes == NULL) {
 		printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
-		unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+		devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
 		return 1;
 	}
 
@@ -3363,7 +3385,7 @@
 			for (j=0; j < i; j++)
 				kfree(scsi_tapes[j].mt_status);
 			kfree(scsi_tapes);
-			unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+			devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
 			return 1;
 		}
 		/* Initialize status */
@@ -3376,7 +3398,7 @@
 				   GFP_ATOMIC);
 	if (st_buffers == NULL) {
 		printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
-		unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+		devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
 		for (i=0; i < st_template.dev_max; i++)
 			kfree(scsi_tapes[i].mt_status);
 		kfree(scsi_tapes);
@@ -3407,11 +3429,17 @@
 static void st_detach(Scsi_Device * SDp)
 {
 	Scsi_Tape *tpnt;
-	int i;
+	int i, mode;
 
 	for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
 		if (tpnt->device == SDp) {
 			tpnt->device = NULL;
+			for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+				devfs_unregister (tpnt->de_r[mode]);
+				tpnt->de_r[mode] = NULL;
+				devfs_unregister (tpnt->de_n[mode]);
+				tpnt->de_n[mode] = NULL;
+			}
 			SDp->attached--;
 			st_template.nr_dev--;
 			st_template.dev_noticed--;
@@ -3436,7 +3464,7 @@
 	int i;
 
 	scsi_unregister_module(MODULE_SCSI_DEV, &st_template);
-	unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+	devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
 	st_registered--;
 	if (scsi_tapes != NULL) {
 		for (i=0; i < st_template.dev_max; ++i)
diff -u --recursive --new-file v2.3.45/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v2.3.45/linux/drivers/scsi/st.h	Tue Jan  4 13:57:17 2000
+++ linux/drivers/scsi/st.h	Wed Feb 16 15:42:05 2000
@@ -8,6 +8,7 @@
 #ifndef _SCSI_H
 #include "scsi.h"
 #endif
+#include <linux/devfs_fs_kernel.h>
 
 /* The tape buffer descriptor. */
 typedef struct {
@@ -85,6 +86,8 @@
 	/* Mode characteristics */
 	ST_mode modes[ST_NBR_MODES];
 	int current_mode;
+	devfs_handle_t de_r[ST_NBR_MODES];  /*  Rewind entries     */
+	devfs_handle_t de_n[ST_NBR_MODES];  /*  No-rewind entries  */
 
 	/* Status variables */
 	int partition;
diff -u --recursive --new-file v2.3.45/linux/drivers/sgi/char/shmiq.c linux/drivers/sgi/char/shmiq.c
--- v2.3.45/linux/drivers/sgi/char/shmiq.c	Thu Feb 10 17:11:13 2000
+++ linux/drivers/sgi/char/shmiq.c	Wed Feb 16 15:42:05 2000
@@ -53,6 +53,7 @@
 #include <linux/wait.h>
 #include <linux/major.h>
 #include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/shmiq.h>
 #include <asm/mman.h>
@@ -443,5 +444,12 @@
 shmiq_init (void)
 {
 	printk ("SHMIQ setup\n");
-	register_chrdev (SHMIQ_MAJOR, "shmiq", &shmiq_fops);
+	devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops);
+	devfs_register (NULL, "shmiq", 0, DEVFS_FL_DEFAULT,
+			SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			&shmiq_fops, NULL);
+	devfs_register_series (NULL, "qcntl%u", 2, DEVFS_FL_DEFAULT,
+			       SHMIQ_MAJOR, 1,
+			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &shmiq_fops, NULL);
 }
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/ac97_codec.c linux/drivers/sound/ac97_codec.c
--- v2.3.45/linux/drivers/sound/ac97_codec.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/sound/ac97_codec.c	Mon Feb 14 09:45:42 2000
@@ -38,6 +38,8 @@
 static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
 static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
 
+#define arraysize(x)	(sizeof(x)/sizeof((x)[0]))
+
 static struct {
 	unsigned int id;
 	char *name;
@@ -380,7 +382,7 @@
 				return -EINVAL;
 
 			/* do we ever want to touch the hardware? */
-			val = codec->read_mixer(card, i);
+			val = codec->read_mixer(codec, i);
 			/* val = codec->mixer_state[i]; */
 			break;
 		}
@@ -494,9 +496,11 @@
 	if ((cap = codec->codec_read(codec, AC97_RESET)) & 0x8000)
 		return 0;
 	
+	codec->name = NULL;
+	codec->codec_init = NULL;
 	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
 	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
-	for (i = 0, codec->name = NULL; i < arraysize (ac97_codec_ids[i]); i++) {
+	for (i = 0; i < arraysize(ac97_codec_ids); i++) {
 		if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
 			codec->name = ac97_codec_ids[i].name;
 			codec->codec_init = ac97_codec_ids[i].init;
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.3.45/linux/drivers/sound/dmasound.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/sound/dmasound.c	Mon Feb 14 13:37:25 2000
@@ -253,6 +253,7 @@
 static volatile struct dbdma_cmd *beep_dbdma_cmd;
 static void (*orig_mksound)(unsigned int, unsigned int);
 static int is_pbook_3400;
+static unsigned char *latch_base;
 static int is_pbook_G3;
 static unsigned char *macio_base;
 
@@ -3695,7 +3696,7 @@
 		} else if (is_pbook_3400) {
 			feature_set(awacs_node, FEATURE_IOBUS_enable);
 			udelay(10);
-			in_8((unsigned char *)0xf301a190);
+			in_8(latch_base + 0x190);
 		}
  		/* Resume pending sounds. */
  		PMacPlay();
@@ -5698,7 +5699,9 @@
 			 * sound input.  The 0x100 enables the SCSI bus
 			 * terminator power.
 			 */
-			in_8((unsigned char *)0xf301a190);
+			latch_base = (unsigned char *) ioremap
+				(0xf301a000, 0x1000);
+			in_8(latch_base + 0x190);
 		} else if (machine_is_compatible("PowerBook1,1")
 			   || machine_is_compatible("AAPL,PowerBook1998")) {
 			struct device_node* mio;
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.3.45/linux/drivers/sound/es1370.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/sound/es1370.c	Tue Feb 15 11:45:28 2000
@@ -2456,7 +2456,7 @@
 				 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
 #define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
 
-static int es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
 {
 	struct es1370_state *s;
 	mm_segment_t fs;
@@ -2569,7 +2569,7 @@
 	return -1;
 }
 
-static void es1370_remove(struct pci_dev *dev)
+static void __devinit es1370_remove(struct pci_dev *dev)
 {
        struct es1370_state *s = (struct es1370_state *)dev->driver_data;
 
@@ -2589,7 +2589,7 @@
        dev->driver_data = NULL;
 }
 
-static const struct pci_device_id id_table[] = {
+static const struct pci_device_id id_table[] __devinitdata = {
 	{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
 	{ 0, 0, 0, 0, 0, 0 }
 };
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- v2.3.45/linux/drivers/sound/es1371.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/sound/es1371.c	Tue Feb 15 11:45:28 2000
@@ -2611,7 +2611,7 @@
 				 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
 #define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
 
-static int es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
 {
 	struct es1371_state *s;
 	mm_segment_t fs;
@@ -2764,7 +2764,7 @@
 	return -1;
 }
 
-static void es1371_remove(struct pci_dev *dev)
+static void __devinit es1371_remove(struct pci_dev *dev)
 {
 	struct es1371_state *s = (struct es1371_state *)dev->driver_data;
 
@@ -2788,7 +2788,7 @@
 	dev->driver_data = NULL;
 }
 
-static const struct pci_device_id id_table[] = {
+static const struct pci_device_id id_table[] __devinitdata = {
 	{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
 	{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
 	{ PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/esssolo1.c linux/drivers/sound/esssolo1.c
--- v2.3.45/linux/drivers/sound/esssolo1.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/sound/esssolo1.c	Tue Feb 15 11:45:28 2000
@@ -2190,10 +2190,9 @@
 				 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
 #define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
 
-static int solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
 {
 	struct solo1_state *s;
-	struct pm_dev *pmdev;
 
 	if (!RSRCISIOREGION(pcidev, 0) ||
 	    !RSRCISIOREGION(pcidev, 1) ||
@@ -2293,7 +2292,7 @@
 	return -1;
 }
 
-static void solo1_remove(struct pci_dev *dev)
+static void __devinit solo1_remove(struct pci_dev *dev)
 {
 	struct solo1_state *s = (struct solo1_state *)dev->driver_data;
 	
@@ -2319,7 +2318,7 @@
 	dev->driver_data = NULL;
 }
 
-static const struct pci_device_id id_table[] = {
+static const struct pci_device_id id_table[] __devinitdata = {
 	{ PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
 	{ 0, 0, 0, 0, 0, 0 }
 };
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- v2.3.45/linux/drivers/sound/sonicvibes.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/sound/sonicvibes.c	Tue Feb 15 11:45:28 2000
@@ -2430,7 +2430,7 @@
 				 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
 #define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
 
-static int sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
 {
 	static const char __initlocaldata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller";
        	struct sv_state *s;
@@ -2598,7 +2598,7 @@
 	return -1;
 }
 
-static void sv_remove(struct pci_dev *dev)
+static void __devinit sv_remove(struct pci_dev *dev)
 {
        struct sv_state *s = (struct sv_state *)dev->driver_data;
 
@@ -2625,7 +2625,7 @@
        dev->driver_data = NULL;
 }
 
-static const struct pci_device_id id_table[] = {
+static const struct pci_device_id id_table[] __devinitdata = {
        { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
        { 0, 0, 0, 0, 0, 0 }
 };
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.3.45/linux/drivers/sound/sound_core.c	Fri Jan  7 19:13:22 2000
+++ linux/drivers/sound/sound_core.c	Wed Feb 16 15:42:05 2000
@@ -43,13 +43,17 @@
 #include <linux/sound.h>
 #include <linux/major.h>
 #include <linux/kmod.h>
- 
- 
+#include <linux/devfs_fs_kernel.h>
+
+#define SOUND_STEP 16
+
+
 struct sound_unit
 {
 	int unit_minor;
 	struct file_operations *unit_fops;
 	struct sound_unit *next;
+	devfs_handle_t de;
 };
 
 #ifdef CONFIG_SOUND_MSNDCLAS
@@ -82,7 +86,7 @@
 			if(*list==NULL || (*list)->unit_minor>n)
 				break;
 			list=&((*list)->next);
-			n+=16;
+			n+=SOUND_STEP;
 		}
 
 		if(n>=top)
@@ -129,6 +133,7 @@
 		if(p->unit_minor==unit)
 		{
 			*list=p->next;
+			devfs_unregister (p->de);
 			kfree(p);
 			MOD_DEC_USE_COUNT;
 			return;
@@ -148,11 +153,15 @@
  *	Allocate the controlling structure and add it to the sound driver
  *	list. Acquires locks as needed
  */
+
+static devfs_handle_t devfs_handle = NULL;
  
-static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top)
+static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode)
 {
 	int r;
 	struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL);
+	char name_buf[16];
+
 	if(s==NULL)
 		return -ENOMEM;
 		
@@ -162,6 +171,13 @@
 	
 	if(r<0)
 		kfree(s);
+	if (r == low)
+		sprintf (name_buf, "%s", name);
+	else
+		sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP);
+	s->de = devfs_register (devfs_handle, name_buf, 0,
+				DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor,
+				S_IFCHR | mode, 0, 0, fops, NULL);
 	return r;
 }
 
@@ -203,21 +219,76 @@
 
 int register_sound_special(struct file_operations *fops, int unit)
 {
-	return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1);
+	char *name;
+
+	switch (unit) {
+	    case 0:
+		name = "mixer";
+		break;
+	    case 1:
+		name = "sequencer";
+		break;
+	    case 2:
+		name = "midi00";
+		break;
+	    case 3:
+		name = "dsp";
+		break;
+	    case 4:
+		name = "audio";
+		break;
+	    case 5:
+		name = "unknown5";
+		break;
+	    case 6:
+		name = "sndstat";
+		break;
+	    case 7:
+		name = "unknown7";
+		break;
+	    case 8:
+		name = "sequencer2";
+		break;
+	    case 9:
+		name = "dmmidi";
+		break;
+	    case 10:
+		name = "dmfm";
+		break;
+	    case 11:
+		name = "unknown11";
+		break;
+	    case 12:
+		name = "adsp";
+		break;
+	    case 13:
+		name = "amidi";
+		break;
+	    case 14:
+		name = "admmidi";
+		break;
+	    default:
+		name = "unknown";
+		break;
+	}
+	return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1,
+				 name, S_IRUGO | S_IWUGO);
 }
  
 EXPORT_SYMBOL(register_sound_special);
 
 int register_sound_mixer(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[0], fops, dev, 0, 128);
+	return sound_insert_unit(&chains[0], fops, dev, 0, 128,
+				 "mixer", S_IRUGO | S_IWUGO);
 }
 
 EXPORT_SYMBOL(register_sound_mixer);
 
 int register_sound_midi(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[2], fops, dev, 2, 130);
+	return sound_insert_unit(&chains[2], fops, dev, 2, 130,
+				 "midi", S_IRUGO | S_IWUGO);
 }
 
 EXPORT_SYMBOL(register_sound_midi);
@@ -229,14 +300,16 @@
  
 int register_sound_dsp(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[3], fops, dev, 3, 131);
+	return sound_insert_unit(&chains[3], fops, dev, 3, 131,
+				 "dsp", S_IWUGO | S_IRUSR | S_IRGRP);
 }
 
 EXPORT_SYMBOL(register_sound_dsp);
 
 int register_sound_synth(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[9], fops, dev, 9, 137);
+	return sound_insert_unit(&chains[9], fops, dev, 9, 137,
+				 "synth", S_IRUGO | S_IWUGO);
 }
 
 EXPORT_SYMBOL(register_sound_synth);
@@ -359,7 +432,8 @@
 {
 	/* We have nothing to really do here - we know the lists must be
 	   empty */
-	unregister_chrdev(SOUND_MAJOR, "sound");
+	devfs_unregister_chrdev(SOUND_MAJOR, "sound");
+	devfs_unregister (devfs_handle);
 }
 
 int init_module(void)
@@ -367,11 +441,12 @@
 int soundcore_init(void)
 #endif
 {
-	if(register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1)
+	if(devfs_register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1)
 	{
 		printk(KERN_ERR "soundcore: sound device already in use.\n");
 		return -EBUSY;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL);
 	/*
 	 *	Now init non OSS drivers
 	 */
diff -u --recursive --new-file v2.3.45/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.3.45/linux/drivers/sound/soundcard.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/sound/soundcard.c	Wed Feb 16 15:42:05 2000
@@ -15,6 +15,9 @@
  *                   integrated sound_switch.c
  * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat,
  *                   which should disappear in the near future)
+ * Eric Dumas      : devfs support (22-Jan-98) <dumas@linux.eu.org> with fixups
+ *                   by C. Scott Ananian <cananian@alumni.princeton.edu>
+ * Richard Gooch   : moved common (non OSS-specific) devices to sound_core.c
  *
  * Rob Riggs		Added persistent DMA buffers support (1998/10/17)
  */
@@ -36,6 +39,8 @@
 #include <linux/wait.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/major.h>
 #endif				/* __KERNEL__ */
 #include <linux/delay.h>
 #include <linux/proc_fs.h>
@@ -93,7 +98,6 @@
 static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
 static int num_mixer_volumes = 0;
 
-
 int *load_mixer_volumes(char *name, int *levels, int present)
 {
 	int             i, n;
@@ -811,6 +815,66 @@
 	return -1;
 }
 
+
+/* These device names follow the official Linux device list,
+ * Documentation/devices.txt.  Let us know if there are other
+ * common names we should support for compatibility.
+ * Only those devices not created by the generic code in sound_core.c are
+ * registered here.
+ */
+static const struct {
+	unsigned short minor;
+	char *name;
+	umode_t mode;
+	int *num;
+} dev_list[] = { /* list of minor devices */
+#ifdef CONFIG_AUDIO
+/* seems to be some confusion here -- this device is not in the device list */
+	{SND_DEV_DSP16,     "dspW",	 S_IWUGO | S_IRUSR | S_IRGRP,
+	 &num_audiodevs},
+	{SND_DEV_AUDIO,     "audio",	 S_IWUGO | S_IRUSR | S_IRGRP,
+	 &num_audiodevs},
+#endif /* CONFIG_AUDIO */
+};
+
+static char * 
+soundcard_make_name(char *buf, char *name, int idx) {
+	if (idx==0)
+		sprintf(buf, "sound/%s", name);
+	else
+		sprintf(buf, "sound/%s%d", name, idx);
+	return buf;
+}
+	
+/* Register/unregister audio entries */
+static void soundcard_register_devfs (int do_register)
+{
+    char name_buf[32];
+    int i, j, num;
+
+    for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++)
+    {
+	num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num;
+	for (j = 0; j < num || j == 0; j++)
+	{
+	    soundcard_make_name (name_buf, dev_list[i].name, j);
+	    if (do_register)
+		devfs_register (NULL, name_buf, 0, DEVFS_FL_NONE,
+				SOUND_MAJOR, dev_list[i].minor+ (j* 0x10),
+				S_IFCHR | dev_list[i].mode, 0, 0,
+				&oss_sound_fops, NULL);
+	    else
+	    {
+		devfs_handle_t de;
+
+		de = devfs_find_handle (NULL, name_buf, 0, 0, 0,
+					DEVFS_SPECIAL_CHR, 0);
+		devfs_unregister (de);
+	    }
+	}
+    }
+}
+
 #ifdef MODULE
 static void
 #else
@@ -849,6 +913,7 @@
 	if (!create_proc_info_entry("sound", 0, NULL, sound_proc_get_info))
 		printk(KERN_ERR "sound: registering /proc/sound failed\n");
 #endif		
+	soundcard_register_devfs(1); /* register after we know # of devices */
 }
 
 #ifdef MODULE
@@ -931,6 +996,7 @@
 		return;
 	}
         remove_proc_entry("sound", NULL);
+	soundcard_register_devfs (0);
 	if (chrdev_registered)
 		destroy_special_devices();
 
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/Config.in linux/drivers/usb/Config.in
--- v2.3.45/linux/drivers/usb/Config.in	Thu Feb 10 17:11:14 2000
+++ linux/drivers/usb/Config.in	Tue Feb 15 11:53:24 2000
@@ -9,6 +9,9 @@
 
 comment 'USB Controllers'
    dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
+   if [ "$CONFIG_USB_UHCI" != "n" ]; then
+      bool '    USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH
+   fi
    dep_tristate '  UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
    if [ "$CONFIG_USB_UHCI_ALT" != "n" ]; then
       bool '    UHCI unlink optimizations (EXPERIMENTAL)' CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
@@ -28,10 +31,8 @@
       bool '    USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC
       bool '    USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR
       bool '    USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT
-      bool '    USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI
+      bool '    USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO
       bool '    USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA
-      bool '    USB Belkin Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_BELKIN
-      bool '    USB Peracom Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_PERACOM
    fi
    dep_tristate '  USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB
    dep_tristate '  USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB
@@ -43,7 +44,8 @@
    fi
    dep_tristate '  USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
    dep_tristate '  DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB 
-
+   dep_tristate '  PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB 
+   
 comment 'USB HID'
    dep_tristate '  USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
    if [ "$CONFIG_USB_HID" != "y" ]; then
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/Makefile linux/drivers/usb/Makefile
--- v2.3.45/linux/drivers/usb/Makefile	Thu Feb 10 17:11:14 2000
+++ linux/drivers/usb/Makefile	Tue Feb 15 11:37:49 2000
@@ -69,6 +69,7 @@
 obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 obj-$(CONFIG_USB_DABUSB)	+= dabusb.o
+obj-$(CONFIG_USB_PLUSB)		+= plusb.o
 obj-$(CONFIG_USB_OV511)		+= ov511.o
 
 # Extract lists of the multi-part drivers.
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/hid.c linux/drivers/usb/hid.c
--- v2.3.45/linux/drivers/usb/hid.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/usb/hid.c	Tue Feb 15 13:47:36 2000
@@ -1233,6 +1233,19 @@
 	}
 }
 
+#define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE	0x0010
+#define USB_DEVICE_ID_WACOM_INTUOS	0x0021
+
+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 },
+	{ 0, 0 }
+};
+
 static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
 {
 	struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0;
@@ -1241,6 +1254,10 @@
 	unsigned rsize = 0;
 	int n;
 
+	for (n = 0; hid_blacklist[n].idVendor; n++)
+		if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) &&
+			(hid_blacklist[n].idProduct == dev->descriptor.idProduct)) return NULL;
+
 	if (interface->bInterfaceClass != USB_INTERFACE_CLASS_HID)
 		return NULL;
 
@@ -1346,7 +1363,7 @@
 	hid_init_input(hid);
 	input_register_device(&hid->input);
 
-	printk(KERN_INFO "input%d: USB HID v%d.%d %s\n",
+	printk(KERN_INFO "input%d: USB HID v%x.%02x %s\n",
 		hid->input.number, hid->version >> 8, hid->version & 0xff,
 		(hid->application & 0xffff) <= 8 ? hid_name[hid->application & 0xffff] : "device");
 
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/keybdev.c linux/drivers/usb/keybdev.c
--- v2.3.45/linux/drivers/usb/keybdev.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/usb/keybdev.c	Tue Feb 15 11:37:33 2000
@@ -52,9 +52,9 @@
 	  2,  3,  5,  4, 38, 40, 37, 41, 39, 50, 56, 42,  6,  7,  8,  9,
 	 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
 	 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
-	 84, 85, 82, 65, 42,105, 10,103,111,  0,  0,  0,  0,  0,  0,  0,
-	 76,125, 75,  0,124,  0,115, 62,116, 59, 60,119, 61,121,114,117,
-	  0,  0,  0,  0,127, 24,  0,113,  0,  0,  0,  0,  0, 55, 55,  0 };
+	 84, 85, 82, 65, 42,  0, 10,103,111,  0,  0,  0,  0,  0,  0,  0,
+	 76,125, 75,105,124,  0,115, 62,116, 59, 60,119, 61,121,114,117,
+	  0,  0,  0,  0,127, 81,  0,113,  0,  0,  0,  0,  0, 55, 55 };
 
 #endif
 
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/plusb.c linux/drivers/usb/plusb.c
--- v2.3.45/linux/drivers/usb/plusb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/plusb.c	Tue Feb 15 22:39:01 2000
@@ -0,0 +1,632 @@
+/*****************************************************************************/
+
+/*
+ *      plusb.c  --  prolific pl-2302 driver.
+ *
+ *      Copyright (C) 2000  Deti Fliegl (deti@fliegl.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.
+ *
+ *
+ *
+ *  $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+//#define DEBUG
+
+#include "usb.h"
+#include "plusb.h"
+
+/* --------------------------------------------------------------------- */
+
+#define NRPLUSB 4
+
+/*-------------------------------------------------------------------*/
+
+static plusb_t plusb[NRPLUSB];
+
+/* --------------------------------------------------------------------- */
+static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src)
+{
+	unsigned long flags;
+	struct list_head *tmp;
+	int ret = 0;
+
+	spin_lock_irqsave (&s->lock, flags);
+
+	if (list_empty (src)) {
+		// no elements in source buffer
+		ret = -1;
+		goto err;
+	}
+	tmp = src->next;
+	list_del (tmp);
+	list_add_tail (tmp, dst);
+
+  err:	spin_unlock_irqrestore (&s->lock, flags);
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+
+static int plusb_my_bulk(plusb_t *s, int pipe, void *data, int size, int *actual_length)
+{
+	int ret;
+
+	dbg("plusb_my_bulk: len:%d",size);
+
+	ret=usb_bulk_msg(s->usbdev, pipe, data, size, actual_length, 500);
+	if(ret<0) {
+		err("plusb: usb_bulk_msg failed(%d)",ret);
+	}
+	
+	if( ret == -EPIPE ) {
+		warn("CLEAR_FEATURE request to remove STALL condition.");
+		if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
+			err("request failed");
+		}
+
+	dbg("plusb_my_bulk: finished act: %d", *actual_length);		
+	return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_bh(void *context)
+{
+	plusb_t *s=context;
+	struct net_device_stats *stats=&s->net_stats;
+	int ret=0;
+	int actual_length;
+	skb_list_t *skb_list;
+	struct sk_buff *skb;
+
+	dbg("plusb_bh: i:%d",in_interrupt());
+
+	while(!list_empty(&s->tx_skb_list)) {
+
+		if(!(s->status&_PLUSB_TXOK))
+			break; 
+		
+		skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list);
+		if(!skb_list->state) {
+			dbg("plusb_bh: not yet ready");
+			schedule();
+			continue;
+		}
+
+		skb=skb_list->skb;
+		ret=plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE),
+		                  skb->data, skb->len, &actual_length);
+	
+		if(ret || skb->len != actual_length ||!(skb->len%64)) {
+			plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE),
+		              NULL, 0, &actual_length);
+		}
+
+		if(!ret) {
+			stats->tx_packets++;
+			stats->tx_bytes+=skb->len;
+                }
+		else {
+			stats->tx_errors++;
+			stats->tx_aborted_errors++;
+		}
+
+		dbg("plusb_bh: dev_kfree_skb");
+
+		dev_kfree_skb(skb);
+		skb_list->state=0;
+		plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list);	
+	}
+
+	dbg("plusb_bh: finished");
+	s->in_bh=0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	skb_list_t *skb_list;
+	int ret=NET_XMIT_SUCCESS;
+
+	dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt());
+
+        if(!s->connected || list_empty(&s->free_skb_list)) {
+		ret=NET_XMIT_CN;
+		goto lab;
+	}	
+
+	plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list);
+	skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list);
+	skb_list->skb=skb;
+	skb_list->state=1;
+
+lab:
+	if(s->in_bh)
+		return ret;
+
+	dbg("plusb_net_xmit: queue_task");
+
+	s->in_bh=1;
+	queue_task(&s->bh, &tq_scheduler);
+
+	dbg("plusb_net_xmit: finished");
+	return ret;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_bulk_complete(urb_t *purb)
+{
+	plusb_t *s=purb->context;
+
+	dbg("plusb_bulk_complete: status:%d length:%d",purb->status,purb->actual_length);
+	if(!s->connected)
+		return;
+
+	if( !purb->status) {
+		struct sk_buff *skb;
+		unsigned char *dst;
+		int len=purb->transfer_buffer_length;
+		struct net_device_stats *stats=&s->net_stats;
+
+		skb=dev_alloc_skb(len);
+
+		if(!skb) {
+			err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len);
+			stats->rx_dropped++;
+			return;
+		}
+
+		dst=(char *)skb_put(skb, len);
+		memcpy( dst, purb->transfer_buffer, len);
+
+		skb->dev=&s->net_dev;
+		skb->protocol=eth_type_trans(skb, skb->dev);
+		stats->rx_packets++;
+		stats->rx_bytes+=len;
+		netif_rx(skb);
+	}
+	else
+		purb->status=0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_int_complete(urb_t *purb)
+{
+	plusb_t *s=purb->context;
+	s->status=((unsigned char*)purb->transfer_buffer)[0]&255;
+#if 0
+	if((s->status&0x3f)!=0x20) {
+		warn("invalid device status %02X", s->status);
+		return;
+	}
+#endif	
+	if(!s->connected)
+		return;
+
+	if(s->status&_PLUSB_RXD) {
+		int ret;
+		
+		if(s->bulkurb->status) {
+			err("plusb_int_complete: URB still in use");
+			return;
+		}
+		
+		ret=usb_submit_urb(s->bulkurb);
+		if(ret && ret!=-EBUSY) {
+			err("plusb_int_complete: usb_submit_urb failed");
+		}
+	}
+		
+	if(purb->status || s->status!=160)
+		dbg("status: %p %d buf: %02X", purb->dev, purb->status, s->status);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_free_all(plusb_t *s)
+{
+	struct list_head *skb;
+	skb_list_t *skb_list;
+	
+	dbg("plusb_free_all");
+	run_task_queue(&tq_immediate);	
+
+	if(s->inturb) {
+		dbg("unlink inturb");
+		usb_unlink_urb(s->inturb);
+	}
+
+	if(s->inturb && s->inturb->transfer_buffer) {
+		dbg("kfree inturb->transfer_buffer");
+		kfree(s->inturb->transfer_buffer);
+		s->inturb->transfer_buffer=NULL;
+	}
+	
+	if(s->inturb) {
+		dbg("free_urb inturb");
+		usb_free_urb(s->inturb);
+		s->inturb=NULL;
+	}
+
+	if(s->bulkurb) {
+		dbg("unlink bulkurb");
+		usb_unlink_urb(s->bulkurb);
+	}
+	
+	if(s->bulkurb && s->bulkurb->transfer_buffer) {
+		dbg("kfree bulkurb->transfer_buffer");
+		kfree(s->bulkurb->transfer_buffer);
+		s->bulkurb->transfer_buffer=NULL;
+	}
+	if(s->bulkurb) {
+		dbg("free_urb bulkurb");
+		usb_free_urb(s->bulkurb);
+		s->bulkurb=NULL;
+	}
+	
+	while(!list_empty(&s->free_skb_list)) {
+		skb=s->free_skb_list.next;
+		list_del(skb);
+		skb_list = list_entry (skb, skb_list_t, skb_list);
+		kfree(skb_list);
+	}
+
+	while(!list_empty(&s->tx_skb_list)) {
+		skb=s->tx_skb_list.next;
+		list_del(skb);
+		skb_list = list_entry (skb, skb_list_t, skb_list);
+		kfree(skb_list);	
+	}
+	dbg("plusb_free_all: finished");	
+}
+
+/*-------------------------------------------------------------------*/
+
+static int plusb_alloc(plusb_t *s)
+{
+	int i;
+	skb_list_t *skb;
+
+	dbg("plusb_alloc");
+	
+	for(i=0 ; i < _SKB_NUM ; i++) {
+		skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL);
+		if(!skb) {
+			err("kmalloc for skb_list failed");
+			goto reject;
+		}
+		memset(skb, 0, sizeof(skb_list_t));
+		list_add(&skb->skb_list, &s->free_skb_list);
+	}
+
+	dbg("inturb allocation:");
+	s->inturb=usb_alloc_urb(0);
+	if(!s->inturb) {
+		err("alloc_urb failed");
+		goto reject;
+	}
+
+	dbg("bulkurb allocation:");	
+	s->bulkurb=usb_alloc_urb(0);
+	if(!s->bulkurb) {
+		err("alloc_urb failed");
+		goto reject;
+	}
+	
+	dbg("bulkurb/inturb init:");
+	s->inturb->dev=s->usbdev;
+	s->inturb->pipe=usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE);
+	s->inturb->transfer_buffer=kmalloc(64, GFP_KERNEL);
+	if(!s->inturb->transfer_buffer) {
+		err("kmalloc failed");
+		goto reject;
+	}
+	
+	s->inturb->transfer_buffer_length=1;
+	s->inturb->complete=plusb_int_complete;
+	s->inturb->context=s;
+	s->inturb->interval=10;
+
+	dbg("inturb submission:");
+	if(usb_submit_urb(s->inturb)<0) {
+		err("usb_submit_urb failed");
+		goto reject;
+	}
+
+	dbg("bulkurb init:");
+	s->bulkurb->dev=s->usbdev;
+	s->bulkurb->pipe=usb_rcvbulkpipe (s->usbdev, _PLUSB_BULKINPIPE);
+	s->bulkurb->transfer_buffer=kmalloc(_BULK_DATA_LEN, GFP_KERNEL);
+	if(!s->bulkurb->transfer_buffer) {
+		err("kmalloc failed");
+		goto reject;
+	}
+	
+	s->bulkurb->transfer_buffer_length=_BULK_DATA_LEN;
+	s->bulkurb->complete=plusb_bulk_complete;
+	s->bulkurb->context=s;
+	
+	dbg("plusb_alloc: finished");
+	
+	return 0;
+
+  reject:
+  	dbg("plusb_alloc: failed");
+	
+	plusb_free_all(s);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------*/
+
+static int plusb_net_open(struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	
+	dbg("plusb_net_open");
+	
+	if(plusb_alloc(s))
+		return -ENOMEM;
+
+	s->opened=1;
+	MOD_INC_USE_COUNT;
+	
+	dbg("plusb_net_open: success");
+	
+	return 0;
+	
+}
+
+/* --------------------------------------------------------------------- */
+
+static int plusb_net_stop(struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	
+	dbg("plusb_net_stop");	
+	
+	plusb_free_all(s);
+	s->opened=0;
+	MOD_DEC_USE_COUNT;
+	dbg("plusb_net_stop:finished");
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device_stats *plusb_net_get_stats(struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	
+	dbg("net_device_stats");
+	
+	return &s->net_stats;
+}
+
+/* --------------------------------------------------------------------- */
+
+static plusb_t *plusb_find_struct (void)
+{
+	int u;
+
+	for (u = 0; u < NRPLUSB; u++) {
+		plusb_t *s = &plusb[u];
+		if (!s->connected)
+			return s;
+	}
+	return NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_disconnect (struct usb_device *usbdev, void *ptr)
+{
+	plusb_t *s = ptr;
+
+	dbg("plusb_disconnect");
+	s->connected = 0;
+	
+	plusb_free_all(s);
+
+	if(!s->opened && s->net_dev.name) {
+		dbg("unregistering netdev: %s",s->net_dev.name);
+		unregister_netdev(&s->net_dev);
+		kfree(s->net_dev.name);
+		s->net_dev.name=NULL;
+	}
+	
+	dbg("plusb_disconnect: finished");
+	MOD_DEC_USE_COUNT;
+}
+
+/* --------------------------------------------------------------------- */
+
+int plusb_net_init(struct net_device *dev)
+{
+	dbg("plusb_net_init");
+	
+	dev->open=plusb_net_open;
+	dev->stop=plusb_net_stop;
+	dev->hard_start_xmit=plusb_net_xmit;
+	dev->get_stats	= plusb_net_get_stats;
+	ether_setup(dev);
+	dev->tx_queue_len = 0;
+	dev->flags = IFF_POINTOPOINT|IFF_NOARP;
+
+	
+	dbg("plusb_net_init: finished");
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum)
+{
+	plusb_t *s;
+
+	dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
+	  usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum);
+
+	if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct != 0x1)
+		return NULL;
+
+	/* We don't handle multiple configurations */
+	if (usbdev->descriptor.bNumConfigurations != 1)
+		return NULL;
+
+	s = plusb_find_struct ();
+	if (!s)
+		return NULL;
+
+	s->usbdev = usbdev;
+
+	if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) {
+		err("set_configuration failed");
+		return NULL;
+	}
+
+	if (usb_set_interface (s->usbdev, 0, 0) < 0) {
+		err("set_interface failed");
+		return NULL;
+	}
+
+	if(!s->net_dev.name) {
+		s->net_dev.name=kmalloc(16, GFP_KERNEL);
+
+		if(!s->net_dev.name || dev_alloc_name(&s->net_dev,"plusb%d")<0)	{
+			err("alloc name failed\n");
+			return NULL;
+		}
+
+		s->net_dev.init=plusb_net_init;
+		s->net_dev.priv=s;
+		if(!register_netdev(&s->net_dev))
+			info("registered: %s", s->net_dev.name);
+		else {
+			err("register_netdev failed");
+			kfree(s->net_dev.name);
+			s->net_dev.name=NULL;
+		}
+	}
+		
+	s->connected = 1;
+
+	if(s->opened) {
+		dbg("net device already allocated, restarting USB transfers");
+		plusb_alloc(s);
+	}
+
+	info("bound to interface: %d dev: %p", ifnum, usbdev);
+	MOD_INC_USE_COUNT;
+	return s;
+}
+/* --------------------------------------------------------------------- */
+
+static struct usb_driver plusb_driver =
+{
+	name: "plusb",
+	probe: plusb_probe,
+	disconnect: plusb_disconnect,
+};
+
+/* --------------------------------------------------------------------- */
+
+int __init plusb_init (void)
+{
+	unsigned u;
+	dbg("plusb_init");
+	
+	/* initialize struct */
+	for (u = 0; u < NRPLUSB; u++) {
+		plusb_t *s = &plusb[u];
+		memset (s, 0, sizeof (plusb_t));
+		s->bh.routine = (void (*)(void *))plusb_bh;
+		s->bh.data = s;
+		INIT_LIST_HEAD (&s->tx_skb_list);
+		INIT_LIST_HEAD (&s->free_skb_list);
+		spin_lock_init (&s->lock);
+	}
+
+	/* register misc device */
+	usb_register (&plusb_driver);
+
+	dbg("plusb_init: driver registered");
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void __exit plusb_cleanup (void)
+{
+	unsigned u;
+
+	dbg("plusb_cleanup");
+	for (u = 0; u < NRPLUSB; u++) {
+		plusb_t *s = &plusb[u];
+		if(s->net_dev.name) {
+			dbg("unregistering netdev: %s",s->net_dev.name);
+			unregister_netdev(&s->net_dev);
+			kfree(s->net_dev.name);
+			s->net_dev.name=NULL;
+		}
+	}
+	usb_deregister (&plusb_driver);
+	dbg("plusb_cleanup: finished");
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de");
+MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000");
+
+/* --------------------------------------------------------------------- */
+int __init init_module (void)
+{
+	return plusb_init ();
+}
+/* --------------------------------------------------------------------- */
+void __exit cleanup_module (void)
+{
+	plusb_cleanup ();
+}
+
+#endif
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/plusb.h linux/drivers/usb/plusb.h
--- v2.3.45/linux/drivers/usb/plusb.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/plusb.h	Tue Feb 15 11:37:33 2000
@@ -0,0 +1,48 @@
+#define _PLUSB_INTPIPE		0x1
+#define _PLUSB_BULKOUTPIPE	0x2
+#define _PLUSB_BULKINPIPE	0x3
+
+#define _SKB_NUM		1000
+//  7  6  5  4  3  2  1  0
+//  tx rx 1  0
+// 1110 0000 rxdata
+// 1010 0000 idle
+// 0010 0000 tx over
+// 0110      tx over + rxd
+
+#define _PLUSB_RXD		0x40
+#define _PLUSB_TXOK		0x80
+
+#ifdef __KERNEL__
+#define _BULK_DATA_LEN		16384
+
+typedef struct
+{
+	struct list_head skb_list;
+	struct sk_buff *skb;
+	int state;
+} skb_list_t,*pskb_list_t;
+
+typedef struct
+{
+	struct usb_device *usbdev;
+
+	int status;
+	int connected;
+	int in_bh;
+	int opened;
+
+	spinlock_t lock;
+
+	urb_t *inturb;
+	urb_t *bulkurb;	
+
+	struct list_head tx_skb_list;
+	struct list_head free_skb_list;
+	struct tq_struct bh;
+
+	struct net_device net_dev;
+	struct net_device_stats net_stats;
+} plusb_t,*pplusb_t;
+
+#endif
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/scanner.c linux/drivers/usb/scanner.c
--- v2.3.45/linux/drivers/usb/scanner.c	Thu Feb 10 17:11:15 2000
+++ linux/drivers/usb/scanner.c	Wed Feb 16 10:35:08 2000
@@ -130,10 +130,29 @@
  *    - Added Microtek X6 ID's. Thanks to Oliver Neukum
  *      <Oliver.Neukum@lrz.uni-muenchen.de>.
  *
+ * 
+ *  0.4.1  2/15/2000
+ *  
+ *    - Fixed 'count' bug in read_scanner(). Thanks to Henrik
+ *      Johansson <henrikjo@post.utfors.se> for identifying it.  Amazing
+ *      it has worked this long.
+ *    - Fixed '>=' bug in both read/write_scanner methods.
+ *    - Cleaned up both read/write_scanner() methods so that they are 
+ *      a little more readable.
+ *    - Added a lot of Microtek ID's.  Thanks to Adrian Perez Jorge.
+ *    - Adopted the __initcall().
+ *    - Added #include <linux/init.h> to scanner.h for __initcall().
+ *    - Added one liner in irq_scanner() to keep gcc from complaining 
+ *      about an unused variable (data) if debugging was disabled
+ *      in scanner.c.
+ *    - Increased the timeout parameter in read_scanner() to 120 Secs.
+ *
  *
  *  TODO
  *
  *    - Select/poll methods
+ *    - More testing
+ *    - Proper registry/assignment for LM9830 ioctl's
  *
  *
  *  Thanks to:
@@ -143,6 +162,9 @@
  *    - To Linus Torvalds for this great OS.
  *    - The GNU folks.
  *    - The folks that forwarded Vendor:Product ID's to me.
+ *    - Johannes Erdfelt for the loaning of a USB analyzer for tracking an
+ *      issue with HP-4100 and uhci.
+ *    - Adolfo Montero for his assistance.
  *    - And anybody else who chimed in with reports and suggestions.
  *
  *  Performance:
@@ -167,6 +189,7 @@
 
 	struct scn_usb_data *scn = urb->context;
 	unsigned char *data = &scn->button;
+	data += 0;		/* Keep gcc from complaining about unused var */
 
 	if (urb->status) {
 		return;
@@ -253,11 +276,11 @@
 	struct scn_usb_data *scn;
 	struct usb_device *dev;
 	
-	ssize_t bytes_written = 0;
+	ssize_t bytes_written = 0; /* Overall count of bytes written */
 	ssize_t ret = 0;
 
-	int copy_size;
-	int partial;
+	int this_write;		/* Number of bytes to write */
+	int partial;		/* Number of bytes successfully written */
 	int result = 0;
 	
 	char *obuf;
@@ -275,15 +298,15 @@
 			break;
 		}
 
-		copy_size = (count > OBUF_SIZE) ? OBUF_SIZE : count;
+		this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count;
 		
-		if (copy_from_user(scn->obuf, buffer, copy_size)) {
+		if (copy_from_user(scn->obuf, buffer, this_write)) {
 			ret = -EFAULT;
 			break;
 		}
 
-		result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, copy_size, &partial, 60*HZ);
-		dbg("write stats(%d): result:%d copy_size:%d partial:%d", scn->scn_minor, result, copy_size, partial);
+		result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ);
+		dbg("write stats(%d): result:%d this_write:%d partial:%d", scn->scn_minor, result, this_write, partial);
 
 		if (result == USB_ST_TIMEOUT) {	/* NAK -- shouldn't happen */
 			warn("write_scanner: NAK recieved.");
@@ -306,7 +329,7 @@
 			printk("\n");
 		}
 #endif
-		if (partial != copy_size) { /* Unable to write complete amount */
+		if (partial != this_write) { /* Unable to write all contents of obuf */
 			ret = -EIO;
 			break;
 		}
@@ -332,10 +355,11 @@
 	struct scn_usb_data *scn;
 	struct usb_device *dev;
 
-	ssize_t read_count, ret = 0;
+	ssize_t bytes_read = 0;	/* Overall count of bytes_read */
+	ssize_t ret = 0;
 
-	int partial;
-	int this_read;
+	int partial;		/* Number of bytes successfully read */
+	int this_read;		/* Max number of bytes to read */
 	int result;
 
 	char *ibuf;
@@ -346,7 +370,7 @@
 
 	dev = scn->scn_dev;
 
-	read_count = 0;
+	bytes_read = 0;
 
 	while (count) {
 		if (signal_pending(current)) {
@@ -354,9 +378,9 @@
 			break;
 		}
 
-		this_read = (count > IBUF_SIZE) ? IBUF_SIZE : count;
+		this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
 		
-		result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, 60*HZ);
+		result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, 120*HZ);
 		dbg("read stats(%d): result:%d this_read:%d partial:%d", scn->scn_minor, result, this_read, partial);
 
 		if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
@@ -382,24 +406,22 @@
 #endif
 
 		if (partial) { /* Data returned */
-			count = this_read = partial;
-		} else {
-			ret = 0;
-			read_count = 0;
-			break;
-		}
-
-		if (this_read) {
 			if (copy_to_user(buffer, ibuf, this_read)) {
 				ret = -EFAULT;
 				break;
 			}
-			count -= this_read;
-			read_count += this_read;
-			buffer += this_read;
+			count -= partial;
+			bytes_read += partial;
+			buffer += partial;
+			
+		} else {
+			ret = 0;
+			break;
 		}
+		
 	}
-	return ret ? ret : read_count;
+	
+	return ret ? ret : bytes_read;
 }
 
 static void *
@@ -442,7 +464,8 @@
  * that this will allow developers a means to produce applications
  * that will support USB products.
  *
- * Until we detect a device which is pleasing, we silently punt.  */
+ * Until we detect a device which is pleasing, we silently punt.
+ */
 
 	do {
 		if (dev->descriptor.idVendor == 0x03f0) {          /* Hewlett Packard */
@@ -460,6 +483,7 @@
 		
 		if (dev->descriptor.idVendor == 0x06bd) {	   /* Agfa */
 		    	if (dev->descriptor.idProduct == 0x0001 || /* SnapScan 1212U */
+		    	    dev->descriptor.idProduct == 0x2061 || /* Another SnapScan 1212U (?) */
 		    	    dev->descriptor.idProduct == 0x0100) { /* SnapScan Touch */
 				valid_device = 1;
 				break;
@@ -490,7 +514,13 @@
 		}
 
 		if (dev->descriptor.idVendor == 0x05da) {          /* Microtek */
-			if (dev->descriptor.idProduct == 0x0099) { /* X6 */
+			if (dev->descriptor.idProduct == 0x0099 || /* ScanMaker X6 - X6U */
+			    dev->descriptor.idProduct == 0x0094 || /* Phantom 336CX - C3 */
+			    dev->descriptor.idProduct == 0x00a0 || /* Phantom 336CX - C3 #2 */
+			    dev->descriptor.idProduct == 0x009a || /* Phantom C6 */
+			    dev->descriptor.idProduct == 0x00a3 || /* ScanMaker V6USL */
+			    dev->descriptor.idProduct == 0x80a3 || /* ScanMaker V6USL #2 */
+			    dev->descriptor.idProduct == 0x80ac) { /* ScanMaker V6UL - SpicyU */
 				valid_device = 1;
 				break;
 			}
@@ -763,11 +793,18 @@
 
 static struct
 file_operations usb_scanner_fops = {
-	read:		read_scanner,
-	write:		write_scanner,
-	ioctl:		ioctl_scanner,
-	open:		open_scanner,
-	release:	close_scanner,
+	NULL,		/* seek */
+	read_scanner,
+	write_scanner,
+	NULL,		/* readdir */
+	NULL,		/* poll */
+	ioctl_scanner,
+	NULL,		/* mmap */
+	open_scanner,
+	NULL,		/* flush */
+	close_scanner,
+	NULL,         
+	NULL,           /* fasync */
 };
 
 static struct
@@ -780,8 +817,15 @@
        SCN_BASE_MNR
 };
 
-int
-usb_scanner_init(void)
+#ifdef MODULE
+void cleanup_module(void)
+{
+	usb_deregister(&scanner_driver);
+}
+int init_module(void)
+#else
+int usb_scanner_init(void)
+#endif
 {
         if (usb_register(&scanner_driver) < 0)
                 return -1;
@@ -790,18 +834,4 @@
 	return 0;
 }
 
-#ifdef MODULE
-
-int
-init_module(void)
-{
-	return usb_scanner_init();
-}
-
-void
-cleanup_module(void)
-{
-	usb_deregister(&scanner_driver);
-}
-#endif
-
+__initcall(usb_scanner_init);
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/scanner.h linux/drivers/usb/scanner.h
--- v2.3.45/linux/drivers/usb/scanner.h	Thu Feb 10 17:11:15 2000
+++ linux/drivers/usb/scanner.h	Wed Feb 16 10:35:08 2000
@@ -2,6 +2,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <asm/uaccess.h>
+#include <linux/init.h>
 #include <linux/malloc.h>
 #include <linux/delay.h>
 #include <linux/ioctl.h>
@@ -10,6 +11,7 @@
 
 #include "usb.h"
 
+/* WARNING: These DATA_DUMP's can produce a lot of data. Caveat Emptor. */
 // #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
 // #define WR_DATA_DUMP /* DEBUG does not have to be defined. */
 
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- v2.3.45/linux/drivers/usb/usb-core.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/usb/usb-core.c	Tue Feb 15 11:37:33 2000
@@ -37,6 +37,7 @@
 int usb_scanner_init(void);
 int usb_stor_init(void);
 int dabusb_init(void);
+int plusb_init(void);
 int usb_mouse_init(void);
 int usb_kbd_init(void);
 int graphire_init(void);
@@ -98,6 +99,9 @@
 #endif
 #ifdef CONFIG_USB_DABUSB
 	dabusb_init();
+#endif
+#ifdef CONFIG_USB_PLUSB
+	plusb_init();
 #endif
 #ifdef CONFIG_USB_MOUSE
 	usb_mouse_init();
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb-serial.c linux/drivers/usb/usb-serial.c
--- v2.3.45/linux/drivers/usb/usb-serial.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/usb/usb-serial.c	Tue Feb 15 11:37:34 2000
@@ -14,6 +14,24 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (02/14/2000) gkh
+ *	Removed the Belkin and Peracom functionality from the driver due to
+ *	the lack of support from the vendor, and me not wanting people to 
+ *	accidenatly buy the device, expecting it to work with Linux.
+ *	Added read_bulk_callback and write_bulk_callback to the type structure
+ *	for the needs of the FTDI and WhiteHEAT driver.
+ *	Changed all reverences to FTDI to FTDI_SIO at the request of Bill
+ *	Ryder.
+ *	Changed the output urb size back to the max endpoint size to make
+ *	the ftdi_sio driver have it easier, and due to the fact that it didn't
+ *	really increase the speed any.
+ *
+ * (02/11/2000) gkh
+ *	Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
+ *	patch from Miles Lott (milos@insync.net).
+ *	Fixed bug with not restoring the minor range that a device grabs, if
+ *	the startup function fails (thanks Miles for finding this).
+ *
  * (02/05/2000) gkh
  *	Added initial framework for the Keyspan PDA serial converter so that
  *	Brian Warner has a place to put his code.
@@ -228,6 +246,23 @@
 }
 
 
+static void return_serial (struct usb_serial *serial)
+{
+	int i;
+
+	dbg("return_serial");
+
+	if (serial == NULL)
+		return;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		serial_table[serial->minor + i] = NULL;
+	}
+
+	return;
+}
+
+
 #ifdef USES_EZUSB_FUNCTIONS
 /* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
 #define CPUCS_REG    0x7F92
@@ -264,67 +299,6 @@
 #endif	/* USES_EZUSB_FUNCTIONS */
 
 
-static void serial_read_bulk (struct urb *urb)
-{
-	struct usb_serial *serial = (struct usb_serial *)urb->context;
-       	struct tty_struct *tty = serial->tty; 
-       	unsigned char *data = urb->transfer_buffer;
-	int i;
-
-	dbg("serial_read_irq");
-
-	if (urb->status) {
-		dbg("nonzero read bulk status received: %d", urb->status);
-		return;
-	}
-
-#ifdef DEBUG
-	if (urb->actual_length) {
-		printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
-		for (i = 0; i < urb->actual_length; ++i) {
-			printk ("0x%.2x ", data[i]);
-		}
-		printk ("\n");
-	}
-#endif
-	
-	if (urb->actual_length) {
-		for (i = 0; i < urb->actual_length ; ++i) {
-			 tty_insert_flip_char(tty, data[i], 0);
-	  	}
-	  	tty_flip_buffer_push(tty);
-	}
-
-	/* Continue trying to always read  */
-	if (usb_submit_urb(urb))
-		dbg("failed resubmitting read urb");
-
-	return;
-}
-
-
-static void serial_write_bulk (struct urb *urb)
-{
-	struct usb_serial *serial = (struct usb_serial *) urb->context; 
-       	struct tty_struct *tty = serial->tty; 
-
-	dbg("serial_write_irq");
-
-	if (urb->status) {
-		dbg("nonzero write bulk status received: %d", urb->status);
-		return;
-	}
-
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
-		(tty->ldisc.write_wakeup)(tty);
-
-	wake_up_interruptible(&tty->write_wait);
-	
-	return;
-}
-
-
-
 /*****************************************************************************
  * Driver tty interface functions
  *****************************************************************************/
@@ -547,53 +521,6 @@
 }
 
 
-#if defined(CONFIG_USB_SERIAL_BELKIN) || defined(CONFIG_USB_SERIAL_PERACOM)
-/*****************************************************************************
- * eTek specific driver functions
- *****************************************************************************/
-static int etek_serial_open (struct tty_struct *tty, struct file *filp)
-{
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("etek_serial_open port %d", port);
-
-	if (serial->active[port]) {
-		dbg ("device already open");
-		return -EINVAL;
-	}
-	serial->active[port] = 1;
- 
-	/*Start reading from the device*/
-	if (usb_submit_urb(&serial->read_urb[port]))
-		dbg("usb_submit_urb(read bulk) failed");
-
-	/* Need to do device specific setup here (control lines, baud rate, etc.) */
-	/* FIXME!!! */
-
-	return (0);
-}
-
-
-static void etek_serial_close(struct tty_struct *tty, struct file * filp)
-{
-	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-	int port = MINOR(tty->device) - serial->minor;
-
-	dbg("etek_serial_close port %d", port);
-	
-	/* Need to change the control lines here */
-	/* FIXME */
-	
-	/* shutdown our bulk reads and writes */
-	usb_unlink_urb (&serial->write_urb[port]);
-	usb_unlink_urb (&serial->read_urb[port]);
-	serial->active[port] = 0;
-}
-#endif	/* defined(CONFIG_USB_SERIAL_BELKIN) || defined(CONFIG_USB_SERIAL_PERACOM) */
-
-
-
 #ifdef CONFIG_USB_SERIAL_WHITEHEAT
 /*****************************************************************************
  * Connect Tech's White Heat specific driver functions
@@ -851,6 +778,9 @@
 				case VISOR_FUNCTION_HOTSYNC:
 					string = "HotSync";
 					break;
+				case VISOR_FUNCTION_CONSOLE:
+					string = "Console";
+					break;
 				case VISOR_FUNCTION_REMOTE_FILE_SYS:
 					string = "Remote File System";
 					break;
@@ -879,11 +809,11 @@
 #endif	/* CONFIG_USB_SERIAL_VISOR*/
 
 
-#ifdef CONFIG_USB_SERIAL_FTDI
+#ifdef CONFIG_USB_SERIAL_FTDI_SIO
 /******************************************************************************
- * FTDI Serial Converter specific driver functions
+ * FTDI SIO Serial Converter specific driver functions
  ******************************************************************************/
-static int  ftdi_serial_open (struct tty_struct *tty, struct file *filp)
+static int  ftdi_sio_serial_open (struct tty_struct *tty, struct file *filp)
 {
 	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
 	int port = MINOR(tty->device) - serial->minor;
@@ -907,7 +837,7 @@
 }
 
 
-static void ftdi_serial_close (struct tty_struct *tty, struct file *filp)
+static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp)
 {
 	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
 	int port = MINOR(tty->device) - serial->minor;
@@ -924,7 +854,7 @@
 }
 
 
-#endif
+#endif	/* CONFIG_USB_SERIAL_FTDI_SIO */
 
 
 #ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
@@ -1111,6 +1041,67 @@
 }
 
 
+static void generic_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial *serial = (struct usb_serial *)urb->context;
+       	struct tty_struct *tty = serial->tty; 
+       	unsigned char *data = urb->transfer_buffer;
+	int i;
+
+	dbg("generic_read_bulk_callback");
+
+	if (urb->status) {
+		dbg("nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+#ifdef DEBUG
+	if (urb->actual_length) {
+		printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
+		for (i = 0; i < urb->actual_length; ++i) {
+			printk ("0x%.2x ", data[i]);
+		}
+		printk ("\n");
+	}
+#endif
+	
+	if (urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			 tty_insert_flip_char(tty, data[i], 0);
+	  	}
+	  	tty_flip_buffer_push(tty);
+	}
+
+	/* Continue trying to always read  */
+	if (usb_submit_urb(urb))
+		dbg("failed resubmitting read urb");
+
+	return;
+}
+
+
+static void generic_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial *serial = (struct usb_serial *) urb->context; 
+       	struct tty_struct *tty = serial->tty; 
+
+	dbg("generic_write_bulk_callback");
+
+	if (urb->status) {
+		dbg("nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+
+	wake_up_interruptible(&tty->write_wait);
+	
+	return;
+}
+
+
+
 static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
 {
 	struct usb_serial *serial = NULL;
@@ -1211,8 +1202,10 @@
 
 				/* if this device type has a startup function, call it */
 				if (type->startup) {
-					if (type->startup (serial))
+					if (type->startup (serial)) {
+						return_serial (serial);
 						return NULL;
+					}
 				}
 
 				/* set up the endpoint information */
@@ -1223,19 +1216,29 @@
 						err("Couldn't allocate bulk_in_buffer");
 						goto probe_error;
 					}
-					FILL_BULK_URB(&serial->read_urb[i], dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
-							serial->bulk_in_buffer[i], buffer_size, serial_read_bulk, serial);
+					if (serial->type->read_bulk_callback) {
+						FILL_BULK_URB(&serial->read_urb[i], dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+								serial->bulk_in_buffer[i], buffer_size, serial->type->read_bulk_callback, serial);
+					} else {
+						FILL_BULK_URB(&serial->read_urb[i], dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+								serial->bulk_in_buffer[i], buffer_size, generic_read_bulk_callback, serial);
+					}
 				}
 
 				for (i = 0; i < num_bulk_out; ++i) {
-					serial->bulk_out_size[i] = bulk_out_endpoint[i]->wMaxPacketSize * 2;
+					serial->bulk_out_size[i] = bulk_out_endpoint[i]->wMaxPacketSize;
 					serial->bulk_out_buffer[i] = kmalloc (serial->bulk_out_size[i], GFP_KERNEL);
 					if (!serial->bulk_out_buffer[i]) {
 						err("Couldn't allocate bulk_out_buffer");
 						goto probe_error;
 					}
-					FILL_BULK_URB(&serial->write_urb[i], dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
-							serial->bulk_out_buffer[i], serial->bulk_out_size[i], serial_write_bulk, serial);
+					if (serial->type->write_bulk_callback) {
+						FILL_BULK_URB(&serial->write_urb[i], dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+								serial->bulk_out_buffer[i], serial->bulk_out_size[i], serial->type->write_bulk_callback, serial);
+					} else {
+						FILL_BULK_URB(&serial->write_urb[i], dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+								serial->bulk_out_buffer[i], serial->bulk_out_size[i], generic_write_bulk_callback, serial);
+					}
 				}
 
 #if 0 /* use this code when WhiteHEAT is up and running */
@@ -1295,7 +1298,6 @@
 			usb_unlink_urb (&serial->write_urb[i]);
 			usb_unlink_urb (&serial->read_urb[i]);
 			serial->active[i] = 0;
-			serial_table[serial->minor + i] = NULL;
 		}
 
 		/* free up any memory that we allocated */
@@ -1313,6 +1315,10 @@
 			info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->minor + i);
 		}
 
+		/* return the minor range that this device had */
+		return_serial (serial);
+
+		/* free up any memory that we allocated */
 		kfree (serial);
 
 	} else {
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb-serial.h linux/drivers/usb/usb-serial.h
--- v2.3.45/linux/drivers/usb/usb-serial.h	Thu Feb 10 17:11:15 2000
+++ linux/drivers/usb/usb-serial.h	Tue Feb 15 11:37:34 2000
@@ -35,17 +35,13 @@
 
 
 /* USB Serial devices vendor ids and device ids that this driver supports */
-#define BELKIN_VENDOR_ID		0x056c
-#define BELKIN_SERIAL_CONVERTER_ID	0x8007
-#define PERACOM_VENDOR_ID		0x0565
-#define PERACOM_SERIAL_CONVERTER_ID	0x0001
 #define CONNECT_TECH_VENDOR_ID		0x0710
 #define CONNECT_TECH_FAKE_WHITE_HEAT_ID	0x0001
 #define CONNECT_TECH_WHITE_HEAT_ID	0x8001
 #define HANDSPRING_VENDOR_ID		0x082d
 #define HANDSPRING_VISOR_ID		0x0100
 #define FTDI_VENDOR_ID			0x0403
-#define FTDI_SERIAL_CONVERTER_ID	0x8372
+#define FTDI_SIO_SERIAL_CONVERTER_ID	0x8372
 #define KEYSPAN_VENDOR_ID		0x06cd
 #define KEYSPAN_PDA_FAKE_ID		0x0103
 #define KEYSPAN_PDA_ID			0x0103
@@ -125,6 +121,8 @@
 	int  (*chars_in_buffer)(struct tty_struct *tty);
 	void (*throttle)(struct tty_struct * tty);
 	void (*unthrottle)(struct tty_struct * tty);
+	void (*read_bulk_callback)(struct urb *urb);
+	void (*write_bulk_callback)(struct urb *urb);
 };
 
 
@@ -135,6 +133,9 @@
 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_chars_in_buffer	(struct tty_struct *tty);
+static void generic_read_bulk_callback	(struct urb *urb);
+static void generic_write_bulk_callback	(struct urb *urb);
+
 
 #ifdef CONFIG_USB_SERIAL_GENERIC
 /* All of the device info needed for the Generic Serial Converter */
@@ -154,59 +155,8 @@
 	write:			generic_serial_write,
 	write_room:		generic_write_room,
 	chars_in_buffer:	generic_chars_in_buffer,
-};
-#endif
-
-#if defined(CONFIG_USB_SERIAL_BELKIN) || defined(CONFIG_USB_SERIAL_PERACOM)
-/* function prototypes for the eTek type converters (this includes Belkin and Peracom) */
-static int  etek_serial_open		(struct tty_struct *tty, struct file *filp);
-static void etek_serial_close		(struct tty_struct *tty, struct file *filp);
-#endif
-
-#ifdef CONFIG_USB_SERIAL_BELKIN
-/* All of the device info needed for the Belkin Serial Converter */
-static __u16	belkin_vendor_id	= BELKIN_VENDOR_ID;
-static __u16	belkin_product_id	= BELKIN_SERIAL_CONVERTER_ID;
-static struct usb_serial_device_type belkin_device = {
-	name:			"Belkin",
-	idVendor:		&belkin_vendor_id,	/* the Belkin vendor id */
-	idProduct:		&belkin_product_id,	/* the Belkin serial converter product id */
-	needs_interrupt_in:	MUST_HAVE,		/* this device must have an interrupt in endpoint */
-	needs_bulk_in:		MUST_HAVE,		/* this device must have a bulk in endpoint */
-	needs_bulk_out:		MUST_HAVE,		/* this device must have a bulk out endpoint */
-	num_interrupt_in:	1,
-	num_bulk_in:		1,
-	num_bulk_out:		1,
-	num_ports:		1,
-	open:			etek_serial_open,
-	close:			etek_serial_close,
-	write:			generic_serial_write,
-	write_room:		generic_write_room,
-	chars_in_buffer:	generic_chars_in_buffer,
-};
-#endif
-
-
-#ifdef CONFIG_USB_SERIAL_PERACOM
-/* All of the device info needed for the Peracom Serial Converter */
-static __u16	peracom_vendor_id	= PERACOM_VENDOR_ID;
-static __u16	peracom_product_id	= PERACOM_SERIAL_CONVERTER_ID;
-static struct usb_serial_device_type peracom_device = {
-	name:			"Peracom",
-	idVendor:		&peracom_vendor_id,	/* the Peracom vendor id */
-	idProduct:		&peracom_product_id,	/* the Peracom serial converter product id */
-	needs_interrupt_in:	MUST_HAVE,		/* this device must have an interrupt in endpoint */
-	needs_bulk_in:		MUST_HAVE,		/* this device must have a bulk in endpoint */
-	needs_bulk_out:		MUST_HAVE,		/* this device must have a bulk out endpoint */
-	num_ports:		1,
-	num_interrupt_in:	1,
-	num_bulk_in:		1,
-	num_bulk_out:		1,
-	open:			etek_serial_open,
-	close:			etek_serial_close,
-	write:			generic_serial_write,
-	write_room:		generic_write_room,
-	chars_in_buffer:	generic_chars_in_buffer,
+	read_bulk_callback:	generic_read_bulk_callback,
+	write_bulk_callback:	generic_write_bulk_callback
 };
 #endif
 
@@ -338,23 +288,25 @@
 	chars_in_buffer:	generic_chars_in_buffer,
 	throttle:		visor_throttle,
 	unthrottle:		visor_unthrottle,
-	startup:		visor_startup
+	startup:		visor_startup,
+	read_bulk_callback:	generic_read_bulk_callback,
+	write_bulk_callback:	generic_write_bulk_callback
 };
 #endif
 
 
-#ifdef CONFIG_USB_SERIAL_FTDI
+#ifdef CONFIG_USB_SERIAL_FTDI_SIO
 /* function prototypes for a FTDI serial converter */
-static int  ftdi_serial_open	(struct tty_struct *tty, struct file *filp);
-static void ftdi_serial_close	(struct tty_struct *tty, struct file *filp);
+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);
 
 /* All of the device info needed for the Handspring Visor */
-static __u16	ftdi_vendor_id	= FTDI_VENDOR_ID;
-static __u16	ftdi_product_id	= FTDI_SERIAL_CONVERTER_ID;
-static struct usb_serial_device_type ftdi_device = {
-	name:			"FTDI",
+static __u16	ftdi_vendor_id		= FTDI_VENDOR_ID;
+static __u16	ftdi_sio_product_id	= FTDI_SIO_SERIAL_CONVERTER_ID;
+static struct usb_serial_device_type ftdi_sio_device = {
+	name:			"FTDI SIO",
 	idVendor:		&ftdi_vendor_id,	/* the FTDI vendor ID */
-	idProduct:		&ftdi_product_id,	/* the FTDI product id */
+	idProduct:		&ftdi_sio_product_id,	/* the FTDI SIO product id */
 	needs_interrupt_in:	MUST_HAVE_NOT,		/* this device must not have an interrupt in endpoint */
 	needs_bulk_in:		MUST_HAVE,		/* this device must have a bulk in endpoint */
 	needs_bulk_out:		MUST_HAVE,		/* this device must have a bulk out endpoint */
@@ -362,11 +314,13 @@
 	num_bulk_in:		1,
 	num_bulk_out:		1,
 	num_ports:		1,
-	open:			ftdi_serial_open,
-	close:			ftdi_serial_close,
+	open:			ftdi_sio_serial_open,
+	close:			ftdi_sio_serial_close,
 	write:			generic_serial_write,
 	write_room:		generic_write_room,
-	chars_in_buffer:	generic_chars_in_buffer
+	chars_in_buffer:	generic_chars_in_buffer,
+	read_bulk_callback:	generic_read_bulk_callback,
+	write_bulk_callback:	generic_write_bulk_callback
 };
 #endif
 
@@ -408,7 +362,9 @@
 	close:			keyspan_pda_serial_close,
 	write:			generic_serial_write,
 	write_room:		generic_write_room,
-	chars_in_buffer:	generic_chars_in_buffer
+	chars_in_buffer:	generic_chars_in_buffer,
+	read_bulk_callback:	generic_read_bulk_callback,
+	write_bulk_callback:	generic_write_bulk_callback
 };
 #endif
 
@@ -423,17 +379,11 @@
 	&whiteheat_fake_device,
 	&whiteheat_device,
 #endif
-#ifdef CONFIG_USB_SERIAL_BELKIN
-	&belkin_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_PERACOM
-	&peracom_device,
-#endif
 #ifdef CONFIG_USB_SERIAL_VISOR
 	&handspring_device,
 #endif
-#ifdef CONFIG_USB_SERIAL_FTDI
-	&ftdi_device,
+#ifdef CONFIG_USB_SERIAL_FTDI_SIO
+	&ftdi_sio_device,
 #endif
 #ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
 	&keyspan_pda_fake_device,
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- v2.3.45/linux/drivers/usb/usb-uhci.c	Thu Feb 10 17:11:15 2000
+++ linux/drivers/usb/usb-uhci.c	Tue Feb 15 11:53:24 2000
@@ -12,9 +12,10 @@
  * (C) Copyright 1999 Johannes Erdfelt
  * (C) Copyright 1999 Randy Dunlap
  *
- * $Id: usb-uhci.c,v 1.185 2000/02/05 21:29:19 acher Exp $
+ * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
@@ -54,7 +55,6 @@
 #define dbg(format, arg...) do {} while (0)
 
 #include <linux/pm.h>
-static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data);
 
 #ifdef DEBUG_SYMBOLS
 	#define _static
@@ -70,9 +70,18 @@
 	static kmem_cache_t *urb_priv_kmem;
 #endif
 
-#define USE_CTRL_DEPTH_FIRST 1  // 0: Breadth first, 1: Depth first (standard)
+#define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+#define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
 
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+#define USE_RECLAMATION_LOOP
+#else
 //#define USE_RECLAMATION_LOOP
+#endif
+
+// stop bandwidth reclamation after (roughly) 50ms (depends also on
+// hub polling interval)
+#define IDLE_TIMEOUT  (HZ/20)
 
 _static int rh_submit_urb (urb_t *urb);
 _static int rh_unlink_urb (urb_t *urb);
@@ -101,22 +110,81 @@
 	}
 }
 /*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, struct list_head *p)
+#ifdef USE_RECLAMATION_LOOP
+_static void enable_desc_loop(uhci_t *s, urb_t *urb)
+{
+	int flags;
+	
+	dbg("enable_desc_loop: enter");
+	
+	spin_lock_irqsave (&s->qh_lock, flags);
+	s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+	s->loop_usage++;
+	((urb_priv_t*)urb->hcpriv)->use_loop=1;
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+	
+	dbg("enable_desc_loop: finished");
+}
+/*-------------------------------------------------------------------*/
+_static void disable_desc_loop(uhci_t *s, urb_t *urb)
+{
+	int flags;
+	
+	dbg("disable_desc_loop: enter\n");
+	
+	spin_lock_irqsave (&s->qh_lock, flags);
+
+	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
+		s->loop_usage--;
+
+		if (!s->loop_usage)
+			s->chain_end->hw.qh.head=UHCI_PTR_TERM;
+
+		((urb_priv_t*)urb->hcpriv)->use_loop=0;
+	}
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+	
+	dbg("disable_desc_loop: finished");
+
+}
+#endif
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
 {
 	unsigned long flags=0;
+	struct list_head *p=&urb->urb_list;
+	
 
 	spin_lock_irqsave (&s->urb_list_lock, flags);
 
+#ifdef USE_RECLAMATION_LOOP
+	{
+		int type;
+		type=usb_pipetype (urb->pipe);
+
+		if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+			enable_desc_loop(s, urb);
+	}
+#endif
+	((urb_priv_t*)urb->hcpriv)->started=jiffies;
 	list_add_tail (p, &s->urb_list);
 	
 	spin_unlock_irqrestore (&s->urb_list_lock, flags);
 }
 
 /*-------------------------------------------------------------------*/
-_static void dequeue_urb (uhci_t *s, struct list_head *p)
+_static void dequeue_urb (uhci_t *s, urb_t *urb)
 {
-	list_del (p);
+#ifdef USE_RECLAMATION_LOOP
+	int type;
+
+	type=usb_pipetype (urb->pipe);
+
+	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+		disable_desc_loop(s, urb);
+#endif
 
+	list_del (&urb->urb_list);
 }
 /*-------------------------------------------------------------------*/
 _static int alloc_td (uhci_desc_t ** new, int flags)
@@ -219,6 +287,7 @@
 	
 	return 0;
 }
+
 /*-------------------------------------------------------------------*/
 _static int delete_desc (uhci_desc_t *element)
 {
@@ -451,11 +520,6 @@
 	insert_qh (s, s->bulk_chain, qh, 0);
 	s->control_chain = qh;
 
-#ifdef USE_RECLAMATION_LOOP
-	s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
-	info("Using loop for bandwidth reclamation.");
-#endif
-
 	for (n = 0; n < 8; n++)
 		s->int_chain[n] = 0;
 
@@ -625,7 +689,7 @@
 	list_add (&qh->desc_list, &urb_priv->desc_list);
 
 	urb->status = -EINPROGRESS;
-	queue_urb (s, &urb->urb_list);	// queue before inserting in desc chain
+	queue_urb (s, urb);	// queue before inserting in desc chain
 
 	qh->hw.qh.element&=~UHCI_PTR_TERM;
 
@@ -635,8 +699,8 @@
 		insert_qh (s, s->control_chain, qh, 1);	// insert after control chain
 	else
 		insert_qh (s, s->bulk_chain, qh, 0);	// insert before bulk chain
-	
 	//uhci_show_queue(qh);
+
 	dbg("uhci_submit_control end");
 	return 0;
 }
@@ -651,11 +715,12 @@
 	unsigned int pipe = urb->pipe;
 	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
 	int info, len;
+	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
 
-	/* shouldn't the clear_halt be done in the USB core or in the client driver? - Thomas */
-	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) &&
-	    usb_clear_halt (urb->dev, usb_pipeendpoint (pipe) | (pipe & USB_DIR_IN)))
+
+	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
 		return -EPIPE;
+
 	if (urb->transfer_buffer_length < 0) {
 		err("Negative transfer length in submit_bulk");
 		return -EINVAL;
@@ -685,7 +750,7 @@
 	do {					// TBD: Really allow zero-length packets?
 		int pktsze = len;
 
-		alloc_td (&td, UHCI_PTR_DEPTH);
+		alloc_td (&td, UHCI_PTR_DEPTH * depth_first);
 
 		if (!td) {
 			delete_qh (s, qh);
@@ -708,7 +773,7 @@
 			td->hw.td.status |= TD_CTRL_IOC;	// last one generates INT
 		//dbg("insert td %p, len %i",td,pktsze);
 
-		insert_td (s, qh, td, UHCI_PTR_DEPTH);
+		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
 		
 		/* Alternate Data0/1 (start with Data0) */
 		usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
@@ -717,7 +782,7 @@
 	list_add (&qh->desc_list, &urb_priv->desc_list);
 
 	urb->status = -EINPROGRESS;
-	queue_urb (s, &urb->urb_list);
+	queue_urb (s, urb);
 	
 	qh->hw.qh.element&=~UHCI_PTR_TERM;
 
@@ -727,6 +792,7 @@
 	dbg("uhci_submit_bulk_urb: exit");
 	return 0;
 }
+
 /*-------------------------------------------------------------------*/
 // unlinks an urb by dequeuing its qh, waits some frames and forgets it
 // Problem: unlinking in interrupt requires waiting for one frame (udelay)
@@ -758,7 +824,7 @@
 
 	if (urb->status == -EINPROGRESS) {
 		// URB probably still in work
-		dequeue_urb (s, &urb->urb_list);
+		dequeue_urb (s, urb);
 		s->unlink_urb_done=1;
 		spin_unlock_irqrestore (&s->urb_list_lock, flags);		
 		
@@ -773,7 +839,7 @@
 				unlink_td (s, td, 1);
 			}
 			// wait at least 1 Frame
-			uhci_wait_ms(1);
+			uhci_wait_ms(1);	
 			while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
 				td = list_entry (p, uhci_desc_t, desc_list);
 				list_del (p);
@@ -991,7 +1057,7 @@
 	list_add_tail (&td->desc_list, &urb_priv->desc_list);
 
 	urb->status = -EINPROGRESS;
-	queue_urb (s, &urb->urb_list);
+	queue_urb (s, urb);
 
 	insert_td_horizontal (s, s->int_chain[nint], td);	// store in INT-TDs
 
@@ -1083,7 +1149,7 @@
 	
 		if (n == last) {
 			urb->status = -EINPROGRESS;
-			queue_urb (s, &urb->urb_list);
+			queue_urb (s, urb);
 		}
 		insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds
 		//uhci_show_td(td);
@@ -1194,14 +1260,44 @@
 #endif
 		return ret;
 	}
-/*
-	urb->status = -EINPROGRESS;
-	queue_urb (s, &urb->urb_list);
 
-	dbg("submit_urb: exit");
-*/
 	return 0;
 }
+#ifdef USE_RECLAMATION_LOOP
+// Removes bandwidth reclamation if URB idles too long
+void check_idling_urbs(uhci_t *s)
+{
+	struct list_head *p,*p2;
+	urb_t *urb;
+	int type;	
+
+	//dbg("check_idling_urbs: enter i:%d",in_interrupt());
+	
+	spin_lock (&s->urb_list_lock);
+	p = s->urb_list.prev;	
+
+	while (p != &s->urb_list) {
+		p2 = p;
+		p = p->prev;
+		urb=list_entry (p2, urb_t, urb_list);
+		type=usb_pipetype (urb->pipe);
+
+#if 0
+		err("URB timers: %li now: %li %i\n",
+			((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
+			type);
+#endif			 
+		if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
+		    (((urb_priv_t*)urb->hcpriv)->use_loop) &&
+		    ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
+			disable_desc_loop(s,urb);
+
+	}
+	spin_unlock (&s->urb_list_lock);
+	
+	//dbg("check_idling_urbs: finished");
+}
+#endif
 /*-------------------------------------------------------------------
  Virtual Root Hub
  -------------------------------------------------------------------*/
@@ -1313,10 +1409,13 @@
 _static void rh_int_timer_do (unsigned long ptr)
 {
 	int len;
-
 	urb_t *urb = (urb_t*) ptr;
 	uhci_t *uhci = urb->dev->bus->hcpriv;
 
+#ifdef USE_RECLAMATION_LOOP
+	check_idling_urbs(uhci);
+#endif
+
 	if (uhci->rh.send) {
 		len = rh_send_irq (urb);
 		if (len > 0) {
@@ -1558,9 +1657,11 @@
 {
 	uhci_t *uhci = urb->dev->bus->hcpriv;
 
-	dbg("Root-Hub unlink IRQ");
-	uhci->rh.send = 0;
-	del_timer (&uhci->rh.rh_int_timer);
+	if (uhci->rh.urb==urb) {
+		dbg("Root-Hub unlink IRQ");
+		uhci->rh.send = 0;
+		del_timer (&uhci->rh.rh_int_timer);
+	}
 	return 0;
 }
 /*-------------------------------------------------------------------*/
@@ -1741,7 +1842,7 @@
 		// got less data than requested
 		if ( (actual_length < maxlength)) {
 			if (urb->transfer_flags & USB_DISABLE_SPD) {
-				ret = -EREMOTEIO;	// treat as real error
+				status = -EREMOTEIO;	// treat as real error
 				dbg("process_transfer: SPD!!");
 				break;	// exit after this TD because SP was detected
 			}
@@ -1777,6 +1878,10 @@
 
 	urb->status = status;
 
+#ifdef USE_RECLAMATION_LOOP	
+	disable_desc_loop(s,urb);
+#endif	
+
 	dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
 		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
 	//dbg("process_transfer: exit");
@@ -1971,7 +2076,7 @@
 		int proceed = 0;
 
 		dbg("dequeued urb: %p", urb);
-		dequeue_urb (s, p);
+		dequeue_urb (s, urb);
 
 #ifdef DEBUG_SLAB
 		kmem_cache_free(urb_priv_kmem, urb->hcpriv);
@@ -2181,6 +2286,23 @@
 	return 0;
 }
 
+_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	uhci_t *s = (uhci_t*) dev->data;
+	dbg("handle_apm_event(%d)", rqst);
+	if (s) {
+		switch (rqst) {
+		case PM_SUSPEND:
+			reset_hc (s);
+			break;
+		case PM_RESUME:
+			start_hc (s);
+			break;
+		}
+	}
+	return 0;
+}
+
 _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
 {
 	uhci_t *s;
@@ -2236,7 +2358,7 @@
 	}
 
 	s->rh.numports = s->maxports;
-
+	s->loop_usage=0;
 	if (init_skel (s)) {
 		usb_free_bus (bus);
 		kfree(s);
@@ -2306,23 +2428,6 @@
 	return -1;
 }
 
-_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
-{
-	uhci_t *s = (uhci_t*) dev->data;
-	dbg("handle_apm_event(%d)", rqst);
-	if (s) {
-		switch (rqst) {
-		case PM_SUSPEND:
-			reset_hc (s);
-			break;
-		case PM_RESUME:
-			start_hc (s);
-			break;
-		}
-	}
-	return 0;
-}
-
 int __init uhci_init (void)
 {
 	int retval = -ENODEV;
@@ -2385,11 +2490,9 @@
 		uhci_cleanup_dev(s);
 	}
 #ifdef DEBUG_SLAB
-	
-	
 	if(kmem_cache_destroy(uhci_desc_kmem))
 		err("uhci_desc_kmem remained");
-		
+
 	if(kmem_cache_destroy(urb_priv_kmem))
 		err("urb_priv_kmem remained");
 #endif
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb-uhci.h linux/drivers/usb/usb-uhci.h
--- v2.3.45/linux/drivers/usb/usb-uhci.h	Thu Feb 10 17:11:15 2000
+++ linux/drivers/usb/usb-uhci.h	Tue Feb 15 11:53:24 2000
@@ -2,7 +2,7 @@
 #define __LINUX_UHCI_H
 
 /*
-   $Id: usb-uhci.h,v 1.39 2000/02/05 20:25:27 acher Exp $
+   $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $
  */
 #define MODNAME "usb-uhci"
 #define VERSTR "version v1.184 time " __TIME__ " " __DATE__
@@ -155,6 +155,8 @@
 typedef struct {
 	struct list_head desc_list;	// list pointer to all corresponding TDs/QHs associated with this request
 	int short_control_packet;
+	unsigned long started;
+	int use_loop;
 } urb_priv_t, *purb_priv_t;
 
 struct virt_root_hub {
@@ -197,6 +199,7 @@
 	spinlock_t qh_lock;
 	spinlock_t td_lock;
 	struct virt_root_hub rh;	//private data of the virtual root hub
+	int loop_usage;            // URBs using bandwidth reclamation
 } uhci_t, *puhci_t;
 
 
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
--- v2.3.45/linux/drivers/usb/usb.c	Sat Feb 12 11:22:11 2000
+++ linux/drivers/usb/usb.c	Wed Feb 16 10:25:40 2000
@@ -588,7 +588,7 @@
 }
 
 /*-------------------------------------------------------------------*/
-// returns status (negative) are length (positive)
+// returns status (negative) or length (positive)
 int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, 
 			    devrequest *cmd,  void *data, int len, int timeout)
 {
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb_storage.c linux/drivers/usb/usb_storage.c
--- v2.3.45/linux/drivers/usb/usb_storage.c	Fri Jan 28 15:09:08 2000
+++ linux/drivers/usb/usb_storage.c	Wed Feb 16 10:25:40 2000
@@ -39,6 +39,14 @@
 #include "usb.h"
 #include "usb_storage.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
+ * to see if this structure may have changed within the SCSI module.
+ * This is by no means foolproof, but it does help us some.
+ */
+#define SCSI_HOST_TEMPLATE_SIZE			(104)
+
 /* direction table -- this indicates the direction of the data
  * transfer for each command code -- a 1 indicates input
  */
@@ -1013,7 +1021,7 @@
 		  bcb.Flags, bcb.Length);
 	result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
 			      US_BULK_CB_WRAP_LEN, &partial, HZ*5);
-	US_DEBUGP("Bulk command transfer result 0x%x\n", result);
+	US_DEBUGP("Bulk command transfer result=%d\n", result);
 
 	/* if we stall, we need to clear it before we go on */
 	if (result == -EPIPE) {
@@ -1327,6 +1335,7 @@
 	NULL,			    /* reset */
 	NULL,			    /* slave_attach */
 	NULL,			    /* bios_param */
+	NULL,			    /* select_queue_depths */
 	1,			    /* can_queue */
 	-1,			    /* this_id */
 	SG_ALL,		    /* sg_tablesize */
@@ -1811,10 +1820,18 @@
  * Initialization and registration
  ***********************************************************************/
 
-int usb_stor_init(void)
+int __init usb_stor_init(void)
 {
 	//  MOD_INC_USE_COUNT;
 
+	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", 
+		       SCSI_HOST_TEMPLATE_SIZE, sizeof(my_host_template)) ;
+
+		return -1 ;
+	}
+
 	/* register the driver, return -1 if error */
 	if (usb_register(&storage_driver) < 0)
 		return -1;
@@ -1823,15 +1840,10 @@
 	return 0;
 }
 
-#ifdef MODULE
-int init_module(void)
+void __exit usb_stor_exit(void)
 {
-	/* MDD: Perhaps we should register the host here */
-	return usb_stor_init();
+	usb_deregister(&storage_driver) ;
 }
 
-void cleanup_module(void)
-{
-	usb_deregister(&storage_driver);
-}
-#endif
+module_init(usb_stor_init) ;
+module_exit(usb_stor_exit) ;
diff -u --recursive --new-file v2.3.45/linux/drivers/video/bwtwofb.c linux/drivers/video/bwtwofb.c
--- v2.3.45/linux/drivers/video/bwtwofb.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/video/bwtwofb.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: bwtwofb.c,v 1.12 2000/01/21 03:57:05 anton Exp $
+/* $Id: bwtwofb.c,v 1.13 2000/02/14 02:50:25 davem Exp $
  * bwtwofb.c: BWtwo frame buffer driver
  *
  * Copyright (C) 1998 Jakub Jelinek   (jj@ultra.linux.cz)
diff -u --recursive --new-file v2.3.45/linux/drivers/video/cgsixfb.c linux/drivers/video/cgsixfb.c
--- v2.3.45/linux/drivers/video/cgsixfb.c	Wed Dec 29 13:13:20 1999
+++ linux/drivers/video/cgsixfb.c	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: cgsixfb.c,v 1.21 1999/11/19 09:56:57 davem Exp $
+/* $Id: cgsixfb.c,v 1.22 2000/02/14 08:44:26 jj Exp $
  * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
@@ -491,7 +491,7 @@
 	int i;
                 
 	spin_lock_irqsave(&fb->lock, flags);
-	bt->addr = index << 24;
+	sbus_writel(index << 24, &bt->addr);
 	for (i = index; count--; i++){
 		sbus_writel(fb->color_map CM(i,0) << 24,
 			    &bt->color_map);
diff -u --recursive --new-file v2.3.45/linux/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- v2.3.45/linux/drivers/video/fbmem.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/video/fbmem.c	Wed Feb 16 15:42:05 2000
@@ -25,6 +25,7 @@
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
+#include <linux/devfs_fs_kernel.h>
 
 #if defined(__mc68000__) || defined(CONFIG_APUS)
 #include <asm/setup.h>
@@ -567,10 +568,13 @@
 	release:	fb_release,
 };
 
+static devfs_handle_t devfs_handle = NULL;
+
 int
 register_framebuffer(struct fb_info *fb_info)
 {
 	int i, j;
+	char name_buf[8];
 	static int fb_ever_opened[FB_MAX];
 	static int first = 1;
 
@@ -597,12 +601,17 @@
 		first = 0;
 		take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
 	}
+	sprintf (name_buf, "%d", i);
+	fb_info->devfs_handle =
+	    devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
+			    FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+			    &fb_fops, NULL);
 
 	return 0;
 }
 
 int
-unregister_framebuffer(const struct fb_info *fb_info)
+unregister_framebuffer(struct fb_info *fb_info)
 {
 	int i, j;
 
@@ -611,7 +620,11 @@
 		if (con2fb_map[j] == i)
 			return -EBUSY;
 	if (!registered_fb[i])
-		return -EINVAL; 
+		return -EINVAL;
+	devfs_unregister (fb_info->devfs_handle);
+	fb_info->devfs_handle = NULL;
+	devfs_unregister (fb_info->devfs_lhandle);
+	fb_info->devfs_lhandle = NULL;
 	registered_fb[i]=NULL;
 	num_registered_fb--;
 	return 0;
@@ -624,7 +637,8 @@
 
 	create_proc_read_entry("fb", 0, 0, fbmem_read_proc, NULL);
 
-	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
+	devfs_handle = devfs_mk_dir (NULL, "fb", 0, NULL);
+	if (devfs_register_chrdev(FB_MAJOR,"fb",&fb_fops))
 		printk("unable to get major %d for fb devs\n", FB_MAJOR);
 
 	/*
diff -u --recursive --new-file v2.3.45/linux/drivers/video/matroxfb.c linux/drivers/video/matroxfb.c
--- v2.3.45/linux/drivers/video/matroxfb.c	Fri Jan  7 19:13:22 2000
+++ linux/drivers/video/matroxfb.c	Mon Feb 14 15:31:14 2000
@@ -90,11 +90,6 @@
 /* Debug register calls, too? */
 #undef MATROXFB_DEBUG_REG
 
-/* Log reentrancy attempts - you must have printstate() patch applied */
-#undef MATROXFB_DEBUG_REENTER
-/* you must define DEBUG_REENTER to get debugged CONSOLEBH... */
-#undef MATROXFB_DEBUG_CONSOLEBH
-
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -1082,46 +1077,6 @@
 #define isMilleniumII(x) (0)
 #endif
 
-#ifdef MATROXFB_DEBUG_REENTER
-static atomic_t guard_counter = ATOMIC_INIT(1);
-static atomic_t guard_printing = ATOMIC_INIT(1);
-static void guard_start(void) {
-	if (atomic_dec_and_test(&guard_counter)) {	/* first level */
-		if (!(bh_mask & (1 << CONSOLE_BH)))	/* and CONSOLE_BH disabled */
-			return;				/* is OK */
-		/* otherwise it is first level with CONSOLE_BH enabled -
-		   - if we are __sti or SMP, reentering from console_bh possible */
-		atomic_dec(&guard_printing);	/* disable reentrancy warning */
-		printk(KERN_DEBUG "matroxfb entered without CONSOLE_BH disabled\n");
-#ifdef printstate
-		printstate();
-#endif
-		atomic_inc(&guard_printing);
-		return;
-	}
-	/* real reentering... You should be already warned by code above */
-	if (atomic_dec_and_test(&guard_printing)) {
-#ifdef printstate
-		printstate();
-#endif
-	}
-	atomic_inc(&guard_printing);
-}
-
-static inline void guard_end(void) {
-	atomic_inc(&guard_counter);
-}
-
-#define CRITBEGIN guard_start();
-#define CRITEND   guard_end();
-
-#else
-
-#define CRITBEGIN
-#define CRITEND
-
-#endif
-
 #define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l))
 
 static void matrox_cfbX_init(WPMINFO struct display* p) {
@@ -1184,8 +1139,6 @@
 
 	DBG("matrox_cfbX_bmove")
 
-	CRITBEGIN
-
 	sx *= fontwidth(p);
 	dx *= fontwidth(p);
 	width *= fontwidth(p);
@@ -1216,8 +1169,6 @@
 	mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
 	mga_ydstlen(dy, height);
 	WaitTillIdle();
-
-	CRITEND
 }
 
 #ifdef FBCON_HAS_CFB4
@@ -1229,8 +1180,6 @@
 	
 	DBG("matrox_cfb4_bmove")
 
-	CRITBEGIN
-
 	if ((sx | dx | width) & fontwidth(p) & 1) {
 		fbcon_cfb4_bmove(p, sy, sx, dy, dx, height, width);
 		return;
@@ -1270,8 +1219,6 @@
 	mga_outl(M_YDST, dy*pixx >> 5);
 	mga_outl(M_LEN | M_EXEC, height);
 	WaitTillIdle();
-
-	CRITEND
 }
 #endif
 
@@ -1280,16 +1227,12 @@
 	
 	DBG("matroxfb_accel_clear")
 
-	CRITBEGIN
-
 	mga_fifo(5);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE);
 	mga_outl(M_FCOL, color);
 	mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
 	mga_ydstlen(sy, height);
 	WaitTillIdle();
-
-	CRITEND
 }
 
 static void matrox_cfbX_clear(u_int32_t color, struct display* p, int sy, int sx, int height, int width) {
@@ -1308,8 +1251,6 @@
 
 	DBG("matrox_cfb4_clear")
 
-	CRITBEGIN
-
 	whattodo = 0; 
 	bgx = attr_bgcol_ec(p, conp);
 	bgx |= bgx << 4;
@@ -1361,8 +1302,6 @@
 			}
 		}
 	}
-
-	CRITEND
 }
 #endif
 
@@ -1410,8 +1349,6 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 
-	CRITBEGIN
-
 	mga_fifo(8);
 	mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
 	
@@ -1423,8 +1360,6 @@
 	mga_outl(M_AR0, (ar3 + charcell - 1) & 0x0003FFFF);
 	mga_ydstlen(yy, fontheight(p));
 	WaitTillIdle();
-
-	CRITEND
 }
 	
 static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, int c, int yy, int xx) {
@@ -1437,8 +1372,6 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 
-	CRITBEGIN
-
 #ifdef __BIG_ENDIAN
 	WaitTillIdle();
 	mga_outl(M_OPMODE, M_OPMODE_8BPP);
@@ -1504,7 +1437,6 @@
 #ifdef __BIG_ENDIAN
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
-	CRITEND
 }
 
 #ifdef FBCON_HAS_CFB8
@@ -1560,8 +1492,6 @@
 	xx *= fontwidth(p);
 	charcell = fontwidth(p) * fontheight(p);
 
-	CRITBEGIN
-
 	mga_fifo(3);
 	mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
 	mga_outl(M_FCOL, fgx);
@@ -1577,8 +1507,6 @@
 		xx += fontwidth(p);
 	}
 	WaitTillIdle();
-
-	CRITEND
 }
 
 static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, const unsigned short* s, int count, int yy, int xx) {
@@ -1613,8 +1541,6 @@
 		easy = 0;
 	}
 
-	CRITBEGIN
-
 #ifdef __BIG_ENDIAN
 	WaitTillIdle();
 	mga_outl(M_OPMODE, M_OPMODE_8BPP);
@@ -1676,7 +1602,6 @@
 #ifdef __BIG_ENDIAN
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
-	CRITEND
 }
 
 #ifdef FBCON_HAS_CFB8
@@ -1739,8 +1664,6 @@
 	xx |= (xx + fontwidth(p)) << 16;
 	xx >>= 1;
 
-	CRITBEGIN
-	
 	mga_fifo(5);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
 	mga_outl(M_FCOL, 0xFFFFFFFF);
@@ -1748,8 +1671,6 @@
 	mga_outl(M_YDST, yy * p->var.xres_virtual >> 6);
 	mga_outl(M_LEN | M_EXEC, fontheight(p));
 	WaitTillIdle();
-
-	CRITEND
 } 
 #endif
 
@@ -1762,16 +1683,12 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 
-	CRITBEGIN
-	
 	mga_fifo(4);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
 	mga_outl(M_FCOL, 0x0F0F0F0F);
 	mga_outl(M_FXBNDRY, ((xx + fontwidth(p)) << 16) | xx);
 	mga_ydstlen(yy, fontheight(p));
 	WaitTillIdle();
-
-	CRITEND
 }
 #endif
 
@@ -1783,16 +1700,12 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 
-	CRITBEGIN
-	
 	mga_fifo(4);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
 	mga_outl(M_FCOL, 0xFFFFFFFF);
 	mga_outl(M_FXBNDRY, ((xx + fontwidth(p)) << 16) | xx);
 	mga_ydstlen(yy, fontheight(p));
 	WaitTillIdle();
-
-	CRITEND
 }
 
 static void matrox_cfbX_clear_margins(struct vc_data* conp, struct display* p, int bottom_only) {
@@ -2034,8 +1947,6 @@
 	cursorbase = ACCESS_FBINFO(video.vbase);
 	h = ACCESS_FBINFO(features.DAC1064.cursorimage);
 
-	CRITBEGIN
-
 #ifdef __BIG_ENDIAN
 	WaitTillIdle();
 	mga_outl(M_OPMODE, M_OPMODE_32BPP);
@@ -2066,8 +1977,6 @@
 #ifdef __BIG_ENDIAN
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
-
-	CRITEND
 }
 	
 static void matroxfb_DAC1064_cursor(struct display* p, int mode, int x, int y) {
@@ -2138,8 +2047,6 @@
 	if (((fsize * width + 31) / 32) * 4 > ACCESS_FBINFO(fastfont.size))
 		return 0;
 
-	CRITBEGIN
-
 	mga_outl(M_OPMODE, M_OPMODE_8BPP);
 	if (width <= 8) {
 		if (width == 8)
@@ -2231,8 +2138,6 @@
 	}
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 
-	CRITEND
-
 	return 1;
 }
 
@@ -2250,8 +2155,6 @@
 	unsigned int step;
 	MINFO_FROM_DISP(p);
 
-	CRITBEGIN
-
 	step = ACCESS_FBINFO(devflags.textstep);
 	srcoff = (sy * p->next_line) + (sx * step);
 	dstoff = (dy * p->next_line) + (dx * step);
@@ -2279,7 +2182,6 @@
 			height--;
 		}
 	}
-	CRITEND
 }
 
 static void matrox_text_clear(struct vc_data* conp, struct display* p, int sy, int sx,
@@ -2293,8 +2195,6 @@
 	offs = sy * p->next_line + sx * step;
 	val = ntohs((attr_bgcol(p, conp->vc_video_erase_char) << 4) | attr_fgcol(p, conp->vc_video_erase_char) | (' ' << 8));
 
-	CRITBEGIN
-	
 	while (height > 0) {
 		int i;
 		for (i = width; i > 0; offs += step, i--)
@@ -2302,7 +2202,6 @@
 		offs += p->next_line - width * step;
 		height--;
 	}
-	CRITEND
 }
 
 static void matrox_text_putc(struct vc_data* conp, struct display* p, int c, int yy, int xx) {
@@ -2316,11 +2215,7 @@
 	chr = attr_fgcol(p,c) | (attr_bgcol(p,c) << 4) | ((c & p->charmask) << 8);
 	if (chr & 0x10000) chr |= 0x08;
 
-	CRITBEGIN
-	
 	mga_writew(ACCESS_FBINFO(video.vbase), offs, ntohs(chr));
-
-	CRITEND
 }
 
 static void matrox_text_putcs(struct vc_data* conp, struct display* p, const unsigned short* s,
@@ -2334,16 +2229,12 @@
 	offs = yy * p->next_line + xx * step;
 	attr = attr_fgcol(p, scr_readw(s)) | (attr_bgcol(p, scr_readw(s)) << 4);
 
-	CRITBEGIN
-	
 	while (count-- > 0) {
 		unsigned int chr = ((scr_readw(s++)) & p->charmask) << 8;
 		if (chr & 0x10000) chr ^= 0x10008;
 		mga_writew(ACCESS_FBINFO(video.vbase), offs, ntohs(attr|chr));
 		offs += step;
 	}
-
-	CRITEND
 }
 
 static void matrox_text_revc(struct display* p, int xx, int yy) {
@@ -2354,11 +2245,7 @@
 	step = ACCESS_FBINFO(devflags.textstep);
 	offs = yy * p->next_line + xx * step + 1;
 	
-	CRITBEGIN
-	
 	mga_writeb(ACCESS_FBINFO(video.vbase), offs, mga_readb(ACCESS_FBINFO(video.vbase), offs) ^ 0x77);
-
-	CRITEND
 }
 
 static int matrox_text_loadfont(WPMINFO struct display* p) {
@@ -2377,8 +2264,6 @@
 	i = 2;
 	font = (u_int8_t*)p->fontdata;
 
-	CRITBEGIN
-
 	mga_setr(M_SEQ_INDEX, 0x02, 0x04);
 	while (fsize--) {
 		int l;
@@ -2392,8 +2277,6 @@
 	}
 	mga_setr(M_SEQ_INDEX, 0x02, 0x03);
 
-	CRITEND
-
 	return 1;
 }
 
@@ -2404,12 +2287,8 @@
 
 	matroxfb_createcursorshape(PMINFO p, 0);
 
-	CRITBEGIN
-	
 	mga_setr(M_CRTC_INDEX, 0x0A, ACCESS_FBINFO(cursor.u));
 	mga_setr(M_CRTC_INDEX, 0x0B, ACCESS_FBINFO(cursor.d) - 1);
-
-	CRITEND
 }
 
 static void matrox_text_cursor(struct display* p, int mode, int x, int y) {
@@ -2422,12 +2301,8 @@
 	if (mode == CM_ERASE) {
 		if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
 
-			CRITBEGIN
-			
 			mga_setr(M_CRTC_INDEX, 0x0A, 0x20);
 
-			CRITEND
-
 			ACCESS_FBINFO(cursor.state) = CM_ERASE;
 		}
 		return;
@@ -2440,15 +2315,11 @@
 	ACCESS_FBINFO(cursor.y) = y;
 	pos = p->next_line / ACCESS_FBINFO(devflags.textstep) * y + x;
 
-	CRITBEGIN
-	
 	mga_setr(M_CRTC_INDEX, 0x0F, pos);
 	mga_setr(M_CRTC_INDEX, 0x0E, pos >> 8);
 
 	mga_setr(M_CRTC_INDEX, 0x0A, ACCESS_FBINFO(cursor.u));
 
-	CRITEND
-
 	ACCESS_FBINFO(cursor.state) = CM_DRAW;
 }
 
@@ -2717,8 +2588,6 @@
 	p3 = ACCESS_FBINFO(currenthw)->CRTCEXT[8] = pos >> 21;
 #endif	
 
-	CRITBEGIN
-
 	mga_setr(M_CRTC_INDEX, 0x0D, p0);
 	mga_setr(M_CRTC_INDEX, 0x0C, p1);
 #ifdef CONFIG_FB_MATROX_32MB
@@ -2726,8 +2595,6 @@
 		mga_setr(M_EXTVGA_INDEX, 0x08, p3);
 #endif
 	mga_setr(M_EXTVGA_INDEX, 0x00, p2);
-
-	CRITEND
 }
 
 	/*
@@ -3480,8 +3347,6 @@
 	
 	DBG("DAC1064_restore_1")
 
-	CRITBEGIN
-	
 	outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]);
 	outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]);
 	outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5]);
@@ -3491,8 +3356,6 @@
 		for (i = 0; i < sizeof(MGA1064_DAC_regs); i++)
 			outDAC1064(PMINFO MGA1064_DAC_regs[i], hw->DACreg[i]);
 	}
-
-	CRITEND
 }
 
 static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw, struct display* p) {
@@ -3501,17 +3364,13 @@
 	
 	DBG("DAC1064_restore_2")
 
-	CRITBEGIN
-	
 	for (i = 0; i < 3; i++)
 		outDAC1064(PMINFO M1064_XPIXPLLCM + i, hw->DACclk[i]);
 	for (tmout = 500000; tmout; tmout--) {
 		if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40)
 			break;
 		udelay(10);
-	};
-
-	CRITEND
+	}
 
 	if (!tmout)
 		printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
@@ -4306,8 +4165,6 @@
 		dprintk("%02X:", hw->ATTR[i]);
 	dprintk("\n");
 
-	CRITBEGIN
-
 	mga_inb(M_ATTR_RESET);
 	mga_outb(M_ATTR_INDEX, 0);
 	mga_outb(M_MISC_REG, hw->MiscOutReg);
@@ -4329,8 +4186,6 @@
 		mga_outb(M_DAC_VAL, hw->DACpal[i]);
 	mga_inb(M_ATTR_RESET);
 	mga_outb(M_ATTR_INDEX, 0x20);
-
-	CRITEND
 }
 
 static int matrox_setcolreg(unsigned regno, unsigned red, unsigned green,
@@ -4434,14 +4289,10 @@
 
 	DBG("MGA1064_restore")
 
-	CRITBEGIN
-	
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 	mga_outb(M_IEN, 0x00);
 	mga_outb(M_CACHEFLUSH, 0x00);
 
-	CRITEND
-
 	DAC1064_restore_1(PMINFO hw, oldhw);
 	vgaHWrestore(PMINFO hw, oldhw);
 	for (i = 0; i < 6; i++)
@@ -4456,10 +4307,7 @@
 
 	DBG("MGAG100_restore")
 
-	CRITBEGIN
-
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
-	CRITEND
 
 	DAC1064_restore_1(PMINFO hw, oldhw);
 	vgaHWrestore(PMINFO hw, oldhw);
@@ -4484,16 +4332,10 @@
 		dprintk("%02X:", hw->CRTCEXT[i]);
 	dprintk("\n");
 
-	CRITBEGIN
-
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 
-	CRITEND
-
 	vgaHWrestore(PMINFO hw, oldhw);
 
-	CRITBEGIN
-
 	for (i = 0; i < 6; i++)
 		mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
 
@@ -4511,13 +4353,11 @@
 		oldhw->DACclk[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
 		oldhw->DACclk[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA);
 	}
-	CRITEND
 	if (!oldhw || memcmp(hw->DACclk, oldhw->DACclk, 6)) {
 		/* agrhh... setting up PLL is very slow on Millenium... */
 		/* Mystique PLL is locked in few ms, but Millenium PLL lock takes about 0.15 s... */
 		/* Maybe even we should call schedule() ? */
 
-		CRITBEGIN
 		outTi3026(PMINFO TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]);
 		outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A);
 		outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0);
@@ -4536,30 +4376,24 @@
 				udelay(10);
 			}
 
-			CRITEND
-
 			if (!tmout)
 				printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
 			else
 				dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout);
-			CRITBEGIN
 		}
 		outTi3026(PMINFO TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]);
 		outTi3026(PMINFO TVP3026_XPLLADDR, 0x00);
 		for (i = 3; i < 6; i++)
 			outTi3026(PMINFO TVP3026_XLOOPPLLDATA, hw->DACclk[i]);
-		CRITEND
 		if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) {
 			int tmout;
 
-			CRITBEGIN
 			outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F);
 			for (tmout = 500000; tmout; --tmout) {
 				if (inTi3026(PMINFO TVP3026_XLOOPPLLDATA) & 0x40) 
 					break;
 				udelay(10);
 			}
-			CRITEND
 			if (!tmout)
 				printk(KERN_ERR "matroxfb: Loop PLL not locked after 5 secs\n");
 			else
@@ -4954,14 +4788,10 @@
 		default: seq = 0x00; crtc = 0x00; break;
 	}
 
-	CRITBEGIN
-	
 	mga_outb(M_SEQ_INDEX, 1);
 	mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
 	mga_outb(M_EXTVGA_INDEX, 1);
 	mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
-
-	CRITEND
 
 #undef minfo
 }
diff -u --recursive --new-file v2.3.45/linux/fs/Config.in linux/fs/Config.in
--- v2.3.45/linux/fs/Config.in	Sun Feb 13 19:29:04 2000
+++ linux/fs/Config.in	Wed Feb 16 15:42:05 2000
@@ -43,6 +43,13 @@
 
 bool '/proc filesystem support' CONFIG_PROC_FS
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool '/dev filesystem support (EXPERIMENTAL)' CONFIG_DEVFS_FS
+  if [ "$CONFIG_DEVFS_FS" = "y" ]; then
+    bool '  Debug devfs' CONFIG_DEVFS_DEBUG
+  fi
+fi
+
 # It compiles as a module for testing only.  It should not be used
 # as a module in general.  If we make this "tristate", a bunch of people
 # who don't know what they are doing turn it on and complain when it
diff -u --recursive --new-file v2.3.45/linux/fs/Makefile linux/fs/Makefile
--- v2.3.45/linux/fs/Makefile	Fri Jan 28 15:09:08 2000
+++ linux/fs/Makefile	Wed Feb 16 15:42:05 2000
@@ -18,8 +18,8 @@
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
 		hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \
-		nfsd nls devpts adfs partitions qnx4 udf bfs cramfs openpromfs \
-		autofs4
+		nfsd nls devpts devfs adfs partitions qnx4 udf bfs cramfs \
+		openpromfs autofs4
 
 SUB_DIRS := partitions
 
@@ -103,6 +103,10 @@
   ifeq ($(CONFIG_ISO9660_FS),m)
   MOD_SUB_DIRS += isofs
   endif
+endif
+
+ifdef CONFIG_DEVFS_FS
+SUB_DIRS += devfs
 endif
 
 ifeq ($(CONFIG_HFS_FS),y)
diff -u --recursive --new-file v2.3.45/linux/fs/bfs/file.c linux/fs/bfs/file.c
--- v2.3.45/linux/fs/bfs/file.c	Thu Feb 10 17:11:16 2000
+++ linux/fs/bfs/file.c	Mon Feb 14 15:26:36 2000
@@ -19,9 +19,9 @@
 #endif
 
 static struct file_operations bfs_file_operations = {
-	read:			generic_file_read,
-	write:			generic_file_write,
-	mmap:			generic_file_mmap,
+	read:	generic_file_read,
+	write:	generic_file_write,
+	mmap:	generic_file_mmap,
 };
 
 static int bfs_move_block(unsigned long from, unsigned long to, kdev_t dev)
@@ -77,7 +77,8 @@
 	/* if the file is not empty and the requested block is within the range
 	   of blocks allocated for this file, we can grant it */
 	if (inode->i_size && phys <= inode->iu_eblock) {
-		dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", create, block, phys);
+		dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", 
+				create, block, phys);
 		bh_result->b_dev = inode->i_dev;
 		bh_result->b_blocknr = phys;
 		bh_result->b_state |= (1UL << BH_Mapped);
@@ -90,7 +91,8 @@
 	/* if the last data block for this file is the last allocated block, we can
 	   extend the file trivially, without moving it anywhere */
 	if (inode->iu_eblock == s->su_lf_eblk) {
-		dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", create, block, phys);
+		dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", 
+				create, block, phys);
 		bh_result->b_dev = inode->i_dev;
 		bh_result->b_blocknr = phys;
 		bh_result->b_state |= (1UL << BH_Mapped);
@@ -104,9 +106,10 @@
 	/* Ok, we have to move this entire file to the next free block */
 	next_free_block = s->su_lf_eblk + 1;
 	if (inode->iu_sblock) { /* if data starts on block 0 then there is no data */
-		err = bfs_move_blocks(inode->i_dev, inode->iu_sblock, inode->iu_eblock, next_free_block);
+		err = bfs_move_blocks(inode->i_dev, inode->iu_sblock, 
+				inode->iu_eblock, next_free_block);
 		if (err) {
-			dprintf("failed to move ino=%08lx -> possible fs corruption\n", inode->i_ino);
+			dprintf("failed to move ino=%08lx -> fs corruption\n", inode->i_ino);
 			goto out;
 		}
 	} else
@@ -126,26 +129,30 @@
 
 static int bfs_writepage(struct dentry *dentry, struct page *page)
 {
-	return block_write_full_page(page,bfs_get_block);
+	return block_write_full_page(page, bfs_get_block);
 }
+
 static int bfs_readpage(struct dentry *dentry, struct page *page)
 {
-	return block_read_full_page(page,bfs_get_block);
+	return block_read_full_page(page, bfs_get_block);
 }
+
 static int bfs_prepare_write(struct page *page, unsigned from, unsigned to)
 {
-	return block_prepare_write(page,from,to,bfs_get_block);
+	return block_prepare_write(page, from, to, bfs_get_block);
 }
+
 static int bfs_bmap(struct address_space *mapping, long block)
 {
-	return generic_block_bmap(mapping,block,bfs_get_block);
+	return generic_block_bmap(mapping, block, bfs_get_block);
 }
+
 struct address_space_operations bfs_aops = {
-	readpage: bfs_readpage,
-	writepage: bfs_writepage,
-	prepare_write: bfs_prepare_write,
-	commit_write: generic_commit_write,
-	bmap: bfs_bmap
+	readpage:	bfs_readpage,
+	writepage:	bfs_writepage,
+	prepare_write:	bfs_prepare_write,
+	commit_write:	generic_commit_write,
+	bmap:		bfs_bmap
 };
 
 struct inode_operations bfs_file_inops = {
diff -u --recursive --new-file v2.3.45/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.3.45/linux/fs/binfmt_elf.c	Thu Feb 10 17:11:16 2000
+++ linux/fs/binfmt_elf.c	Mon Feb 14 15:31:14 2000
@@ -269,14 +269,8 @@
 	    if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
 	    if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
 	    vaddr = eppnt->p_vaddr;
-	    if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
+	    if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
 	    	elf_type |= MAP_FIXED;
-#ifdef __sparc__
-	    } else {
-		load_addr = get_unmapped_area(0, eppnt->p_filesz +
-					ELF_PAGEOFFSET(vaddr));
-#endif
-	    }
 
 	    map_addr = do_mmap(file,
 			    load_addr + ELF_PAGESTART(vaddr),
diff -u --recursive --new-file v2.3.45/linux/fs/block_dev.c linux/fs/block_dev.c
--- v2.3.45/linux/fs/block_dev.c	Thu Feb 10 17:11:16 2000
+++ linux/fs/block_dev.c	Wed Feb 16 15:42:05 2000
@@ -10,6 +10,7 @@
 #include <linux/fcntl.h>
 #include <linux/malloc.h>
 #include <linux/kmod.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/uaccess.h>
 
@@ -454,7 +455,7 @@
 	Return the function table of a device.
 	Load the driver if needed.
 */
-static const struct block_device_operations * get_blkfops(unsigned int major)
+const struct block_device_operations * get_blkfops(unsigned int major)
 {
 	const struct block_device_operations *ret = NULL;
 
@@ -518,11 +519,20 @@
 int check_disk_change(kdev_t dev)
 {
 	int i;
-	const struct block_device_operations * bdops;
+	const struct block_device_operations * bdops = NULL;
 	struct super_block * sb;
 
 	i = MAJOR(dev);
-	if (i >= MAX_BLKDEV || (bdops = blkdevs[i].bdops) == NULL)
+	if (i < MAX_BLKDEV)
+		bdops = blkdevs[i].bdops;
+	if (bdops == NULL) {
+		devfs_handle_t de;
+
+		de = devfs_find_handle (NULL, NULL, 0, i, MINOR (dev),
+					DEVFS_SPECIAL_BLK, 0);
+		if (de) bdops = devfs_get_ops (de);
+	}
+	if (bdops == NULL)
 		return 0;
 	if (bdops->check_media_change == NULL)
 		return 0;
diff -u --recursive --new-file v2.3.45/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.3.45/linux/fs/coda/psdev.c	Thu Feb 10 17:11:16 2000
+++ linux/fs/coda/psdev.c	Wed Feb 16 15:42:05 2000
@@ -29,6 +29,7 @@
 #include <linux/delay.h>
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/poll.h>
@@ -59,7 +60,7 @@
 
 struct coda_sb_info coda_super_info;
 struct venus_comm coda_upc_comm;
-	
+
 /*
  * Device operations
  */
@@ -358,13 +359,21 @@
 	return status;
 }
 
+static devfs_handle_t devfs_handle = NULL;
+
 int init_coda_psdev(void)
 {
-	if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) {
+	if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev",
+				 &coda_psdev_fops)) {
               printk(KERN_ERR "coda_psdev: unable to get major %d\n", 
 		     CODA_PSDEV_MAJOR);
               return -EIO;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "coda", 4, NULL);
+	devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE,
+			       CODA_PSDEV_MAJOR, 0,
+			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &coda_psdev_fops, NULL);
 	memset(&coda_upc_comm, 0, sizeof(coda_upc_comm));
 	memset(&coda_super_info, 0, sizeof(coda_super_info));
 	init_waitqueue_head(&coda_upc_comm.vc_waitq);
@@ -407,7 +416,8 @@
         if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) {
                 printk("coda: failed to unregister filesystem\n");
         }
-        unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+	devfs_unregister (devfs_handle);
+        devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
 	coda_sysctl_clean();
 }
 
diff -u --recursive --new-file v2.3.45/linux/fs/devfs/Makefile linux/fs/devfs/Makefile
--- v2.3.45/linux/fs/devfs/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/devfs/Makefile	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,39 @@
+#
+# Makefile for the linux devfs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := devfs.o
+OX_OBJS  := base.o util.o
+
+# Special case to support building documentation
+ifndef TOPDIR
+TOPDIR = ../..
+endif
+
+include $(TOPDIR)/Rules.make
+
+
+# Rule to build documentation
+doc:	base.c util.c
+	@echo '$$PACKAGE devfs' > devfs.doc
+	@echo '$$NAME Linux Kernel' >> devfs.doc
+	@echo '$$SUMMARY devfs (Device FileSystem) functions' >> devfs.doc
+	@echo '$$SYNOPSIS "#include <linux/devfs_fs.h>"' >> devfs.doc
+	@echo '$$END' >> devfs.doc
+	c2doc base.c util.c >> devfs.doc
+	karma_doc2man -section 9 devfs.doc .
+	rm devfs.doc
+	gzip --best *.9
+	mv *.9.gz /usr/man/man9
+
+
+# Rule for test compiling
+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
diff -u --recursive --new-file v2.3.45/linux/fs/devfs/base.c linux/fs/devfs/base.c
--- v2.3.45/linux/fs/devfs/base.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/devfs/base.c	Wed Feb 16 15:42:06 2000
@@ -0,0 +1,3467 @@
+/*  devfs (Device FileSystem) driver.
+
+    Copyright (C) 1998-2000  Richard Gooch
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+    The postal address is:
+      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+    ChangeLog
+
+    19980110   Richard Gooch <rgooch@atnf.csiro.au>
+               Original version.
+  v0.1
+    19980111   Richard Gooch <rgooch@atnf.csiro.au>
+               Created per-fs inode table rather than using inode->u.generic_ip
+  v0.2
+    19980111   Richard Gooch <rgooch@atnf.csiro.au>
+               Created .epoch inode which has a ctime of 0.
+	       Fixed loss of named pipes when dentries lost.
+	       Fixed loss of inode data when devfs_register() follows mknod().
+  v0.3
+    19980111   Richard Gooch <rgooch@atnf.csiro.au>
+               Fix for when compiling with CONFIG_KERNELD.
+    19980112   Richard Gooch <rgooch@atnf.csiro.au>
+               Fix for readdir() which sometimes didn't show entries.
+	       Added <<tolerant>> option to <devfs_register>.
+  v0.4
+    19980113   Richard Gooch <rgooch@atnf.csiro.au>
+               Created <devfs_fill_file> function.
+  v0.5
+    19980115   Richard Gooch <rgooch@atnf.csiro.au>
+               Added subdirectory support. Major restructuring.
+    19980116   Richard Gooch <rgooch@atnf.csiro.au>
+               Fixed <find_by_dev> to not search major=0,minor=0.
+	       Added symlink support.
+  v0.6
+    19980120   Richard Gooch <rgooch@atnf.csiro.au>
+               Created <devfs_mk_dir> function and support directory unregister
+    19980120   Richard Gooch <rgooch@atnf.csiro.au>
+               Auto-ownership uses real uid/gid rather than effective uid/gid.
+  v0.7
+    19980121   Richard Gooch <rgooch@atnf.csiro.au>
+               Supported creation of sockets.
+  v0.8
+    19980122   Richard Gooch <rgooch@atnf.csiro.au>
+               Added DEVFS_FL_HIDE_UNREG flag.
+	       Interface change to <devfs_mk_symlink>.
+               Created <devfs_symlink> to support symlink(2).
+  v0.9
+    19980123   Richard Gooch <rgooch@atnf.csiro.au>
+               Added check to <devfs_fill_file> to check inode is in devfs.
+	       Added optional traversal of symlinks.
+  v0.10
+    19980124   Richard Gooch <rgooch@atnf.csiro.au>
+               Created <devfs_get_flags> and <devfs_set_flags>.
+  v0.11
+    19980125   C. Scott Ananian <cananian@alumni.princeton.edu>
+               Created <devfs_find_handle>.
+    19980125   Richard Gooch <rgooch@atnf.csiro.au>
+               Allow removal of symlinks.
+  v0.12
+    19980125   Richard Gooch <rgooch@atnf.csiro.au>
+               Created <devfs_set_symlink_destination>.
+    19980126   Richard Gooch <rgooch@atnf.csiro.au>
+               Moved DEVFS_SUPER_MAGIC into header file.
+	       Added DEVFS_FL_HIDE flag.
+	       Created <devfs_get_maj_min>.
+	       Created <devfs_get_handle_from_inode>.
+	       Fixed minor bug in <find_by_dev>.
+    19980127   Richard Gooch <rgooch@atnf.csiro.au>
+	       Changed interface to <find_by_dev>, <find_entry>,
+	       <devfs_unregister>, <devfs_fill_file> and <devfs_find_handle>.
+	       Fixed inode times when symlink created with symlink(2).
+  v0.13
+    19980129   C. Scott Ananian <cananian@alumni.princeton.edu>
+               Exported <devfs_set_symlink_destination>, <devfs_get_maj_min>
+	       and <devfs_get_handle_from_inode>.
+    19980129   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_unlink> to support unlink(2).
+  v0.14
+    19980129   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed kerneld support for entries in devfs subdirectories.
+    19980130   Richard Gooch <rgooch@atnf.csiro.au>
+	       Bugfixes in <call_kerneld>.
+  v0.15
+    19980207   Richard Gooch <rgooch@atnf.csiro.au>
+	       Call kerneld when looking up unregistered entries.
+  v0.16
+    19980326   Richard Gooch <rgooch@atnf.csiro.au>
+	       Modified interface to <devfs_find_handle> for symlink traversal.
+  v0.17
+    19980331   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed persistence bug with device numbers for manually created
+	       device files.
+	       Fixed problem with recreating symlinks with different content.
+  v0.18
+    19980401   Richard Gooch <rgooch@atnf.csiro.au>
+	       Changed to CONFIG_KMOD.
+	       Hide entries which are manually unlinked.
+	       Always invalidate devfs dentry cache when registering entries.
+	       Created <devfs_rmdir> to support rmdir(2).
+	       Ensure directories created by <devfs_mk_dir> are visible.
+  v0.19
+    19980402   Richard Gooch <rgooch@atnf.csiro.au>
+	       Invalidate devfs dentry cache when making directories.
+	       Invalidate devfs dentry cache when removing entries.
+	       Fixed persistence bug with fifos.
+  v0.20
+    19980421   Richard Gooch <rgooch@atnf.csiro.au>
+	       Print process command when debugging kerneld/kmod.
+	       Added debugging for register/unregister/change operations.
+    19980422   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added "devfs=" boot options.
+  v0.21
+    19980426   Richard Gooch <rgooch@atnf.csiro.au>
+	       No longer lock/unlock superblock in <devfs_put_super>.
+	       Drop negative dentries when they are released.
+	       Manage dcache more efficiently.
+  v0.22
+    19980427   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added DEVFS_FL_AUTO_DEVNUM flag.
+  v0.23
+    19980430   Richard Gooch <rgooch@atnf.csiro.au>
+	       No longer set unnecessary methods.
+  v0.24
+    19980504   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added PID display to <call_kerneld> debugging message.
+	       Added "after" debugging message to <call_kerneld>.
+    19980519   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added "diread" and "diwrite" boot options.
+    19980520   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed persistence problem with permissions.
+  v0.25
+    19980602   Richard Gooch <rgooch@atnf.csiro.au>
+	       Support legacy device nodes.
+	       Fixed bug where recreated inodes were hidden.
+  v0.26
+    19980602   Richard Gooch <rgooch@atnf.csiro.au>
+	       Improved debugging in <get_vfs_inode>.
+    19980607   Richard Gooch <rgooch@atnf.csiro.au>
+	       No longer free old dentries in <devfs_mk_dir>.
+	       Free all dentries for a given entry when deleting inodes.
+  v0.27
+    19980627   Richard Gooch <rgooch@atnf.csiro.au>
+	       Limit auto-device numbering to majors 128 to 239.
+  v0.28
+    19980629   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed inode times persistence problem.
+  v0.29
+    19980704   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed spelling in <devfs_readlink> debug.
+	       Fixed bug in <devfs_setup> parsing "dilookup".
+  v0.30
+    19980705   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed devfs inode leak when manually recreating inodes.
+	       Fixed permission persistence problem when recreating inodes.
+  v0.31
+    19980727   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed harmless "unused variable" compiler warning.
+	       Fixed modes for manually recreated device nodes.
+  v0.32
+    19980728   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added NULL devfs inode warning in <devfs_read_inode>.
+	       Force all inode nlink values to 1.
+  v0.33
+    19980730   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added "dimknod" boot option.
+	       Set inode nlink to 0 when freeing dentries.
+	       Fixed modes for manually recreated symlinks.
+  v0.34
+    19980802   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed bugs in recreated directories and symlinks.
+  v0.35
+    19980806   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed bugs in recreated device nodes.
+    19980807   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed bug in currently unused <devfs_get_handle_from_inode>.
+	       Defined new <devfs_handle_t> type.
+	       Improved debugging when getting entries.
+	       Fixed bug where directories could be emptied.
+  v0.36
+    19980809   Richard Gooch <rgooch@atnf.csiro.au>
+	       Replaced dummy .epoch inode with .devfsd character device.
+    19980810   Richard Gooch <rgooch@atnf.csiro.au>
+	       Implemented devfsd protocol revision 0.
+  v0.37
+    19980819   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added soothing message to warning in <devfs_d_iput>.
+  v0.38
+    19980829   Richard Gooch <rgooch@atnf.csiro.au>
+	       Use GCC extensions for structure initialisations.
+	       Implemented async open notification.
+	       Incremented devfsd protocol revision to 1.
+  v0.39
+    19980908   Richard Gooch <rgooch@atnf.csiro.au>
+	       Moved async open notification to end of <devfs_open>.
+  v0.40
+    19980910   Richard Gooch <rgooch@atnf.csiro.au>
+	       Prepended "/dev/" to module load request.
+	       Renamed <call_kerneld> to <call_kmod>.
+  v0.41
+    19980910   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed typo "AYSNC" -> "ASYNC".
+  v0.42
+    19980910   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added open flag for files.
+  v0.43
+    19980927   Richard Gooch <rgooch@atnf.csiro.au>
+	       Set i_blocks=0 and i_blksize=1024 in <devfs_read_inode>.
+  v0.44
+    19981005   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added test for empty <<name>> in <devfs_find_handle>.
+	       Renamed <generate_path> to <devfs_generate_path> and published.
+  v0.45
+    19981006   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_get_fops>.
+  v0.46
+    19981007   Richard Gooch <rgooch@atnf.csiro.au>
+	       Limit auto-device numbering to majors 144 to 239.
+  v0.47
+    19981010   Richard Gooch <rgooch@atnf.csiro.au>
+	       Updated <devfs_follow_link> for VFS change in 2.1.125.
+  v0.48
+    19981022   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created DEVFS_ FL_COMPAT flag.
+  v0.49
+    19981023   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created "nocompat" boot option.
+  v0.50
+    19981025   Richard Gooch <rgooch@atnf.csiro.au>
+	       Replaced "mount" boot option with "nomount".
+  v0.51
+    19981110   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created "only" boot option.
+  v0.52
+    19981112   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added DEVFS_FL_REMOVABLE flag.
+  v0.53
+    19981114   Richard Gooch <rgooch@atnf.csiro.au>
+	       Only call <scan_dir_for_removable> on first call to
+	       <devfs_readdir>.
+  v0.54
+    19981205   Richard Gooch <rgooch@atnf.csiro.au>
+	       Updated <devfs_rmdir> for VFS change in 2.1.131.
+  v0.55
+    19981218   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_mk_compat>.
+    19981220   Richard Gooch <rgooch@atnf.csiro.au>
+	       Check for partitions on removable media in <devfs_lookup>.
+  v0.56
+    19990118   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added support for registering regular files.
+	       Created <devfs_set_file_size>.
+	       Update devfs inodes from entries if not changed through FS.
+  v0.57
+    19990124   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed <devfs_fill_file> to only initialise temporary inodes.
+	       Trap for NULL fops in <devfs_register>.
+	       Return -ENODEV in <devfs_fill_file> for non-driver inodes.
+  v0.58
+    19990126   Richard Gooch <rgooch@atnf.csiro.au>
+	       Switched from PATH_MAX to DEVFS_PATHLEN.
+  v0.59
+    19990127   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created "nottycompat" boot option.
+  v0.60
+    19990318   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed <devfsd_read> to not overrun event buffer.
+  v0.61
+    19990329   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_auto_unregister>.
+  v0.62
+    19990330   Richard Gooch <rgooch@atnf.csiro.au>
+	       Don't return unregistred entries in <devfs_find_handle>.
+	       Panic in <devfs_unregister> if entry unregistered.
+    19990401   Richard Gooch <rgooch@atnf.csiro.au>
+	       Don't panic in <devfs_auto_unregister> for duplicates.
+  v0.63
+    19990402   Richard Gooch <rgooch@atnf.csiro.au>
+	       Don't unregister already unregistered entries in <unregister>.
+  v0.64
+    19990510   Richard Gooch <rgooch@atnf.csiro.au>
+	       Disable warning messages when unable to read partition table for
+	       removable media.
+  v0.65
+    19990512   Richard Gooch <rgooch@atnf.csiro.au>
+	       Updated <devfs_lookup> for VFS change in 2.3.1-pre1.
+	       Created "oops-on-panic" boot option.
+	       Improved debugging in <devfs_register> and <devfs_unregister>.
+  v0.66
+    19990519   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added documentation for some functions.
+    19990525   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed "oops-on-panic" boot option: now always Oops.
+  v0.67
+    19990531   Richard Gooch <rgooch@atnf.csiro.au>
+	       Improved debugging in <devfs_register>.
+  v0.68
+    19990604   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added "diunlink" and "nokmod" boot options.
+	       Removed superfluous warning message in <devfs_d_iput>.
+  v0.69
+    19990611   Richard Gooch <rgooch@atnf.csiro.au>
+	       Took account of change to <d_alloc_root>.
+  v0.70
+    19990614   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created separate event queue for each mounted devfs.
+	       Removed <devfs_invalidate_dcache>.
+	       Created new ioctl()s.
+	       Incremented devfsd protocol revision to 3.
+	       Fixed bug when re-creating directories: contents were lost.
+	       Block access to inodes until devfsd updates permissions.
+    19990615   Richard Gooch <rgooch@atnf.csiro.au>
+	       Support 2.2.x kernels.
+  v0.71
+    19990623   Richard Gooch <rgooch@atnf.csiro.au>
+	       Switched to sending process uid/gid to devfsd.
+	       Renamed <call_kmod> to <try_modload>.
+	       Added DEVFSD_NOTIFY_LOOKUP event.
+    19990624   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added DEVFSD_NOTIFY_CHANGE event.
+	       Incremented devfsd protocol revision to 4.
+  v0.72
+    19990713   Richard Gooch <rgooch@atnf.csiro.au>
+	       Return EISDIR rather than EINVAL for read(2) on directories.
+  v0.73
+    19990809   Richard Gooch <rgooch@atnf.csiro.au>
+	       Changed <devfs_setup> to new __init scheme.
+  v0.74
+    19990901   Richard Gooch <rgooch@atnf.csiro.au>
+	       Changed remaining function declarations to new __init scheme.
+  v0.75
+    19991013   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_get_info>, <devfs_set_info>,
+	       <devfs_get_first_child> and <devfs_get_next_sibling>.
+	       Added <<dir>> parameter to <devfs_register>, <devfs_mk_compat>,
+	       <devfs_mk_dir> and <devfs_find_handle>.
+	       Work sponsored by SGI.
+  v0.76
+    19991017   Richard Gooch <rgooch@atnf.csiro.au>
+	       Allow multiple unregistrations.
+	       Work sponsored by SGI.
+  v0.77
+    19991026   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added major and minor number to devfsd protocol.
+	       Incremented devfsd protocol revision to 5.
+	       Work sponsored by SGI.
+  v0.78
+    19991030   Richard Gooch <rgooch@atnf.csiro.au>
+	       Support info pointer for all devfs entry types.
+	       Added <<info>> parameter to <devfs_mk_dir> and
+	       <devfs_mk_symlink>.
+	       Work sponsored by SGI.
+  v0.79
+    19991031   Richard Gooch <rgooch@atnf.csiro.au>
+	       Support "../" when searching devfs namespace.
+	       Work sponsored by SGI.
+  v0.80
+    19991101   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_get_unregister_slave>.
+	       Work sponsored by SGI.
+  v0.81
+    19991103   Richard Gooch <rgooch@atnf.csiro.au>
+	       Exported <devfs_get_parent>.
+	       Work sponsored by SGI.
+  v0.82
+    19991104   Richard Gooch <rgooch@atnf.csiro.au>
+               Removed unused <devfs_set_symlink_destination>.
+    19991105   Richard Gooch <rgooch@atnf.csiro.au>
+               Do not hide entries from devfsd or children.
+	       Removed DEVFS_ FL_TTY_COMPAT flag.
+	       Removed "nottycompat" boot option.
+	       Removed <devfs_mk_compat>.
+	       Work sponsored by SGI.
+  v0.83
+    19991107   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added DEVFS_ FL_WAIT flag.
+	       Work sponsored by SGI.
+  v0.84
+    19991107   Richard Gooch <rgooch@atnf.csiro.au>
+	       Support new "disc" naming scheme in <get_removable_partition>.
+	       Allow NULL fops in <devfs_register>.
+	       Work sponsored by SGI.
+  v0.85
+    19991110   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fall back to major table if NULL fops given to <devfs_register>.
+	       Work sponsored by SGI.
+  v0.86
+    19991204   Richard Gooch <rgooch@atnf.csiro.au>
+	       Support fifos when unregistering.
+	       Work sponsored by SGI.
+  v0.87
+    19991209   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags.
+	       Work sponsored by SGI.
+  v0.88
+    19991214   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed kmod support.
+	       Work sponsored by SGI.
+  v0.89
+    19991216   Richard Gooch <rgooch@atnf.csiro.au>
+	       Improved debugging in <get_vfs_inode>.
+	       Ensure dentries created by devfsd will be cleaned up.
+	       Work sponsored by SGI.
+  v0.90
+    19991223   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_get_name>.
+	       Work sponsored by SGI.
+  v0.91
+    20000203   Richard Gooch <rgooch@atnf.csiro.au>
+	       Ported to kernel 2.3.42.
+	       Removed <devfs_fill_file>.
+	       Work sponsored by SGI.
+  v0.92
+*/
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/locks.h>
+#include <linux/kdev_t.h>
+#include <linux/devfs_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/smp.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#define DEVFS_VERSION            "0.92 (20000203)"
+
+#ifndef DEVFS_NAME
+#  define DEVFS_NAME "devfs"
+#endif
+
+/*  Compatibility for 2.2.x kernel series  */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1))
+#  define init_waitqueue_head(p)  init_waitqueue(p)
+#  define DECLARE_WAITQUEUE(wait, p) struct wait_queue wait = {p, NULL}
+typedef struct wait_queue *wait_queue_head_t;
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,6))
+#  define D_ALLOC_ROOT(inode) d_alloc_root (inode, NULL)
+#else
+#  define D_ALLOC_ROOT(inode) d_alloc_root (inode)
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13))
+#  define SETUP_STATIC
+#  define __setup(a,b)
+#else
+#  define SETUP_STATIC static
+#endif
+
+#define INODE_TABLE_INC 250
+#define FIRST_INODE 1
+
+#define STRING_LENGTH 256
+
+#define MIN_DEVNUM 36864  /*  Use major numbers 144   */
+#define MAX_DEVNUM 61439  /*  through 239, inclusive  */
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#define IS_HIDDEN(de) (( ((de)->hide && !is_devfsd_or_child(fs_info)) || (!(de)->registered&& !(de)->show_unreg)))
+
+#define DEBUG_NONE         0x00000
+#define DEBUG_MODULE_LOAD  0x00001
+#define DEBUG_REGISTER     0x00002
+#define DEBUG_UNREGISTER   0x00004
+#define DEBUG_SET_FLAGS    0x00008
+#define DEBUG_S_PUT        0x00010
+#define DEBUG_I_LOOKUP     0x00020
+#define DEBUG_I_CREATE     0x00040
+#define DEBUG_I_READ       0x00080
+#define DEBUG_I_WRITE      0x00100
+#define DEBUG_I_UNLINK     0x00200
+#define DEBUG_I_RLINK      0x00400
+#define DEBUG_I_FLINK      0x00800
+#define DEBUG_I_MKNOD      0x01000
+#define DEBUG_F_READDIR    0x02000
+#define DEBUG_D_DELETE     0x04000
+#define DEBUG_D_RELEASE    0x08000
+#define DEBUG_D_IPUT       0x10000
+#define DEBUG_ALL          (DEBUG_MODULE_LOAD | DEBUG_REGISTER | \
+			    DEBUG_SET_FLAGS | DEBUG_I_LOOKUP |   \
+			    DEBUG_I_UNLINK | DEBUG_I_MKNOD |     \
+			    DEBUG_D_RELEASE | DEBUG_D_IPUT)
+#define DEBUG_DISABLED     DEBUG_NONE
+
+#define OPTION_NONE             0x00
+#define OPTION_SHOW             0x01
+#define OPTION_NOMOUNT          0x02
+#define OPTION_ONLY             0x04
+
+#define OOPS(format, args...) {printk (format, ## args); \
+                               printk ("Forcing Oops\n"); \
+                               *(int *) 0 = 0;}
+
+struct directory_type
+{
+    struct devfs_entry *first;
+    struct devfs_entry *last;
+    unsigned int num_removable;
+};
+
+struct file_type
+{
+    unsigned long size;
+};
+
+struct device_type
+{
+    unsigned short major;
+    unsigned short minor;
+};
+
+struct fcb_type  /*  File, char, block type  */
+{
+    uid_t default_uid;
+    gid_t default_gid;
+    void *ops;
+    union 
+    {
+	struct file_type file;
+	struct device_type device;
+    }
+    u;
+    unsigned char auto_owner:1;
+    unsigned char aopen_notify:1;
+    unsigned char removable:1;  /*  Belongs in device_type, but save space   */
+    unsigned char open:1;       /*  Not entirely correct                     */
+};
+
+struct symlink_type
+{
+    unsigned int length;  /*  Not including the NULL-termimator  */
+    char *linkname;       /*  This is NULL-terminated            */
+};
+
+struct fifo_type
+{
+    uid_t uid;
+    gid_t gid;
+};
+
+struct devfs_entry
+{
+    void *info;
+    union 
+    {
+	struct directory_type dir;
+	struct fcb_type fcb;
+	struct symlink_type symlink;
+	struct fifo_type fifo;
+    }
+    u;
+    struct devfs_entry *prev;    /*  Previous entry in the parent directory  */
+    struct devfs_entry *next;    /*  Next entry in the parent directory      */
+    struct devfs_entry *parent;  /*  The parent directory                    */
+    struct devfs_entry *slave;   /*  Another entry to unregister             */
+    struct devfs_inode *first_inode;
+    struct devfs_inode *last_inode;
+    umode_t mode;
+    unsigned short namelen;  /*  I think 64k+ filenames are a way off...  */
+    unsigned char registered:1;
+    unsigned char show_unreg:1;
+    unsigned char hide:1;
+    char name[1];            /*  This is just a dummy: the allocated array is
+				 bigger. This is NULL-terminated  */
+};
+
+/*  The root of the device tree  */
+static struct devfs_entry *root_entry = NULL;
+
+struct devfs_inode  /*  This structure is for "persistent" inode storage  */
+{
+    time_t atime;
+    time_t mtime;
+    time_t ctime;
+    unsigned int ino;          /*  Inode number as seen in the VFS  */
+    struct devfs_entry *de;
+    struct fs_info *fs_info;
+    struct devfs_inode *prev;  /*  This pair are used to associate a list of */
+    struct devfs_inode *next;  /*  inodes (one per FS) for a devfs entry     */
+    struct dentry *dentry;
+#ifdef CONFIG_DEVFS_TUNNEL
+    struct dentry *covered;
+#endif
+    umode_t mode;
+    uid_t uid;
+    gid_t gid;
+    nlink_t nlink;
+};
+
+struct devfsd_buf_entry
+{
+    void *data;
+    unsigned int type;
+    umode_t mode;
+    uid_t uid;
+    gid_t gid;
+};
+
+struct fs_info      /*  This structure is for each mounted devfs  */
+{
+    unsigned int num_inodes;    /*  Number of inodes created         */
+    unsigned int table_size;    /*  Size of the inode pointer table  */
+    struct devfs_inode **table;
+    struct super_block *sb;
+    volatile struct devfsd_buf_entry *devfsd_buffer;
+    volatile unsigned int devfsd_buf_in;
+    volatile unsigned int devfsd_buf_out;
+    volatile int devfsd_sleeping;
+    volatile int devfsd_buffer_in_use;
+    volatile struct task_struct *devfsd_task;
+    volatile struct file *devfsd_file;
+    volatile unsigned long devfsd_event_mask;
+    atomic_t devfsd_overrun_count;
+    wait_queue_head_t devfsd_wait_queue;
+    wait_queue_head_t revalidate_wait_queue;
+    struct fs_info *prev;
+    struct fs_info *next;
+    unsigned char require_explicit:1;
+};
+
+static struct fs_info *first_fs = NULL;
+static struct fs_info *last_fs = NULL;
+static unsigned int next_devnum_char = MIN_DEVNUM;
+static unsigned int next_devnum_block = MIN_DEVNUM;
+static const int devfsd_buf_size = PAGE_SIZE / sizeof(struct devfsd_buf_entry);
+#ifdef CONFIG_DEVFS_DEBUG
+#  ifdef MODULE
+unsigned int devfs_debug = DEBUG_NONE;
+#  else
+static unsigned int devfs_debug_init __initdata = DEBUG_NONE;
+static unsigned int devfs_debug = DEBUG_NONE;
+#  endif
+#endif
+static unsigned int boot_options = OPTION_NONE;
+
+/*  Forward function declarations  */
+static struct devfs_entry *search_for_entry (struct devfs_entry *dir,
+					     const char *name,
+					     unsigned int namelen, int mkdir,
+					     int mkfile, int *is_new,
+					     int traverse_symlink);
+static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
+			    loff_t *ppos);
+static int devfsd_ioctl (struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg);
+static int devfsd_close (struct inode *inode, struct file *file);
+
+
+/*  Devfs daemon file operations  */
+static struct file_operations devfsd_fops =
+{
+    read:    devfsd_read,
+    ioctl:   devfsd_ioctl,
+    release: devfsd_close,
+};
+
+
+/*  Support functions follow  */
+
+static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent,
+						    const char *name,
+						    unsigned int namelen,
+						    int traverse_symlink)
+/*  [SUMMARY] Search for a devfs entry inside another devfs entry.
+    <parent> The parent devfs entry.
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>.
+    <traverse_symlink> If TRUE then the entry is traversed if it is a symlink.
+    [RETURNS] A pointer to the entry on success, else NULL.
+*/
+{
+    struct devfs_entry *curr;
+
+    if ( !S_ISDIR (parent->mode) )
+    {
+	printk ("%s: entry is not a directory\n", DEVFS_NAME);
+	return NULL;
+    }
+    for (curr = parent->u.dir.first; curr != NULL; curr = curr->next)
+    {
+	if (curr->namelen != namelen) continue;
+	if (memcmp (curr->name, name, namelen) == 0) break;
+	/*  Not found: try the next one  */
+    }
+    if (curr == NULL) return NULL;
+    if (!S_ISLNK (curr->mode) || !traverse_symlink) return curr;
+    /*  Need to follow the link: this is a stack chomper  */
+    return search_for_entry (parent,
+			     curr->u.symlink.linkname, curr->u.symlink.length,
+			     FALSE, FALSE, NULL, TRUE);
+    return curr;
+}   /*  End Function search_for_entry_in_dir  */
+
+static struct devfs_entry *create_entry (struct devfs_entry *parent,
+					 const char *name,unsigned int namelen)
+{
+    struct devfs_entry *new;
+
+    if ( name && (namelen < 1) ) namelen = strlen (name);
+    if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL )
+	return NULL;
+    memset (new, 0, sizeof *new + namelen);
+    new->parent = parent;
+    if (name) memcpy (new->name, name, namelen);
+    new->namelen = namelen;
+    if (parent == NULL) return new;
+    new->prev = parent->u.dir.last;
+    /*  Insert into the parent directory's list of children  */
+    if (parent->u.dir.first == NULL) parent->u.dir.first = new;
+    else parent->u.dir.last->next = new;
+    parent->u.dir.last = new;
+    return new;
+}   /*  End Function create_entry  */
+
+static struct devfs_entry *get_root_entry (void)
+/*  [SUMMARY] Get the root devfs entry.
+    [RETURNS] The root devfs entry on success, else NULL.
+*/
+{
+    struct devfs_entry *new;
+
+    /*  Always ensure the root is created  */
+    if (root_entry != NULL) return root_entry;
+    if ( ( root_entry = create_entry (NULL, NULL, 0) ) == NULL ) return NULL;
+    root_entry->registered = TRUE;
+    root_entry->mode = S_IFDIR;
+    /*  And create the entry for ".devfsd"  */
+    if ( ( new = create_entry (root_entry, ".devfsd", 0) ) == NULL )
+	return NULL;
+    new->registered = TRUE;
+    new->u.fcb.u.device.major = next_devnum_char >> 8;
+    new->u.fcb.u.device.minor = next_devnum_char & 0xff;
+    ++next_devnum_char;
+    new->mode = S_IFCHR | S_IRUSR | S_IWUSR;
+    new->u.fcb.default_uid = 0;
+    new->u.fcb.default_gid = 0;
+    new->u.fcb.ops = &devfsd_fops;
+    return root_entry;
+}   /*  End Function get_root_entry  */
+
+static struct devfs_entry *search_for_entry (struct devfs_entry *dir,
+					     const char *name,
+					     unsigned int namelen, int mkdir,
+					     int mkfile, int *is_new,
+					     int traverse_symlink)
+/*  [SUMMARY] Search for an entry in the devfs tree.
+    <dir> The parent directory to search from. If this is NULL the root is used
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>.
+    <mkdir> If TRUE intermediate directories are created as needed.
+    <mkfile> If TRUE the file entry is created if it doesn't exist.
+    <is_new> If the returned entry was newly made, TRUE is written here. If
+    this is NULL nothing is written here.
+    <traverse_symlink> If TRUE then symbolic links are traversed.
+    [NOTE] If the entry is created, then it will be in the unregistered state.
+    [RETURNS] A pointer to the entry on success, else NULL.
+*/
+{
+    int len;
+    const char *subname, *stop, *ptr;
+    struct devfs_entry *entry;
+
+    if (is_new) *is_new = FALSE;
+    if (dir == NULL) dir = get_root_entry ();
+    if (dir == NULL) return NULL;
+    /*  Extract one filename component  */
+    subname = name;
+    stop = name + namelen;
+    while (subname < stop)
+    {
+	/*  Search for a possible '/'  */
+	for (ptr = subname; (ptr < stop) && (*ptr != '/'); ++ptr);
+	if (ptr >= stop)
+	{
+	    /*  Look for trailing component  */
+	    len = stop - subname;
+	    entry = search_for_entry_in_dir (dir, subname, len,
+					     traverse_symlink);
+	    if (entry != NULL) return entry;
+	    if (!mkfile) return NULL;
+	    entry = create_entry (dir, subname, len);
+	    if (entry && is_new) *is_new = TRUE;
+	    return entry;
+	}
+	/*  Found '/': search for directory  */
+	if (strncmp (subname, "../", 3) == 0)
+	{
+	    /*  Going up  */
+	    dir = dir->parent;
+	    if (dir == NULL) return NULL;  /*  Cannot escape from devfs  */
+	    subname += 3;
+	    continue;
+	}
+	len = ptr - subname;
+	entry = search_for_entry_in_dir (dir, subname, len, traverse_symlink);
+	if (!entry && !mkdir) return NULL;
+	if (entry == NULL)
+	{
+	    /*  Make it  */
+	    if ( ( entry = create_entry (dir, subname, len) ) == NULL )
+		return NULL;
+	    entry->mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+	    if (is_new) *is_new = TRUE;
+	}
+	if ( !S_ISDIR (entry->mode) )
+	{
+	    printk ("%s: existing non-directory entry\n", DEVFS_NAME);
+	    return NULL;
+	}
+	/*  Ensure an unregistered entry is re-registered and visible  */
+	entry->registered = TRUE;
+	entry->hide = FALSE;
+	subname = ptr + 1;
+	dir = entry;
+    }
+    return NULL;
+}   /*  End Function search_for_entry  */
+
+static struct devfs_entry *find_by_dev (struct devfs_entry *dir,
+					unsigned int major, unsigned int minor,
+					char type)
+/*  [SUMMARY] Find a devfs entry in a directory.
+    <major> The major number to search for.
+    <minor> The minor number to search for.
+    <type> The type of special file to search for. This may be either
+    DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK.
+    [RETURNS] The devfs_entry pointer on success, else NULL.
+*/
+{
+    struct devfs_entry *entry, *de;
+
+    if (dir == NULL) return NULL;
+    if ( !S_ISDIR (dir->mode) )
+    {
+	printk ("%s: find_by_dev(): not a directory\n", DEVFS_NAME);
+	return NULL;
+    }
+    /*  First search files in this directory  */
+    for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
+    {
+	if ( !S_ISCHR (entry->mode) && !S_ISBLK (entry->mode) ) continue;
+	if ( S_ISCHR (entry->mode) && (type != DEVFS_SPECIAL_CHR) ) continue;
+	if ( S_ISBLK (entry->mode) && (type != DEVFS_SPECIAL_BLK) ) continue;
+	if ( (entry->u.fcb.u.device.major == major) &&
+	     (entry->u.fcb.u.device.minor == minor) ) return entry;
+	/*  Not found: try the next one  */
+    }
+    /*  Now recursively search the subdirectories: this is a stack chomper  */
+    for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
+    {
+	if ( !S_ISDIR (entry->mode) ) continue;
+	de = find_by_dev (entry, major, minor, type);
+	if (de) return de;
+    }
+    return NULL;
+}   /*  End Function find_by_dev  */
+
+static struct devfs_entry *find_entry (devfs_handle_t dir,
+				       const char *name, unsigned int namelen,
+				       unsigned int major, unsigned int minor,
+				       char type, int traverse_symlink)
+/*  [SUMMARY] Find a devfs entry.
+    <dir> The handle to the parent devfs directory entry. If this is NULL the
+    name is relative to the root of the devfs.
+    <name> The name of the entry. This is ignored if <<handle>> is not NULL.
+    <namelen> The number of characters in <<name>>, not including a NULL
+    terminator. If this is 0, then <<name>> must be NULL-terminated and the
+    length is computed internally.
+    <major> The major number. This is used if <<handle>> and <<name>> are NULL.
+    <minor> The minor number. This is used if <<handle>> and <<name>> are NULL.
+    [NOTE] If <<major>> and <<minor>> are both 0, searching by major and minor
+    numbers is disabled.
+    <type> The type of special file to search for. This may be either
+    DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK.
+    <traverse_symlink> If TRUE then symbolic links are traversed.
+    [RETURNS] The devfs_entry pointer on success, else NULL.
+*/
+{
+    struct devfs_entry *entry;
+
+    if (name != NULL)
+    {
+	if (namelen < 1) namelen = strlen (name);
+	if (name[0] == '/')
+	{
+	    /*  Skip leading pathname component  */
+	    if (namelen < 2)
+	    {
+		printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name);
+		return NULL;
+	    }
+	    for (++name, --namelen; (*name != '/') && (namelen > 0);
+		 ++name, --namelen);
+	    if (namelen < 2)
+	    {
+		printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name);
+		return NULL;
+	    }
+	    ++name;
+	    --namelen;
+	}
+	entry = search_for_entry (dir, name, namelen, TRUE, FALSE, NULL,
+				  traverse_symlink);
+	if (entry != NULL) return entry;
+    }
+    /*  Have to search by major and minor: slow  */
+    if ( (major == 0) && (minor == 0) ) return NULL;
+    return find_by_dev (root_entry, major, minor, type);
+}   /*  End Function find_entry  */
+
+static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode)
+{
+    struct fs_info *fs_info;
+
+    if (inode == NULL) return NULL;
+    if (inode->i_ino < FIRST_INODE) return NULL;
+    fs_info = inode->i_sb->u.generic_sbp;
+    if (fs_info == NULL) return NULL;
+    if (inode->i_ino - FIRST_INODE >= fs_info->num_inodes) return NULL;
+    return fs_info->table[inode->i_ino - FIRST_INODE];
+}   /*  End Function get_devfs_inode_from_vfs_inode  */
+
+static void free_dentries (struct devfs_entry *de)
+/*  [SUMMARY] Free the dentries for a device entry and invalidate inodes.
+    <de> The entry.
+    [RETURNS] Nothing.
+*/
+{
+    struct devfs_inode *di;
+    struct dentry *dentry;
+
+    for (di = de->first_inode; di != NULL; di = di->next)
+    {
+	dentry = di->dentry;
+	if (dentry != NULL)
+	{
+	    dget (dentry);
+	    di->dentry = NULL;
+	    /*  Forcefully remove the inode  */
+	    if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0;
+	    d_drop (dentry);
+	    dput (dentry);
+	}
+    }
+}   /*  End Function free_dentries  */
+
+static int is_devfsd_or_child (struct fs_info *fs_info)
+/*  [SUMMARY] Test if the current process is devfsd or one of its children.
+    <fs_info> The filesystem information.
+    [RETURNS] TRUE if devfsd or child, else FALSE.
+*/
+{
+    struct task_struct *p;
+
+    for (p = current; p != &init_task; p = p->p_opptr)
+    {
+	if (p == fs_info->devfsd_task) return (TRUE);
+    }
+    return (FALSE);
+}   /*  End Function is_devfsd_or_child  */
+
+static inline int devfsd_queue_empty (struct fs_info *fs_info)
+/*  [SUMMARY] Test if devfsd has work pending in its event queue.
+    <fs_info> The filesystem information.
+    [RETURNS] TRUE if the queue is empty, else FALSE.
+*/
+{
+    return (fs_info->devfsd_buf_out == fs_info->devfsd_buf_in) ? TRUE : FALSE;
+}   /*  End Function devfsd_queue_empty  */
+
+static int wait_for_devfsd_finished (struct fs_info *fs_info)
+/*  [SUMMARY] Wait for devfsd to finish processing its event queue.
+    <fs_info> The filesystem information.
+    [RETURNS] TRUE if no more waiting will be required, else FALSE.
+*/
+{
+    DECLARE_WAITQUEUE (wait, current);
+
+    if (fs_info->devfsd_task == NULL) return (TRUE);
+    if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE;
+    if ( is_devfsd_or_child (fs_info) ) return (FALSE);
+    add_wait_queue (&fs_info->revalidate_wait_queue, &wait);
+    current->state = TASK_UNINTERRUPTIBLE;
+    if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping)
+	if (fs_info->devfsd_task) schedule();
+    remove_wait_queue (&fs_info->revalidate_wait_queue, &wait);
+    current->state = TASK_RUNNING;
+    return (TRUE);
+}   /*  End Function wait_for_devfsd_finished  */
+
+static int devfsd_notify_one (void *data, unsigned int type, umode_t mode,
+			      uid_t uid, gid_t gid, struct fs_info *fs_info)
+/*  [SUMMARY] Notify a single devfsd daemon of a change.
+    <data> Data to be passed.
+    <type> The type of change.
+    <mode> The mode of the entry.
+    <uid> The user ID.
+    <gid> The group ID.
+    <fs_info> The filesystem info.
+    [RETURNS] TRUE if an event was queued and devfsd woken up, else FALSE.
+*/
+{
+    unsigned int next_pos;
+    unsigned long flags;
+    struct devfsd_buf_entry *entry;
+    static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+    if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE);
+    next_pos = fs_info->devfsd_buf_in + 1;
+    if (next_pos >= devfsd_buf_size) next_pos = 0;
+    if (next_pos == fs_info->devfsd_buf_out)
+    {
+	/*  Running up the arse of the reader: drop it  */
+	atomic_inc (&fs_info->devfsd_overrun_count);
+	return (FALSE);
+    }
+    spin_lock_irqsave (&lock, flags);
+    fs_info->devfsd_buffer_in_use = TRUE;
+    next_pos = fs_info->devfsd_buf_in + 1;
+    if (next_pos >= devfsd_buf_size) next_pos = 0;
+    entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer +
+	fs_info->devfsd_buf_in;
+    entry->data = data;
+    entry->type = type;
+    entry->mode = mode;
+    entry->uid = uid;
+    entry->gid = gid;
+    fs_info->devfsd_buf_in = next_pos;
+    fs_info->devfsd_buffer_in_use = FALSE;
+    spin_unlock_irqrestore (&lock, flags);
+    wake_up_interruptible (&fs_info->devfsd_wait_queue);
+    return (TRUE);
+}   /*  End Function devfsd_notify_one  */
+
+static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait)
+/*  [SUMMARY] Notify all devfsd daemons of a change.
+    <de> The devfs entry that has changed.
+    <type> The type of change event.
+    <wait> If TRUE, the functions waits for all daemons to finish processing
+    the event.
+    [RETURNS] Nothing.
+*/
+{
+    struct fs_info *fs_info;
+
+    for (fs_info = first_fs; fs_info != NULL; fs_info = fs_info->next)
+    {
+	if (devfsd_notify_one (de, type, de->mode, current->euid,
+			       current->egid, fs_info) && wait)
+	    wait_for_devfsd_finished (fs_info);
+    }
+}   /*  End Function devfsd_notify  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_register (devfs_handle_t dir,
+			       const char *name, unsigned int namelen,
+			       unsigned int flags,
+			       unsigned int major, unsigned int minor,
+			       umode_t mode, uid_t uid, gid_t gid,
+			       void *ops, void *info)
+/*  [SUMMARY] Register a device entry.
+    <dir> The handle to the parent devfs directory entry. If this is NULL the
+    new name is relative to the root of the devfs.
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>, not including a NULL
+    terminator. If this is 0, then <<name>> must be NULL-terminated and the
+    length is computed internally.
+    <flags> A set of bitwise-ORed flags (DEVFS_FL_*).
+    <major> The major number. Not needed for regular files.
+    <minor> The minor number. Not needed for regular files.
+    <mode> The default file mode.
+    <uid> The default UID of the file.
+    <guid> The default GID of the file.
+    <ops> The <<file_operations>> or <<block_device_operations>> structure.
+    This must not be externally deallocated.
+    <info> An arbitrary pointer which will be written to the <<private_data>>
+    field of the <<file>> structure passed to the device driver. You can set
+    this to whatever you like, and change it once the file is opened (the next
+    file opened will not see this change).
+    [RETURNS] A handle which may later be used in a call to
+    [<devfs_unregister>]. On failure NULL is returned.
+*/
+{
+    int is_new;
+    struct devfs_entry *de;
+
+    if (name == NULL)
+    {
+	printk ("%s: devfs_register(): NULL name pointer\n", DEVFS_NAME);
+	return NULL;
+    }
+    if (ops == NULL)
+    {
+	if ( S_ISCHR (mode) ) ops = get_chrfops (major, 0);
+	else if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major);
+	if (ops == NULL)
+	{
+	    printk ("%s: devfs_register(%s): NULL ops pointer\n",
+		    DEVFS_NAME, name);
+	    return NULL;
+	}
+	printk ("%s: devfs_register(%s): NULL ops, got %p from major table\n",
+		DEVFS_NAME, name, ops);
+    }
+    if ( S_ISDIR (mode) )
+    {
+	printk("%s: devfs_register(%s): creating directories is not allowed\n",
+	       DEVFS_NAME, name);
+	return NULL;
+    }
+    if ( S_ISLNK (mode) )
+    {
+	printk ("%s: devfs_register(%s): creating symlinks is not allowed\n",
+		DEVFS_NAME, name);
+	return NULL;
+    }
+    if (namelen < 1) namelen = strlen (name);
+    if ( S_ISCHR (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) )
+    {
+	if (next_devnum_char >= MAX_DEVNUM)
+	{
+	    printk ("%s: devfs_register(%s): exhausted char device numbers\n",
+		    DEVFS_NAME, name);
+	    return NULL;
+	}
+	major = next_devnum_char >> 8;
+	minor = next_devnum_char & 0xff;
+	++next_devnum_char;
+    }
+    if ( S_ISBLK (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) )
+    {
+	if (next_devnum_block >= MAX_DEVNUM)
+	{
+	    printk ("%s: devfs_register(%s): exhausted block device numbers\n",
+		    DEVFS_NAME, name);
+	    return NULL;
+	}
+	major = next_devnum_block >> 8;
+	minor = next_devnum_block & 0xff;
+	++next_devnum_block;
+    }
+    de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE);
+    if (de == NULL)
+    {
+	printk ("%s: devfs_register(): could not create entry: \"%s\"\n",
+		DEVFS_NAME, name);
+	return NULL;
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_REGISTER)
+	printk ("%s: devfs_register(%s): de: %p %s\n",
+		DEVFS_NAME, name, de, is_new ? "new" : "existing");
+#endif
+    if (!is_new)
+    {
+	/*  Existing entry  */
+	if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) &&
+	     !S_ISREG (de->mode) )
+	{
+	    printk ("%s: devfs_register(): existing non-device/file entry: \"%s\"\n",
+		    DEVFS_NAME, name);
+	    return NULL;
+	}
+	if (de->registered)
+	{
+	    printk("%s: devfs_register(): device already registered: \"%s\"\n",
+		   DEVFS_NAME, name);
+	    return NULL;
+	}
+	/*  If entry already exists free any dentries associated with it  */
+	if (de->registered) free_dentries (de);
+    }
+    de->registered = TRUE;
+    if ( S_ISCHR (mode) || S_ISBLK (mode) )
+    {
+	de->u.fcb.u.device.major = major;
+	de->u.fcb.u.device.minor = minor;
+    }
+    else if ( S_ISREG (mode) ) de->u.fcb.u.file.size = 0;
+    else
+    {
+	printk ("%s: devfs_register(): illegal mode: %x\n",
+		DEVFS_NAME, mode);
+	return (NULL);
+    }
+    de->info = info;
+    de->mode = mode;
+    de->u.fcb.default_uid = uid;
+    de->u.fcb.default_gid = gid;
+    de->registered = TRUE;
+    de->u.fcb.ops = ops;
+    de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
+    de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE;
+    if (flags & DEVFS_FL_REMOVABLE)
+    {
+	de->u.fcb.removable = TRUE;
+	++de->parent->u.dir.num_removable;
+    }
+    de->u.fcb.open = FALSE;
+    de->show_unreg = ( (boot_options & OPTION_SHOW)
+			|| (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE;
+    de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
+    devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
+    return de;
+}   /*  End Function devfs_register  */
+
+static void unregister (struct devfs_entry *de)
+/*  [SUMMARY] Unregister a device entry.
+    <de> The entry to unregister.
+    [RETURNS] Nothing.
+*/
+{
+    struct devfs_entry *child;
+
+    if ( (child = de->slave) != NULL )
+    {
+	de->slave = NULL;  /* Unhook first in case slave is parent directory */
+	unregister (child);
+    }
+    if (de->registered)
+    {
+	devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0);
+	free_dentries (de);
+    }
+    de->info = NULL;
+    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+    {
+	de->registered = FALSE;
+	de->u.fcb.ops = NULL;
+	return;
+    }
+    if ( S_ISLNK (de->mode) )
+    {
+	de->registered = FALSE;
+	if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname);
+	de->u.symlink.linkname = NULL;
+	return;
+    }
+    if ( S_ISFIFO (de->mode) )
+    {
+	de->registered = FALSE;
+	return;
+    }
+    if (!de->registered) return;
+    if ( !S_ISDIR (de->mode) )
+    {
+	printk ("%s: unregister(): unsupported type\n", DEVFS_NAME);
+	return;
+    }
+    de->registered = FALSE;
+    /*  Now recursively search the subdirectories: this is a stack chomper  */
+    for (child = de->u.dir.first; child != NULL; child = child->next)
+    {
+#ifdef CONFIG_DEVFS_DEBUG
+	if (devfs_debug & DEBUG_UNREGISTER)
+	    printk ("%s: unregister(): child->name: \"%s\" child: %p\n",
+		    DEVFS_NAME, child->name, child);
+#endif
+	unregister (child);
+    }
+}   /*  End Function unregister  */
+
+/*PUBLIC_FUNCTION*/
+void devfs_unregister (devfs_handle_t de)
+/*  [SUMMARY] Unregister a device entry.
+    <de> A handle previously created by [<devfs_register>] or returned from
+    [<devfs_find_handle>]. If this is NULL the routine does nothing.
+    [RETURNS] Nothing.
+*/
+{
+    if (de == NULL) return;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_UNREGISTER)
+	printk ("%s: devfs_unregister(): de->name: \"%s\" de: %p\n",
+		DEVFS_NAME, de->name, de);
+#endif
+    unregister (de);
+}   /*  End Function devfs_unregister  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_mk_symlink (devfs_handle_t dir,
+		      const char *name, unsigned int namelen,
+		      unsigned int flags,
+		      const char *link, unsigned int linklength,
+		      devfs_handle_t *handle, void *info)
+/*  [SUMMARY] Create a symbolic link in the devfs namespace.
+    <dir> The handle to the parent devfs directory entry. If this is NULL the
+    new name is relative to the root of the devfs.
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>, not including a NULL
+    terminator. If this is 0, then <<name>> must be NULL-terminated and the
+    length is computed internally.
+    <flags> A set of bitwise-ORed flags (DEVFS_FL_*).
+    <link> The destination name.
+    <linklength> The number of characters in <<link>>, not including a NULL
+    terminator. If this is 0, then <<link>> must be NULL-terminated and the
+    length is computed internally.
+    <handle> The handle to the symlink entry is written here. This may be NULL.
+    <info> An arbitrary pointer which will be associated with the entry.
+    [RETURNS] 0 on success, else a negative error code is returned.
+*/
+{
+    int is_new;
+    char *newname;
+    struct devfs_entry *de;
+
+    if (handle != NULL) *handle = NULL;
+    if (name == NULL)
+    {
+	printk ("%s: devfs_mk_symlink(): NULL name pointer\n", DEVFS_NAME);
+	return -EINVAL;
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_REGISTER)
+	printk ("%s: devfs_mk_symlink(%s)\n", DEVFS_NAME, name);
+#endif
+    if (namelen < 1) namelen = strlen (name);
+    if (link == NULL)
+    {
+	printk ("%s: devfs_mk_symlink(): NULL link pointer\n", DEVFS_NAME);
+	return -EINVAL;
+    }
+    if (linklength < 1) linklength = strlen (link);
+    de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE);
+    if (de == NULL) return -ENOMEM;
+    if (!S_ISLNK (de->mode) && de->registered)
+    {
+	printk ("%s: devfs_mk_symlink(): non-link entry already exists\n",
+		DEVFS_NAME);
+	return -EEXIST;
+    }
+    if (handle != NULL) *handle = de;
+    de->mode = S_IFLNK | S_IRUGO | S_IXUGO;
+    de->info = info;
+    de->show_unreg = ( (boot_options & OPTION_SHOW)
+			|| (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE;
+    de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
+    /*  Note there is no need to fiddle the dentry cache if the symlink changes
+	as the symlink follow method is called every time it's needed  */
+    if ( de->registered && (linklength == de->u.symlink.length) )
+    {
+	/*  New link is same length as old link  */
+	if (memcmp (link, de->u.symlink.linkname, linklength) == 0) return 0;
+	return -EEXIST;  /*  Contents would change  */
+    }
+    /*  Have to create/update  */
+    if (de->registered) return -EEXIST;
+    de->registered = TRUE;
+    if ( ( newname = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL )
+    {
+	struct devfs_entry *parent = de->parent;
+
+	if (!is_new) return -ENOMEM;
+	/*  Have to clean up  */
+	if (de->prev == NULL) parent->u.dir.first = de->next;
+	else de->prev->next = de->next;
+	if (de->next == NULL) parent->u.dir.last = de->prev;
+	else de->next->prev = de->prev;
+	kfree (de);
+	return -ENOMEM;
+    }
+    if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname);
+    de->u.symlink.linkname = newname;
+    memcpy (de->u.symlink.linkname, link, linklength);
+    de->u.symlink.linkname[linklength] = '\0';
+    de->u.symlink.length = linklength;
+    return 0;
+}   /*  End Function devfs_mk_symlink  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name,
+			     unsigned int namelen, void *info)
+/*  [SUMMARY] Create a directory in the devfs namespace.
+    <dir> The handle to the parent devfs directory entry. If this is NULL the
+    new name is relative to the root of the devfs.
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>, not including a NULL
+    terminator. If this is 0, then <<name>> must be NULL-terminated and the
+    length is computed internally.
+    <info> An arbitrary pointer which will be associated with the entry.
+    [NOTE] Use of this function is optional. The [<devfs_register>] function
+    will automatically create intermediate directories as needed. This function
+    is provided for efficiency reasons, as it provides a handle to a directory.
+    [RETURNS] A handle which may later be used in a call to
+    [<devfs_unregister>]. On failure NULL is returned.
+*/
+{
+    int is_new;
+    struct devfs_entry *de;
+
+    if (name == NULL)
+    {
+	printk ("%s: devfs_mk_dir(): NULL name pointer\n", DEVFS_NAME);
+	return NULL;
+    }
+    if (namelen < 1) namelen = strlen (name);
+    de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE);
+    if (de == NULL)
+    {
+	printk ("%s: devfs_mk_dir(): could not create entry: \"%s\"\n",
+		DEVFS_NAME, name);
+	return NULL;
+    }
+    if (!S_ISDIR (de->mode) && de->registered)
+    {
+	printk ("%s: devfs_mk_dir(): existing non-directory entry: \"%s\"\n",
+		DEVFS_NAME, name);
+	return NULL;
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_REGISTER)
+	printk ("%s: devfs_mk_dir(%s): de: %p %s\n",
+		DEVFS_NAME, name, de, is_new ? "new" : "existing");
+#endif
+    if (!S_ISDIR (de->mode) && !is_new)
+    {
+	/*  Transmogrifying an old entry  */
+	de->u.dir.first = NULL;
+	de->u.dir.last = NULL;
+    }
+    de->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+    de->info = info;
+    if (!de->registered) de->u.dir.num_removable = 0;
+    de->registered = TRUE;
+    de->show_unreg = (boot_options & OPTION_SHOW) ? TRUE : FALSE;
+    de->hide = FALSE;
+    return de;
+}   /*  End Function devfs_mk_dir  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_find_handle (devfs_handle_t dir,
+				  const char *name, unsigned int namelen,
+				  unsigned int major, unsigned int minor,
+				  char type, int traverse_symlinks)
+/*  [SUMMARY] Find the handle of a devfs entry.
+    <dir> The handle to the parent devfs directory entry. If this is NULL the
+    name is relative to the root of the devfs.
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>, not including a NULL
+    terminator. If this is 0, then <<name>> must be NULL-terminated and the
+    length is computed internally.
+    <major> The major number. This is used if <<name>> is NULL.
+    <minor> The minor number. This is used if <<name>> is NULL.
+    <type> The type of special file to search for. This may be either
+    DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK.
+    <traverse_symlinks> If TRUE then symlink entries in the devfs namespace are
+    traversed. Symlinks pointing out of the devfs namespace will cause a
+    failure. Symlink traversal consumes stack space.
+    [RETURNS] A handle which may later be used in a call to
+    [<devfs_unregister>], [<devfs_get_flags>], or [<devfs_set_flags>].
+    On failure NULL is returned.
+*/
+{
+    devfs_handle_t de;
+
+    if ( (name != NULL) && (name[0] == '\0') ) name = NULL;
+    de = find_entry (dir, name, namelen, major, minor, type,
+		     traverse_symlinks);
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    return de;
+}   /*  End Function devfs_find_handle  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_get_flags (devfs_handle_t de, unsigned int *flags)
+/*  [SUMMARY] Get the flags for a devfs entry.
+    <de> The handle to the device entry.
+    <flags> The flags are written here.
+    [RETURNS] 0 on success, else a negative error code.
+*/
+{
+    unsigned int fl = 0;
+
+    if (de == NULL) return -EINVAL;
+    if (!de->registered) return -ENODEV;
+    if (de->show_unreg) fl |= DEVFS_FL_SHOW_UNREG;
+    if (de->hide) fl |= DEVFS_FL_HIDE;
+    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+    {
+	if (de->u.fcb.auto_owner) fl |= DEVFS_FL_AUTO_OWNER;
+	if (de->u.fcb.aopen_notify) fl |= DEVFS_FL_AOPEN_NOTIFY;
+	if (de->u.fcb.removable) fl |= DEVFS_FL_REMOVABLE;
+    }
+    *flags = fl;
+    return 0;
+}   /*  End Function devfs_get_flags  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_set_flags (devfs_handle_t de, unsigned int flags)
+/*  [SUMMARY] Set the flags for a devfs entry.
+    <de> The handle to the device entry.
+    <flags> The flags to set. Unset flags are cleared.
+    [RETURNS] 0 on success, else a negative error code.
+*/
+{
+    if (de == NULL) return -EINVAL;
+    if (!de->registered) return -ENODEV;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_SET_FLAGS)
+	printk ("%s: devfs_set_flags(): de->name: \"%s\"\n",
+		DEVFS_NAME, de->name);
+#endif
+    de->show_unreg = (flags & DEVFS_FL_SHOW_UNREG) ? TRUE : FALSE;
+    de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
+    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+    {
+	de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
+	de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE:FALSE;
+	if ( de->u.fcb.removable && !(flags & DEVFS_FL_REMOVABLE) )
+	{
+	    de->u.fcb.removable = FALSE;
+	    --de->parent->u.dir.num_removable;
+	}
+	else if ( !de->u.fcb.removable && (flags & DEVFS_FL_REMOVABLE) )
+	{
+	    de->u.fcb.removable = TRUE;
+	    ++de->parent->u.dir.num_removable;
+	}
+    }
+    return 0;
+}   /*  End Function devfs_set_flags  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_get_maj_min (devfs_handle_t de, unsigned int *major,
+		       unsigned int *minor)
+/*  [SUMMARY] Get the major and minor numbers for a devfs entry.
+    <de> The handle to the device entry.
+    <major> The major number is written here. This may be NULL.
+    <minor> The minor number is written here. This may be NULL.
+    [RETURNS] 0 on success, else a negative error code.
+*/
+{
+    if (de == NULL) return -EINVAL;
+    if (!de->registered) return -ENODEV;
+    if ( S_ISDIR (de->mode) ) return -EISDIR;
+    if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL;
+    if (major != NULL) *major = de->u.fcb.u.device.major;
+    if (minor != NULL) *minor = de->u.fcb.u.device.minor;
+    return 0;
+}   /*  End Function devfs_get_maj_min  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_get_handle_from_inode (struct inode *inode)
+/*  [SUMMARY] Get the devfs handle for a VFS inode.
+    <inode> The VFS inode.
+    [RETURNS] The devfs handle on success, else NULL.
+*/
+{
+    struct devfs_inode *di;
+
+    if (!inode || !inode->i_sb) return NULL;
+    if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL;
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if (!di) return NULL;
+    return di->de;
+}   /*  End Function devfs_get_handle_from_inode  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_generate_path (devfs_handle_t de, char *path, int buflen)
+/*  [SUMMARY] Generate a pathname for an entry, relative to the devfs root.
+    <de> The devfs entry.
+    <path> The buffer to write the pathname to. The pathname and '\0'
+    terminator will be written at the end of the buffer.
+    <buflen> The length of the buffer.
+    [RETURNS] The offset in the buffer where the pathname starts on success,
+    else a negative error code.
+*/
+{
+    int pos;
+
+    if (de == NULL) return -EINVAL;
+    if (de->namelen >= buflen) return -ENAMETOOLONG; /*  Must be first       */
+    if (de->parent == NULL) return buflen;           /*  Don't prepend root  */
+    pos = buflen - de->namelen - 1;
+    memcpy (path + pos, de->name, de->namelen);
+    path[buflen - 1] = '\0';
+    for (de = de->parent; de->parent != NULL; de = de->parent)
+    {
+	if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG;
+	path[--pos] = '/';
+	pos -= de->namelen;
+	memcpy (path + pos, de->name, de->namelen);
+    }
+    return pos;
+}   /*  End Function devfs_generate_path  */
+
+/*PUBLIC_FUNCTION*/
+void *devfs_get_ops (devfs_handle_t de)
+/*  [SUMMARY] Get the device operations for a devfs entry.
+    <de> The handle to the device entry.
+    [RETURNS] A pointer to the device operations on success, else NULL.
+*/
+{
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+	return de->u.fcb.ops;
+    return NULL;
+}   /*  End Function devfs_get_ops  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_set_file_size (devfs_handle_t de, unsigned long size)
+/*  [SUMMARY] Set the file size for a devfs regular file.
+    <de> The handle to the device entry.
+    <size> The new file size.
+    [RETURNS] 0 on success, else a negative error code.
+*/
+{
+    struct devfs_inode *di;
+
+    if (de == NULL) return -EINVAL;
+    if (!de->registered) return -EINVAL;
+    if ( !S_ISREG (de->mode) ) return -EINVAL;
+    if (de->u.fcb.u.file.size == size) return 0;
+    de->u.fcb.u.file.size = size;
+    for (di = de->first_inode; di != NULL; di = di->next)
+    {
+	if (di->dentry == NULL) continue;
+	if (di->dentry->d_inode == NULL) continue;
+	di->dentry->d_inode->i_size = size;
+    }
+    return 0;
+}   /*  End Function devfs_set_file_size  */
+
+/*PUBLIC_FUNCTION*/
+void *devfs_get_info (devfs_handle_t de)
+/*  [SUMMARY] Get the info pointer written to <<private_data>> upon open.
+    <de> The handle to the device entry.
+    [RETURNS] The info pointer.
+*/
+{
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    return de->info;
+}   /*  End Function devfs_get_info  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_set_info (devfs_handle_t de, void *info)
+/*  [SUMMARY] Set the info pointer written to <<private_data>> upon open.
+    <de> The handle to the device entry.
+    [RETURNS] 0 on success, else a negative error code.
+*/
+{
+    if (de == NULL) return -EINVAL;
+    if (!de->registered) return -EINVAL;
+    de->info = info;
+    return 0;
+}   /*  End Function devfs_set_info  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_get_parent (devfs_handle_t de)
+/*  [SUMMARY] Get the parent device entry.
+    <de> The handle to the device entry.
+    [RETURNS] The parent device entry if it exists, else NULL.
+*/
+{
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    return de->parent;
+}   /*  End Function devfs_get_parent  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_get_first_child (devfs_handle_t de)
+/*  [SUMMARY] Get the first leaf node in a directory.
+    <de> The handle to the device entry.
+    [RETURNS] The leaf node device entry if it exists, else NULL.
+*/
+{
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    if ( !S_ISDIR (de->mode) ) return NULL;
+    return de->u.dir.first;
+}   /*  End Function devfs_get_first_child  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
+/*  [SUMMARY] Get the next sibling leaf node. for a device entry.
+    <de> The handle to the device entry.
+    [RETURNS] The leaf node device entry if it exists, else NULL.
+*/
+{
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    return de->next;
+}   /*  End Function devfs_get_next_sibling  */
+
+/*PUBLIC_FUNCTION*/
+void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave)
+/*  [SUMMARY] Configure a devfs entry to be automatically unregistered.
+    <master> The master devfs entry. Only one slave may be registered.
+    <slave> The devfs entry which will be automatically unregistered when the
+    master entry is unregistered. It is illegal to call [<devfs_unregister>] on
+    this entry.
+    [RETURNS] Nothing.
+*/
+{
+    if (master == NULL) return;
+    if (master->slave != NULL)
+    {
+	/*  Because of the dumbness of the layers above, ignore duplicates  */
+	if (master->slave == slave) return;
+	printk ("%s: devfs_auto_unregister(): only one slave allowed\n",
+		DEVFS_NAME);
+	OOPS ("  master: \"%s\"  old slave: \"%s\"  new slave: \"%s\"\n",
+	      master->name, master->slave->name, slave->name);
+    }
+    master->slave = slave;
+}   /*  End Function devfs_auto_unregister  */
+
+/*PUBLIC_FUNCTION*/
+devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master)
+/*  [SUMMARY] Get the slave entry which will be automatically unregistered.
+    <master> The master devfs entry.
+    [RETURNS] The slave which will be unregistered when <<master>> is
+    unregistered.
+*/
+{
+    if (master == NULL) return NULL;
+    return master->slave;
+}   /*  End Function devfs_get_unregister_slave  */
+
+/*PUBLIC_FUNCTION*/
+const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen)
+/*  [SUMMARY] Get the name for a device entry in its parent directory.
+    <de> The handle to the device entry.
+    <namelen> The length of the name is written here. This may be NULL.
+    [RETURNS] The name on success, else NULL.
+*/
+{
+    if (de == NULL) return NULL;
+    if (!de->registered) return NULL;
+    if (namelen != NULL) *namelen = de->namelen;
+    return de->name;
+}   /*  End Function devfs_get_name  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_register_chrdev (unsigned int major, const char *name,
+			   struct file_operations *fops)
+/*  [SUMMARY] Optionally register a conventional character driver.
+    [PURPOSE] This function will register a character driver provided the
+    "devfs=only" option was not provided at boot time.
+    <major> The major number for the driver.
+    <name> The name of the driver (as seen in /proc/devices).
+    <fops> The file_operations structure pointer.
+    [RETURNS] 0 on success, else a negative error code on failure.
+*/
+{
+    if (boot_options & OPTION_ONLY) return 0;
+    return register_chrdev (major, name, fops);
+}   /*  End Function devfs_register_chrdev  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_register_blkdev (unsigned int major, const char *name,
+			   struct block_device_operations *bdops)
+/*  [SUMMARY] Optionally register a conventional block driver.
+    [PURPOSE] This function will register a block driver provided the
+    "devfs=only" option was not provided at boot time.
+    <major> The major number for the driver.
+    <name> The name of the driver (as seen in /proc/devices).
+    <bdops> The block_device_operations structure pointer.
+    [RETURNS] 0 on success, else a negative error code on failure.
+*/
+{
+    if (boot_options & OPTION_ONLY) return 0;
+    return register_blkdev (major, name, bdops);
+}   /*  End Function devfs_register_blkdev  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_unregister_chrdev (unsigned int major, const char *name)
+/*  [SUMMARY] Optionally unregister a conventional character driver.
+    [PURPOSE] This function will unregister a character driver provided the
+    "devfs=only" option was not provided at boot time.
+    <major> The major number for the driver.
+    <name> The name of the driver (as seen in /proc/devices).
+    [RETURNS] 0 on success, else a negative error code on failure.
+*/
+{
+    if (boot_options & OPTION_ONLY) return 0;
+    return unregister_chrdev (major, name);
+}   /*  End Function devfs_unregister_chrdev  */
+
+/*PUBLIC_FUNCTION*/
+int devfs_unregister_blkdev (unsigned int major, const char *name)
+/*  [SUMMARY] Optionally unregister a conventional block driver.
+    [PURPOSE] This function will unregister a block driver provided the
+    "devfs=only" option was not provided at boot time.
+    <major> The major number for the driver.
+    <name> The name of the driver (as seen in /proc/devices).
+    [RETURNS] 0 on success, else a negative error code on failure.
+*/
+{
+    if (boot_options & OPTION_ONLY) return 0;
+    return unregister_blkdev (major, name);
+}   /*  End Function devfs_unregister_blkdev  */
+
+#ifndef MODULE
+
+/*UNPUBLISHED_FUNCTION*/
+SETUP_STATIC int __init devfs_setup (char *str)
+/*  [SUMMARY] Process kernel boot options.
+    <str> The boot options after the "devfs=".
+    <unused> Unused.
+    [RETURNS] Nothing.
+*/
+{
+    while ( (*str != '\0') && !isspace (*str) )
+    {
+#  ifdef CONFIG_DEVFS_DEBUG
+	if (strncmp (str, "dall", 4) == 0)
+	{
+	    devfs_debug_init |= DEBUG_ALL;
+	    str += 4;
+	}
+	else if (strncmp (str, "dmod", 4) == 0)
+	{
+	    devfs_debug_init |= DEBUG_MODULE_LOAD;
+	    str += 4;
+	}
+	else if (strncmp (str, "dreg", 4) == 0)
+	{
+	    devfs_debug_init |= DEBUG_REGISTER;
+	    str += 4;
+	}
+	else if (strncmp (str, "dunreg", 6) == 0)
+	{
+	    devfs_debug_init |= DEBUG_UNREGISTER;
+	    str += 6;
+	}
+	else if (strncmp (str, "diread", 6) == 0)
+	{
+	    devfs_debug_init |= DEBUG_I_READ;
+	    str += 6;
+	}
+	else if (strncmp (str, "dchange", 7) == 0)
+	{
+	    devfs_debug_init |= DEBUG_SET_FLAGS;
+	    str += 7;
+	}
+	else if (strncmp (str, "diwrite", 7) == 0)
+	{
+	    devfs_debug_init |= DEBUG_I_WRITE;
+	    str += 7;
+	}
+	else if (strncmp (str, "dimknod", 7) == 0)
+	{
+	    devfs_debug_init |= DEBUG_I_MKNOD;
+	    str += 7;
+	}
+	else if (strncmp (str, "dilookup", 8) == 0)
+	{
+	    devfs_debug_init |= DEBUG_I_LOOKUP;
+	    str += 8;
+	}
+	else if (strncmp (str, "diunlink", 8) == 0)
+	{
+	    devfs_debug_init |= DEBUG_I_UNLINK;
+	    str += 8;
+	}
+	else
+#  endif  /*  CONFIG_DEVFS_DEBUG  */
+	if (strncmp (str, "show", 4) == 0)
+	{
+	    boot_options |= OPTION_SHOW;
+	    str += 4;
+	}
+	else if (strncmp (str, "only", 4) == 0)
+	{
+	    boot_options |= OPTION_ONLY;
+	    str += 4;
+	}
+	else if (strncmp (str, "nomount", 7) == 0)
+	{
+	    boot_options |= OPTION_NOMOUNT;
+	    str += 7;
+	}
+	else
+	return 0;
+	if (*str != ',') return 0;
+	++str;
+    }
+    return 1;
+}   /*  End Function devfs_setup  */
+
+__setup("devfs=", devfs_setup);
+
+#endif  /*  !MODULE  */
+
+EXPORT_SYMBOL(devfs_register);
+EXPORT_SYMBOL(devfs_unregister);
+EXPORT_SYMBOL(devfs_mk_symlink);
+EXPORT_SYMBOL(devfs_mk_dir);
+EXPORT_SYMBOL(devfs_find_handle);
+EXPORT_SYMBOL(devfs_get_flags);
+EXPORT_SYMBOL(devfs_set_flags);
+EXPORT_SYMBOL(devfs_get_maj_min);
+EXPORT_SYMBOL(devfs_get_handle_from_inode);
+EXPORT_SYMBOL(devfs_generate_path);
+EXPORT_SYMBOL(devfs_get_ops);
+EXPORT_SYMBOL(devfs_set_file_size);
+EXPORT_SYMBOL(devfs_get_info);
+EXPORT_SYMBOL(devfs_set_info);
+EXPORT_SYMBOL(devfs_get_parent);
+EXPORT_SYMBOL(devfs_get_first_child);
+EXPORT_SYMBOL(devfs_get_next_sibling);
+EXPORT_SYMBOL(devfs_auto_unregister);
+EXPORT_SYMBOL(devfs_get_unregister_slave);
+EXPORT_SYMBOL(devfs_register_chrdev);
+EXPORT_SYMBOL(devfs_register_blkdev);
+EXPORT_SYMBOL(devfs_unregister_chrdev);
+EXPORT_SYMBOL(devfs_unregister_blkdev);
+
+#ifdef CONFIG_DEVFS_DEBUG
+MODULE_PARM(devfs_debug, "i");
+#endif
+
+static void update_devfs_inode_from_entry (struct devfs_inode *di)
+{
+    if (di == NULL) return;
+    if (di->de == NULL)
+    {
+	printk ("%s: update_devfs_inode_from_entry(): NULL entry\n",
+		DEVFS_NAME);
+	return;
+    }
+    if ( S_ISDIR (di->de->mode) )
+    {
+	di->mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+	di->uid = 0;
+	di->gid = 0;
+    }
+    else if ( S_ISLNK (di->de->mode) )
+    {
+	di->mode = S_IFLNK | S_IRUGO | S_IXUGO;
+	di->uid = 0;
+	di->gid = 0;
+    }
+    else if ( S_ISFIFO (di->de->mode) )
+    {
+	di->mode = di->de->mode;
+	di->uid = di->de->u.fifo.uid;
+	di->gid = di->de->u.fifo.gid;
+    }
+    else
+    {
+	if (di->de->u.fcb.auto_owner)
+	{
+	    mode_t mode = di->de->mode;
+	    
+	    di->mode = (mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO;
+	}
+	else
+	{
+	    di->mode = di->de->mode;
+	}
+	di->uid = di->de->u.fcb.default_uid;
+	di->gid = di->de->u.fcb.default_gid;
+    }
+}   /*  End Function update_devfs_inode_from_entry  */
+
+static struct devfs_inode *create_devfs_inode (struct devfs_entry *entry,
+					       struct fs_info *fs_info)
+/*  [SUMMARY] Create a devfs inode entry.
+    <entry> The devfs entry to associate the new inode with.
+    <fs_info> The FS info.
+    [RETURNS] A pointer to the devfs inode on success, else NULL.
+*/
+{
+    struct devfs_inode *di, **table;
+
+    /*  First ensure table size is enough  */
+    if (fs_info->num_inodes >= fs_info->table_size)
+    {
+	if ( ( table = kmalloc (sizeof *table *
+				(fs_info->table_size + INODE_TABLE_INC),
+				GFP_KERNEL) ) == NULL ) return NULL;
+	fs_info->table_size += INODE_TABLE_INC;
+#ifdef CONFIG_DEVFS_DEBUG
+	if (devfs_debug & DEBUG_I_CREATE)
+	    printk ("%s: create_devfs_inode(): grew inode table to: %u entries\n",
+		    DEVFS_NAME, fs_info->table_size);
+#endif
+	if (fs_info->table)
+	{
+	    memcpy (table, fs_info->table, sizeof *table *fs_info->num_inodes);
+	    kfree (fs_info->table);
+	}
+	fs_info->table = table;
+    }
+    if ( ( di = kmalloc (sizeof *di, GFP_KERNEL) ) == NULL ) return NULL;
+    memset (di, 0, sizeof *di);
+    di->ino = fs_info->num_inodes + FIRST_INODE;
+    di->nlink = 1;
+    fs_info->table[fs_info->num_inodes] = di;
+    ++fs_info->num_inodes;
+    di->de = entry;
+    di->fs_info = fs_info;
+    di->prev = entry->last_inode;
+    if (entry->first_inode == NULL) entry->first_inode = di;
+    else entry->last_inode->next = di;
+    entry->last_inode = di;
+    update_devfs_inode_from_entry (di);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_CREATE)
+	printk ("%s: create_devfs_inode(): new di(%u): %p\n",
+		DEVFS_NAME, di->ino, di);
+#endif
+    return di;
+}   /*  End Function create_devfs_inode  */
+
+static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info,
+			const char *name, unsigned namelen,
+			char buf[STRING_LENGTH])
+/*  [SUMMARY] Notify devfsd of an inode lookup.
+    <parent> The parent devfs entry.
+    <fs_info> The filesystem info.
+    <name> The device name.
+    <namelen> The number of characters in <<name>>.
+    <buf> A working area that will be used. This must not go out of scope until
+    devfsd is idle again.
+    [RETURNS] 0 on success, else a negative error code.
+*/
+{
+    int pos;
+
+    if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) )
+	return -ENOENT;
+    if ( is_devfsd_or_child (fs_info) ) return -ENOENT;
+    if (namelen >= STRING_LENGTH) return -ENAMETOOLONG;
+    memcpy (buf + STRING_LENGTH - namelen - 1, name, namelen);
+    buf[STRING_LENGTH - 1] = '\0';
+    pos = devfs_generate_path (parent, buf, STRING_LENGTH - namelen - 1);
+    if (pos < 0) return pos;
+    buf[STRING_LENGTH - namelen - 2] = '/';
+    if ( !devfsd_notify_one (buf + pos, DEVFSD_NOTIFY_LOOKUP, 0,
+			     current->euid, current->egid, fs_info) )
+	return -ENOENT;
+    /*  Possible success  */
+    return 0;
+}   /*  End Function try_modload  */
+
+static void delete_fs (struct fs_info *fs_info)
+{
+    unsigned int count;
+    struct devfs_inode *di;
+    struct devfs_entry *de;
+
+    if (fs_info == NULL) return;
+    for (count = 0; count < fs_info->num_inodes; ++count)
+    {
+	/*  Unhook this inode from the devfs tree  */
+	di = fs_info->table[count];
+	de = di->de;
+	if (di->prev == NULL) de->first_inode = di->next;
+	else di->prev->next = di->next;
+	if (di->next == NULL) de->last_inode = di->prev;
+	else di->next->prev = di->prev;
+	memset (di, 0, sizeof *di);
+	kfree (di);
+    }
+    if (fs_info->table) kfree (fs_info->table);
+    if (fs_info->prev == NULL) first_fs = fs_info->next;
+    else fs_info->prev->next = fs_info->next;
+    if (fs_info->next == NULL) last_fs = fs_info->prev;
+    else fs_info->next->prev = fs_info->prev;
+    memset (fs_info, 0, sizeof *fs_info);
+    kfree (fs_info);
+}   /*  End Function delete_fs  */
+
+static int check_disc_changed (struct devfs_entry *de)
+/*  [SUMMARY] Check if a removable disc was changed.
+    <de> The device.
+    [RETURNS] 1 if the media was changed, else 0.
+*/
+{
+    int tmp;
+    kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
+    struct block_device_operations *bdops = de->u.fcb.ops;
+    struct super_block * sb;
+    extern int warn_no_part;
+
+    if ( !S_ISBLK (de->mode) ) return 0;
+    if (bdops == NULL) return 0;
+    if (bdops->check_media_change == NULL) return 0;
+    if ( !bdops->check_media_change (dev) ) return 0;
+    printk ( KERN_DEBUG "VFS: Disk change detected on device %s\n",
+	     kdevname (dev) );
+    sb = get_super (dev);
+    if ( sb && invalidate_inodes (sb) )
+	printk("VFS: busy inodes on changed media..\n");
+    invalidate_buffers (dev);
+    /*  Ugly hack to disable messages about unable to read partition table  */
+    tmp = warn_no_part;
+    warn_no_part = 0;
+    if (bdops->revalidate) bdops->revalidate (dev);
+    warn_no_part = tmp;
+    return 1;
+}   /*  End Function check_disc_changed  */
+
+static void scan_dir_for_removable (struct devfs_entry *dir)
+/*  [SUMMARY] Scan a directory for removable media devices and check media.
+    <dir> The directory.
+    [RETURNS] Nothing.
+*/
+{
+    struct devfs_entry *de;
+
+    if (dir->u.dir.num_removable < 1) return;
+    for (de = dir->u.dir.first; de != NULL; de = de->next)
+    {
+	if (!de->registered) continue;
+	if ( !S_ISBLK (de->mode) ) continue;
+	if (!de->u.fcb.removable) continue;
+	check_disc_changed (de);
+    }
+}   /*  End Function scan_dir_for_removable  */
+
+static int get_removable_partition (struct devfs_entry *dir, const char *name,
+				    unsigned int namelen)
+/*  [SUMMARY] Get removable media partition.
+    <dir> The parent directory.
+    <name> The name of the entry.
+    <namelen> The number of characters in <<name>>.
+    [RETURNS] 1 if the media was changed, else 0.
+*/
+{
+    struct devfs_entry *de;
+
+    for (de = dir->u.dir.first; de != NULL; de = de->next)
+    {
+	if (!de->registered) continue;
+	if ( !S_ISBLK (de->mode) ) continue;
+	if (!de->u.fcb.removable) continue;
+	if (strcmp (de->name, "disc") == 0) return check_disc_changed (de);
+	/*  Support for names where the partition is appended to the disc name
+	 */
+	if (de->namelen >= namelen) continue;
+	if (strncmp (de->name, name, de->namelen) != 0) continue;
+	return check_disc_changed (de);
+    }
+    return 0;
+}   /*  End Function get_removable_partition  */
+
+
+/*  Superblock operations follow  */
+
+extern struct inode_operations devfs_iops;
+
+static void devfs_read_inode (struct inode *inode)
+{
+    struct devfs_inode *di;
+
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if (di == NULL)
+    {
+	printk ("%s: read_inode(%d): VFS inode: %p  NO devfs_inode\n",
+		DEVFS_NAME, (int) inode->i_ino, inode);
+	return;
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_READ)
+	printk ("%s: read_inode(%d): VFS inode: %p  devfs_inode: %p\n",
+		DEVFS_NAME, (int) inode->i_ino, inode, di);
+#endif
+    inode->i_size = 0;
+    inode->i_blocks = 0;
+    inode->i_blksize = 1024;
+    inode->i_op = &devfs_iops;
+    inode->i_rdev = NODEV;
+    if ( S_ISCHR (di->mode) )
+	inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major,
+			       di->de->u.fcb.u.device.minor);
+    else if ( S_ISBLK (di->mode) )
+    {
+	inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major,
+			       di->de->u.fcb.u.device.minor);
+	inode->i_bdev = bdget (inode->i_rdev);
+	if (inode->i_bdev) inode->i_bdev->bd_op = di->de->u.fcb.ops;
+	else printk ("%s: read_inode(%d): no block device from bdget()\n",
+		     DEVFS_NAME, (int) inode->i_ino);
+    }
+    else if ( S_ISFIFO (di->mode) ) inode->i_op = &fifo_inode_operations;
+    else if ( S_ISREG (di->mode) ) inode->i_size = di->de->u.fcb.u.file.size;
+    inode->i_mode = di->mode;
+    inode->i_uid = di->uid;
+    inode->i_gid = di->gid;
+    inode->i_atime = di->atime;
+    inode->i_mtime = di->mtime;
+    inode->i_ctime = di->ctime;
+    inode->i_nlink = di->nlink;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_READ)
+	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
+		DEVFS_NAME, (int) inode->i_mode,
+		(int) inode->i_uid, (int) inode->i_gid);
+#endif
+}   /*  End Function devfs_read_inode  */
+
+static void devfs_write_inode (struct inode *inode)
+{
+    int index;
+    struct devfs_inode *di;
+    struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+    if (inode->i_ino < FIRST_INODE) return;
+    index = inode->i_ino - FIRST_INODE;
+    if (index >= fs_info->num_inodes)
+    {
+	printk ("%s: writing inode: %lu for which there is no entry!\n",
+		DEVFS_NAME, inode->i_ino);
+	return;
+    }
+    di = fs_info->table[index];
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_WRITE)
+    {
+	printk ("%s: write_inode(%d): VFS inode: %p  devfs_inode: %p\n",
+		DEVFS_NAME, (int) inode->i_ino, inode, di);
+	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
+		DEVFS_NAME, (int) inode->i_mode,
+		(int) inode->i_uid, (int) inode->i_gid);
+    }
+#endif
+    di->mode = inode->i_mode;
+    di->uid = inode->i_uid;
+    di->gid = inode->i_gid;
+    di->atime = inode->i_atime;
+    di->mtime = inode->i_mtime;
+    di->ctime = inode->i_ctime;
+}   /*  End Function devfs_write_inode  */
+
+static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr)
+{
+    int retval;
+    struct devfs_inode *di;
+    struct inode *inode = dentry->d_inode;
+    struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if (di == NULL) return -ENODEV;
+    retval = inode_change_ok (inode, iattr);
+    if (retval != 0) return retval;
+    inode_setattr (inode, iattr);
+    if ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) )
+	devfsd_notify_one (di->de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
+			   inode->i_uid, inode->i_gid, fs_info);
+    return 0;
+}   /*  End Function devfs_notify_change  */
+
+static void devfs_put_super (struct super_block *sb)
+{
+    struct fs_info *fs_info = sb->u.generic_sbp;
+
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_S_PUT)
+	printk ("%s: put_super(): devfs ptr: %p\n", DEVFS_NAME, fs_info);
+#endif
+    sb->s_dev = 0;
+#ifdef CONFIG_DEVFS_TUNNEL
+    dput (fs_info->table[0]->covered);
+#endif
+    delete_fs (fs_info);
+    MOD_DEC_USE_COUNT;
+}   /*  End Function devfs_put_super  */
+
+static int devfs_statfs (struct super_block *sb, struct statfs *buf,int bufsiz)
+{
+    struct statfs tmp;
+
+    tmp.f_type = DEVFS_SUPER_MAGIC;
+    tmp.f_bsize = PAGE_SIZE / sizeof (long);
+    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;
+}   /*  End Function devfs_statfs  */
+
+static struct super_operations devfs_sops =
+{ 
+    read_inode:    devfs_read_inode,
+    write_inode:   devfs_write_inode,
+    notify_change: devfs_notify_change,
+    put_super:     devfs_put_super,
+    statfs:        devfs_statfs,
+};
+
+static struct inode *get_vfs_inode (struct super_block *sb,
+				    struct devfs_inode *di,
+				    struct dentry *dentry)
+/*  [SUMMARY] Get a VFS inode.
+    <sb> The super block.
+    <di> The devfs inode.
+    <dentry> The dentry to register with the devfs inode.
+    [RETURNS] The inode on success, else NULL.
+*/
+{
+    struct inode *inode;
+
+    if (di->dentry != NULL)
+    {
+	printk ("%s: get_vfs_inode(%u): old di->dentry: %p \"%s\"  new dentry: %p \"%s\"\n",
+		DEVFS_NAME, di->ino, di->dentry, di->dentry->d_name.name,
+		dentry, dentry->d_name.name);
+	printk ("  old inode: %p\n", di->dentry->d_inode);
+	return NULL;
+    }
+    if ( ( inode = iget (sb, di->ino) ) == NULL ) return NULL;
+    di->dentry = dentry;
+    return inode;
+}   /*  End Function get_vfs_inode  */
+
+
+/*  File operations for device entries follow  */
+
+static int devfs_read (struct file *file, char *buf, size_t len, loff_t *ppos)
+{
+    if ( S_ISDIR (file->f_dentry->d_inode->i_mode) ) return -EISDIR;
+    return -EINVAL;
+}   /*  End Function devfs_read  */
+
+static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir)
+{
+    int err, count;
+    int stored = 0;
+    struct fs_info *fs_info;
+    struct devfs_inode *di;
+    struct devfs_entry *parent, *de;
+    struct inode *inode = file->f_dentry->d_inode;
+
+    if (inode == NULL)
+    {
+	printk ("%s: readdir(): NULL inode\n", DEVFS_NAME);
+	return -EBADF;
+    }
+    if ( !S_ISDIR (inode->i_mode) )
+    {
+	printk ("%s: readdir(): inode is not a directory\n", DEVFS_NAME);
+	return -ENOTDIR;
+    }
+    fs_info = inode->i_sb->u.generic_sbp;
+    di = get_devfs_inode_from_vfs_inode (file->f_dentry->d_inode);
+    parent = di->de;
+    if ( (long) file->f_pos < 0 ) return -EINVAL;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_F_READDIR)
+	printk ("%s: readdir(): fs_info: %p  pos: %ld\n", DEVFS_NAME,
+		fs_info, (long) file->f_pos);
+#endif
+    switch ( (long) file->f_pos )
+    {
+      case 0:
+	scan_dir_for_removable (parent);
+	err = (*filldir) (dirent, "..", 2, file->f_pos,
+			  file->f_dentry->d_parent->d_inode->i_ino);
+	if (err == -EINVAL) break;
+	if (err < 0) return err;
+	file->f_pos++;
+	++stored;
+	/*  Fall through  */
+      case 1:
+	err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino);
+	if (err == -EINVAL) break;
+	if (err < 0) return err;
+	file->f_pos++;
+	++stored;
+	/*  Fall through  */
+      default:
+	/*  Skip entries  */
+	count = file->f_pos - 2;
+	for (de = parent->u.dir.first; (de != NULL) && (count > 0);
+	     de = de->next)
+	{
+	    if ( IS_HIDDEN (de) ) continue;
+	    if (!fs_info->require_explicit)
+	    {
+		--count;
+		continue;
+	    }
+	    /*  Must search for an inode for this FS  */
+	    for (di = de->first_inode; di != NULL; di = di->next)
+	    {
+		if (fs_info == di->fs_info) break;
+	    }
+	    if (di != NULL) --count;
+	}
+	/*  Now add all remaining entries  */
+	for (; de != NULL; de = de->next)
+	{
+	    if ( IS_HIDDEN (de) ) continue;
+	    /*  Must search for an inode for this FS  */
+	    for (di = de->first_inode; di != NULL; di = di->next)
+	    {
+		if (fs_info == di->fs_info) break;
+	    }
+	    if (di == NULL)
+	    {
+		if (fs_info->require_explicit) continue;
+		/*  Have to create the inode right now  */
+		di = create_devfs_inode (de, fs_info);
+		if (di == NULL) return -ENOMEM;
+	    }
+	    else if (di->ctime == 0) update_devfs_inode_from_entry (di);
+	    err = (*filldir) (dirent, de->name, de->namelen,
+			      file->f_pos, di->ino);
+	    if (err == -EINVAL) break;
+	    if (err < 0) return err;
+	    file->f_pos++;
+	    ++stored;
+	}
+	break;
+    }
+    return stored;
+}   /*  End Function devfs_readdir  */
+
+static int devfs_open (struct inode *inode, struct file *file)
+{
+    int err;
+    struct fcb_type *df;
+    struct devfs_inode *di;
+    struct dentry *dentry = file->f_dentry;
+    struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if (di == NULL) return -ENODEV;
+    if ( S_ISDIR (di->de->mode) ) return 0;
+    df = &di->de->u.fcb;
+    if (!di->de->registered) return -ENODEV;
+    file->private_data = di->de->info;
+    if ( S_ISBLK (inode->i_mode) )
+    {
+	file->f_op = &def_blk_fops;
+	if (df->ops) inode->i_bdev->bd_op = df->ops;
+    }
+    else file->f_op = df->ops;
+    if (file->f_op)
+	err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
+    else
+    {
+	/*  Fallback to legacy scheme  */
+	if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
+	else err = -ENODEV;
+    }
+    if (err < 0) return err;
+    /*  Open was successful  */
+    df->open = TRUE;
+    if (dentry->d_count != 1) return 0;       /*  No fancy operations  */
+    /*  This is the first open  */
+    if (df->auto_owner)
+    {
+	/*  Change the ownership/protection  */
+	di->mode = (di->mode & ~S_IALLUGO) | (di->de->mode & S_IRWXUGO);
+	di->uid = current->euid;
+	di->gid = current->egid;
+	inode->i_mode = di->mode;
+	inode->i_uid = di->uid;
+	inode->i_gid = di->gid;
+    }
+    if (df->aopen_notify)
+	devfsd_notify_one (di->de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode,
+			   current->euid, current->egid, fs_info);
+    return 0;
+}   /*  End Function devfs_open  */
+
+static struct file_operations devfs_fops =
+{
+    read: devfs_read,
+    readdir: devfs_readdir,
+    open: devfs_open,
+};
+
+
+/*  Dentry operations for device entries follow  */
+
+static void devfs_d_release (struct dentry *dentry)
+/*  [SUMMARY] Callback for when a dentry is freed.
+*/
+{
+#ifdef CONFIG_DEVFS_DEBUG
+    struct inode *inode = dentry->d_inode;
+
+    if (devfs_debug & DEBUG_D_RELEASE)
+	printk ("%s: d_release(): dentry: %p inode: %p\n",
+		DEVFS_NAME, dentry, inode);
+#endif
+}   /*  End Function devfs_d_release  */
+
+static void devfs_d_iput (struct dentry *dentry, struct inode *inode)
+/*  [SUMMARY] Callback for when a dentry loses its inode.
+*/
+{
+    struct devfs_inode *di;
+
+    di = get_devfs_inode_from_vfs_inode (inode);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_D_IPUT)
+	printk ("%s: d_iput(): dentry: %p inode: %p di: %p  di->dentry: %p\n",
+		DEVFS_NAME, dentry, inode, di, di->dentry);
+#endif
+    if (di->dentry == dentry)
+    {
+	di->dentry = NULL;
+#ifdef CONFIG_DEVFS_TUNNEL
+	dput (di->covered);
+	di->covered = NULL;
+#endif
+    }
+    iput (inode);
+}   /*  End Function devfs_d_iput  */
+
+static void devfs_d_delete (struct dentry *dentry);
+
+static struct dentry_operations devfs_dops =
+{
+    d_delete:     devfs_d_delete,
+    d_release:    devfs_d_release,
+    d_iput:       devfs_d_iput,
+};
+
+static int devfs_d_revalidate_wait (struct dentry *dentry, int flags);
+
+static struct dentry_operations devfs_wait_dops =
+{
+    d_delete:     devfs_d_delete,
+    d_release:    devfs_d_release,
+    d_iput:       devfs_d_iput,
+    d_revalidate: devfs_d_revalidate_wait,
+};
+
+static void devfs_d_delete (struct dentry *dentry)
+/*  [SUMMARY] Callback for when all files for a dentry are closed.
+*/
+{
+    struct inode *inode = dentry->d_inode;
+    struct devfs_inode *di;
+    struct fs_info *fs_info;
+
+    if (dentry->d_op == &devfs_wait_dops) dentry->d_op = &devfs_dops;
+    /*  Unhash dentry if negative (has no inode)  */
+    if (inode == NULL)
+    {
+#ifdef CONFIG_DEVFS_DEBUG
+	if (devfs_debug & DEBUG_D_DELETE)
+	    printk ("%s: d_delete(): dropping negative dentry: %p\n",
+		    DEVFS_NAME, dentry);
+#endif
+	d_drop (dentry);
+	return;
+    }
+    fs_info = inode->i_sb->u.generic_sbp;
+    di = get_devfs_inode_from_vfs_inode (inode);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_D_DELETE)
+	printk ("%s: d_delete(): dentry: %p  inode: %p  devfs_inode: %p\n",
+		DEVFS_NAME, dentry, inode, di);
+#endif
+    if (di == NULL) return;
+    if (di->de == NULL) return;
+    if ( !S_ISCHR (di->mode) && !S_ISBLK (di->mode) && !S_ISREG (di->mode) )
+	return;
+    if (!di->de->u.fcb.open) return;
+    di->de->u.fcb.open = FALSE;
+    if (di->de->u.fcb.aopen_notify)
+	devfsd_notify_one (di->de, DEVFSD_NOTIFY_CLOSE, inode->i_mode,
+			   current->euid, current->egid, fs_info);
+    if (!di->de->u.fcb.auto_owner) return;
+    /*  Change the ownership/protection back  */
+    di->mode = (di->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO;
+    di->uid = di->de->u.fcb.default_uid;
+    di->gid = di->de->u.fcb.default_gid;
+    inode->i_mode = di->mode;
+    inode->i_uid = di->uid;
+    inode->i_gid = di->gid;
+}   /*  End Function devfs_d_delete  */
+
+static int devfs_d_revalidate_wait (struct dentry *dentry, int flags)
+{
+    devfs_handle_t de = dentry->d_fsdata;
+    struct inode *dir = dentry->d_parent->d_inode;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+
+    if (!de || de->registered)
+    {
+	if ( !dentry->d_inode && is_devfsd_or_child (fs_info) )
+	{
+	    struct devfs_inode *di = NULL;
+	    struct inode *inode;
+
+#ifdef CONFIG_DEVFS_DEBUG
+	    char txt[STRING_LENGTH];
+
+	    memset (txt, 0, STRING_LENGTH);
+	    memcpy (txt, dentry->d_name.name,
+		    (dentry->d_name.len >= STRING_LENGTH) ?
+		    (STRING_LENGTH - 1) : dentry->d_name.len);
+	    if (devfs_debug & DEBUG_I_LOOKUP)
+		printk ("%s: d_revalidate(): dentry: %p name: \"%s\" by: \"%s\"\n",
+			DEVFS_NAME, dentry, txt, current->comm);
+#endif
+	    if (de)
+	    {
+		/*  Search for an inode for this FS  */
+		for (di = de->first_inode; di != NULL; di = di->next)
+		    if (di->fs_info == fs_info) break;
+	    }
+	    if (de == NULL)
+	    {
+		devfs_handle_t parent;
+		struct devfs_inode *pi;
+
+		pi = get_devfs_inode_from_vfs_inode (dir);
+		parent = pi->de;
+		de = search_for_entry_in_dir (parent, dentry->d_name.name,
+					      dentry->d_name.len, FALSE);
+	    }
+	    if (de == NULL) return 1;
+	    /*  Create an inode, now that the driver information is available
+	     */
+	    if (di == NULL) di = create_devfs_inode (de, fs_info);
+	    else if (di->ctime == 0) update_devfs_inode_from_entry (di);
+	    else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO);
+	    if (di == NULL) return 1;
+	    if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL )
+		return 1;
+#ifdef CONFIG_DEVFS_DEBUG
+	    if (devfs_debug & DEBUG_I_LOOKUP)
+		printk ("%s: d_revalidate(): new VFS inode(%u): %p  devfs_inode: %p\n",
+			DEVFS_NAME, di->ino, inode, di);
+#endif
+	    d_instantiate (dentry, inode);
+	    return 1;
+	}
+    }
+    if ( wait_for_devfsd_finished (fs_info) ) dentry->d_op = &devfs_dops;
+    return 1;
+}   /*  End Function devfs_d_revalidate_wait  */
+
+
+/*  Inode operations for device entries follow  */
+
+static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry)
+{
+    struct fs_info *fs_info;
+    struct devfs_inode *di = NULL;
+    struct devfs_inode *pi;
+    struct devfs_entry *parent, *de;
+    struct inode *inode;
+    char txt[STRING_LENGTH];
+
+    /*  Set up the dentry operations before anything else, to ensure cleaning
+	up on any error  */
+    dentry->d_op = &devfs_dops;
+    if (dir == NULL)
+    {
+	printk ("%s: lookup(): NULL directory inode\n", DEVFS_NAME);
+	return ERR_PTR (-ENOTDIR);
+    }
+    if ( !S_ISDIR (dir->i_mode) ) return ERR_PTR (-ENOTDIR);
+    memset (txt, 0, STRING_LENGTH);
+    memcpy (txt, dentry->d_name.name,
+	    (dentry->d_name.len >= STRING_LENGTH) ?
+	    (STRING_LENGTH - 1) : dentry->d_name.len);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_LOOKUP)
+	printk ("%s: lookup(%s): dentry: %p by: \"%s\"\n",
+		DEVFS_NAME, txt, dentry, current->comm);
+#endif
+    fs_info = dir->i_sb->u.generic_sbp;
+    /*  First try to get the devfs entry for this directory  */
+    pi = get_devfs_inode_from_vfs_inode (dir);
+    if (pi == NULL) return ERR_PTR (-EINVAL);
+    parent = pi->de;
+    if (!parent->registered) return ERR_PTR (-ENOENT);
+    /*  Try to reclaim an existing devfs entry  */
+    de = search_for_entry_in_dir (parent,
+				  dentry->d_name.name, dentry->d_name.len,
+				  FALSE);
+    if (de)
+    {
+	/*  Search for an inode for this FS  */
+	for (di = de->first_inode; di != NULL; di = di->next)
+	    if (di->fs_info == fs_info) break;
+    }
+    if (fs_info->require_explicit)
+    {
+	if (di == NULL)
+	{
+	    /*  Make the dentry negative so a subsequent operation can deal
+		with it (for the benefit of mknod()). Leaving the dentry
+		unhashed will cause <lock_parent> to fail which in turns causes
+		<do_mknod> to fail  */
+	    d_add (dentry, NULL);
+	    return NULL;
+	}
+    }
+    if ( ( (de == NULL) || !de->registered ) &&
+	 (parent->u.dir.num_removable > 0) &&
+	 get_removable_partition (parent, dentry->d_name.name,
+				  dentry->d_name.len) )
+    {
+	if (de == NULL)
+	    de = search_for_entry_in_dir (parent, dentry->d_name.name,
+					  dentry->d_name.len, FALSE);
+    }
+    if ( (de == NULL) || (!de->registered) )
+    {
+	/*  Try with devfsd. For any kind of failure, leave a negative dentry
+	    so someone else can deal with it (in the case where the sysadmin
+	    does a mknod()). It's important to do this before hashing the
+	    dentry, so that the devfsd queue is filled before revalidates
+	    can start  */
+	if (try_modload (parent, fs_info,
+			 dentry->d_name.name, dentry->d_name.len, txt) < 0)
+	{
+	    d_add (dentry, NULL);
+	    return NULL;
+	}
+	/*  devfsd claimed success  */
+	dentry->d_op = &devfs_wait_dops;
+	dentry->d_fsdata = de;
+	d_add (dentry, NULL);  /*  Open the floodgates  */
+	/*  Unlock directory semaphore, which will release any waiters. They
+	    will get the hashed dentry, and may be forced to wait for
+	    revalidation  */
+	up (&dir->i_sem);
+	devfs_d_revalidate_wait (dentry, 0);  /*  I might have to wait too  */
+	down (&dir->i_sem);      /*  Grab it again because them's the rules  */
+	/*  If someone else has been so kind as to make the inode, we go home
+	    early  */
+	if (dentry->d_inode) return NULL;
+	if (de && !de->registered) return NULL;
+	if (de == NULL)
+	    de = search_for_entry_in_dir (parent, dentry->d_name.name,
+					  dentry->d_name.len, FALSE);
+	if (de == NULL) return NULL;
+	/*  OK, there's an entry now, but no VFS inode yet  */
+    }
+    else
+    {
+	dentry->d_op = &devfs_wait_dops;
+	d_add (dentry, NULL);  /*  Open the floodgates  */
+    }
+    /*  Create an inode, now that the driver information is available  */
+    if (di == NULL) di = create_devfs_inode (de, fs_info);
+    else if (di->ctime == 0) update_devfs_inode_from_entry (di);
+    else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO);
+    if (di == NULL) return ERR_PTR (-ENOMEM);
+    if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL )
+	return ERR_PTR (-ENOMEM);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_LOOKUP)
+	printk ("%s: lookup(): new VFS inode(%u): %p  devfs_inode: %p\n",
+		DEVFS_NAME, di->ino, inode, di);
+#endif
+    d_instantiate (dentry, inode);
+    /*  Unlock directory semaphore, which will release any waiters. They will
+	get the hashed dentry, and may be forced to wait for revalidation  */
+    up (&dir->i_sem);
+    if (dentry->d_op == &devfs_wait_dops)
+	devfs_d_revalidate_wait (dentry, 0);  /*  I might have to wait too  */
+    down (&dir->i_sem);          /*  Grab it again because them's the rules  */
+    return NULL;
+}   /*  End Function devfs_lookup  */
+
+static int devfs_link (struct dentry *old_dentry, struct inode *dir,
+		       struct dentry *dentry)
+{
+    /*struct inode *inode = old_dentry->d_inode;*/
+    char txt[STRING_LENGTH];
+
+    memset (txt, 0, STRING_LENGTH);
+    memcpy (txt, old_dentry->d_name.name, old_dentry->d_name.len);
+    txt[STRING_LENGTH - 1] = '\0';
+    printk ("%s: link of \"%s\"\n", DEVFS_NAME, txt);
+    return -EPERM;
+}   /*  End Function devfs_link  */
+
+static int devfs_unlink (struct inode *dir, struct dentry *dentry)
+{
+    struct devfs_inode *di;
+
+#ifdef CONFIG_DEVFS_DEBUG
+    char txt[STRING_LENGTH];
+
+    if (devfs_debug & DEBUG_I_UNLINK)
+    {
+	memset (txt, 0, STRING_LENGTH);
+	memcpy (txt, dentry->d_name.name, dentry->d_name.len);
+	txt[STRING_LENGTH - 1] = '\0';
+	printk ("%s: unlink(%s)\n", DEVFS_NAME, txt);
+    }
+#endif
+
+    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
+    if (!dentry || !dentry->d_inode) return -ENOENT;
+    di = get_devfs_inode_from_vfs_inode (dentry->d_inode);
+    if (di == NULL) return -ENOENT;
+    if (!di->de->registered) return -ENOENT;
+    di->de->registered = FALSE;
+    di->de->hide = TRUE;
+    free_dentries (di->de);
+    return 0;
+}   /*  End Function devfs_unlink  */
+
+static int devfs_symlink (struct inode *dir, struct dentry *dentry,
+			  const char *symname)
+{
+    int err;
+    struct fs_info *fs_info;
+    struct devfs_inode *pi;
+    struct devfs_inode *di = NULL;
+    struct devfs_entry *parent, *de;
+    struct inode *inode;
+
+    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
+    fs_info = dir->i_sb->u.generic_sbp;
+    /*  First try to get the devfs entry for this directory  */
+    pi = get_devfs_inode_from_vfs_inode (dir);
+    if (pi == NULL) return -EINVAL;
+    parent = pi->de;
+    if (!parent->registered) return -ENOENT;
+    err = devfs_mk_symlink (parent, dentry->d_name.name, dentry->d_name.len,
+			    DEVFS_FL_NONE, symname, 0, &de, NULL);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_DISABLED)
+	printk ("%s: symlink(): errcode from <devfs_mk_symlink>: %d\n",
+		DEVFS_NAME, err);
+#endif
+    if (err < 0) return err;
+    /*  Search for an inode for this FS  */
+    for (di = de->first_inode; di != NULL; di = di->next)
+    {
+	if (di->fs_info == fs_info) break;
+    }
+    if (di == NULL) di = create_devfs_inode (de, fs_info);
+    if (di == NULL) return -ENOMEM;
+    di->mode = de->mode;
+    di->atime = CURRENT_TIME;
+    di->mtime = CURRENT_TIME;
+    di->ctime = CURRENT_TIME;
+    if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL )
+	return -ENOMEM;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_DISABLED)
+	printk ("%s: symlink(): new VFS inode(%u): %p  dentry: %p\n",
+		DEVFS_NAME, di->ino, inode, dentry);
+#endif
+    de->hide = FALSE;
+    d_instantiate (dentry, inode);
+    devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+		       inode->i_uid, inode->i_gid, fs_info);
+    return 0;
+}   /*  End Function devfs_symlink  */
+
+static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+    int is_new;
+    struct fs_info *fs_info;
+    struct devfs_inode *di = NULL;
+    struct devfs_inode *pi;
+    struct devfs_entry *parent, *de;
+    struct inode *inode;
+
+    mode = (mode & ~S_IFMT) | S_IFDIR;
+    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
+    fs_info = dir->i_sb->u.generic_sbp;
+    /*  We are allowed to create the directory  */
+    /*  First try to get the devfs entry for this directory  */
+    pi = get_devfs_inode_from_vfs_inode (dir);
+    if (pi == NULL) return -EINVAL;
+    parent = pi->de;
+    if (!parent->registered) return -ENOENT;
+    /*  Try to reclaim an existing devfs entry, create if there isn't one  */
+    de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
+			   FALSE, TRUE, &is_new, FALSE);
+    if (de == NULL) return -ENOMEM;
+    if (de->registered)
+    {
+	printk ("%s: mkdir(): existing entry\n", DEVFS_NAME);
+	return -EEXIST;
+    }
+    de->registered = TRUE;
+    de->hide = FALSE;
+    if (!S_ISDIR (de->mode) && !is_new)
+    {
+	/*  Transmogrifying an old entry  */
+	de->u.dir.first = NULL;
+	de->u.dir.last = NULL;
+    }
+    de->mode = mode;
+    de->u.dir.num_removable = 0;
+    /*  Search for an inode for this FS  */
+    for (di = de->first_inode; di != NULL; di = di->next)
+    {
+	if (di->fs_info == fs_info) break;
+    }
+    if (di == NULL) di = create_devfs_inode (de, fs_info);
+    if (di == NULL) return -ENOMEM;
+    di->mode = mode;
+    di->uid = current->euid;
+    di->gid = current->egid;
+    di->atime = CURRENT_TIME;
+    di->mtime = CURRENT_TIME;
+    di->ctime = CURRENT_TIME;
+    if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL )
+	return -ENOMEM;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_DISABLED)
+	printk ("%s: mkdir(): new VFS inode(%u): %p  dentry: %p\n",
+		DEVFS_NAME, di->ino, inode, dentry);
+#endif
+    d_instantiate (dentry, inode);
+    devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+		       inode->i_uid, inode->i_gid, fs_info);
+    return 0;
+}   /*  End Function devfs_mkdir  */
+
+static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
+{
+    int has_children = FALSE;
+    struct fs_info *fs_info;
+    struct devfs_inode *di = NULL;
+    struct devfs_entry *de, *child;
+    struct inode *inode = dentry->d_inode;
+
+    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
+    if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL;
+    if (inode == dir) return -EPERM;
+    fs_info = dir->i_sb->u.generic_sbp;
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if (di == NULL) return -ENOENT;
+    de = di->de;
+    if (!de->registered) return -ENOENT;
+    if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
+    for (child = de->u.dir.first; child != NULL; child = child->next)
+    {
+	if (child->registered)
+	{
+	    has_children = TRUE;
+	    break;
+	}
+    }
+    if (has_children) return -ENOTEMPTY;
+    de->registered = FALSE;
+    de->hide = TRUE;
+    free_dentries (de);
+    return 0;
+}   /*  End Function devfs_rmdir  */
+
+static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
+			int rdev)
+{
+    int is_new;
+    struct fs_info *fs_info;
+    struct devfs_inode *di = NULL;
+    struct devfs_inode *pi;
+    struct devfs_entry *parent, *de;
+    struct inode *inode;
+
+#ifdef CONFIG_DEVFS_DEBUG
+    char txt[STRING_LENGTH];
+
+    if (devfs_debug & DEBUG_I_MKNOD)
+    {
+	memset (txt, 0, STRING_LENGTH);
+	memcpy (txt, dentry->d_name.name, dentry->d_name.len);
+	txt[STRING_LENGTH - 1] = '\0';
+	printk ("%s: mknod(%s): mode: 0%o  dev: %d\n",
+		DEVFS_NAME, txt, mode, rdev);
+    }
+#endif
+
+    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
+    fs_info = dir->i_sb->u.generic_sbp;
+    if ( !S_ISBLK (mode) && !S_ISCHR (mode) && !S_ISFIFO (mode) &&
+	 !S_ISSOCK (mode) ) return -EPERM;
+    /*  We are allowed to create the node  */
+    /*  First try to get the devfs entry for this directory  */
+    pi = get_devfs_inode_from_vfs_inode (dir);
+    if (pi == NULL) return -EINVAL;
+    parent = pi->de;
+    if (!parent->registered) return -ENOENT;
+    /*  Try to reclaim an existing devfs entry, create if there isn't one  */
+    de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
+			   FALSE, TRUE, &is_new, FALSE);
+    if (de == NULL) return -ENOMEM;
+    if (!de->registered)
+    {
+	/*  Since we created the devfs entry we get to choose things  */
+	de->info = NULL;
+	de->mode = mode;
+	if ( S_ISBLK (mode) || S_ISCHR (mode) )
+	{
+	    de->u.fcb.u.device.major = MAJOR (rdev);
+	    de->u.fcb.u.device.minor = MINOR (rdev);
+	    de->u.fcb.default_uid = current->euid;
+	    de->u.fcb.default_gid = current->egid;
+	    de->u.fcb.ops = NULL;
+	    de->u.fcb.auto_owner = FALSE;
+	    de->u.fcb.aopen_notify = FALSE;
+	    de->u.fcb.open = FALSE;
+	}
+	else if ( S_ISFIFO (mode) )
+	{
+	    de->u.fifo.uid = current->euid;
+	    de->u.fifo.gid = current->egid;
+	}
+    }
+    de->registered = TRUE;
+    de->show_unreg = FALSE;
+    de->hide = FALSE;
+    /*  Search for an inode for this FS  */
+    for (di = de->first_inode; di != NULL; di = di->next)
+    {
+	if (di->fs_info == fs_info) break;
+    }
+    if (di == NULL) di = create_devfs_inode (de, fs_info);
+    if (di == NULL) return -ENOMEM;
+    di->mode = mode;
+    di->uid = current->euid;
+    di->gid = current->egid;
+    di->atime = CURRENT_TIME;
+    di->mtime = CURRENT_TIME;
+    di->ctime = CURRENT_TIME;
+    if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL )
+	return -ENOMEM;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_MKNOD)
+	printk ("%s:   new VFS inode(%u): %p  dentry: %p\n",
+		DEVFS_NAME, di->ino, inode, dentry);
+#endif
+    d_instantiate (dentry, inode);
+    devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+		       inode->i_uid, inode->i_gid, fs_info);
+    return 0;
+}   /*  End Function devfs_mknod  */
+
+static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen)
+{
+    struct inode *inode = dentry->d_inode;
+    struct devfs_inode *di;
+
+    if ( !inode || !S_ISLNK (inode->i_mode) ) return -EINVAL;
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if (di == NULL) return -ENOENT;
+    if (!di->de->registered) return -ENOENT;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_RLINK)
+	printk ("%s: readlink(): dentry: %p\n", DEVFS_NAME, dentry);
+#endif
+    if (buflen > di->de->u.symlink.length + 1)
+	buflen = di->de->u.symlink.length + 1;
+    if (copy_to_user (buffer, di->de->u.symlink.linkname, buflen) == 0)
+	return buflen;
+    return -EFAULT;
+}   /*  End Function devfs_readlink  */
+
+static struct dentry *devfs_follow_link (struct dentry *dentry,
+					 struct dentry *base,
+					 unsigned int follow)
+{
+    struct inode *inode = dentry->d_inode;
+    struct devfs_inode *di;
+
+    if ( !inode || !S_ISLNK (inode->i_mode) )
+    {
+	dget (dentry);
+	dput (base);
+	return dentry;
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_FLINK)
+	printk ("%s: follow_link(): dentry: %p\n", DEVFS_NAME, dentry);
+#endif
+    di = get_devfs_inode_from_vfs_inode (inode);
+    if ( (di == NULL) || !di->de->registered )
+    {
+	dput (base);
+	return ERR_PTR (-ENOENT);
+    }
+    base = lookup_dentry (di->de->u.symlink.linkname, base, follow);
+    return base;
+}   /*  End Function devfs_follow_link  */
+
+static struct inode_operations devfs_iops =
+{
+    default_file_ops: &devfs_fops,
+    lookup:           devfs_lookup,
+    link:             devfs_link,
+    unlink:           devfs_unlink,
+    symlink:          devfs_symlink,
+    mkdir:            devfs_mkdir,
+    rmdir:            devfs_rmdir,
+    mknod:            devfs_mknod,
+    readlink:         devfs_readlink,
+    follow_link:      devfs_follow_link,
+};
+
+static struct super_block *devfs_read_super (struct super_block *sb,
+					     void *data, int silent)
+{
+    char *aopt = data;
+    struct fs_info *fs_info = NULL;
+    struct devfs_inode *di;
+    struct inode *root_inode = NULL;
+
+    if (get_root_entry () == NULL) goto out_no_root;
+    if ( ( fs_info = kmalloc (sizeof *fs_info, GFP_KERNEL) ) == NULL )
+	return NULL;
+    memset (fs_info, 0, sizeof *fs_info);
+    atomic_set (&fs_info->devfsd_overrun_count, 0);
+    init_waitqueue_head (&fs_info->devfsd_wait_queue);
+    init_waitqueue_head (&fs_info->revalidate_wait_queue);
+    fs_info->prev = last_fs;
+    if (first_fs == NULL) first_fs = fs_info;
+    else last_fs->next = fs_info;
+    last_fs = fs_info;
+    fs_info->sb = sb;
+    if (aopt)
+    {
+	if (strcmp (aopt, "explicit") == 0) fs_info->require_explicit = TRUE;
+    }
+    lock_super (sb);
+    sb->u.generic_sbp = fs_info;
+    sb->s_blocksize = 1024;
+    sb->s_blocksize_bits = 10;
+    sb->s_magic = DEVFS_SUPER_MAGIC;
+    sb->s_op = &devfs_sops;
+    di = create_devfs_inode (root_entry, fs_info);
+    if (di == NULL) goto out_no_root;
+    if (di->ino != 1)
+    {
+	printk ("%s: read_super: root inode number is: %d!\n",
+		DEVFS_NAME, di->ino);
+	goto out_no_root;
+    }
+    if ( ( root_inode = get_vfs_inode (sb, di, NULL) ) == NULL )
+	goto out_no_root;
+    sb->s_root = D_ALLOC_ROOT (root_inode);
+    if (!sb->s_root) goto out_no_root;
+#ifdef CONFIG_DEVFS_TUNNEL
+    di->covered = dget (sb->s_root->d_covered);
+#endif
+    unlock_super (sb);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_DISABLED)
+	printk ("%s: read super, made devfs ptr: %p\n",
+		DEVFS_NAME, sb->u.generic_sbp);
+#endif
+    MOD_INC_USE_COUNT;
+    return sb;
+
+out_no_root:
+    printk ("devfs_read_super: get root inode failed\n");
+    delete_fs (fs_info);
+    if (root_inode) iput (root_inode);
+    sb->s_dev = 0;
+    unlock_super (sb);
+    return NULL;
+}   /*  End Function devfs_read_super  */
+
+
+static struct file_system_type devfs_fs_type =
+{
+    DEVFS_NAME, 
+    0,
+    devfs_read_super, 
+    NULL,
+};
+
+
+/*  File operations for devfsd follow  */
+
+static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
+			    loff_t *ppos)
+{
+    int done = FALSE;
+    int ival;
+    loff_t pos, devname_offset, tlen, rpos;
+    struct devfsd_notify_struct info;
+    struct devfsd_buf_entry *entry;
+    struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp;
+    DECLARE_WAITQUEUE (wait, current);
+
+    /*  Can't seek (pread) on this device  */
+    if (ppos != &file->f_pos) return -ESPIPE;
+    /*  Verify the task has grabbed the queue  */
+    if (fs_info->devfsd_task != current) return -EPERM;
+    info.major = 0;
+    info.minor = 0;
+    /*  Block for a new entry  */
+    add_wait_queue (&fs_info->devfsd_wait_queue, &wait);
+    current->state = TASK_INTERRUPTIBLE;
+    while ( devfsd_queue_empty (fs_info) )
+    {
+	fs_info->devfsd_sleeping = TRUE;
+	wake_up (&fs_info->revalidate_wait_queue);
+	schedule ();
+	fs_info->devfsd_sleeping = FALSE;
+	if ( signal_pending (current) )
+	{
+	    remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
+	    current->state = TASK_RUNNING;
+	    return -EINTR;
+	}
+    }
+    remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
+    current->state = TASK_RUNNING;
+    /*  Now play with the data  */
+    ival = atomic_read (&fs_info->devfsd_overrun_count);
+    if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count);
+    info.overrun_count = ival;
+    entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer +
+	fs_info->devfsd_buf_out;
+    info.type = entry->type;
+    info.mode = entry->mode;
+    info.uid = entry->uid;
+    info.gid = entry->gid;
+    if (entry->type == DEVFSD_NOTIFY_LOOKUP)
+    {
+	info.namelen = strlen (entry->data);
+	pos = 0;
+	memcpy (info.devname, entry->data, info.namelen + 1);
+    }
+    else
+    {
+	devfs_handle_t de = entry->data;
+
+	if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+	{
+	    info.major = de->u.fcb.u.device.major;
+	    info.minor = de->u.fcb.u.device.minor;
+	}
+	pos = devfs_generate_path (de, info.devname, DEVFS_PATHLEN);
+	if (pos < 0) return pos;
+	info.namelen = DEVFS_PATHLEN - pos - 1;
+	if (info.mode == 0) info.mode = de->mode;
+    }
+    devname_offset = info.devname - (char *) &info;
+    rpos = *ppos;
+    if (rpos < devname_offset)
+    {
+	/*  Copy parts of the header  */
+	tlen = devname_offset - rpos;
+	if (tlen > len) tlen = len;
+	if ( copy_to_user (buf, (char *) &info + rpos, tlen) )
+	{
+	    return -EFAULT;
+	}
+	rpos += tlen;
+	buf += tlen;
+	len -= tlen;
+    }
+    if ( (rpos >= devname_offset) && (len > 0) )
+    {
+	/*  Copy the name  */
+	tlen = info.namelen + 1;
+	if (tlen > len) tlen = len;
+	else done = TRUE;
+	if ( copy_to_user (buf, info.devname + pos + rpos - devname_offset,
+			   tlen) )
+	{
+	    return -EFAULT;
+	}
+	rpos += tlen;
+    }
+    tlen = rpos - *ppos;
+    if (done)
+    {
+	unsigned int next_pos = fs_info->devfsd_buf_out + 1;
+
+	if (next_pos >= devfsd_buf_size) next_pos = 0;
+	fs_info->devfsd_buf_out = next_pos;
+	*ppos = 0;
+    }
+    else *ppos = rpos;
+    return tlen;
+}   /*  End Function devfsd_read  */
+
+static int devfsd_ioctl (struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+    int ival;
+    struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+    switch (cmd)
+    {
+      case DEVFSDIOC_GET_PROTO_REV:
+	ival = DEVFSD_PROTOCOL_REVISION_KERNEL;
+	if ( copy_to_user ( (void *)arg, &ival, sizeof ival ) ) return -EFAULT;
+	break;
+      case DEVFSDIOC_SET_EVENT_MASK:
+	/*  Ensure only one reader has access to the queue. This scheme will
+	    work even if the global kernel lock were to be removed, because it
+	    doesn't matter who gets in first, as long as only one gets it  */
+	if (fs_info->devfsd_task == NULL)
+	{
+#ifdef __SMP__
+	    /*  Looks like no-one has it: check again and grab, with interrupts
+		disabled  */
+	    __cli ();
+	    if (fs_info->devfsd_task == NULL)
+#endif
+	    {
+		fs_info->devfsd_event_mask = 0;  /*  Temporary disable  */
+		fs_info->devfsd_task = current;
+	    }
+#ifdef __SMP__
+	    __sti ();
+#endif
+	}
+	/*  Verify the task has grabbed the queue  */
+	if (fs_info->devfsd_task != current) return -EBUSY;
+	fs_info->devfsd_file = file;
+	fs_info->devfsd_buffer = (void *) __get_free_page (GFP_KERNEL);
+	if (fs_info->devfsd_buffer == NULL)
+	{
+	    devfsd_close (inode, file);
+	    return -ENOMEM;
+	}
+	fs_info->devfsd_buf_out = fs_info->devfsd_buf_in;
+	fs_info->devfsd_event_mask = arg;  /*  Let the masses come forth  */
+	break;
+      case DEVFSDIOC_RELEASE_EVENT_QUEUE:
+	if (fs_info->devfsd_file != file) return -EPERM;
+	return devfsd_close (inode, file);
+	/*break;*/
+#ifdef CONFIG_DEVFS_DEBUG
+      case DEVFSDIOC_SET_DEBUG_MASK:
+	if ( copy_from_user (&ival, (void *) arg, sizeof ival) )return -EFAULT;
+	devfs_debug = ival;
+	break;
+#endif
+      default:
+	return -ENOIOCTLCMD;
+    }
+    return 0;
+}   /*  End Function devfsd_ioctl  */
+
+static int devfsd_close (struct inode *inode, struct file *file)
+{
+    struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+    if (fs_info->devfsd_file != file) return 0;
+    fs_info->devfsd_event_mask = 0;
+    fs_info->devfsd_file = NULL;
+    if (fs_info->devfsd_buffer)
+    {
+	while (fs_info->devfsd_buffer_in_use) schedule ();
+	free_page ( (unsigned long) fs_info->devfsd_buffer );
+    }
+    fs_info->devfsd_buffer = NULL;
+    fs_info->devfsd_task = NULL;
+    wake_up (&fs_info->revalidate_wait_queue);
+    return 0;
+}   /*  End Function devfsd_close  */
+
+
+#ifdef MODULE
+int init_module (void)
+#else
+int __init init_devfs_fs (void)
+#endif
+{
+    printk ("%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n",
+	    DEVFS_NAME, DEVFS_VERSION);
+#if defined(CONFIG_DEVFS_DEBUG) && !defined(MODULE)
+    devfs_debug = devfs_debug_init;
+    printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
+#endif
+#if !defined(MODULE)
+    printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
+#endif
+    return register_filesystem (&devfs_fs_type);
+}
+
+#ifndef MODULE
+void __init mount_devfs_fs (void)
+{
+    int err;
+    extern int do_mount (struct block_device *bdev, const char *dev_name,
+			 const char *dir_name, const char * type, int flags,
+			 void * data);
+
+    if ( (boot_options & OPTION_NOMOUNT) ) return;
+    err = do_mount (NULL, "none", "/dev", "devfs", 0, "");
+    if (err == 0) printk ("Mounted devfs on /dev\n");
+    else printk ("Warning: unable to mount devfs, err: %d\n", err);
+}   /*  End Function mount_devfs_fs  */
+#endif
+
+#ifdef MODULE
+static void free_entry (struct devfs_entry *parent)
+{
+    struct devfs_entry *de, *next;
+
+    if (parent == NULL) return;
+    for (de = parent->u.dir.first; de != NULL; de = next)
+    {
+	next = de->next;
+	if (de->first_inode != NULL)
+	{
+	    printk ("%s: free_entry(): unfreed inodes!\n", DEVFS_NAME);
+	}
+	if ( S_ISDIR (de->mode) )
+	{
+	    /*  Recursively free the subdirectories: this is a stack chomper */
+	    free_entry (de);
+	}
+	else kfree (de);
+    }
+    kfree (parent);
+}   /*  End Function free_entry  */
+
+void cleanup_module (void)
+{
+    unregister_filesystem (&devfs_fs_type);
+    if (first_fs != NULL)
+    {
+	printk ("%s: cleanup_module(): still mounted mounted filesystems!\n",
+		DEVFS_NAME);
+    }
+    free_entry (root_entry);
+}
+#endif  /*  MODULE  */
diff -u --recursive --new-file v2.3.45/linux/fs/devfs/util.c linux/fs/devfs/util.c
--- v2.3.45/linux/fs/devfs/util.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/devfs/util.c	Wed Feb 16 15:42:06 2000
@@ -0,0 +1,160 @@
+/*  devfs (Device FileSystem) utilities.
+
+    Copyright (C) 1999-2000  Richard Gooch
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+    The postal address is:
+      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+    ChangeLog
+
+    19991031   Richard Gooch <rgooch@atnf.csiro.au>
+               Created.
+    19991103   Richard Gooch <rgooch@atnf.csiro.au>
+               Created <_devfs_convert_name> and supported SCSI and IDE CD-ROMs
+    20000203   Richard Gooch <rgooch@atnf.csiro.au>
+               Changed operations pointer type to void *.
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/locks.h>
+#include <linux/kdev_t.h>
+#include <linux/devfs_fs_kernel.h>
+
+
+/*  Private functions follow  */
+
+static void __init _devfs_convert_name (char *new, const char *old, int disc)
+/*  [SUMMARY] Convert from an old style location-based name to new style.
+    <new> The new name will be written here.
+    <old> The old name.
+    <disc> If true, disc partitioning information should be processed.
+    [RETURNS] Nothing.
+*/
+{
+    int host, bus, target, lun;
+    char *ptr;
+    char part[8];
+
+    /*  Decode "c#b#t#u#"  */
+    if (old[0] != 'c') return;
+    host = simple_strtol (old + 1, &ptr, 10);
+    if (ptr[0] != 'b') return;
+    bus = simple_strtol (ptr + 1, &ptr, 10);
+    if (ptr[0] != 't') return;
+    target = simple_strtol (ptr + 1, &ptr, 10);
+    if (ptr[0] != 'u') return;
+    lun = simple_strtol (ptr + 1, &ptr, 10);
+    if (disc)
+    {
+	/*  Decode "p#"  */
+	if (ptr[0] == 'p') sprintf (part, "part%s", ptr + 1);
+	else strcpy (part, "disc");
+    }
+    else part[0] = '\0';
+    sprintf (new, "/host%d/bus%d/target%d/lun%d/%s",
+	     host, bus, target, lun, part);
+}   /*  End Function _devfs_convert_name  */
+
+
+/*  Public functions follow  */
+
+/*PUBLIC_FUNCTION*/
+void __init devfs_make_root (const char *name)
+/*  [SUMMARY] Create the root FS device entry if required.
+    <name> The name of the root FS device, as passed by "root=".
+    [RETURNS] Nothing.
+*/
+{
+    char dest[64];
+
+    if ( (strncmp (name, "sd/", 3) == 0) || (strncmp (name, "sr/", 3) == 0) )
+    {
+	strcpy (dest, "../scsi");
+	_devfs_convert_name (dest + 7, name + 3, (name[1] == 'd') ? 1 : 0);
+    }
+    else if ( (strncmp (name, "ide/hd/", 7) == 0) ||
+	      (strncmp (name, "ide/cd/", 7) == 0) )
+    {
+	strcpy (dest, "..");
+	_devfs_convert_name (dest + 2, name + 7, (name[4] == 'h') ? 1 : 0);
+    }
+    else return;
+    devfs_mk_symlink (NULL, name, 0, DEVFS_FL_DEFAULT, dest, 0, NULL,NULL);
+}   /*  End Function devfs_make_root  */
+
+/*PUBLIC_FUNCTION*/
+void devfs_register_tape (devfs_handle_t de)
+/*  [SUMMARY] Register a tape device in the "/dev/tapes" hierarchy.
+    <de> Any tape device entry in the device directory.
+    [RETURNS] Nothing.
+*/
+{
+    int pos;
+    devfs_handle_t parent, slave;
+    char name[16], dest[64];
+    static unsigned int tape_counter = 0;
+    static devfs_handle_t tape_dir = NULL;
+
+    if (tape_dir == NULL) tape_dir = devfs_mk_dir (NULL, "tapes", 5, NULL);
+    parent = devfs_get_parent (de);
+    pos = devfs_generate_path (parent, dest + 3, sizeof dest - 3);
+    if (pos < 0) return;
+    strncpy (dest + pos, "../", 3);
+    sprintf (name, "tape%u", tape_counter++);
+    devfs_mk_symlink (tape_dir, name, 0, DEVFS_FL_DEFAULT, dest + pos, 0,
+		      &slave, NULL);
+    devfs_auto_unregister (de, slave);
+}   /*  End Function devfs_register_tape  */
+EXPORT_SYMBOL(devfs_register_tape);
+
+/*PUBLIC_FUNCTION*/
+void devfs_register_series (devfs_handle_t dir, const char *format,
+			    unsigned int num_entries, unsigned int flags,
+			    unsigned int major, unsigned int minor_start,
+			    umode_t mode, uid_t uid, gid_t gid,
+			    void *ops, void *info)
+/*  [SUMMARY] Register a sequence of device entries.
+    <dir> The handle to the parent devfs directory entry. If this is NULL the
+    new names are relative to the root of the devfs.
+    <format> The printf-style format string. A single "%u" is allowed.
+    <flags> A set of bitwise-ORed flags (DEVFS_FL_*).
+    <major> The major number. Not needed for regular files.
+    <minor_start> The starting minor number. Not needed for regular files.
+    <mode> The default file mode.
+    <uid> The default UID of the file.
+    <guid> The default GID of the file.
+    <ops> The <<file_operations>> or <<block_device_operations>> structure.
+    This must not be externally deallocated.
+    <info> An arbitrary pointer which will be written to the <<private_data>>
+    field of the <<file>> structure passed to the device driver. You can set
+    this to whatever you like, and change it once the file is opened (the next
+    file opened will not see this change).
+    [RETURNS] Nothing.
+*/
+{
+    unsigned int count;
+    char devname[128];
+
+    for (count = 0; count < num_entries; ++count)
+    {
+	sprintf (devname, format, count);
+	devfs_register (dir, devname, 0, flags, major, minor_start + count,
+			mode, uid, gid, ops, info);
+    }
+}   /*  End Function devfs_register_series  */
+EXPORT_SYMBOL(devfs_register_series);
diff -u --recursive --new-file v2.3.45/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.3.45/linux/fs/filesystems.c	Fri Jan 28 15:09:08 2000
+++ linux/fs/filesystems.c	Wed Feb 16 15:42:06 2000
@@ -14,6 +14,7 @@
 #include <linux/msdos_fs.h>
 #include <linux/umsdos_fs.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/nfs_fs.h>
 #include <linux/iso_fs.h>
 #include <linux/sysv_fs.h>
@@ -85,6 +86,8 @@
 #ifdef CONFIG_PROC_FS
 	init_proc_fs();
 #endif
+
+	init_devfs_fs();  /*  Header file may make this empty  */
 
 #ifdef CONFIG_LOCKD
 	nlmxdr_init();
diff -u --recursive --new-file v2.3.45/linux/fs/nfsd/nfs3xdr.c linux/fs/nfsd/nfs3xdr.c
--- v2.3.45/linux/fs/nfsd/nfs3xdr.c	Tue Dec 14 01:27:24 1999
+++ linux/fs/nfsd/nfs3xdr.c	Wed Feb 16 14:56:36 2000
@@ -181,7 +181,7 @@
 	} else {
 		p = enc64(p, (u64) inode->i_size);
 	}
-	p = enc64(p, inode->i_blksize * inode->i_blocks);
+	p = enc64(p, ((u64)inode->i_blocks) << 9);
 	*p++ = htonl((u32) MAJOR(inode->i_rdev));
 	*p++ = htonl((u32) MINOR(inode->i_rdev));
 	p = enc64(p, (u64) inode->i_dev);
@@ -211,7 +211,7 @@
 	} else {
 		p = enc64(p, (u64) fhp->fh_post_size);
 	}
-	p = enc64(p, fhp->fh_post_blksize * fhp->fh_post_blocks);
+	p = enc64(p, ((u64)fhp->fh_post_blocks) << 9);
 	*p++ = htonl((u32) MAJOR(fhp->fh_post_rdev));
 	*p++ = htonl((u32) MINOR(fhp->fh_post_rdev));
 	p = enc64(p, (u64) inode->i_dev);
diff -u --recursive --new-file v2.3.45/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.3.45/linux/fs/nfsd/nfsfh.c	Sun Feb 13 19:29:04 2000
+++ linux/fs/nfsd/nfsfh.c	Wed Feb 16 14:56:36 2000
@@ -301,7 +301,7 @@
 	struct dentry *dentry, *result = NULL;
 	struct dentry *tmp;
 	int  found =0;
-	long err;
+	int err;
 	/* This semaphore is needed to make sure that only one unconnected (free)
 	 * dcache path ever exists, as otherwise two partial paths might get
 	 * joined together, which would be very confusing.
diff -u --recursive --new-file v2.3.45/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.3.45/linux/fs/nfsd/nfssvc.c	Tue Dec 14 01:27:24 1999
+++ linux/fs/nfsd/nfssvc.c	Wed Feb 16 14:56:36 2000
@@ -42,7 +42,13 @@
 extern struct svc_program	nfsd_program;
 static void			nfsd(struct svc_rqst *rqstp);
 struct timeval			nfssvc_boot = { 0, 0 };
-static int			nfsd_active = 0;
+static struct svc_serv 		*nfsd_serv = NULL;
+
+struct nfsd_list {
+	struct list_head 	list;
+	struct task_struct	*task;
+};
+struct list_head nfsd_list = LIST_HEAD_INIT(nfsd_list);
 
 /*
  * Maximum number of nfsd processes
@@ -52,45 +58,60 @@
 int
 nfsd_svc(unsigned short port, int nrservs)
 {
-	struct svc_serv *	serv;
-	int			error;
+	int	error;
+	int	none_left;	
+	struct list_head *victim;
 
 	dprintk("nfsd: creating service\n");
 	error = -EINVAL;
 	if (nrservs <= 0)
-		goto out;
+		nrservs = 0;
 	if (nrservs > NFSD_MAXSERVS)
 		nrservs = NFSD_MAXSERVS;
-	nfsd_nservers = nrservs;
-
-	error = -ENOMEM;
-	nfsd_racache_init();     /* Readahead param cache */
-	if (nfsd_nservers == 0)
+	
+	/* Readahead param cache - will no-op if it already exists */
+	error =	nfsd_racache_init(2*nrservs);
+	if (error<0)
 		goto out;
-	  
-	serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
-	if (serv == NULL)
-		goto out;
-
-	error = svc_makesock(serv, IPPROTO_UDP, port);
-	if (error < 0)
-		goto failure;
+	if (!nfsd_serv) {
+		nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
+		if (nfsd_serv == NULL)
+			goto out;
+		error = svc_makesock(nfsd_serv, IPPROTO_UDP, port);
+		if (error < 0)
+			goto failure;
 
 #if 0	/* Don't even pretend that TCP works. It doesn't. */
-	error = svc_makesock(serv, IPPROTO_TCP, port);
-	if (error < 0)
-		goto failure;
+		error = svc_makesock(nfsd_serv, IPPROTO_TCP, port);
+		if (error < 0)
+			goto failure;
 #endif
-
-	while (nrservs--) {
-		error = svc_create_thread(nfsd, serv);
+		get_fast_time(&nfssvc_boot);		/* record boot time */
+	} else
+		nfsd_serv->sv_nrthreads++;
+	nrservs -= (nfsd_serv->sv_nrthreads-1);
+	while (nrservs > 0) {
+		nrservs--;
+		error = svc_create_thread(nfsd, nfsd_serv);
 		if (error < 0)
 			break;
 	}
-
-failure:
-	svc_destroy(serv);		/* Release server */
-out:
+	victim = nfsd_list.next;
+	while (nrservs < 0 && victim != &nfsd_list) {
+		struct nfsd_list *nl =
+			list_entry(victim,struct nfsd_list, list);
+		victim = victim->next;
+		send_sig(SIGKILL, nl->task, 1);
+		nrservs++;
+	}
+ failure:
+	none_left = (nfsd_serv->sv_nrthreads == 1);
+	svc_destroy(nfsd_serv);		/* Release server */
+	if (none_left) {
+		nfsd_serv = NULL;
+		nfsd_racache_shutdown();
+	}
+ out:
 	return error;
 }
 
@@ -101,7 +122,8 @@
 nfsd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
-	int		oldumask, err;
+	int		err;
+	struct nfsd_list me;
 
 	/* Lock module and set up kernel thread */
 	MOD_INC_USE_COUNT;
@@ -109,17 +131,17 @@
 	exit_mm(current);
 	current->session = 1;
 	current->pgrp = 1;
+	sprintf(current->comm, "nfsd");
+	current->fs->umask = 0;
+
 	/* Let svc_process check client's authentication. */
 	rqstp->rq_auth = 1;
-	sprintf(current->comm, "nfsd");
 
-	oldumask = current->fs->umask;		/* Set umask to 0.  */
-	current->fs->umask = 0;
-	if (!nfsd_active++) {
-		nfssvc_boot = xtime;		/* record boot time */
-	}
 	lockd_up();				/* start lockd */
 
+	me.task = current;
+	list_add(&me.list, &nfsd_list);
+
 	/*
 	 * The main request loop
 	 */
@@ -173,17 +195,16 @@
 
 	/* Release lockd */
 	lockd_down();
-	if (!--nfsd_active) {
-		printk("nfsd: last server exiting\n");
-		/* revoke all exports */
-		nfsd_export_shutdown();
-		/* release read-ahead cache */
-	        nfsd_racache_shutdown();
+
+	/* Check if this is last thread */
+	if (serv->sv_nrthreads==1) {
+		nfsd_serv = NULL;
+	        nfsd_racache_shutdown();	/* release read-ahead cache */
 	}
+	list_del(&me.list);
 
-	/* Destroy the thread */
+	/* Release the thread */
 	svc_exit_thread(rqstp);
-	current->fs->umask = oldumask;
 
 	/* Release module */
 	MOD_DEC_USE_COUNT;
diff -u --recursive --new-file v2.3.45/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.3.45/linux/fs/nfsd/vfs.c	Sun Feb 13 19:29:04 2000
+++ linux/fs/nfsd/vfs.c	Wed Feb 16 14:56:36 2000
@@ -73,8 +73,6 @@
 				p_rawin;
 };
 
-int nfsd_nservers = 0;
-#define FILECACHE_MAX		(2 * nfsd_nservers) 
 static struct raparms *		raparml = NULL;
 static struct raparms *		raparm_cache = NULL;
 
@@ -272,10 +270,7 @@
 		if (err)
 			goto out_nfserr;
 
-		err = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
-				  iap->ia_size<inode->i_size ? iap->ia_size : inode->i_size,
-				  abs(inode->i_size - iap->ia_size));
-
+		err = locks_verify_truncate(inode, NULL, iap->ia_size);
 		if (err)
 			goto out_nfserr;
 		DQUOT_INIT(inode);
@@ -377,7 +372,7 @@
 	int			error;
 
 	error = fh_verify(rqstp, fhp, 0, MAY_NOP);
-	if (error < 0)
+	if (error)
 		goto out;
 
 	export = fhp->fh_export;
@@ -1061,9 +1056,7 @@
 	err = get_write_access(inode);
 	if (err)
 		goto out_nfserr;
-	err = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
-				  size<inode->i_size ? size : inode->i_size,
-				  abs(inode->i_size - size));
+	err = locks_verify_truncate(inode, NULL, size);
 	if (err)
 		goto out_nfserr;
 
@@ -1703,34 +1696,34 @@
 {
 	if (!raparm_cache)
 		return;
-	dprintk("nfsd: freeing %d readahead buffers.\n", FILECACHE_MAX);
+	dprintk("nfsd: freeing readahead buffers.\n");
 	kfree(raparml);
-	nfsd_nservers = 0;
 	raparm_cache = raparml = NULL;
 }
 /*
  * Initialize readahead param cache
  */
-void
-nfsd_racache_init(void)
+int
+nfsd_racache_init(int cache_size)
 {
 	int	i;
 
 	if (raparm_cache)
-		return;
-	raparml = kmalloc(sizeof(struct raparms) * FILECACHE_MAX, GFP_KERNEL);
+		return 0;
+	raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL);
 
 	if (raparml != NULL) {
 		dprintk("nfsd: allocating %d readahead buffers.\n",
-			FILECACHE_MAX);
-		memset(raparml, 0, sizeof(struct raparms) * FILECACHE_MAX);
-		for (i = 0; i < FILECACHE_MAX - 1; i++) {
+			cache_size);
+		memset(raparml, 0, sizeof(struct raparms) * cache_size);
+		for (i = 0; i < cache_size - 1; i++) {
 			raparml[i].p_next = raparml + i + 1;
 		}
 		raparm_cache = raparml;
 	} else {
 		printk(KERN_WARNING
 		       "nfsd: Could not allocate memory read-ahead cache.\n");
-		nfsd_nservers = 0;
+		return -ENOMEM;
 	}
+	return 0;
 }
diff -u --recursive --new-file v2.3.45/linux/fs/open.c linux/fs/open.c
--- v2.3.45/linux/fs/open.c	Sun Feb 13 19:29:04 2000
+++ linux/fs/open.c	Wed Feb 16 12:59:58 2000
@@ -112,9 +112,7 @@
 	if (error)
 		goto dput_and_out;
 
-	error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
-				  length < inode->i_size ? length : inode->i_size,
-				  abs(inode->i_size - length));
+	error = locks_verify_truncate(inode, NULL, length);
 	if (!error) {
 		DQUOT_INIT(inode);
 		error = do_truncate(dentry, length);
@@ -157,9 +155,7 @@
 	error = -EPERM;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto out_putf;
-	error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
-				  length<inode->i_size ? length : inode->i_size,
-				  abs(inode->i_size - length));
+	error = locks_verify_truncate(inode, file, length);
 	lock_kernel();
 	if (!error)
 		error = do_truncate(dentry, length);
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/acorn.c linux/fs/partitions/acorn.c
--- v2.3.45/linux/fs/partitions/acorn.c	Tue Jan 11 22:31:43 2000
+++ linux/fs/partitions/acorn.c	Wed Feb 16 15:42:06 2000
@@ -466,7 +466,7 @@
 		r = adfspart_check_POWERTEC(hd, dev, first_sector, first_part_minor);
 #endif
 	if (r < 0)
-		printk(" unable to read boot sectors / partition sectors\n");
+		if (warn_no_part) printk(" unable to read boot sectors / partition sectors\n");
 	else if (r)
 		printk("\n");
 	return r;
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/amiga.c linux/fs/partitions/amiga.c
--- v2.3.45/linux/fs/partitions/amiga.c	Sun Aug 15 11:44:29 1999
+++ linux/fs/partitions/amiga.c	Wed Feb 16 15:42:06 2000
@@ -54,7 +54,7 @@
 
 	for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) {
 		if(!(bh = bread(dev,blk,blocksize))) {
-			printk("Dev %s: unable to read RDB block %d\n",
+			if (warn_no_part) printk("Dev %s: unable to read RDB block %d\n",
 			       kdevname(dev),blk);
 			goto rdb_done;
 		}
@@ -80,7 +80,7 @@
 			brelse(bh);
 			for (part = 1; blk > 0 && part <= 16; part++) {
 				if (!(bh = bread(dev,blk,blocksize))) {
-					printk("Dev %s: unable to read partition block %d\n",
+					if (warn_no_part) printk("Dev %s: unable to read partition block %d\n",
 						       kdevname(dev),blk);
 					goto rdb_done;
 				}
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/atari.c linux/fs/partitions/atari.c
--- v2.3.45/linux/fs/partitions/atari.c	Tue Sep  7 12:14:07 1999
+++ linux/fs/partitions/atari.c	Wed Feb 16 15:42:06 2000
@@ -48,7 +48,7 @@
 
   bh = bread (dev, 0, get_ptable_blocksize(dev));
   if (!bh) {
-      printk (" unable to read block 0 (partition table)\n");
+      if (warn_no_part) printk (" unable to read block 0 (partition table)\n");
       return -1;
   }
 
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/check.c linux/fs/partitions/check.c
--- v2.3.45/linux/fs/partitions/check.c	Thu Feb 10 17:11:18 2000
+++ linux/fs/partitions/check.c	Wed Feb 16 15:42:06 2000
@@ -38,6 +38,7 @@
 extern void initrd_load(void);
 
 struct gendisk *gendisk_head;
+int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
 
 static int (*check_part[])(struct gendisk *hd, kdev_t dev, unsigned long first_sect, int first_minor) = {
 #ifdef CONFIG_ACORN_PARTITION
@@ -82,6 +83,14 @@
 	const char *maj = hd->major_name;
 	int unit = (minor >> hd->minor_shift) + 'a';
 
+	part = minor & ((1 << hd->minor_shift) - 1);
+	if (hd->part[minor].de) {
+		int pos;
+
+		pos = devfs_generate_path (hd->part[minor].de, buf, 64);
+		if (pos >= 0)
+			return buf + pos;
+	}
 	/*
 	 * IDE devices use multiple major numbers, but the drives
 	 * are named as:  {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
@@ -110,7 +119,6 @@
 			maj = "hd";
 			break;
 	}
-	part = minor & ((1 << hd->minor_shift) - 1);
 	if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) {
 		unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16;
 		if (unit > 'z') {
@@ -153,13 +161,20 @@
  */
 void add_gd_partition(struct gendisk *hd, int minor, int start, int size)
 {
+#ifndef CONFIG_DEVFS_FS
 	char buf[40];
+#endif
+
 	hd->part[minor].start_sect = start;
 	hd->part[minor].nr_sects   = size;
+#ifdef CONFIG_DEVFS_FS
+	printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
+#else
 	if (hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7)
 		printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
 	else
 		printk(" %s", disk_name(hd, minor, buf));
+#endif
 }
 
 int get_hardsect_size(kdev_t dev)
@@ -217,7 +232,7 @@
 int get_partition_list(char * page)
 {
 	struct gendisk *p;
-	char buf[40];
+	char buf[64];
 	int n, len;
 
 	len = sprintf(page, "major minor  #blocks  name\n\n");
@@ -237,9 +252,10 @@
 
 static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor)
 {
+	devfs_handle_t de = NULL;
 	static int first_time = 1;
 	unsigned long first_sector;
-	char buf[40];
+	char buf[64];
 	int i;
 
 	if (first_time)
@@ -256,12 +272,104 @@
 		return;
 	}
 
-	printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
+	if (hd->de_arr)
+		de = hd->de_arr[MINOR(dev) >> hd->minor_shift];
+	i = devfs_generate_path (de, buf, sizeof buf);
+	if (i >= 0)
+		printk(KERN_INFO " /dev/%s:", buf + i);
+	else
+		printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
 	for (i = 0; check_part[i]; i++)
 		if (check_part[i](hd, dev, first_sector, first_part_minor))
-			return;
+			goto setup_devfs;
 
 	printk(" unknown partition table\n");
+setup_devfs:
+	i = first_part_minor - 1;
+	devfs_register_partitions (hd, i, hd->sizes ? 0 : 1);
+}
+
+#ifdef CONFIG_DEVFS_FS
+static void devfs_register_partition (struct gendisk *dev, int minor, int part)
+{
+	int devnum = minor >> dev->minor_shift;
+	devfs_handle_t dir;
+	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
+	char devname[16];
+
+	if (dev->part[minor + part].de) return;
+	dir = devfs_get_parent (dev->part[minor].de);
+	if (!dir) return;
+	if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
+		devfs_flags |= DEVFS_FL_REMOVABLE;
+	sprintf (devname, "part%d", part);
+	dev->part[minor + part].de =
+	    devfs_register (dir, devname, 0, devfs_flags,
+			    dev->major, minor + part,
+			    S_IFBLK | S_IRUSR | S_IWUSR, 0, 0,
+			    dev->fops, NULL);
+}
+
+static void devfs_register_disc (struct gendisk *dev, int minor)
+{
+	int pos = 0;
+	int devnum = minor >> dev->minor_shift;
+	devfs_handle_t dir, slave;
+	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
+	char dirname[64], symlink[16];
+	static unsigned int disc_counter = 0;
+	static devfs_handle_t devfs_handle = NULL;
+
+	if (dev->part[minor].de) return;
+	if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
+		devfs_flags |= DEVFS_FL_REMOVABLE;
+	if (dev->de_arr) {
+		dir = dev->de_arr[devnum];
+		if (!dir)  /*  Aware driver wants to block disc management  */
+			return;
+		pos = devfs_generate_path (dir, dirname + 3, sizeof dirname-3);
+		if (pos < 0) return;
+		strncpy (dirname + pos, "../", 3);
+	}
+	else {
+		/*  Unaware driver: construct "real" directory  */
+		sprintf (dirname, "../%s/disc%d", dev->major_name, devnum);
+		dir = devfs_mk_dir (NULL, dirname + 3, 0, NULL);
+	}
+	if (!devfs_handle)
+		devfs_handle = devfs_mk_dir (NULL, "discs", 5, NULL);
+	sprintf (symlink, "disc%u", disc_counter++);
+	devfs_mk_symlink (devfs_handle, symlink, 0, DEVFS_FL_DEFAULT,
+			  dirname + pos, 0, &slave, NULL);
+	dev->part[minor].de =
+	    devfs_register (dir, "disc", 4, devfs_flags, dev->major, minor,
+			    S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, dev->fops,NULL);
+	devfs_auto_unregister (dev->part[minor].de, slave);
+	if (!dev->de_arr)
+		devfs_auto_unregister (slave, dir);
+}
+#endif  /*  CONFIG_DEVFS_FS  */
+
+void devfs_register_partitions (struct gendisk *dev, int minor, int unregister)
+{
+#ifdef CONFIG_DEVFS_FS
+	int part;
+
+	if (!unregister)
+		devfs_register_disc (dev, minor);
+	for (part = 1; part < dev->max_p; part++) {
+		if ( unregister || (dev->part[part + minor].nr_sects < 1) ) {
+			devfs_unregister (dev->part[part + minor].de);
+			dev->part[part + minor].de = NULL;
+			continue;
+		}
+		devfs_register_partition (dev, minor, part);
+	}
+	if (unregister) {
+		devfs_unregister (dev->part[minor].de);
+		dev->part[minor].de = NULL;
+	}
+#endif  /*  CONFIG_DEVFS_FS  */
 }
 
 /*
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/check.h linux/fs/partitions/check.h
--- v2.3.45/linux/fs/partitions/check.h	Thu Aug 12 12:26:06 1999
+++ linux/fs/partitions/check.h	Wed Feb 16 15:42:06 2000
@@ -8,3 +8,5 @@
  * Get the default block size for this device
  */
 unsigned int get_ptable_blocksize(kdev_t dev);
+
+extern int warn_no_part;
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/msdos.c linux/fs/partitions/msdos.c
--- v2.3.45/linux/fs/partitions/msdos.c	Fri Jan 21 18:19:17 2000
+++ linux/fs/partitions/msdos.c	Wed Feb 16 15:42:06 2000
@@ -353,7 +353,7 @@
 read_mbr:
 #endif
 	if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
-		printk(" unable to read partition table\n");
+		if (warn_no_part) printk(" unable to read partition table\n");
 		return -1;
 	}
 	data = bh->b_data;
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/osf.c linux/fs/partitions/osf.c
--- v2.3.45/linux/fs/partitions/osf.c	Thu Aug 12 12:26:06 1999
+++ linux/fs/partitions/osf.c	Wed Feb 16 15:42:06 2000
@@ -57,7 +57,7 @@
 	struct d_partition * partition;
 
 	if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
-		printk("unable to read partition table\n");
+		if (warn_no_part) printk("unable to read partition table\n");
 		return -1;
 	}
 	label = (struct disklabel *) (bh->b_data+64);
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/sgi.c linux/fs/partitions/sgi.c
--- v2.3.45/linux/fs/partitions/sgi.c	Fri Jan 28 15:09:09 2000
+++ linux/fs/partitions/sgi.c	Wed Feb 16 15:42:06 2000
@@ -44,7 +44,7 @@
 	struct sgi_partition *p;
 
 	if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) {
-		printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev));
+		if (warn_no_part) printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev));
 		return -1;
 	}
 	label = (struct sgi_disklabel *) bh->b_data;
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/sun.c linux/fs/partitions/sun.c
--- v2.3.45/linux/fs/partitions/sun.c	Fri Jan 28 15:09:09 2000
+++ linux/fs/partitions/sun.c	Wed Feb 16 15:42:06 2000
@@ -48,7 +48,7 @@
 	unsigned long spc;
 
 	if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) {
-		printk(KERN_WARNING "Dev %s: unable to read partition table\n",
+		if (warn_no_part) printk(KERN_WARNING "Dev %s: unable to read partition table\n",
 		       kdevname(dev));
 		return -1;
 	}
diff -u --recursive --new-file v2.3.45/linux/fs/partitions/ultrix.c linux/fs/partitions/ultrix.c
--- v2.3.45/linux/fs/partitions/ultrix.c	Thu Aug 12 12:26:06 1999
+++ linux/fs/partitions/ultrix.c	Wed Feb 16 15:42:06 2000
@@ -35,7 +35,7 @@
 
 	bh = bread (dev, SBLOCK, get_ptable_blocksize(dev));
 	if (!bh) {
-		printk (" unable to read block 0x%lx\n", SBLOCK);
+		if (warn_no_part) printk (" unable to read block 0x%lx\n", SBLOCK);
 		return -1;
 	}
 	
diff -u --recursive --new-file v2.3.45/linux/fs/super.c linux/fs/super.c
--- v2.3.45/linux/fs/super.c	Thu Feb 10 17:11:19 2000
+++ linux/fs/super.c	Wed Feb 16 15:42:06 2000
@@ -15,12 +15,14 @@
  *
  *  Added kerneld support: Jacques Gelinas and Bjorn Ekwall
  *  Added change_root: Werner Almesberger & Hans Lermen, Feb '96
+ *  Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998
  */
 
 #include <linux/config.h>
 #include <linux/malloc.h>
 #include <linux/locks.h>
 #include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/fd.h>
 #include <linux/init.h>
 #include <linux/quotaops.h>
@@ -1080,6 +1082,8 @@
 		goto out;
 
 	if (fstype->fs_flags & FS_REQUIRES_DEV) {
+		struct block_device_operations *bdops;
+
 		dentry = namei(dev_name);
 		retval = PTR_ERR(dentry);
 		if (IS_ERR(dentry))
@@ -1095,6 +1099,8 @@
 			goto dput_and_out;
 
 		bdev = inode->i_bdev;
+		bdops = devfs_get_ops ( devfs_get_handle_from_inode (inode) );
+		if (bdops) bdev->bd_op = bdops;
 	}
 
 	page = 0;
@@ -1123,6 +1129,9 @@
 	struct block_device *bdev = NULL;
 	mode_t mode;
 	int retval;
+	void *handle;
+	char path[64];
+	int path_start = -1;
 
 #ifdef CONFIG_ROOT_NFS
 	if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
@@ -1178,9 +1187,22 @@
 	}
 #endif
 
+	devfs_make_root (root_device_name);
+	handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME, 0,
+	                            MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
+				    DEVFS_SPECIAL_BLK, 1);
+	if (handle)  /*  Sigh: bd*() functions only paper over the cracks  */
+	{
+	    unsigned major, minor;
+
+	    devfs_get_maj_min (handle, &major, &minor);
+	    ROOT_DEV = MKDEV (major, minor);
+	}
 	bdev = bdget(kdev_t_to_nr(ROOT_DEV));
 	if (!bdev)
 		panic(__FUNCTION__ ": unable to allocate root device");
+	bdev->bd_op = devfs_get_ops (handle);
+	path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
 	mode = FMODE_READ;
 	if (!(root_mountflags & MS_RDONLY))
 		mode |= FMODE_WRITE;
@@ -1189,13 +1211,15 @@
 		root_mountflags |= MS_RDONLY;
 		retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS);
 	}
-	if (retval)
+	if (retval) {
 	        /*
 		 * Allow the user to distinguish between failed open
 		 * and bad superblock on root device.
 		 */
-		printk("VFS: Cannot open root device %s\n",
-		       kdevname(ROOT_DEV));
+		printk ("VFS: Cannot open root device \"%s\" or %s\n",
+			root_device_name, kdevname (ROOT_DEV));
+		printk ("Please append a correct \"root=\" boot option\n");
+	}
 	else for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
   		if (!(fs_type->fs_flags & FS_REQUIRES_DEV))
   			continue;
@@ -1207,7 +1231,16 @@
 			printk ("VFS: Mounted root (%s filesystem)%s.\n",
 				fs_type->name,
 				(sb->s_flags & MS_RDONLY) ? " readonly" : "");
-			vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
+			if (path_start >= 0) {
+				devfs_mk_symlink (NULL,
+						  "root", 0, DEVFS_FL_DEFAULT,
+						  path + 5 + path_start, 0,
+						  NULL, NULL);
+				memcpy (path + path_start, "/dev/", 5);
+				vfsmnt = add_vfsmnt (sb, path + path_start,
+						     "/");
+			}
+			else vfsmnt = add_vfsmnt (sb, "/dev/root", "/");
 			if (vfsmnt) {
 				bdput(bdev); /* sb holds a reference */
 				return;
@@ -1343,6 +1376,18 @@
 		printk(KERN_CRIT "New root is busy. Staying in initrd.\n");
 		return -EBUSY;
 	}
+	/*  First unmount devfs if mounted  */
+	dir_d = lookup_dentry ("/dev", NULL, 1);
+	if (!IS_ERR(dir_d)) {
+		struct super_block *sb = dir_d->d_inode->i_sb;
+
+		if (sb && (dir_d->d_inode == sb->s_root->d_inode) &&
+		    (sb->s_magic == DEVFS_SUPER_MAGIC)) {
+			dput (dir_d);
+			do_umount (sb->s_dev, 0, 0);
+		}
+		else dput (dir_d);
+	}
 	ROOT_DEV = new_root_dev;
 	mount_root();
 	dput(old_root);
@@ -1351,6 +1396,7 @@
 	shrink_dcache();
 	printk("change_root: old root has d_count=%d\n", old_root->d_count);
 #endif
+	mount_devfs_fs ();
 	/*
 	 * Get the new mount directory
 	 */
diff -u --recursive --new-file v2.3.45/linux/fs/tunnel.c linux/fs/tunnel.c
--- v2.3.45/linux/fs/tunnel.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/tunnel.c	Wed Feb 16 15:42:06 2000
@@ -0,0 +1,50 @@
+/*  fs/tunnel.c: utility functions to support VFS tunnelling
+
+    Copyright (C) 1999  Richard Gooch
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+    The postal address is:
+      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+    ChangeLog
+
+    19991121   Richard Gooch <rgooch@atnf.csiro.au>
+               Created.
+*/
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+
+/*PUBLIC_FUNCTION*/
+struct dentry *vfs_tunnel_lookup (const struct dentry *dentry,
+				  const struct dentry *parent,
+				  const struct dentry *covered)
+/*  [SUMMARY] Lookup the corresponding dentry in the mounted-over FS.
+    <dentry> The dentry which is in the overmounting FS.
+    <parent> The parent of the dentry in the mounted-over FS. This may be NULL.
+    <covered> The dentry covered by the root dentry of the overmounting FS.
+    [RETURNS] A dentry on success, else NULL.
+*/
+{
+    struct dentry *root = dentry->d_sb->s_root;
+
+    if (covered == root) return NULL;
+    if (parent) return lookup_dentry (dentry->d_name.name, parent, 0);
+}   /*  End Function vfs_tunnel_lookup  */
diff -u --recursive --new-file v2.3.45/linux/include/asm-alpha/page.h linux/include/asm-alpha/page.h
--- v2.3.45/linux/include/asm-alpha/page.h	Tue Dec  7 09:32:49 1999
+++ linux/include/asm-alpha/page.h	Mon Feb 14 15:34:21 2000
@@ -109,6 +109,20 @@
 #define BUG()		__asm__ __volatile__("call_pal 129 # bugchk")
 #define PAGE_BUG(page)	BUG()
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* !ASSEMBLY */
 
 /* to align the pointer to the (next) page boundary */
diff -u --recursive --new-file v2.3.45/linux/include/asm-arm/page.h linux/include/asm-arm/page.h
--- v2.3.45/linux/include/asm-arm/page.h	Fri Jan 21 18:19:17 2000
+++ linux/include/asm-arm/page.h	Mon Feb 14 15:34:21 2000
@@ -65,6 +65,20 @@
 #define BUG()		__bug(__FILE__, __LINE__, NULL)
 #define PAGE_BUG(page)	__bug(__FILE__, __LINE__, page)
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* !__ASSEMBLY__ */
 
 #include <asm/arch/memory.h>
diff -u --recursive --new-file v2.3.45/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- v2.3.45/linux/include/asm-i386/floppy.h	Wed Aug 18 16:43:32 1999
+++ linux/include/asm-i386/floppy.h	Wed Feb 16 12:55:09 2000
@@ -190,7 +190,7 @@
 
 static unsigned long dma_mem_alloc(unsigned long size)
 {
-	return __get_dma_pages(GFP_KERNEL,__get_order(size));
+	return __get_dma_pages(GFP_KERNEL,get_order(size));
 }
 
 
@@ -207,7 +207,7 @@
 	if((unsigned int) addr >= (unsigned int) high_memory)
 		return vfree((void *)addr);
 	else
-		free_pages(addr, __get_order(size));		
+		free_pages(addr, get_order(size));		
 }
 
 #define fd_dma_mem_free(addr, size)  _fd_dma_mem_free(addr, size) 
diff -u --recursive --new-file v2.3.45/linux/include/asm-i386/page.h linux/include/asm-i386/page.h
--- v2.3.45/linux/include/asm-i386/page.h	Thu Feb 10 17:11:20 2000
+++ linux/include/asm-i386/page.h	Wed Feb 16 12:54:41 2000
@@ -94,6 +94,20 @@
 	BUG(); \
 } while (0)
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* __ASSEMBLY__ */
 
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
diff -u --recursive --new-file v2.3.45/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.3.45/linux/include/asm-i386/pgtable.h	Sun Feb 13 19:29:04 2000
+++ linux/include/asm-i386/pgtable.h	Wed Feb 16 12:54:46 2000
@@ -130,7 +130,8 @@
  * area for the same reason. ;)
  */
 #define VMALLOC_OFFSET	(8*1024*1024)
-#define VMALLOC_START	(((unsigned long) high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_START	(((unsigned long) high_memory + 2*VMALLOC_OFFSET-1) & \
+						~(VMALLOC_OFFSET-1))
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
 #define VMALLOC_END	(FIXADDR_START)
 
diff -u --recursive --new-file v2.3.45/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.3.45/linux/include/asm-i386/processor.h	Fri Jan 21 18:19:17 2000
+++ linux/include/asm-i386/processor.h	Wed Feb 16 14:36:52 2000
@@ -419,4 +419,16 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+struct microcode {
+	unsigned int hdrver;
+	unsigned int rev;
+	unsigned int date;
+	unsigned int sig;
+	unsigned int cksum;
+	unsigned int ldrver;
+	unsigned int pf;
+	unsigned int reserved[5];
+	unsigned int bits[500];
+};
+
 #endif /* __ASM_I386_PROCESSOR_H */
diff -u --recursive --new-file v2.3.45/linux/include/asm-ia64/page.h linux/include/asm-ia64/page.h
--- v2.3.45/linux/include/asm-ia64/page.h	Thu Feb 10 17:11:20 2000
+++ linux/include/asm-ia64/page.h	Mon Feb 14 15:34:22 2000
@@ -127,6 +127,18 @@
 #define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0)
 #define PAGE_BUG(page) do { BUG(); } while (0)
 
+extern __inline__ int get_order(unsigned long size)
+{
+        double d = size - 1;
+	long order;
+                
+	__asm__ ("getf.exp %0=%1" : "=r"(order) : "f"(d));
+	order = order - PAGE_SHIFT - 0xffff + 1;
+	if (order < 0)
+		order = 0;
+	return order;
+}
+
 #endif /* !ASSEMBLY */
 
 #define PAGE_OFFSET		0xe000000000000000
diff -u --recursive --new-file v2.3.45/linux/include/asm-m68k/page.h linux/include/asm-m68k/page.h
--- v2.3.45/linux/include/asm-m68k/page.h	Fri Jan 28 15:09:09 2000
+++ linux/include/asm-m68k/page.h	Mon Feb 14 15:34:21 2000
@@ -97,6 +97,20 @@
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* !__ASSEMBLY__ */
 
 #include <asm/page_offset.h>
diff -u --recursive --new-file v2.3.45/linux/include/asm-mips/page.h linux/include/asm-mips/page.h
--- v2.3.45/linux/include/asm-mips/page.h	Fri Jun 25 17:37:53 1999
+++ linux/include/asm-mips/page.h	Mon Feb 14 15:34:21 2000
@@ -65,6 +65,20 @@
 
 #endif /* !defined (STRICT_MM_TYPECHECKS) */
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* _LANGUAGE_ASSEMBLY */
 
 /* to align the pointer to the (next) page boundary */
diff -u --recursive --new-file v2.3.45/linux/include/asm-ppc/hardirq.h linux/include/asm-ppc/hardirq.h
--- v2.3.45/linux/include/asm-ppc/hardirq.h	Sun Feb 13 19:29:04 2000
+++ linux/include/asm-ppc/hardirq.h	Tue Feb 15 22:39:01 2000
@@ -1,6 +1,7 @@
 #ifndef __ASM_HARDIRQ_H
 #define __ASM_HARDIRQ_H
 
+#include <linux/config.h>
 #include <asm/smp.h>
 
 extern unsigned int local_irq_count[NR_CPUS];
diff -u --recursive --new-file v2.3.45/linux/include/asm-ppc/page.h linux/include/asm-ppc/page.h
--- v2.3.45/linux/include/asm-ppc/page.h	Sun Feb 13 19:29:04 2000
+++ linux/include/asm-ppc/page.h	Mon Feb 14 15:34:21 2000
@@ -113,6 +113,21 @@
 #define MAP_PAGE_RESERVED	(1<<15)
 
 extern unsigned long get_zero_page_fast(void);
+
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* __KERNEL__ */
 #endif /* __ASSEMBLY__ */
 #endif /* _PPC_PAGE_H */
diff -u --recursive --new-file v2.3.45/linux/include/asm-ppc/pgtable.h linux/include/asm-ppc/pgtable.h
--- v2.3.45/linux/include/asm-ppc/pgtable.h	Sun Feb 13 19:29:04 2000
+++ linux/include/asm-ppc/pgtable.h	Mon Feb 14 13:37:25 2000
@@ -69,6 +69,8 @@
 extern void flush_icache_range(unsigned long, unsigned long);
 extern void __flush_page_to_ram(unsigned long page_va);
 #define flush_page_to_ram(page)	__flush_page_to_ram(page_address(page))
+extern void __flush_icache_page(unsigned long page_va);
+#define flush_icache_page(vma, page) __flush_icache_page(page_address(page))
 
 extern unsigned long va_to_phys(unsigned long address);
 extern pte_t *va_to_pte(struct task_struct *tsk, unsigned long address);
diff -u --recursive --new-file v2.3.45/linux/include/asm-sh/page.h linux/include/asm-sh/page.h
--- v2.3.45/linux/include/asm-sh/page.h	Sun Nov  7 16:37:34 1999
+++ linux/include/asm-sh/page.h	Mon Feb 14 15:34:21 2000
@@ -76,6 +76,21 @@
 #define PAGE_BUG(page) do { \
 	BUG(); \
 } while (0)
+
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.3.45/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h
--- v2.3.45/linux/include/asm-sparc/page.h	Tue Feb  1 01:35:44 2000
+++ linux/include/asm-sparc/page.h	Mon Feb 14 15:34:21 2000
@@ -129,6 +129,20 @@
 
 #define TASK_UNMAPPED_BASE	BTFIXUP_SETHI(sparc_unmapped_base)
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #else /* !(__ASSEMBLY__) */
 
 #define __pgprot(x)	(x)
diff -u --recursive --new-file v2.3.45/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.3.45/linux/include/asm-sparc/pgtable.h	Thu Feb 10 17:11:21 2000
+++ linux/include/asm-sparc/pgtable.h	Mon Feb 14 15:31:14 2000
@@ -448,4 +448,7 @@
 
 #endif /* !(__ASSEMBLY__) */
 
+/* We provide our own get_unmapped_area to cope with VA holes for userland */
+#define HAVE_ARCH_UNMAPPED_AREA
+
 #endif /* !(_SPARC_PGTABLE_H) */
diff -u --recursive --new-file v2.3.45/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h
--- v2.3.45/linux/include/asm-sparc64/page.h	Wed Dec 29 13:13:21 1999
+++ linux/include/asm-sparc64/page.h	Mon Feb 14 15:34:21 2000
@@ -126,6 +126,20 @@
 
 extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
 #endif /* !(__ASSEMBLY__) */
 
 #endif /* !(__KERNEL__) */
diff -u --recursive --new-file v2.3.45/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.3.45/linux/include/asm-sparc64/pgtable.h	Sun Feb 13 19:29:04 2000
+++ linux/include/asm-sparc64/pgtable.h	Mon Feb 14 15:31:14 2000
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.118 1999/12/21 21:24:35 davem Exp $
+/* $Id: pgtable.h,v 1.119 2000/02/14 02:53:44 davem Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -280,17 +280,19 @@
  *    table.
  * 4) Splat.
  */
-extern void flush_icache_page(unsigned long phys_page);
+extern void __flush_icache_page(unsigned long phys_page);
 #define update_mmu_cache(__vma, __address, _pte) \
 do { \
 	unsigned short __flags = ((__vma)->vm_flags); \
 	if ((__flags & VM_EXEC) != 0 && \
 	    ((pte_val(_pte) & (_PAGE_PRESENT | _PAGE_WRITE | _PAGE_MODIFIED)) == \
 	     (_PAGE_PRESENT | _PAGE_WRITE | _PAGE_MODIFIED))) { \
-		flush_icache_page(pte_pagenr(_pte) << PAGE_SHIFT); \
+		__flush_icache_page(pte_pagenr(_pte) << PAGE_SHIFT); \
 	} \
 } while(0)
 
+#define flush_icache_page(vma, pg)	do { } while(0)
+
 /* Make a non-present pseudo-TTE. */
 extern inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space)
 {
@@ -350,5 +352,8 @@
 			       unsigned long size, pgprot_t prot, int space);
 
 #endif /* !(__ASSEMBLY__) */
+
+/* We provide our own get_unmapped_area to cope with VA holes for userland */
+#define HAVE_ARCH_UNMAPPED_AREA
 
 #endif /* !(_SPARC64_PGTABLE_H) */
diff -u --recursive --new-file v2.3.45/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v2.3.45/linux/include/linux/ax25.h	Wed Jul 22 13:45:58 1998
+++ linux/include/linux/ax25.h	Tue Feb 15 17:17:55 2000
@@ -29,9 +29,11 @@
 #define SIOCAX25NOUID		(SIOCPROTOPRIVATE+3)
 #define SIOCAX25OPTRT		(SIOCPROTOPRIVATE+7)
 #define SIOCAX25CTLCON		(SIOCPROTOPRIVATE+8)
-#define SIOCAX25GETINFO		(SIOCPROTOPRIVATE+9)
+#define SIOCAX25GETINFOOLD	(SIOCPROTOPRIVATE+9)
 #define SIOCAX25ADDFWD		(SIOCPROTOPRIVATE+10)
 #define SIOCAX25DELFWD		(SIOCPROTOPRIVATE+11)
+#define SIOCAX25DEVCTL          (SIOCPROTOPRIVATE+12)
+#define SIOCAX25GETINFO         (SIOCPROTOPRIVATE+13)
 
 #define AX25_SET_RT_IPMODE	2
 
@@ -80,6 +82,17 @@
         ax25_address            digi_addr[AX25_MAX_DIGIS];
 };
 
+/* this will go away. Please do not export to user land */
+struct ax25_info_struct_depreciated {
+	unsigned int	n2, n2count;
+	unsigned int	t1, t1timer;
+	unsigned int	t2, t2timer;
+	unsigned int	t3, t3timer;
+	unsigned int	idle, idletimer;
+	unsigned int	state;
+	unsigned int	rcv_q, snd_q;
+};
+
 struct ax25_info_struct {
 	unsigned int	n2, n2count;
 	unsigned int	t1, t1timer;
@@ -88,6 +101,9 @@
 	unsigned int	idle, idletimer;
 	unsigned int	state;
 	unsigned int	rcv_q, snd_q;
+	unsigned int	vs, vr, va, vs_max;
+	unsigned int	paclen;
+	unsigned int	window;
 };
 
 struct ax25_fwd_struct {
diff -u --recursive --new-file v2.3.45/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.3.45/linux/include/linux/blk.h	Fri Jan  7 19:13:23 2000
+++ linux/include/linux/blk.h	Wed Feb 16 12:56:33 2000
@@ -96,6 +96,18 @@
  * code duplication in drivers.
  */
 
+extern inline void blkdev_dequeue_request(struct request * req)
+{
+	if (req->q)
+	{
+		if (req->cmd == READ)
+			req->q->elevator.read_pendings--;
+		req->q->nr_segments -= req->nr_segments;
+		req->q = NULL;
+	}
+	list_del(&req->queue);
+}
+
 int end_that_request_first(struct request *req, int uptodate, char *name);
 void end_that_request_last(struct request *req);
 
@@ -373,7 +385,10 @@
 #if !defined(IDE_DRIVER)
 
 #ifndef CURRENT
-#define CURRENT (blk_dev[MAJOR_NR].request_queue.current_request)
+#define CURRENT blkdev_entry_next_request(&blk_dev[MAJOR_NR].request_queue.queue_head)
+#endif
+#ifndef QUEUE_EMPTY
+#define QUEUE_EMPTY list_empty(&blk_dev[MAJOR_NR].request_queue.queue_head)
 #endif
 
 #ifndef DEVICE_NAME
@@ -418,7 +433,7 @@
 #endif
 
 #define INIT_REQUEST \
-	if (!CURRENT) {\
+	if (QUEUE_EMPTY) {\
 		CLEAR_INTR; \
 		return; \
 	} \
@@ -446,7 +461,7 @@
 	add_blkdev_randomness(MAJOR(req->rq_dev));
 #endif
 	DEVICE_OFF(req->rq_dev);
-	CURRENT = req->next;
+	blkdev_dequeue_request(req);
 	end_that_request_last(req);
 }
 
diff -u --recursive --new-file v2.3.45/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v2.3.45/linux/include/linux/blkdev.h	Thu Feb 10 17:11:21 2000
+++ linux/include/linux/blkdev.h	Wed Feb 16 12:54:56 2000
@@ -5,6 +5,10 @@
 #include <linux/sched.h>
 #include <linux/genhd.h>
 #include <linux/tqueue.h>
+#include <linux/list.h>
+
+struct request_queue;
+typedef struct request_queue request_queue_t;
 
 /*
  * Ok, this is an expanded form so that we can use the same
@@ -13,6 +17,9 @@
  * for read/write completion.
  */
 struct request {
+	struct list_head queue;
+	int elevator_sequence;
+
 	volatile int rq_status;	/* should split this into a few status bits */
 #define RQ_INACTIVE		(-1)
 #define RQ_ACTIVE		1
@@ -33,27 +40,42 @@
 	struct semaphore * sem;
 	struct buffer_head * bh;
 	struct buffer_head * bhtail;
-	struct request * next;
+	request_queue_t * q;
 };
 
-typedef struct request_queue request_queue_t;
 typedef int (merge_request_fn) (request_queue_t *q, 
 				struct request  *req,
-				struct buffer_head *bh);
+				struct buffer_head *bh,
+				int);
 typedef int (merge_requests_fn) (request_queue_t *q, 
 				 struct request  *req,
-				 struct request  *req2);
+				 struct request  *req2,
+				 int);
 typedef void (request_fn_proc) (request_queue_t *q);
 typedef request_queue_t * (queue_proc) (kdev_t dev);
 typedef void (make_request_fn) (int rw, struct buffer_head *bh);
 typedef void (plug_device_fn) (request_queue_t *q, kdev_t device);
 typedef void (unplug_device_fn) (void *q);
 
+typedef struct elevator_s
+{
+	int sequence;
+	int read_latency;
+	int write_latency;
+	int max_bomb_segments;
+	int read_pendings;
+} elevator_t;
+
 struct request_queue
 {
-	struct request		* current_request;
+	struct list_head queue_head;
+	/* together with queue_head for cacheline sharing */
+	elevator_t elevator;
+	unsigned int nr_segments;
+
 	request_fn_proc		* request_fn;
-	merge_request_fn	* merge_fn;
+	merge_request_fn	* back_merge_fn;
+	merge_request_fn	* front_merge_fn;
 	merge_requests_fn	* merge_requests_fn;
 	make_request_fn		* make_request_fn;
 	plug_device_fn		* plug_device_fn;
@@ -141,5 +163,13 @@
 /* read-ahead in pages.. */
 #define MAX_READAHEAD	31
 #define MIN_READAHEAD	3
+
+#define ELEVATOR_DEFAULTS ((elevator_t) { 0, NR_REQUEST>>1, NR_REQUEST<<5, 4, 0, })
+
+#define blkdev_entry_to_request(entry) list_entry((entry), struct request, queue)
+#define blkdev_entry_next_request(entry) blkdev_entry_to_request((entry)->next)
+#define blkdev_entry_prev_request(entry) blkdev_entry_to_request((entry)->prev)
+#define blkdev_next_request(req) blkdev_entry_to_request((req)->queue.next)
+#define blkdev_prev_request(req) blkdev_entry_to_request((req)->queue.prev)
 
 #endif
diff -u --recursive --new-file v2.3.45/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.3.45/linux/include/linux/cdrom.h	Fri Jan 28 15:09:09 2000
+++ linux/include/linux/cdrom.h	Wed Feb 16 15:42:06 2000
@@ -705,6 +705,7 @@
 };
 
 #ifdef __KERNEL__
+#include <linux/devfs_fs_kernel.h>
 
 struct cdrom_write_settings {
 	unsigned char fpacket;		/* fixed/variable packets */
@@ -718,6 +719,7 @@
 	struct cdrom_device_ops  *ops;  /* link to device_ops */
 	struct cdrom_device_info *next; /* next device_info for this major */
 	void *handle;		        /* driver-dependent data */
+	devfs_handle_t de;		/* real driver creates this  */
 /* specifications */
         kdev_t dev;	                /* device number */
 	int mask;                       /* mask of capability: disables them */
diff -u --recursive --new-file v2.3.45/linux/include/linux/console.h linux/include/linux/console.h
--- v2.3.45/linux/include/linux/console.h	Thu Feb 10 17:11:21 2000
+++ linux/include/linux/console.h	Wed Feb 16 12:54:42 2000
@@ -16,6 +16,7 @@
 
 #include <linux/types.h>
 #include <linux/kdev_t.h>
+#include <linux/spinlock.h>
 
 struct vc_data;
 struct console_font_op;
@@ -89,6 +90,8 @@
 #define CON_PRINTBUFFER	(1)
 #define CON_CONSDEV	(2) /* Last on the command line */
 #define CON_ENABLED	(4)
+
+extern spinlock_t console_lock;
 
 struct console
 {
diff -u --recursive --new-file v2.3.45/linux/include/linux/devfs_fs.h linux/include/linux/devfs_fs.h
--- v2.3.45/linux/include/linux/devfs_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/devfs_fs.h	Wed Feb 16 15:42:06 2000
@@ -0,0 +1,42 @@
+#ifndef _LINUX_DEVFS_FS_H
+#define _LINUX_DEVFS_FS_H
+
+#include <linux/ioctl.h>
+
+#define DEVFSD_PROTOCOL_REVISION_KERNEL  5
+
+#define	DEVFSD_IOCTL_BASE	'd'
+
+/*  These are the various ioctls  */
+#define DEVFSDIOC_GET_PROTO_REV         _IOR(DEVFSD_IOCTL_BASE, 0, int)
+#define DEVFSDIOC_SET_EVENT_MASK        _IOW(DEVFSD_IOCTL_BASE, 2, int)
+#define DEVFSDIOC_RELEASE_EVENT_QUEUE   _IOW(DEVFSD_IOCTL_BASE, 3, int)
+#define DEVFSDIOC_SET_DEBUG_MASK        _IOW(DEVFSD_IOCTL_BASE, 4, int)
+
+#define DEVFSD_NOTIFY_REGISTERED    0
+#define DEVFSD_NOTIFY_UNREGISTERED  1
+#define DEVFSD_NOTIFY_ASYNC_OPEN    2
+#define DEVFSD_NOTIFY_CLOSE         3
+#define DEVFSD_NOTIFY_LOOKUP        4
+#define DEVFSD_NOTIFY_CHANGE        5
+#define DEVFSD_NOTIFY_CREATE        6
+
+#define DEVFS_PATHLEN               1024  /*  Never change this otherwise the
+					      binary interface will change   */
+
+struct devfsd_notify_struct
+{
+    unsigned int type;           /*  DEVFSD_NOTIFY_* value                   */
+    unsigned int mode;           /*  Mode of the inode or device entry       */
+    unsigned int major;          /*  Major number of device entry            */
+    unsigned int minor;          /*  Minor number of device entry            */
+    unsigned int uid;            /*  Uid of process, inode or device entry   */
+    unsigned int gid;            /*  Gid of process, inode or device entry   */
+    unsigned int overrun_count;  /*  Number of lost events                   */
+    unsigned int namelen;        /*  Number of characters not including '\0' */
+    /*  The device name MUST come last                                       */
+    char devname[DEVFS_PATHLEN]; /*  This will be '\0' terminated            */
+};
+
+
+#endif  /*  _LINUX_DEVFS_FS_H  */
diff -u --recursive --new-file v2.3.45/linux/include/linux/devfs_fs_kernel.h linux/include/linux/devfs_fs_kernel.h
--- v2.3.45/linux/include/linux/devfs_fs_kernel.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/devfs_fs_kernel.h	Wed Feb 16 15:42:06 2000
@@ -0,0 +1,258 @@
+#ifndef _LINUX_DEVFS_FS_KERNEL_H
+#define _LINUX_DEVFS_FS_KERNEL_H
+
+#include <linux/fs.h>
+#include <linux/config.h>
+
+#define DEVFS_SUPER_MAGIC                0x1373
+
+#define IS_DEVFS_INODE(inode) (DEVFS_SUPER_MAGIC == (inode)->i_sb->s_magic)
+
+#define DEVFS_MINOR(inode) \
+    ({unsigned int m; /* evil GCC trickery */ \
+      ((inode)->i_sb && \
+       ((inode)->i_sb->s_magic==DEVFS_SUPER_MAGIC) && \
+       (devfs_get_maj_min(devfs_get_handle_from_inode((inode)),NULL,&m)==0) \
+      ) ? m : MINOR((inode)->r_dev); })
+
+
+#define DEVFS_FL_NONE         0x000  /* This helps making code more readable */
+#define DEVFS_FL_AUTO_OWNER   0x001  /* When a closed inode is opened the
+					ownerships are set to the opening
+					process and the protection is set to
+					that given in <<mode>>. When the inode
+					is closed, ownership reverts back to
+					<<uid>> and <<gid>> and the protection
+					is set to read-write for all         */
+#define DEVFS_FL_SHOW_UNREG   0x002  /* Show unregistered entries in
+					directory listings                   */
+#define DEVFS_FL_HIDE         0x004  /* Do not show entry in directory list  */
+#define DEVFS_FL_AUTO_DEVNUM  0x008  /* Automatically generate device number */
+#define DEVFS_FL_AOPEN_NOTIFY 0x010  /* Asynchronously notify devfsd on open */
+#define DEVFS_FL_REMOVABLE    0x020  /* This is a removable media device     */
+#define DEVFS_FL_WAIT         0x040  /* Wait for devfsd to finish            */
+#define DEVFS_FL_DEFAULT      DEVFS_FL_NONE
+
+
+#define DEVFS_SPECIAL_CHR     0
+#define DEVFS_SPECIAL_BLK     1
+
+typedef struct devfs_entry * devfs_handle_t;
+
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#  define ROOT_DEVICE_NAME ((real_root_dev ==ROOT_DEV) ? root_device_name:NULL)
+#else
+#  define ROOT_DEVICE_NAME root_device_name
+#endif
+
+
+#ifdef CONFIG_DEVFS_FS
+extern devfs_handle_t devfs_register (devfs_handle_t dir,
+				      const char *name, unsigned int namelen,
+				      unsigned int flags,
+				      unsigned int major, unsigned int minor,
+				      umode_t mode, uid_t uid, gid_t gid,
+				      void *ops, void *info);
+extern void devfs_unregister (devfs_handle_t de);
+extern int devfs_mk_symlink (devfs_handle_t dir,
+			     const char *name, unsigned int namelen,
+			     unsigned int flags,
+			     const char *link, unsigned int linklength,
+			     devfs_handle_t *handle, void *info);
+extern devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name,
+				    unsigned int namelen, void *info);
+extern devfs_handle_t devfs_find_handle (devfs_handle_t dir,
+					 const char *name,unsigned int namelen,
+					 unsigned int major,unsigned int minor,
+					 char type, int traverse_symlinks);
+extern int devfs_get_flags (devfs_handle_t de, unsigned int *flags);
+extern int devfs_set_flags (devfs_handle_t de, unsigned int flags);
+extern int devfs_get_maj_min (devfs_handle_t de, 
+			      unsigned int *major, unsigned int *minor);
+extern devfs_handle_t devfs_get_handle_from_inode (struct inode *inode);
+extern int devfs_generate_path (devfs_handle_t de, char *path, int buflen);
+extern void *devfs_get_ops (devfs_handle_t de);
+extern int devfs_set_file_size (devfs_handle_t de, unsigned long size);
+extern void *devfs_get_info (devfs_handle_t de);
+extern int devfs_set_info (devfs_handle_t de, void *info);
+extern devfs_handle_t devfs_get_parent (devfs_handle_t de);
+extern devfs_handle_t devfs_get_first_child (devfs_handle_t de);
+extern devfs_handle_t devfs_get_next_sibling (devfs_handle_t de);
+extern void devfs_auto_unregister (devfs_handle_t master,devfs_handle_t slave);
+extern devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master);
+extern const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen);
+extern int devfs_register_chrdev (unsigned int major, const char *name,
+				  struct file_operations *fops);
+extern int devfs_register_blkdev (unsigned int major, const char *name,
+				  struct block_device_operations *bdops);
+extern int devfs_unregister_chrdev (unsigned int major, const char *name);
+extern int devfs_unregister_blkdev (unsigned int major, const char *name);
+
+extern void devfs_register_tape (devfs_handle_t de);
+extern void devfs_register_series (devfs_handle_t dir, const char *format,
+				   unsigned int num_entries,
+				   unsigned int flags, unsigned int major,
+				   unsigned int minor_start,
+				   umode_t mode, uid_t uid, gid_t gid,
+				   void *ops, void *info);
+
+extern int init_devfs_fs (void);
+extern void mount_devfs_fs (void);
+extern void devfs_make_root (const char *name);
+#else  /*  CONFIG_DEVFS_FS  */
+static inline devfs_handle_t devfs_register (devfs_handle_t dir,
+					     const char *name,
+					     unsigned int namelen,
+					     unsigned int flags,
+					     unsigned int major,
+					     unsigned int minor,
+					     umode_t mode,
+					     uid_t uid, gid_t gid,
+					     void *ops, void *info)
+{
+    return NULL;
+}
+static inline void devfs_unregister (devfs_handle_t de)
+{
+    return;
+}
+static inline int devfs_mk_symlink (devfs_handle_t dir,
+				    const char *name, unsigned int namelen,
+				    unsigned int flags,
+				    const char *link, unsigned int linklength,
+				    devfs_handle_t *handle, void *info)
+{
+    return 0;
+}
+static inline devfs_handle_t devfs_mk_dir (devfs_handle_t dir,
+					   const char *name,
+					   unsigned int namelen, void *info)
+{
+    return NULL;
+}
+static inline devfs_handle_t devfs_find_handle (devfs_handle_t dir,
+						const char *name,
+						unsigned int namelen,
+						unsigned int major,
+						unsigned int minor,
+						char type,
+						int traverse_symlinks)
+{
+    return NULL;
+}
+static inline int devfs_get_flags (devfs_handle_t de, unsigned int *flags)
+{
+    return 0;
+}
+static inline int devfs_set_flags (devfs_handle_t de, unsigned int flags)
+{
+    return 0;
+}
+static inline int devfs_get_maj_min (devfs_handle_t de, 
+				     unsigned int *major, unsigned int *minor)
+{
+    return 0;
+}
+static inline devfs_handle_t devfs_get_handle_from_inode (struct inode *inode)
+{
+    return NULL;
+}
+static inline int devfs_generate_path (devfs_handle_t de, char *path,
+				       int buflen)
+{
+    return -ENOSYS;
+}
+static inline void *devfs_get_ops (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline int devfs_set_file_size (devfs_handle_t de, unsigned long size)
+{
+    return -ENOSYS;
+}
+static inline void *devfs_get_info (devfs_handle_t de, unsigned long size)
+{
+    return NULL;
+}
+static inline int devfs_set_info (devfs_handle_t de, void *info)
+{
+    return 0;
+}
+static inline devfs_handle_t devfs_get_parent (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline devfs_handle_t devfs_get_first_child (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline void devfs_auto_unregister (devfs_handle_t master,
+					  devfs_handle_t slave)
+{
+    return;
+}
+static inline devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master)
+{
+    return NULL;
+}
+static inline const char *devfs_get_name (devfs_handle_t de,
+					  unsigned int *namelen)
+{
+    return NULL;
+}
+static inline int devfs_register_chrdev (unsigned int major, const char *name,
+					 struct file_operations *fops)
+{
+    return register_chrdev (major, name, fops);
+}
+static inline int devfs_register_blkdev (unsigned int major, const char *name,
+					 struct block_device_operations *bdops)
+{
+    return register_blkdev (major, name, bdops);
+}
+static inline int devfs_unregister_chrdev (unsigned int major,const char *name)
+{
+    return unregister_chrdev (major, name);
+}
+static inline int devfs_unregister_blkdev (unsigned int major,const char *name)
+{
+    return unregister_blkdev (major, name);
+}
+
+static inline void devfs_register_tape (devfs_handle_t de)
+{
+    return;
+}
+
+static inline void devfs_register_series (devfs_handle_t dir,
+					  const char *format,
+					  unsigned int num_entries,
+					  unsigned int flags,
+					  unsigned int major,
+					  unsigned int minor_start,
+					  umode_t mode, uid_t uid, gid_t gid,
+					  void *ops, void *info)
+{
+    return;
+}
+
+static inline int init_devfs_fs (void)
+{
+    return 0;
+}
+static inline void mount_devfs_fs (void)
+{
+    return;
+}
+static inline void devfs_make_root (const char *name)
+{
+    return;
+}
+#endif  /*  CONFIG_DEVFS_FS  */
+
+#endif  /*  _LINUX_DEVFS_FS_KERNEL_H  */
diff -u --recursive --new-file v2.3.45/linux/include/linux/fb.h linux/include/linux/fb.h
--- v2.3.45/linux/include/linux/fb.h	Fri Jan 21 18:19:17 2000
+++ linux/include/linux/fb.h	Wed Feb 16 15:42:06 2000
@@ -221,6 +221,7 @@
 
 #include <linux/fs.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 
 struct fb_info;
@@ -277,6 +278,8 @@
    struct display *disp;		/* initial display variable */
    struct vc_data *display_fg;		/* Console visible on this display */
    char fontname[40];			/* default font name */
+   devfs_handle_t devfs_handle;         /* Devfs handle for new name         */
+   devfs_handle_t devfs_lhandle;        /* Devfs handle for compat. symlink  */
    int (*changevar)(int);		/* tell console var has changed */
    int (*switch_con)(int, struct fb_info*);
 					/* tell fb to switch consoles */
@@ -371,7 +374,7 @@
 
 /* drivers/video/fbmem.c */
 extern int register_framebuffer(struct fb_info *fb_info);
-extern int unregister_framebuffer(const struct fb_info *fb_info);
+extern int unregister_framebuffer(struct fb_info *fb_info);
 
 extern int num_registered_fb;
 extern struct fb_info *registered_fb[FB_MAX];
diff -u --recursive --new-file v2.3.45/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.3.45/linux/include/linux/fs.h	Sun Feb 13 19:29:04 2000
+++ linux/include/linux/fs.h	Wed Feb 16 15:42:06 2000
@@ -744,6 +744,19 @@
 	return 0;
 }
 
+extern inline int locks_verify_truncate(struct inode *inode,
+				    struct file *filp,
+				    loff_t size)
+{
+	if (inode->i_flock && MANDATORY_LOCK(inode))
+		return locks_mandatory_area(
+			FLOCK_VERIFY_WRITE, inode, filp,
+			size < inode->i_size ? size : inode->i_size,
+			abs(inode->i_size - size)
+		);
+	return 0;
+}
+
 
 /* fs/open.c */
 
@@ -774,6 +787,8 @@
 extern int blkdev_put(struct block_device *, int);
 
 /* fs/devices.c */
+extern const struct block_device_operations *get_blkfops(unsigned int);
+extern struct file_operations *get_chrfops(unsigned int, unsigned int);
 extern int register_chrdev(unsigned int, const char *, struct file_operations *);
 extern int unregister_chrdev(unsigned int, const char *);
 extern int chrdev_open(struct inode *, struct file *);
@@ -1003,6 +1018,8 @@
 unsigned long generate_cluster(kdev_t, int b[], int);
 unsigned long generate_cluster_swab32(kdev_t, int b[], int);
 extern kdev_t ROOT_DEV;
+extern char root_device_name[];
+
 
 extern void show_buffers(void);
 extern void mount_root(void);
diff -u --recursive --new-file v2.3.45/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.3.45/linux/include/linux/genhd.h	Thu Feb 10 17:11:21 2000
+++ linux/include/linux/genhd.h	Wed Feb 16 15:42:06 2000
@@ -29,7 +29,7 @@
 #define EZD_PARTITION		0x55	/* EZ-DRIVE */
 #define DM6_AUX1PARTITION	0x51	/* no DDO:  use xlated geom */
 #define DM6_AUX3PARTITION	0x53	/* no DDO:  use xlated geom */
-	
+
 struct partition {
 	unsigned char boot_ind;		/* 0x80 - active */
 	unsigned char head;		/* starting head */
@@ -43,12 +43,18 @@
 	unsigned int nr_sects;		/* nr of sectors in partition */
 } __attribute__((packed));
 
+#ifdef __KERNEL__
+#  include <linux/devfs_fs_kernel.h>
+
 struct hd_struct {
 	long start_sect;
 	long nr_sects;
 	int type;			/* currently RAID or normal */
+	devfs_handle_t de;              /* primary (master) devfs entry  */
 };
 
+#define GENHD_FL_REMOVABLE  1
+
 struct gendisk {
 	int major;			/* major number of driver */
 	const char *major_name;		/* name of major driver */
@@ -62,7 +68,12 @@
 
 	void *real_devices;		/* internal use */
 	struct gendisk *next;
+	struct block_device_operations *fops;
+
+	devfs_handle_t *de_arr;         /* one per physical disc */
+	char *flags;                    /* one per physical disc */
 };
+#endif  /*  __KERNEL__  */
 
 #ifdef CONFIG_SOLARIS_X86_PARTITION
 
@@ -217,7 +228,11 @@
 
 char *disk_name (struct gendisk *hd, int minor, char *buf);
 
+extern void devfs_register_partitions (struct gendisk *dev, int minor,
+				       int unregister);
+
 int get_hardsect_size(kdev_t dev);
+
 #endif
 
 #endif
diff -u --recursive --new-file v2.3.45/linux/include/linux/hysdn_if.h linux/include/linux/hysdn_if.h
--- v2.3.45/linux/include/linux/hysdn_if.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hysdn_if.h	Tue Feb 15 11:40:43 2000
@@ -0,0 +1,49 @@
+/* $Id: hysdn_if.h,v 1.1 2000/02/10 19:47:50 werner Exp $
+
+ * Linux driver for HYSDN cards, ioctl definitions shared by hynetmgr and driver.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.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, 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.
+ *
+ * $Log: hysdn_if.h,v $
+ * Revision 1.1  2000/02/10 19:47:50  werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+/****************/
+/* error values */
+/****************/
+#define ERR_NONE             0 /* no error occured */
+#define ERR_ALREADY_BOOT  1000 /* we are already booting */
+#define EPOF_BAD_MAGIC    1001 /* bad magic in POF header */
+#define ERR_BOARD_DPRAM   1002 /* board DPRAM failed */
+#define EPOF_INTERNAL     1003 /* internal POF handler error */
+#define EPOF_BAD_IMG_SIZE 1004 /* POF boot image size invalid */
+#define ERR_BOOTIMG_FAIL  1005 /* 1. stage boot image did not start */
+#define ERR_BOOTSEQ_FAIL  1006 /* 2. stage boot seq handshake timeout */
+#define ERR_POF_TIMEOUT   1007 /* timeout waiting for card pof ready */
+#define ERR_NOT_BOOTED    1008 /* operation only allowed when booted */
+#define ERR_CONF_LONG     1009 /* conf line is to long */ 
+#define ERR_INV_CHAN      1010 /* invalid channel number */ 
+#define ERR_ASYNC_TIME    1011 /* timeout sending async data */ 
+
+
+
+
diff -u --recursive --new-file v2.3.45/linux/include/linux/ide.h linux/include/linux/ide.h
--- v2.3.45/linux/include/linux/ide.h	Fri Jan 28 15:09:09 2000
+++ linux/include/linux/ide.h	Wed Feb 16 15:42:06 2000
@@ -13,6 +13,7 @@
 #include <linux/hdsmart.h>
 #include <linux/blkdev.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <asm/hdreg.h>
 
 /*
@@ -142,7 +143,7 @@
 /*
  * Some more useful definitions
  */
-#define IDE_MAJOR_NAME	"ide"	/* the same for all i/f; see also genhd.c */
+#define IDE_MAJOR_NAME	"hd"	/* the same for all i/f; see also genhd.c */
 #define MAJOR_NAME	IDE_MAJOR_NAME
 #define PARTN_BITS	6	/* number of minor dev bits for partitions */
 #define PARTN_MASK	((1<<PARTN_BITS)-1)	/* a useful bit mask */
@@ -290,6 +291,7 @@
 	char		name[4];	/* drive name, such as "hda" */
 	void 		*driver;	/* (ide_driver_t *) */
 	void		*driver_data;	/* extra driver data */
+	devfs_handle_t	de;		/* directory for device */
 	struct proc_dir_entry *proc;	/* /proc/ide/ directory entry */
 	void		*settings;	/* /proc/ide/ drive settings */
 	char		driver_req[10];	/* requests specific driver */
diff -u --recursive --new-file v2.3.45/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.3.45/linux/include/linux/interrupt.h	Sat Feb 12 11:22:11 2000
+++ linux/include/linux/interrupt.h	Wed Feb 16 12:54:42 2000
@@ -23,7 +23,6 @@
    
 enum {
 	TIMER_BH = 0,
-	CONSOLE_BH,
 	TQUEUE_BH,
 	DIGI_BH,
 	SERIAL_BH,
@@ -47,8 +46,8 @@
 
 /* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
    frequency threaded job scheduling. For almost all the purposes
-   tasklets are more than enough. F.e. KEYBOARD_BH, CONSOLE_BH, all serial
-   device BHs et al. are converted to tasklets, not to softirqs.
+   tasklets are more than enough. F.e. all serial device BHs et
+   al. should be converted to tasklets, not to softirqs.
  */
 
 enum
@@ -242,24 +241,6 @@
 {
 	tasklet_hi_schedule(bh_task_vec+nr);
 }
-
-extern __inline__ void disable_bh_nosync(int nr)
-{
-	tasklet_disable_nosync(bh_task_vec+nr);
-}
-
-extern __inline__ void disable_bh(int nr)
-{
-	tasklet_disable_nosync(bh_task_vec+nr);
-	if (!in_interrupt())
-		tasklet_unlock_wait(bh_task_vec+nr);
-}
-
-extern __inline__ void enable_bh(int nr)
-{
-	tasklet_enable(bh_task_vec+nr);
-}
-
 
 extern void init_bh(int nr, void (*routine)(void));
 extern void remove_bh(int nr);
diff -u --recursive --new-file v2.3.45/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.3.45/linux/include/linux/isdn.h	Thu Nov 11 20:11:53 1999
+++ linux/include/linux/isdn.h	Wed Feb 16 15:42:06 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.81 1999/10/27 21:21:18 detabc Exp $
+/* $Id: isdn.h,v 1.90 2000/02/06 21:50:00 detabc Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,52 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.90  2000/02/06 21:50:00  detabc
+ * add rewriting of socket's and frame's saddr for udp-ipv4 dynip-connections.
+ * Include checksum-recompute of ip- and udp-header's.
+ *
+ * Revision 1.89  2000/02/05 22:11:33  detabc
+ * Add rewriting of socket's and frame's saddr adressfield for
+ * dynip-connections.  Only for tcp/ipv4 and switchable per interface.
+ * Include checksum-recompute of ip- and tcp-header's.
+ *
+ * Revision 1.88  2000/01/20 19:59:43  keil
+ * Add FAX Class 1 support
+ *
+ * Revision 1.87  2000/01/09 20:43:15  detabc
+ * exand logical bind-group's for both call's (in and out).
+ * add first part of kernel-config-help for abc-extension.
+ *
+ * Revision 1.86  1999/12/05 16:06:09  detabc
+ * add resethandling for rawip-compression.
+ * at now all B2-Protocols are usable with rawip-compression
+ *
+ * Revision 1.85  1999/11/30 11:29:06  detabc
+ * add a on the fly frame-counter and limit
+ *
+ * Revision 1.84  1999/11/28 14:49:08  detabc
+ * In case of rawip-compress adjust dev[x]->ibytes/obytes to reflect the
+ * uncompressed size.
+ *
+ * Revision 1.83  1999/11/26 15:54:59  detabc
+ * added compression (isdn_bsdcompress) for rawip interfaces with x75i B2-protocol.
+ *
+ * Revision 1.82  1999/11/20 22:14:14  detabc
+ * added channel dial-skip in case of external use
+ * (isdn phone or another isdn device) on the same NTBA.
+ * usefull with two or more card's connected the different NTBA's.
+ * global switchable in kernel-config and also per netinterface.
+ *
+ * add auto disable of netinterface's in case of:
+ * 	to many connection's in short time.
+ * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
+ * 	or remote side.
+ * 	wrong password's or something else to a ISP (syncppp).
+ *
+ * possible encapsulations for this future are:
+ * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
+ * and ISDN_NET_ENCAP_CISCOHDLCK.
+ *
  * Revision 1.81  1999/10/27 21:21:18  detabc
  * Added support for building logically-bind-group's per interface.
  * usefull for outgoing call's with more then one isdn-card.
@@ -333,6 +379,12 @@
 #undef CONFIG_ISDN_WITH_ABC_IPV4_DYNADDR
 #undef CONFIG_ISDN_WITH_ABC_RCV_NO_HUPTIMER
 #undef CONFIG_ISDN_WITH_ABC_ICALL_BIND
+#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 
 
 
 /* New ioctl-codes */
@@ -359,6 +411,7 @@
 #define IIOCGETCPS  _IO('I',21)
 #define IIOCGETDVR  _IO('I',22)
 #define IIOCNETLCR  _IO('I',23) /* dwabc ioctl for LCR from isdnlog */
+#define IIOCNETDWRSET  _IO('I',24) /* dwabc ioctl to reset abc-values to default on a net-interface */
 
 #define IIOCNETALN  _IO('I',32)
 #define IIOCNETDLN  _IO('I',33)
@@ -494,6 +547,10 @@
 #  include <linux/concap.h>
 #endif
 
+#ifdef CONFIG_DEVFS_FS
+#  include <linux/devfs_fs_kernel.h>
+#endif
+
 #include <linux/isdnif.h>
 
 #define ISDN_DRVIOCTL_MASK       0x7f  /* Mask for Device-ioctl */
@@ -922,6 +979,16 @@
 	isdn_v110_stream  *v110[ISDN_MAX_CHANNELS];  /* V.110 private data         */
 	struct semaphore  sem;                       /* serialize list access*/
 	isdn_module       *modules;
+	unsigned long     global_features;
+#ifdef CONFIG_DEVFS_FS
+	devfs_handle_t devfs_handle_isdninfo;
+	devfs_handle_t devfs_handle_isdnctrl;
+	devfs_handle_t devfs_handle_isdnX[ISDN_MAX_CHANNELS];
+	devfs_handle_t devfs_handle_isdnctrlX[ISDN_MAX_CHANNELS];
+#  ifdef CONFIG_ISDN_PPP
+	devfs_handle_t devfs_handle_ipppX[ISDN_MAX_CHANNELS];
+#  endif
+#endif
 } isdn_dev;
 
 extern isdn_dev *dev;
diff -u --recursive --new-file v2.3.45/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v2.3.45/linux/include/linux/isdnif.h	Thu Nov 11 20:11:53 1999
+++ linux/include/linux/isdnif.h	Tue Feb 15 11:40:43 2000
@@ -1,4 +1,4 @@
-/* $Id: isdnif.h,v 1.32 1999/10/11 22:03:00 keil Exp $
+/* $Id: isdnif.h,v 1.33 2000/01/20 19:59:43 keil Exp $
  *
  * Linux ISDN subsystem
  *
@@ -22,6 +22,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.33  2000/01/20 19:59:43  keil
+ * Add FAX Class 1 support
+ *
  * Revision 1.32  1999/10/11 22:03:00  keil
  * COMPAT_NEED_UACCESS (no include in isdn_compat.h)
  *
@@ -175,10 +178,11 @@
 /*
  * Values for Layer-3-protocol-selection
  */
-#define ISDN_PROTO_L3_TRANS  0   /* Transparent                 */
-#define ISDN_PROTO_L3_TRANSDSP 1   /* Transparent with DSP    */
-#define ISDN_PROTO_L3_FAX    2   /* Fax Group 2/3             */
-#define ISDN_PROTO_L3_MAX    7   /* Max. 8 Protocols            */
+#define ISDN_PROTO_L3_TRANS	0	/* Transparent */
+#define ISDN_PROTO_L3_TRANSDSP	1	/* Transparent with DSP */
+#define ISDN_PROTO_L3_FCLASS2	2	/* Fax Group 2/3 CLASS 2 */
+#define ISDN_PROTO_L3_FCLASS1	3	/* Fax Group 2/3 CLASS 1 */
+#define ISDN_PROTO_L3_MAX	7	/* Max. 8 Protocols */
 
 #ifdef __KERNEL__
 
@@ -362,7 +366,8 @@
 /* Layer 3 */
 #define ISDN_FEATURE_L3_TRANS   (0x10000 << ISDN_PROTO_L3_TRANS)
 #define ISDN_FEATURE_L3_TRANSDSP (0x10000 << ISDN_PROTO_L3_TRANSDSP)
-#define ISDN_FEATURE_L3_FAX	(0x10000 << ISDN_PROTO_L3_FAX)
+#define ISDN_FEATURE_L3_FCLASS2	(0x10000 << ISDN_PROTO_L3_FCLASS2)
+#define ISDN_FEATURE_L3_FCLASS1	(0x10000 << ISDN_PROTO_L3_FCLASS1)
 
 #define ISDN_FEATURE_L3_MASK    (0x0FF0000) /* Max. 8 Protocols */
 #define ISDN_FEATURE_L3_SHIFT   (16)
@@ -463,6 +468,33 @@
 
 #endif /* TTY_FAX */
 
+#define ISDN_FAX_CLASS1_FAE	0
+#define ISDN_FAX_CLASS1_FTS	1
+#define ISDN_FAX_CLASS1_FRS	2
+#define ISDN_FAX_CLASS1_FTM	3
+#define ISDN_FAX_CLASS1_FRM	4
+#define ISDN_FAX_CLASS1_FTH	5
+#define ISDN_FAX_CLASS1_FRH	6
+#define ISDN_FAX_CLASS1_CTRL	7
+
+#define ISDN_FAX_CLASS1_OK	0
+#define ISDN_FAX_CLASS1_CONNECT	1
+#define ISDN_FAX_CLASS1_NOCARR	2
+#define ISDN_FAX_CLASS1_ERROR	3
+#define ISDN_FAX_CLASS1_FCERROR	4
+#define ISDN_FAX_CLASS1_QUERY	5
+
+typedef struct {
+	__u8	cmd;
+	__u8	subcmd;
+	__u8	para[50];
+} aux_s;
+
+#define AT_COMMAND	0
+#define AT_EQ_VALUE	1
+#define AT_QUERY	2
+#define AT_EQ_QUERY	3
+
 /* CAPI structs */
 
 /* this is compatible to the old union size */
@@ -494,13 +526,14 @@
 	int   command;		/* Command or Status (see above) */
 	ulong arg;		/* Additional Data               */
 	union {
-		ulong errcode;	/* Type of error with STAT_L1ERR         */
-		int length;	/* Amount of bytes sent with STAT_BSENT  */
-		u_char num[50];/* Additional Data			*/
+		ulong errcode;	/* Type of error with STAT_L1ERR	*/
+		int length;	/* Amount of bytes sent with STAT_BSENT	*/
+		u_char num[50];	/* Additional Data			*/
 		setup_parm setup;/* For SETUP msg			*/
 		capi_msg cmsg;	/* For CAPI like messages		*/
-		char display[85];/* display message data          */ 
-		dss1_cmd_stat dss1_io; /* DSS1 IO-parameter/result */
+		char display[85];/* display message data		*/ 
+		dss1_cmd_stat dss1_io; /* DSS1 IO-parameter/result	*/
+		aux_s aux;	/* for modem commands/indications	*/
 #ifdef CONFIG_ISDN_TTY_FAX
 		T30_s	*fax;	/* Pointer to ttys fax struct		*/
 #endif
diff -u --recursive --new-file v2.3.45/linux/include/linux/joystick.h linux/include/linux/joystick.h
--- v2.3.45/linux/include/linux/joystick.h	Wed Dec  8 14:11:28 1999
+++ linux/include/linux/joystick.h	Wed Feb 16 15:42:06 2000
@@ -129,6 +129,7 @@
 #define JS_BUFF_SIZE		64		/* output buffer size */
 
 #include <linux/version.h>
+#include <linux/devfs_fs_kernel.h>
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
 #error "You need to use at least v2.2 Linux kernel."
@@ -217,6 +218,7 @@
 	int num_axes;
 	int num_buttons;
 	char *name;
+	devfs_handle_t devfs_handle;
 };
 
 struct js_list {
diff -u --recursive --new-file v2.3.45/linux/include/linux/kbd_kern.h linux/include/linux/kbd_kern.h
--- v2.3.45/linux/include/linux/kbd_kern.h	Thu Feb 10 17:11:22 2000
+++ linux/include/linux/kbd_kern.h	Wed Feb 16 12:55:50 2000
@@ -69,6 +69,8 @@
 extern unsigned char getledstate(void);
 extern void setledstate(struct kbd_struct *kbd, unsigned int led);
 
+extern struct tasklet_struct console_tasklet;
+
 extern int do_poke_blanked_console;
 
 extern void (*kbd_ledfunc)(unsigned int led);
@@ -76,13 +78,13 @@
 extern inline void show_console(void)
 {
 	do_poke_blanked_console = 1;
-	mark_bh(CONSOLE_BH);
+	tasklet_schedule(&console_tasklet);
 }
 
 extern inline void set_console(int nr)
 {
 	want_console = nr;
-	mark_bh(CONSOLE_BH);
+	tasklet_schedule(&console_tasklet);
 }
 
 extern inline void set_leds(void)
@@ -162,7 +164,7 @@
 extern inline void con_schedule_flip(struct tty_struct *t)
 {
 	queue_task(&t->flip.tqueue, &con_task_queue);
-	mark_bh(CONSOLE_BH);
+	tasklet_schedule(&console_tasklet);
 }
 
 #endif
diff -u --recursive --new-file v2.3.45/linux/include/linux/kernelcapi.h linux/include/linux/kernelcapi.h
--- v2.3.45/linux/include/linux/kernelcapi.h	Thu Nov 11 20:11:53 1999
+++ linux/include/linux/kernelcapi.h	Tue Feb 15 11:40:43 2000
@@ -1,11 +1,15 @@
 /*
- * $Id: kernelcapi.h,v 1.4 1999/09/10 17:24:19 calle Exp $
+ * $Id: kernelcapi.h,v 1.5 2000/01/28 16:45:40 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.5  2000/01/28 16:45:40  calle
+ * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard),
+ * will search named driver and call the add_card function if one exist.
+ *
  * Revision 1.4  1999/09/10 17:24:19  calle
  * Changes for proposed standard for CAPI2.0:
  * - AK148 "Linux Exention"
@@ -57,8 +61,17 @@
 	int flag;
 } kcapi_flagdef;
 
+typedef struct kcapi_carddef {
+	char		driver[32];
+	unsigned int	port;
+	unsigned	irq;
+	unsigned int	membase;
+	int		cardnr;
+} kcapi_carddef;
+
 /* new ioctls >= 10 */
 #define KCAPI_CMD_TRACE		10
+#define KCAPI_CMD_ADDCARD	11	/* add card to named driver */
 
 /* 
  * flag > 2 => trace also data
diff -u --recursive --new-file v2.3.45/linux/include/linux/lp.h linux/include/linux/lp.h
--- v2.3.45/linux/include/linux/lp.h	Thu Feb 10 17:11:22 2000
+++ linux/include/linux/lp.h	Wed Feb 16 08:55:54 2000
@@ -26,8 +26,6 @@
 #define LP_TRUST_IRQ_  0x0200 /* obsolete */
 #define LP_NO_REVERSE  0x0400 /* No reverse mode available. */
 #define LP_DATA_AVAIL  0x0800 /* Data is available. */
-#define LP_HAVE_PORT_BIT   12 /* (0x1000) Port is claimed. */
-#define LP_PORT_BUSY   (1<<13) /* Reading or writing. */
 
 /* 
  * bit defines for 8255 status port
diff -u --recursive --new-file v2.3.45/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h
--- v2.3.45/linux/include/linux/miscdevice.h	Thu Feb 10 17:11:22 2000
+++ linux/include/linux/miscdevice.h	Wed Feb 16 15:42:06 2000
@@ -1,6 +1,8 @@
 #ifndef _LINUX_MISCDEVICE_H
 #define _LINUX_MISCDEVICE_H
 
+#include <linux/devfs_fs_kernel.h>
+
 #define BUSMOUSE_MINOR 0
 #define PSMOUSE_MINOR  1
 #define MS_BUSMOUSE_MINOR 2
@@ -36,6 +38,7 @@
 	const char *name;
 	struct file_operations *fops;
 	struct miscdevice * next, * prev;
+	devfs_handle_t devfs_handle;
 };
 
 extern int misc_register(struct miscdevice * misc);
diff -u --recursive --new-file v2.3.45/linux/include/linux/nbd.h linux/include/linux/nbd.h
--- v2.3.45/linux/include/linux/nbd.h	Thu Nov 11 20:11:53 1999
+++ linux/include/linux/nbd.h	Wed Feb 16 10:56:45 2000
@@ -60,8 +60,7 @@
 	struct socket * sock;
 	struct file * file; 		/* If == NULL, device is not ready, yet	*/
 	int magic;			/* FIXME: not if debugging is off	*/
-	struct request *head;	/* Requests are added here...			*/
-	struct request *tail;
+	struct list_head queue_head;	/* Requests are added here...			*/
 	struct semaphore queue_lock;
 };
 #endif
diff -u --recursive --new-file v2.3.45/linux/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- v2.3.45/linux/include/linux/nfsd/nfsd.h	Tue Feb  1 01:35:44 2000
+++ linux/include/linux/nfsd/nfsd.h	Wed Feb 16 14:56:36 2000
@@ -75,7 +75,7 @@
 
 /* nfsd/vfs.c */
 int		fh_lock_parent(struct svc_fh *, struct dentry *);
-void		nfsd_racache_init(void);
+int		nfsd_racache_init(int);
 void		nfsd_racache_shutdown(void);
 int		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
 				const char *, int, struct svc_fh *);
@@ -181,11 +181,6 @@
  * Time of server startup
  */
 extern struct timeval	nfssvc_boot;
-
-/*
- * The number of nfsd threads.
- */
-extern int		nfsd_nservers;
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.45/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.3.45/linux/include/linux/parport.h	Thu Nov 11 20:11:53 1999
+++ linux/include/linux/parport.h	Wed Feb 16 12:56:37 2000
@@ -345,11 +345,11 @@
 extern void parport_unregister_driver (struct parport_driver *);
 
 /* parport_register_device declares that a device is connected to a
-   port, and tells the kernel all it needs to know.  pf is the
-   preemption function (may be NULL for no callback) kf is the wake-up
-   function (may be NULL for no callback) irq_func is the interrupt
-   handler (may be NULL for no interrupts) handle is a user pointer
-   that gets handed to callback functions.  */
+   port, and tells the kernel all it needs to know.
+   - pf is the preemption function (may be NULL for no callback)
+   - kf is the wake-up function (may be NULL for no callback)
+   - irq_func is the interrupt handler (may be NULL for no interrupts)
+   - handle is a user pointer that gets handed to callback functions.  */
 struct pardevice *parport_register_device(struct parport *port, 
 			  const char *name,
 			  int (*pf)(void *), void (*kf)(void *),
diff -u --recursive --new-file v2.3.45/linux/include/linux/raid/md_k.h linux/include/linux/raid/md_k.h
--- v2.3.45/linux/include/linux/raid/md_k.h	Thu Feb 10 17:11:22 2000
+++ linux/include/linux/raid/md_k.h	Wed Feb 16 15:42:06 2000
@@ -329,7 +329,7 @@
 
 #define THREAD_WAKEUP  0
 
-#define MAX_DISKNAME_LEN 32
+#define MAX_DISKNAME_LEN 64
 
 typedef struct dev_name_s {
 	struct md_list_head list;
diff -u --recursive --new-file v2.3.45/linux/include/linux/tty_driver.h linux/include/linux/tty_driver.h
--- v2.3.45/linux/include/linux/tty_driver.h	Wed Aug 18 16:43:32 1999
+++ linux/include/linux/tty_driver.h	Wed Feb 16 15:42:06 2000
@@ -197,10 +197,15 @@
  *	optimize for this case if this flag is set.  (Note that there
  * 	is also a promise, if the above case is true, not to signal
  * 	overruns, either.)
+ *
+ * TTY_DRIVER_NO_DEVFS --- if set, do not create devfs entries. This
+ *	is only used by tty_register_driver().
+ *
  */
 #define TTY_DRIVER_INSTALLED		0x0001
 #define TTY_DRIVER_RESET_TERMIOS	0x0002
 #define TTY_DRIVER_REAL_RAW		0x0004
+#define TTY_DRIVER_NO_DEVFS		0x0008
 
 /* tty driver types */
 #define TTY_DRIVER_TYPE_SYSTEM		0x0001
diff -u --recursive --new-file v2.3.45/linux/include/linux/videodev.h linux/include/linux/videodev.h
--- v2.3.45/linux/include/linux/videodev.h	Fri Jan 28 15:09:09 2000
+++ linux/include/linux/videodev.h	Wed Feb 16 15:42:06 2000
@@ -9,6 +9,7 @@
 #if LINUX_VERSION_CODE >= 0x020100
 #include <linux/poll.h>
 #endif
+#include <linux/devfs_fs_kernel.h>
 
 struct video_device
 {
@@ -30,6 +31,7 @@
 	void *priv;		/* Used to be 'private' but that upsets C++ */
 	int busy;
 	int minor;
+	devfs_handle_t devfs_handle;
 };
 
 extern int videodev_init(void);
diff -u --recursive --new-file v2.3.45/linux/include/pcmcia/ciscode.h linux/include/pcmcia/ciscode.h
--- v2.3.45/linux/include/pcmcia/ciscode.h	Thu Nov 11 20:11:54 1999
+++ linux/include/pcmcia/ciscode.h	Tue Feb 15 08:53:46 2000
@@ -1,5 +1,5 @@
 /*
- * ciscode.h 1.39 1999/10/25 20:23:17
+ * ciscode.h 1.40 2000/02/01 19:06:40
  *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
@@ -90,6 +90,8 @@
 #define PRODID_OSITECH_JACK_288		0x0002
 #define PRODID_OSITECH_JACK_336		0x0007
 #define PRODID_OSITECH_SEVEN		0x0008
+
+#define MANFID_PIONEER			0x000b
 
 #define MANFID_PSION			0x016c
 
diff -u --recursive --new-file v2.3.45/linux/include/pcmcia/ss.h linux/include/pcmcia/ss.h
--- v2.3.45/linux/include/pcmcia/ss.h	Tue Jan  4 13:57:21 2000
+++ linux/include/pcmcia/ss.h	Tue Feb 15 08:53:46 2000
@@ -1,5 +1,5 @@
 /*
- * ss.h 1.25 1999/10/25 20:23:17
+ * ss.h 1.26 2000/02/04 20:35:21
  *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
@@ -45,6 +45,7 @@
 #define SS_CARDBUS	0x0800
 #define SS_3VCARD	0x1000
 #define SS_XVCARD	0x2000
+#define SS_PENDING	0x4000
 
 /* for InquireSocket */
 typedef struct socket_cap_t {
diff -u --recursive --new-file v2.3.45/linux/include/pcmcia/version.h linux/include/pcmcia/version.h
--- v2.3.45/linux/include/pcmcia/version.h	Fri Jan 21 18:19:17 2000
+++ linux/include/pcmcia/version.h	Tue Feb 15 08:53:46 2000
@@ -1,4 +1,4 @@
-/* version.h 1.81 1999/12/23 21:37:32 (David Hinds) */
+/* version.h 1.83 2000/02/03 02:16:14 (David Hinds) */
 
-#define CS_RELEASE "3.1.9"
-#define CS_RELEASE_CODE 0x3109
+#define CS_RELEASE "3.1.11"
+#define CS_RELEASE_CODE 0x310b
diff -u --recursive --new-file v2.3.45/linux/init/main.c linux/init/main.c
--- v2.3.45/linux/init/main.c	Sat Feb 12 11:22:11 2000
+++ linux/init/main.c	Wed Feb 16 15:42:06 2000
@@ -13,6 +13,7 @@
 
 #include <linux/config.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/unistd.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
@@ -122,6 +123,8 @@
 
 int root_mountflags = MS_RDONLY;
 char *execute_command = NULL;
+char root_device_name[64];
+
 
 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
@@ -249,6 +252,7 @@
 kdev_t __init name_to_kdev_t(char *line)
 {
 	int base = 0;
+
 	if (strncmp(line,"/dev/",5) == 0) {
 		struct dev_name_struct *dev = root_dev_names;
 		line += 5;
@@ -267,7 +271,18 @@
 
 static int __init root_dev_setup(char *line)
 {
+	int i;
+	char ch;
+
 	ROOT_DEV = name_to_kdev_t(line);
+	memset (root_device_name, 0, sizeof root_device_name);
+	if (strncmp (line, "/dev/", 5) == 0) line += 5;
+	for (i = 0; i < sizeof root_device_name - 1; ++i)
+	{
+	    ch = line[i];
+	    if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break;
+	    root_device_name[i] = ch;
+	}
 	return 1;
 }
 
@@ -676,6 +691,8 @@
 #endif
 	/* Mount the root filesystem.. */
 	mount_root();
+
+	mount_devfs_fs ();
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	root_mountflags = real_root_mountflags;
diff -u --recursive --new-file v2.3.45/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.3.45/linux/kernel/ksyms.c	Sun Feb 13 19:29:04 2000
+++ linux/kernel/ksyms.c	Wed Feb 16 15:42:06 2000
@@ -261,6 +261,7 @@
 EXPORT_SYMBOL(set_device_ro);
 EXPORT_SYMBOL(bmap);
 EXPORT_SYMBOL(sync_dev);
+EXPORT_SYMBOL(devfs_register_partitions);
 EXPORT_SYMBOL(blkdev_open);
 EXPORT_SYMBOL(blkdev_get);
 EXPORT_SYMBOL(blkdev_put);
diff -u --recursive --new-file v2.3.45/linux/kernel/printk.c linux/kernel/printk.c
--- v2.3.45/linux/kernel/printk.c	Fri Sep 10 23:57:37 1999
+++ linux/kernel/printk.c	Mon Feb 14 15:31:14 2000
@@ -318,14 +318,14 @@
 	unsigned long flags;
 	int len = strlen(s);
 
-	spin_lock_irqsave(&console_lock,flags);
+	spin_lock_irqsave(&console_lock, flags);
 	c = console_drivers;
 	while(c) {
 		if ((c->flags & CON_ENABLED) && c->write)
 			c->write(c, s, len);
 		c = c->next;
 	}
-	spin_unlock_irqrestore(&console_lock,flags);
+	spin_unlock_irqrestore(&console_lock, flags);
 }
 
 void unblank_console(void)
@@ -333,14 +333,14 @@
 	struct console *c;
 	unsigned long flags;
 	
-	spin_lock_irqsave(&console_lock,flags);
+	spin_lock_irqsave(&console_lock, flags);
 	c = console_drivers;
 	while(c) {
 		if ((c->flags & CON_ENABLED) && c->unblank)
 			c->unblank();
 		c = c->next;
 	}
-	spin_unlock_irqrestore(&console_lock,flags);
+	spin_unlock_irqrestore(&console_lock, flags);
 }
 
 /*
@@ -402,7 +402,7 @@
 	 *	Put this console in the list - keep the
 	 *	preferred driver at the head of the list.
 	 */
-	spin_lock_irqsave(&console_lock,flags);
+	spin_lock_irqsave(&console_lock, flags);
 	if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
 		console->next = console_drivers;
 		console_drivers = console;
@@ -445,7 +445,7 @@
 		j = 0;
 	}
 done:
-	spin_unlock_irqrestore(&console_lock,flags);
+	spin_unlock_irqrestore(&console_lock, flags);
 }
 
 
@@ -455,7 +455,7 @@
 	unsigned long flags;
 	int res = 1;
 
-	spin_lock_irqsave(&console_lock,flags);
+	spin_lock_irqsave(&console_lock, flags);
 	if (console_drivers == console) {
 		console_drivers=console->next;
 		res = 0;
@@ -471,7 +471,7 @@
 		}
 	}
 	
-	spin_unlock_irqrestore(&console_lock,flags);
+	spin_unlock_irqrestore(&console_lock, flags);
 	return res;
 }
 	
diff -u --recursive --new-file v2.3.45/linux/mm/filemap.c linux/mm/filemap.c
--- v2.3.45/linux/mm/filemap.c	Sun Feb 13 19:29:04 2000
+++ linux/mm/filemap.c	Mon Feb 14 15:02:39 2000
@@ -479,8 +479,8 @@
 	struct page *alias;
 	unsigned long flags;
 
-	flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
-	page->flags = flags | (1 << PG_locked);
+	flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error));
+	page->flags = flags | (1 << PG_locked) | (1 << PG_referenced);
 	get_page(page);
 	page->index = offset;
 	add_page_to_inode_queue(mapping, page);
diff -u --recursive --new-file v2.3.45/linux/mm/mmap.c linux/mm/mmap.c
--- v2.3.45/linux/mm/mmap.c	Sun Feb 13 19:29:04 2000
+++ linux/mm/mmap.c	Mon Feb 14 15:31:14 2000
@@ -347,6 +347,7 @@
  * For mmap() without MAP_FIXED and shmat() with addr=0.
  * Return value 0 means ENOMEM.
  */
+#ifndef HAVE_ARCH_UNMAPPED_AREA
 unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
 {
 	struct vm_area_struct * vmm;
@@ -366,6 +367,7 @@
 		addr = vmm->vm_end;
 	}
 }
+#endif
 
 #define vm_avl_empty	(struct vm_area_struct *) NULL
 
@@ -580,7 +582,7 @@
 	unsigned long start, unsigned long end)
 {
 	unsigned long first = start & PGDIR_MASK;
-	unsigned long last = (end + PGDIR_SIZE - 1) & PGDIR_MASK;
+	unsigned long last = end + PGDIR_SIZE - 1;
 	unsigned long start_index, end_index;
 
 	if (!prev) {
@@ -615,8 +617,10 @@
 	 */
 	start_index = pgd_index(first);
 	end_index = pgd_index(last);
-	if (end_index > start_index)
+	if (end_index > start_index) {
 		clear_page_tables(mm, start_index, end_index - start_index);
+		flush_tlb_pgtables(mm, first & PGDIR_MASK, last & PGDIR_MASK);
+	}
 }
 
 /* Munmap is split into 2 main parts -- this part which finds
diff -u --recursive --new-file v2.3.45/linux/mm/mremap.c linux/mm/mremap.c
--- v2.3.45/linux/mm/mremap.c	Wed Dec 15 10:43:17 1999
+++ linux/mm/mremap.c	Mon Feb 14 15:31:14 2000
@@ -165,19 +165,14 @@
  *
  * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise
  * This option implies MREMAP_MAYMOVE.
- *
- * "__new_addr" toying in order to not change the saved stack layout
- * for old x86 binaries that don't want %edi to change..
  */
-asmlinkage unsigned long sys_mremap(unsigned long addr,
+unsigned long do_mremap(unsigned long addr,
 	unsigned long old_len, unsigned long new_len,
-	unsigned long flags, unsigned long __new_addr)
+	unsigned long flags, unsigned long new_addr)
 {
-	unsigned long new_addr = __new_addr;
 	struct vm_area_struct *vma;
 	unsigned long ret = -EINVAL;
 
-	down(&current->mm->mmap_sem);
 	if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
 		goto out;
 
@@ -287,6 +282,17 @@
 		ret = move_vma(vma, addr, old_len, new_len, new_addr);
 	}
 out:
+	return ret;
+}
+
+asmlinkage unsigned long sys_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr)
+{
+	unsigned long ret;
+
+	down(&current->mm->mmap_sem);
+	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
 	up(&current->mm->mmap_sem);
 	return ret;
 }
diff -u --recursive --new-file v2.3.45/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.3.45/linux/mm/swapfile.c	Thu Feb 10 17:11:23 2000
+++ linux/mm/swapfile.c	Wed Feb 16 15:42:06 2000
@@ -634,11 +634,15 @@
 
 	if (S_ISBLK(swap_dentry->d_inode->i_mode)) {
 		kdev_t dev = swap_dentry->d_inode->i_rdev;
+		struct block_device_operations *bdops;
 
 		p->swap_device = dev;
 		set_blocksize(dev, PAGE_SIZE);
 		
 		bdev = swap_dentry->d_inode->i_bdev;
+		bdops = devfs_get_ops ( devfs_get_handle_from_inode
+					(swap_dentry->d_inode) );
+		if (bdops) bdev->bd_op = bdops;
 
 		error = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_SWAP);
 		if (error)
diff -u --recursive --new-file v2.3.45/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.3.45/linux/net/ax25/af_ax25.c	Tue Feb  1 01:35:44 2000
+++ linux/net/ax25/af_ax25.c	Tue Feb 15 17:17:55 2000
@@ -1574,8 +1574,10 @@
 				return -EPERM;
 			return ax25_ctl_ioctl(cmd, (void *)arg);
 
-		case SIOCAX25GETINFO: {
+		case SIOCAX25GETINFO: 
+		case SIOCAX25GETINFOOLD: {
 			struct ax25_info_struct ax25_info;
+
 			ax25_info.t1        = sk->protinfo.ax25->t1   / HZ;
 			ax25_info.t2        = sk->protinfo.ax25->t2   / HZ;
 			ax25_info.t3        = sk->protinfo.ax25->t3   / HZ;
@@ -1589,8 +1591,28 @@
 			ax25_info.state     = sk->protinfo.ax25->state;
 			ax25_info.rcv_q     = atomic_read(&sk->rmem_alloc);
 			ax25_info.snd_q     = atomic_read(&sk->wmem_alloc);
-			if (copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info)))
-				return -EFAULT;
+			ax25_info.vs        = sk->protinfo.ax25->vs;
+			ax25_info.vr        = sk->protinfo.ax25->vr;
+			ax25_info.va        = sk->protinfo.ax25->va;
+			ax25_info.vs_max    = sk->protinfo.ax25->vs; /* reserved */
+			ax25_info.paclen    = sk->protinfo.ax25->paclen;
+			ax25_info.window    = sk->protinfo.ax25->window;
+
+			/* old structure? */
+			if (cmd == SIOCAX25GETINFOOLD) {
+				static int warned = 0;
+				if (!warned) {
+					printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n",
+						current->comm);
+					warned=1;
+				}
+
+				if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated)))
+					return -EFAULT;
+			} else {
+				if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct)))
+					return -EINVAL;
+			} 
 			return 0;
 		}
 
diff -u --recursive --new-file v2.3.45/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.3.45/linux/net/ipv4/tcp_timer.c	Thu Feb 10 17:11:24 2000
+++ linux/net/ipv4/tcp_timer.c	Tue Feb 15 08:44:15 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.73 2000/02/09 11:16:42 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.74 2000/02/14 20:56:30 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -659,6 +659,7 @@
 				if (req->retrans == 0)
 					lopt->qlen_young--;
 				tcp_openreq_free(req);
+				continue;
 			}
 			reqp = &req->dl_next;
 		}
diff -u --recursive --new-file v2.3.45/linux/net/netlink/netlink_dev.c linux/net/netlink/netlink_dev.c
--- v2.3.45/linux/net/netlink/netlink_dev.c	Thu Feb 10 17:11:25 2000
+++ linux/net/netlink/netlink_dev.c	Wed Feb 16 15:42:06 2000
@@ -25,6 +25,7 @@
 #include <linux/netlink.h>
 #include <linux/poll.h>
 #include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -175,12 +176,35 @@
 	release:	netlink_release,
 };
 
+static devfs_handle_t devfs_handle = NULL;
+
+static void __init make_devfs_entries (const char *name, int minor)
+{
+	devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT,
+			NETLINK_MAJOR, minor,
+			S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			&netlink_fops, NULL);
+}
+
 int __init init_netlink(void)
 {
-	if (register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) {
+	if (devfs_register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) {
 		printk(KERN_ERR "netlink: unable to get major %d\n", NETLINK_MAJOR);
 		return -EIO;
 	}
+	devfs_handle = devfs_mk_dir (NULL, "netlink", 7, NULL);
+	/*  Someone tell me the official names for the uppercase ones  */
+	make_devfs_entries ("route", 0);
+	make_devfs_entries ("skip", 1);
+	make_devfs_entries ("USERSOCK", 2);
+	make_devfs_entries ("fwmonitor", 3);
+	make_devfs_entries ("ARPD", 8);
+	make_devfs_entries ("ROUTE6", 11);
+	make_devfs_entries ("IP6_FW", 13);
+	devfs_register_series (devfs_handle, "tap%u", 16, DEVFS_FL_DEFAULT,
+			       NETLINK_MAJOR, 16,
+			       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+			       &netlink_fops, NULL);
 	return 0;
 }
 
@@ -194,7 +218,8 @@
 
 void cleanup_module(void)
 {
-	unregister_chrdev(NET_MAJOR,"netlink");
+	devfs_unregister (devfs_handle);
+	devfs_unregister_chrdev(NETLINK_MAJOR, "netlink");
 }
 
 #endif
