## Automatically generated incremental diff
## From:   linux-2.5.3-pre2
## To:     linux-2.5.3-pre3
## Robot:  $Id: make-incremental-diff,v 1.9 2001/12/10 00:06:56 hpa Exp $

diff -urN linux-2.5.3-pre2/CREDITS linux/CREDITS
--- linux-2.5.3-pre2/CREDITS	Mon Jan 21 17:22:31 2002
+++ linux/CREDITS	Mon Jan 21 17:23:22 2002
@@ -1281,6 +1281,13 @@
 D: bug toaster (A1 sauce makes all the difference)
 D: Random linux hacker
 
+N: Tim Hockin
+E: thockin@hockin.org
+W: http://www.hockin.org/~thockin
+D: Natsemi ethernet
+D: Cobalt Networks (x86) support
+D: This-and-That
+
 N: Dirk Hohndel
 E: hohndel@suse.de
 D: The XFree86[tm] Project
@@ -1451,11 +1458,10 @@
 
 N: Dave Jones
 E: davej@suse.de
-W: http://www.suse.de/~davej
-D: Moved PCI bridge tuning to userspace (Powertweak).
-D: Various x86 (& clones) setup code hacking.
-D: AFFS fixes for 2.3.x
-D: Various Janitorial hacks. (kernel-janitor.sourceforge.net)
+W: http://www.codemonkey.org.uk
+D: x86 errata/setup maintenance.
+D: Backport/Forwardport merge monkey.
+D: Various Janitor work.
 S: c/o SuSE Linux UK Ltd
 S: The Kinetic Centre
 S: Theobald Street
@@ -1660,6 +1666,13 @@
 S: 80-283 Gdansk
 S: Poland
 
+N: Jakob Kemi
+E: jakob.kemi@telia.com
+D: V4L W9966 Webcam driver
+S: Forsbyvägen 33
+S: 74143 Knivsta
+S: Sweden
+
 N: Gero Kuhlmann
 E: gero@gkminix.han.de
 D: mounting root via NFS
@@ -1832,6 +1845,11 @@
 S: 00200 Helsinki
 S: Finland
 
+N: Daniel J. Maas
+E: dmaas@dcine.com
+W: http://www.maasdigital.com
+D: dv1394
+
 N: Hamish Macdonald
 E: hamishm@lucent.com
 D: Linux/68k port
@@ -2526,7 +2544,7 @@
 S: Australia
 
 N: Gerard Roudier
-E: groudier@iplus.fr
+E: groudier@free.fr
 D: Contributed to asynchronous read-ahead improvement
 S: 21 Rue Carnot
 S: 95170 Deuil La Barre
diff -urN linux-2.5.3-pre2/Documentation/Changes linux/Documentation/Changes
--- linux-2.5.3-pre2/Documentation/Changes	Fri Nov 30 08:53:20 2001
+++ linux/Documentation/Changes	Mon Jan 21 17:23:22 2002
@@ -50,10 +50,10 @@
 
 o  Gnu C                  2.95.3                  # gcc --version
 o  Gnu make               3.77                    # make --version
-o  binutils               2.9.5.0.24              # ld -v
+o  binutils               2.9.5.0.25              # ld -v
 o  util-linux             2.10o                   # fdformat --version
 o  modutils               2.4.2                   # insmod -V
-o  e2fsprogs              1.19                    # tune2fs
+o  e2fsprogs              1.25                    # tune2fs
 o  reiserfsprogs          3.x.0j                  # reiserfsck 2>&1|grep reiserfsprogs
 o  pcmcia-cs              3.1.21                  # cardmgr -V
 o  PPP                    2.4.0                   # pppd --version
@@ -304,8 +304,7 @@
 
 E2fsprogs
 ---------
-o  <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.19.tar.gz>
-o  <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.19-0.src.rpm>
+o  <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.25.tar.gz>
 
 Reiserfsprogs
 -------------
diff -urN linux-2.5.3-pre2/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.5.3-pre2/Documentation/Configure.help	Mon Jan 21 17:22:32 2002
+++ linux/Documentation/Configure.help	Mon Jan 21 17:23:22 2002
@@ -8144,6 +8144,19 @@
   this option only if you have an IEEE 1394 video device connected to
   an OHCI-1394 card.
 
+OHCI-DV I/O support
+CONFIG_IEEE1394_DV1394
+  This driver allows you to transmit and receive DV (digital video)
+  streams on an OHCI-1394 card using a simple frame-oriented
+  interface.
+
+  The user-space API for dv1394 is documented in dv1394.h.
+
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read <file:Documentation/modules.txt>.  The module
+  will be called dv1394.o.
+
 SBP-2 support (Harddisks etc.)
 CONFIG_IEEE1394_SBP2
   This option enables you to use SBP-2 devices connected to your IEEE
diff -urN linux-2.5.3-pre2/Documentation/DocBook/Makefile linux/Documentation/DocBook/Makefile
--- linux-2.5.3-pre2/Documentation/DocBook/Makefile	Tue Jan  8 13:15:52 2002
+++ linux/Documentation/DocBook/Makefile	Mon Jan 21 17:23:22 2002
@@ -93,6 +93,8 @@
 		$(TOPDIR)/drivers/net/8390.c \
 		$(TOPDIR)/drivers/char/serial.c \
 		$(TOPDIR)/drivers/pci/pci.c \
+		$(TOPDIR)/drivers/hotplug/pci_hotplug_core.c \
+		$(TOPDIR)/drivers/hotplug/pci_hotplug_util.c \
 		$(TOPDIR)/drivers/block/ll_rw_blk.c \
 		$(TOPDIR)/drivers/sound/sound_core.c \
 		$(TOPDIR)/drivers/sound/sound_firmware.c \
diff -urN linux-2.5.3-pre2/Documentation/DocBook/kernel-api.tmpl linux/Documentation/DocBook/kernel-api.tmpl
--- linux-2.5.3-pre2/Documentation/DocBook/kernel-api.tmpl	Tue Jan  8 13:15:52 2002
+++ linux/Documentation/DocBook/kernel-api.tmpl	Mon Jan 21 17:23:22 2002
@@ -162,6 +162,10 @@
      <sect1><title>PCI Support Library</title>
 !Edrivers/pci/pci.c
      </sect1>
+     <sect1><title>PCI Hotplug Support Library</title>
+!Edrivers/hotplug/pci_hotplug_core.c
+!Edrivers/hotplug/pci_hotplug_util.c
+     </sect1>
      <sect1><title>MCA Architecture</title>
 	<sect2><title>MCA Device Functions</title>
 !Earch/i386/kernel/mca.c
diff -urN linux-2.5.3-pre2/Documentation/IRQ-affinity.txt linux/Documentation/IRQ-affinity.txt
--- linux-2.5.3-pre2/Documentation/IRQ-affinity.txt	Thu Feb 24 22:41:16 2000
+++ linux/Documentation/IRQ-affinity.txt	Mon Jan 21 17:23:22 2002
@@ -8,7 +8,7 @@
 affinity then the value will not change from the default 0xffffffff.
 
 Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting
-the IRQ to CPU4-8 (this is an 8-CPU SMP box):
+the IRQ to CPU4-7 (this is an 8-CPU SMP box):
 
 [root@moon 44]# cat smp_affinity
 ffffffff
diff -urN linux-2.5.3-pre2/Documentation/filesystems/devfs/ChangeLog linux/Documentation/filesystems/devfs/ChangeLog
--- linux-2.5.3-pre2/Documentation/filesystems/devfs/ChangeLog	Mon Jan 14 09:40:29 2002
+++ linux/Documentation/filesystems/devfs/ChangeLog	Mon Jan 21 17:23:22 2002
@@ -1869,3 +1869,11 @@
 - Added support for multiple Compaq cpqarray controllers
 
 - Fixed (rare, old) race in <devfs_lookup>
+===============================================================================
+Changes for patch v207
+
+- Fixed deadlock bug in <devfs_d_revalidate_wait>
+
+- Tag VFS deletable in <devfs_mk_symlink> if handle ignored
+
+- Updated README from master HTML file
diff -urN linux-2.5.3-pre2/Documentation/filesystems/devfs/README linux/Documentation/filesystems/devfs/README
--- linux-2.5.3-pre2/Documentation/filesystems/devfs/README	Thu Dec 27 08:29:14 2001
+++ linux/Documentation/filesystems/devfs/README	Mon Jan 21 17:23:22 2002
@@ -3,7 +3,16 @@
 
 Linux Devfs (Device File System) FAQ
 Richard Gooch
-21-DEC-2001
+20-JAN-2002
+
+
+Document languages:
+
+
+
+
+
+
 
 -----------------------------------------------------------------------------
 
@@ -69,6 +78,7 @@
 What I don't like about devfs
 How to report bugs
 Strange kernel messages
+Compilation problems with devfsd
 
 
 Other resources
@@ -837,8 +847,8 @@
 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
+can mount devfs directly over /dev without losing most functionality
+(i.e. losing 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 devfs, so I don't lose anything.
 
@@ -1477,6 +1487,7 @@
 What I don't like about devfs
 How to report bugs
 Strange kernel messages
+Compilation problems with devfsd
 
 
 
@@ -1677,7 +1688,7 @@
 minor limitation
 
 
-simplying increasing the device number size is insufficient. Apart
+simply increasing the device number size is insufficient. Apart
 from causing a lot of pain, it doesn't solve the management issues
 of a /dev with thousands or more device nodes
 
@@ -1828,6 +1839,29 @@
 
 
 
+
+Compilation problems with devfsd
+
+Usually, you can compile devfsd just by typing in
+make in the source directory, followed by a make
+install (as root). Sometimes, you may have problems, particularly
+on broken configurations.
+
+
+
+error messages relating to DEVFSD_NOTIFY_DELETE
+
+This happened because you have an ancient set of kernel headers
+installed in /usr/include/linux or /usr/src/linux.
+Install kernel 2.4.10 or later. You may need to pass the
+KERNEL_DIR variable to make (if you did not install
+the new kernel sources as /usr/src/linux), or you may copy
+the devfs_fs.h file in the kernel source tree into
+/usr/include/linux.
+
+
+
+
 -----------------------------------------------------------------------------
 
 
@@ -1882,9 +1916,25 @@
 
 
 
+The document master (in English) by rgooch@atnf.csiro.au is
+available at
+
+http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html
+
+
+
 A Korean translation by viatoris@nownuri.net is available at
 
-http://home.nownuri.net/~viatoris/devfs/devfs.html 
+http://home.nownuri.net/~viatoris/devfs/devfs.html
+
+A newer version is under construcation at
 
+http://viatoris.new21.org/devfs/devfs.html
 
 
+
+
+-----------------------------------------------------------------------------
+Most flags courtesy of ITA's 
+Flags of All Countries
+used with permission. 
diff -urN linux-2.5.3-pre2/Documentation/kernel-parameters.txt linux/Documentation/kernel-parameters.txt
--- linux-2.5.3-pre2/Documentation/kernel-parameters.txt	Wed Jun 20 11:21:33 2001
+++ linux/Documentation/kernel-parameters.txt	Mon Jan 21 17:23:22 2002
@@ -125,6 +125,13 @@
 
 	BusLogic=	[HW,SCSI]
 
+	cachesize=	[BUGS=ix86] Override level 2 CPU cache size detection.
+			Sometimes CPU hardware bugs make them report the cache
+			size incorrectly. The kernel will attempt work arounds
+			to fix known problems, but for some CPUs it is not
+			possible to determine what the correct size should be.
+			This option provides an override for these situations.
+
 	cdu31a=		[HW,CD]
 
 	chandev=	[HW,NET] 
diff -urN linux-2.5.3-pre2/Documentation/pci.txt linux/Documentation/pci.txt
--- linux-2.5.3-pre2/Documentation/pci.txt	Sun Nov  4 09:31:57 2001
+++ linux/Documentation/pci.txt	Mon Jan 21 17:23:22 2002
@@ -104,6 +104,10 @@
 	If you are sure the driver is not a hotplug driver then use only 
 	__init/exit __initdata/exitdata.
 
+        Pointers to functions marked as __devexit must be created using
+        __devexit_p(function_name).  That will generate the function
+        name or NULL if the __devexit function will be discarded.
+
 
 2. How to find PCI devices manually (the old style)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff -urN linux-2.5.3-pre2/MAINTAINERS linux/MAINTAINERS
--- linux-2.5.3-pre2/MAINTAINERS	Mon Jan 21 17:22:32 2002
+++ linux/MAINTAINERS	Mon Jan 21 17:23:22 2002
@@ -412,6 +412,12 @@
 L:	linux-decnet-user@lists.sourceforge.net
 S:	Maintained
 
+DELL LAPTOP SMM DRIVER
+P:	Massimo Dal Zotto
+M:	dz@debian.org
+W:	http://www.debian.org/~dz/i8k/
+S:	Maintained
+
 DEVICE NUMBER REGISTRY
 P:	H. Peter Anvin
 M:	hpa@zytor.com
@@ -926,6 +932,12 @@
 W:     http://www.sistina.com/lvm
 S:     Maintained 
 
+LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers
+P:	Gerard Roudier
+M:	groudier@free.fr
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
 M68K
 P:	Jes Sorensen
 M:	jes@trained-monkey.org
@@ -1010,6 +1022,11 @@
 M:	andrewtv@usa.net
 S:	Maintained
 
+NATSEMI ETHERNET DRIVER (DP8381x)
+P: Tim Hockin
+M:	thockin@hockin.org
+S:	Maintained
+
 NCP FILESYSTEM
 P:	Petr Vandrovec
 M:	vandrove@vc.cvut.cz
@@ -1133,8 +1150,8 @@
 S:	Maintained
 
 OPL3-SA2, SA3, and SAx DRIVER
-P:	Scott Murray
-M:	scott@spiteful.org
+P:	Zwane Mwaikambo
+M:	zwane@commfireservices.com
 L:	linux-sound@vger.kernel.org
 S:	Maintained
 
diff -urN linux-2.5.3-pre2/Makefile linux/Makefile
--- linux-2.5.3-pre2/Makefile	Mon Jan 21 17:22:32 2002
+++ linux/Makefile	Mon Jan 21 17:23:22 2002
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 3
-EXTRAVERSION =-pre2
+EXTRAVERSION =-pre3
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.5.3-pre2/arch/cris/Makefile linux/arch/cris/Makefile
--- linux-2.5.3-pre2/arch/cris/Makefile	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/Makefile	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.22 2001/10/01 14:42:38 bjornw Exp $
+# $Id: Makefile,v 1.3 2002/01/21 15:21:23 bjornw Exp $
 # cris/Makefile
 #
 # This file is included by the global makefile so that you can add your own
@@ -29,18 +29,17 @@
               -e s/@CONFIG_ETRAX_DRAM_SIZE_M@/$(CONFIG_ETRAX_DRAM_SIZE)/ \
               < $(LD_SCRIPT) > $(LD_SCRIPT).tmp; \
      else true; \
-     fi && $(CROSS_COMPILE)gcc -mlinux -nostdlib
+     fi && $(CROSS_COMPILE)ld -mcrislinux
 
-LINKFLAGS = -mlinux -T $(LD_SCRIPT).tmp
+LINKFLAGS = -T $(LD_SCRIPT).tmp
 
 # objcopy is used to make binary images from the resulting linked file
 
 OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
 
-# normally, gcc on a linux box adds __linux__ but we do it "manually"
-# -mlinux enables -march=v10, -fno-underscores among others
+# -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others
 
-CFLAGS := $(CFLAGS) -mlinux -fno-strict-aliasing -pipe -D__linux__
+CFLAGS := $(CFLAGS) -mlinux -pipe
 
 ifdef CONFIG_ETRAX_KGDB
 CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g
@@ -57,7 +56,7 @@
 # each others config options
 SUBDIRS += arch/cris/boot/rescue
 endif
-CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o
+CORE_FILES := arch/cris/kernel/kernel.o arch/cris/mm/mm.o $(CORE_FILES)
 DRIVERS += arch/cris/drivers/drivers.o
 LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a)
 LIBS := $(TOPDIR)/arch/cris/lib/lib.a $(LIBS) $(TOPDIR)/arch/cris/lib/lib.a $(LIBGCC)
diff -urN linux-2.5.3-pre2/arch/cris/README.mm linux/arch/cris/README.mm
--- linux-2.5.3-pre2/arch/cris/README.mm	Thu Feb  8 16:32:44 2001
+++ linux/arch/cris/README.mm	Mon Jan 21 17:23:22 2002
@@ -3,6 +3,9 @@
 HISTORY:
 
 $Log: README.mm,v $
+Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+Import of Linux 2.5.1
+
 Revision 1.1  2000/07/10 16:25:21  bjornw
 Initial revision
 
diff -urN linux-2.5.3-pre2/arch/cris/boot/compressed/README linux/arch/cris/boot/compressed/README
--- linux-2.5.3-pre2/arch/cris/boot/compressed/README	Thu Feb  8 16:32:44 2001
+++ linux/arch/cris/boot/compressed/README	Mon Jan 21 17:23:22 2002
@@ -1,6 +1,6 @@
 Creation of the self-extracting compressed kernel image (vmlinuz)
 -----------------------------------------------------------------
-$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $
+$Id: README,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
 
 This can be slightly confusing because it's a process with many steps.
 
diff -urN linux-2.5.3-pre2/arch/cris/boot/compressed/misc.c linux/arch/cris/boot/compressed/misc.c
--- linux-2.5.3-pre2/arch/cris/boot/compressed/misc.c	Tue May  1 16:04:56 2001
+++ linux/arch/cris/boot/compressed/misc.c	Mon Jan 21 17:23:22 2002
@@ -1,7 +1,7 @@
 /*
  * misc.c
  *
- * $Id: misc.c,v 1.6 2001/04/09 10:00:21 starvik Exp $
+ * $Id: misc.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * 
  * This is a collection of several routines from gzip-1.0.3 
  * adapted for Linux.
diff -urN linux-2.5.3-pre2/arch/cris/boot/rescue/head.S linux/arch/cris/boot/rescue/head.S
--- linux-2.5.3-pre2/arch/cris/boot/rescue/head.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/boot/rescue/head.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.8 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: head.S,v 1.2 2001/12/18 13:35:12 bjornw Exp $
  * 
  * Rescue code, made to reside at the beginning of the
  * flash-memory. when it starts, it checks a partition
@@ -114,7 +114,7 @@
 
 #define NOP_DI 0xf025050f
 #define RAM_INIT_MAGIC 0x56902387
-		
+
 	.text
 	
 	;; This is the entry point of the rescue code
@@ -144,7 +144,13 @@
 jumptarget:	
 	.dword	0xffffffff	; can be overwritten later to insert new code
 	
-no_newjump:	
+no_newjump:
+#ifdef CONFIG_ETRAX_ETHERNET		
+	;; Start MII clock to make sure it is running when tranceiver is reset
+	move.d 0x3, $r0    ; enable = on, phy = mii_clk
+	move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+	
 	;; We need to setup the bus registers before we start using the DRAM
 #include "../../lib/dram_init.S"
 
diff -urN linux-2.5.3-pre2/arch/cris/boot/rescue/kimagerescue.S linux/arch/cris/boot/rescue/kimagerescue.S
--- linux-2.5.3-pre2/arch/cris/boot/rescue/kimagerescue.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/boot/rescue/kimagerescue.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: kimagerescue.S,v 1.5 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: kimagerescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * 
  * Rescue code to be prepended on a kimage and copied to the
  * rescue serial port.
diff -urN linux-2.5.3-pre2/arch/cris/boot/rescue/testrescue.S linux/arch/cris/boot/rescue/testrescue.S
--- linux-2.5.3-pre2/arch/cris/boot/rescue/testrescue.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/boot/rescue/testrescue.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: testrescue.S,v 1.3 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: testrescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  *
  * Simple testcode to download by the rescue block.
  * Just lits some LEDs to show it was downloaded correctly.
diff -urN linux-2.5.3-pre2/arch/cris/config.in linux/arch/cris/config.in
--- linux-2.5.3-pre2/arch/cris/config.in	Thu Jan 10 16:23:44 2002
+++ linux/arch/cris/config.in	Mon Jan 21 17:23:22 2002
@@ -35,6 +35,9 @@
 bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB
 
 bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then
+	bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+fi
 
 endmenu
 
@@ -250,6 +253,6 @@
 if [ "$CONFIG_PROFILE" = "y" ]; then
   int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
-endmenu
 
 source lib/Config.in
+endmenu
diff -urN linux-2.5.3-pre2/arch/cris/drivers/Config.in linux/arch/cris/drivers/Config.in
--- linux-2.5.3-pre2/arch/cris/drivers/Config.in	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/Config.in	Mon Jan 21 17:23:22 2002
@@ -175,11 +175,11 @@
   hex  '  PB user changeable bits mask' CONFIG_ETRAX_PB_CHANGEABLE_BITS FF
 fi
 
-bool 'ARTPEC-1 support' CONFIG_JULIETTE
-
-if [ "$CONFIG_JULIETTE" = "y" ]; then
-   source arch/cris/drivers/juliette/Config.in
-fi
+#bool 'ARTPEC-1 support' CONFIG_JULIETTE
+#
+#if [ "$CONFIG_JULIETTE" = "y" ]; then
+#   source arch/cris/drivers/juliette/Config.in
+#fi
 
 bool 'USB host' CONFIG_ETRAX_USB_HOST
 if [ "$CONFIG_ETRAX_USB_HOST" = "y" ]; then
diff -urN linux-2.5.3-pre2/arch/cris/drivers/axisflashmap.c linux/arch/cris/drivers/axisflashmap.c
--- linux-2.5.3-pre2/arch/cris/drivers/axisflashmap.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/axisflashmap.c	Mon Jan 21 17:23:22 2002
@@ -11,6 +11,17 @@
  * partition split defined below.
  *
  * $Log: axisflashmap.c,v $
+ * Revision 1.2  2001/12/18 13:35:15  bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.17  2001/11/12 19:42:38  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.16  2001/11/08 11:18:58  jonashg
+ * Always read from uncached address to avoid problems with flushing
+ * cachelines after write and MTD-erase. No performance loss have been
+ * seen yet.
+ *
  * Revision 1.15  2001/10/19 12:41:04  jonashg
  * Name of probe has changed in MTD.
  *
@@ -121,7 +132,7 @@
 static void flash_copy_from(struct map_info *map, void *to,
 			    unsigned long from, ssize_t len)
 {
-	memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len);
+	memcpy(to, (void *)(FLASH_UNCACHED_ADDR + from), len);
 }
 
 static void flash_write8(struct map_info *map, __u8 d, unsigned long adr)
@@ -237,7 +248,7 @@
 	int use_default_ptable = 1; /* Until proven otherwise */
 	const char *pmsg = "  /dev/flash%d at 0x%x, size 0x%x\n";
 
-	printk(KERN_NOTICE "Axis flash mapping: %x at %x\n",
+	printk(KERN_NOTICE "Axis flash mapping: %x at %lx\n",
 	       WINDOW_SIZE, FLASH_CACHED_ADDR);
 
 #ifdef CONFIG_MTD_CFI
diff -urN linux-2.5.3-pre2/arch/cris/drivers/bluetooth/Makefile linux/arch/cris/drivers/bluetooth/Makefile
--- linux-2.5.3-pre2/arch/cris/drivers/bluetooth/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/cris/drivers/bluetooth/Makefile	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,25 @@
+include $(APPS)/Rules.elinux
+
+all:
+
+install:	src/bluetooth.c include/btcommon.h
+	ln -sfn ../../arch/cris/drivers/bluetooth/include ../../../../include/linux/bluetooth
+	if ! grep arch/cris/drivers/bluetooth/src/Config.in ../Config.in; then \
+	  echo '' >> ../Config.in; \
+	  echo 'if [ "$$CONFIG_ETRAX_SERIAL" = "y" ]; then' >> ../Config.in; \
+	  echo '  source arch/cris/drivers/bluetooth/src/Config.in' >> ../Config.in; \
+	  echo 'fi' >> ../Config.in; \
+	fi
+	if ! grep bluetooth/src/bt.o ../Makefile; then \
+	  perl -pi -e "s:include:obj-\\\$$(CONFIG_BLUETOOTH)                 += bluetooth/src/bt.o\nsubdir-\\\$$(CONFIG_BLUETOOTH)              += bluetooth/src\n\ninclude:" ../Makefile; \
+	fi
+
+clean:
+
+src/bluetooth.c:
+	@echo "You must install the OpenBT src directory before install can be done here!"
+	@exit 1
+
+include/btcommon.h:
+	@echo "You must install the OpenBT include directory before install can be done here!"
+	@exit 1
diff -urN linux-2.5.3-pre2/arch/cris/drivers/ds1302.c linux/arch/cris/drivers/ds1302.c
--- linux-2.5.3-pre2/arch/cris/drivers/ds1302.c	Wed Jul  4 11:50:38 2001
+++ linux/arch/cris/drivers/ds1302.c	Mon Jan 21 17:23:22 2002
@@ -7,6 +7,9 @@
 *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
 *!
 *! $Log: ds1302.c,v $
+*! Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+*! Import of Linux 2.5.1
+*!
 *! Revision 1.11  2001/06/14 12:35:52  jonashg
 *! The ATA hack is back. It is unfortunately the only way to set g27 to output.
 *!
@@ -82,7 +85,7 @@
 *!
 *! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
 *!
-*! $Id: ds1302.c,v 1.11 2001/06/14 12:35:52 jonashg Exp $
+*! $Id: ds1302.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
 *!
 *!***************************************************************************/
 
diff -urN linux-2.5.3-pre2/arch/cris/drivers/eeprom.c linux/arch/cris/drivers/eeprom.c
--- linux-2.5.3-pre2/arch/cris/drivers/eeprom.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/drivers/eeprom.c	Mon Jan 21 17:23:22 2002
@@ -20,6 +20,9 @@
 *!                                  in the spin-lock.
 *!
 *!  $Log: eeprom.c,v $
+*!  Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+*!  Import of Linux 2.5.1
+*!
 *!  Revision 1.8  2001/06/15 13:24:29  jonashg
 *!  * Added verification of pointers from userspace in read and write.
 *!  * Made busy counter volatile.
diff -urN linux-2.5.3-pre2/arch/cris/drivers/ethernet.c linux/arch/cris/drivers/ethernet.c
--- linux-2.5.3-pre2/arch/cris/drivers/ethernet.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/drivers/ethernet.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.18 2001/10/03 14:40:43 jonashg Exp $
+/* $Id: ethernet.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,6 +7,24 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
+ * Revision 1.2  2001/12/18 13:35:15  bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.21  2001/11/23 11:54:49  starvik
+ * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list
+ * Removed compiler warnings
+ *
+ * Revision 1.20  2001/11/12 19:26:00  pkj
+ * * Corrected e100_negotiate() to not assign half to current_duplex when
+ *   it was supposed to compare them...
+ * * Cleaned up failure handling in e100_open().
+ * * Fixed compiler warnings.
+ *
+ * Revision 1.19  2001/11/09 07:43:09  starvik
+ * Added full duplex support
+ * Added ioctl to set speed and duplex
+ * Clear LED timer only runs when LED is lit
+ *
  * Revision 1.18  2001/10/03 14:40:43  jonashg
  * Update rx_bytes counter.
  *
@@ -104,6 +122,7 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
+#include <asm/ethernet.h>
 
 //#define ETHDEBUG
 #define D(x)
@@ -120,7 +139,7 @@
 
 static struct sockaddr default_mac = {
 	0,
-        { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+	{ 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
 };
 
 /* Information that need to be kept for each board. */
@@ -136,6 +155,14 @@
 };
 
 
+/* Duplex settings */
+enum duplex
+{
+	half,
+	full,
+	autoneg
+};
+
 /* Dma descriptors etc. */
 
 #define RX_BUF_SIZE 32768
@@ -148,9 +175,18 @@
 /* 
 ** MDIO constants.
 */
-#define MDIO_BASE_STATUS_REG   0x1
-#define MDIO_BASE_CONTROL_REG  0x0
-#define MDIO_LINK_UP_MASK      0x4
+#define MDIO_BASE_STATUS_REG                0x1
+#define MDIO_BASE_CONTROL_REG               0x0
+#define MDIO_BC_NEGOTIATE                0x0200
+#define MDIO_BC_FULL_DUPLEX_MASK         0x0100
+#define MDIO_BC_AUTO_NEG_MASK            0x1000
+#define MDIO_BC_SPEED_SELECT_MASK        0x2000
+#define MDIO_ADVERTISMENT_REG               0x4
+#define MDIO_ADVERT_100_FD                0x100
+#define MDIO_ADVERT_100_HD                0x080
+#define MDIO_ADVERT_10_FD                 0x040
+#define MDIO_ADVERT_10_HD                 0x020
+#define MDIO_LINK_UP_MASK                   0x4
 #define MDIO_START                          0x1
 #define MDIO_READ                           0x2
 #define MDIO_WRITE                          0x1
@@ -158,6 +194,7 @@
 
 /* Broadcom specific */
 #define MDIO_AUX_CTRL_STATUS_REG           0x18
+#define MDIO_FULL_DUPLEX_IND                0x1
 #define MDIO_SPEED                          0x2
 #define MDIO_PHYS_ADDR                      0x0
 
@@ -165,18 +202,25 @@
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
 #define NET_FLASH_PAUSE                (HZ/100) /* 10 ms */
 #define NET_LINK_UP_CHECK_INTERVAL       (2*HZ) /* 2 s   */
+#define NET_DUPLEX_CHECK_INTERVAL        (2*HZ) /* 2 s   */
 
 #define NO_NETWORK_ACTIVITY 0
 #define NETWORK_ACTIVITY    1
 
 #define RX_DESC_BUF_SIZE   256
 #define NBR_OF_RX_DESC     (RX_BUF_SIZE / \
-			    RX_DESC_BUF_SIZE)
+                            RX_DESC_BUF_SIZE)
 
 #define GET_BIT(bit,val)   (((val) >> (bit)) & 0x01)
 
+/* Define some macros to access ETRAX 100 registers */
+#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+					  IO_FIELD(##reg##, field, val)
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+					  IO_STATE(##reg##, field, val)
+
 static etrax_dma_descr *myNextRxDesc;  /* Points to the next descriptor to
-					  to be processed */
+                                          to be processed */
 static etrax_dma_descr *myLastRxDesc;  /* The last processed descriptor */
 static etrax_dma_descr *myPrevRxDesc;  /* The descriptor right before myNextRxDesc */
 
@@ -187,13 +231,21 @@
 
 static struct sk_buff *tx_skb;
 
+static unsigned int network_rec_config_shadow = 0;
+
 /* Network speed indication. */
 static struct timer_list speed_timer;
 static struct timer_list clear_led_timer;
-static int current_speed;
+static int current_speed; /* Speed read from tranceiver */
+static int current_speed_selection; /* Speed selected by user */
 static int led_next_time;
 static int led_active;
 
+/* Duplex */
+static struct timer_list duplex_timer;
+static int full_duplex;
+static enum duplex current_duplex;
+
 /* Index to functions, as function prototypes. */
 
 static int etrax_ethernet_init(struct net_device *dev);
@@ -206,6 +258,8 @@
 static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void e100_rx(struct net_device *dev);
 static int e100_close(struct net_device *dev);
+static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static void e100_tx_timeout(struct net_device *dev);
 static struct net_device_stats *e100_get_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 static void e100_hardware_send_packet(char *buf, int length);
@@ -213,6 +267,11 @@
 static void update_tx_stats(struct net_device_stats *);
 
 static void e100_check_speed(unsigned long dummy);
+static void e100_set_speed(unsigned long speed);
+static void e100_check_duplex(unsigned long dummy);
+static void e100_set_duplex(enum duplex);
+static void e100_negotiate(void);
+
 static unsigned short e100_get_mdio_reg(unsigned char reg_num);
 static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
 static void e100_send_mdio_bit(unsigned char bit);
@@ -278,6 +337,8 @@
 	dev->get_stats          = e100_get_stats;
 	dev->set_multicast_list = set_multicast_list;
 	dev->set_mac_address    = e100_set_mac_address;
+	dev->do_ioctl           = e100_ioctl;
+	dev->tx_timeout         = e100_tx_timeout;
 
 	/* set the default MAC address */
 
@@ -287,7 +348,7 @@
 
 	/* Initialise receive descriptors */
 
-	for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+	for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
 		RxDescList[i].ctrl   = 0;
 		RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
 		RxDescList[i].next   = virt_to_phys(&RxDescList[i + 1]);
@@ -313,12 +374,18 @@
 	/* Initialize speed indicator stuff. */
 
 	current_speed = 10;
+	current_speed_selection = 0; /* Auto */
 	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
 	speed_timer.function = e100_check_speed;
 	add_timer(&speed_timer);
+        
 	clear_led_timer.function = e100_clear_network_leds;
-	clear_led_timer.expires = jiffies + HZ/10;
-	add_timer(&clear_led_timer);
+        
+	full_duplex = 0;
+	current_duplex = autoneg;
+	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;		
+	duplex_timer.function = e100_check_duplex;
+	add_timer(&duplex_timer);
 
 	return 0;
 }
@@ -335,7 +402,7 @@
 
 	/* remember it */
 
-        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
 
 	/* Write it to the hardware.
 	 * Note the way the address is wrapped:
@@ -409,21 +476,21 @@
 
 	if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rx_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit0;
 	}
 
 	/* allocate the irq corresponding to the transmitting DMA */
 
 	if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100tx_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit1;
 	}
 
 	/* allocate the irq corresponding to the network errors etc */
 
 	if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit2;
 	}
 
 	/*
@@ -431,18 +498,12 @@
 	 * and clean up on failure.
 	 */
 
-	if(request_dma(NETWORK_TX_DMA_NBR, cardname)) {
-		goto grace_exit;
+	if (request_dma(NETWORK_TX_DMA_NBR, cardname)) {
+		goto grace_exit3;
 	}
 
-	if(request_dma(NETWORK_RX_DMA_NBR, cardname)) {
-	grace_exit:
-		/* this will cause some 'trying to free free irq' but what the heck... */
-		free_dma(NETWORK_TX_DMA_NBR);
-		free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
-		free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
-		free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
-		return -EAGAIN;
+	if (request_dma(NETWORK_RX_DMA_NBR, cardname)) {
+		goto grace_exit4;
 	}
 
 	/* give the HW an idea of what MAC address we want */
@@ -459,9 +520,10 @@
 
 	*R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
 #else
-	*R_NETWORK_REC_CONFIG =
-		IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
-		IO_STATE(R_NETWORK_REC_CONFIG, ma0,       enable);
+	SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive);
+	SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable);
+	SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+	*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 #endif
 
 	*R_NETWORK_GEN_CONFIG =
@@ -507,6 +569,17 @@
 	netif_start_queue(dev);
 
 	return 0;
+
+grace_exit4:
+	free_dma(NETWORK_TX_DMA_NBR);
+grace_exit3:
+	free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
+grace_exit2:
+	free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
+grace_exit1:
+	free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
+grace_exit0:
+	return -EAGAIN;
 }
 
 
@@ -532,10 +605,119 @@
 	add_timer(&speed_timer);
 }
 
+static void
+e100_negotiate(void)
+{
+	unsigned short cmd;
+	unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	int bitCounter;
+
+	/* Discard old speed and duplex settings */
+	data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | 
+	          MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD);
+  
+	switch (current_speed_selection) {
+		case 10 :
+			if (current_duplex == full)
+				data |= MDIO_ADVERT_10_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_10_HD;
+			else
+				data |= MDIO_ADVERT_10_HD |  MDIO_ADVERT_10_FD;
+			break;
+
+		case 100 :
+			 if (current_duplex == full)
+				data |= MDIO_ADVERT_100_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_100_HD;
+			else
+				data |= MDIO_ADVERT_100_HD |  MDIO_ADVERT_100_FD;
+			break;
+
+		case 0 : /* Auto */
+			 if (current_duplex == full)
+				data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD;
+			else
+				data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+			break;
+
+		default : /* assume autoneg speed and duplex */
+			data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | 
+			        MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+	}
+
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	      (MDIO_ADVERTISMENT_REG<< 2);
+
+	e100_send_mdio_cmd(cmd, 1);
+
+	/* Data... */
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+		e100_send_mdio_bit(GET_BIT(bitCounter, data));
+	}
+
+	/* Renegotiate with link partner */
+	data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
+	data |= MDIO_BC_NEGOTIATE;
+
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	      (MDIO_BASE_CONTROL_REG<< 2);
+
+	e100_send_mdio_cmd(cmd, 1);
+
+	/* Data... */
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+		e100_send_mdio_bit(GET_BIT(bitCounter, data));
+	}  
+}
+
+static void
+e100_set_speed(unsigned long speed)
+{
+	current_speed_selection = speed;
+	e100_negotiate();
+}
+
+static void
+e100_check_duplex(unsigned long dummy)
+{
+	unsigned long data;
+
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+        
+	if (data & MDIO_FULL_DUPLEX_IND) {
+		if (!full_duplex) { /* Duplex changed to full? */
+			full_duplex = 1;
+			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+		}
+	} else { /* half */
+		if (full_duplex) { /* Duplex changed to half? */
+			full_duplex = 0;
+			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+		}
+	}
+
+	/* Reinitialize the timer. */
+	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+	add_timer(&duplex_timer);
+}
+
+static void 
+e100_set_duplex(enum duplex new_duplex)
+{
+	current_duplex = new_duplex;
+	e100_negotiate();
+}
+
+
 static unsigned short
 e100_get_mdio_reg(unsigned char reg_num)
 {
-	unsigned long flags;
 	unsigned short cmd;    /* Data to be sent on MDIO port */
 	unsigned short data;   /* Data read from MDIO */
 	int bitCounter;
@@ -549,7 +731,7 @@
 	data = 0;
 	
 	/* Data... */
-	for(bitCounter=15; bitCounter>=0 ; bitCounter--) {
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
 		data |= (e100_receive_mdio_bit() << bitCounter);
 	}
 
@@ -563,14 +745,14 @@
 	unsigned char data = 0x2;
 	
 	/* Preamble */
-	for(bitCounter = 31; bitCounter>= 0; bitCounter--)
+	for (bitCounter = 31; bitCounter>= 0; bitCounter--)
 		e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
 
-	for(bitCounter = 15; bitCounter >= 2; bitCounter--)
+	for (bitCounter = 15; bitCounter >= 2; bitCounter--)
 		e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
 
 	/* Turnaround */
-	for(bitCounter = 1; bitCounter >= 0 ; bitCounter--)
+	for (bitCounter = 1; bitCounter >= 0 ; bitCounter--)
 		if (write_cmd)
 			e100_send_mdio_bit(GET_BIT(bitCounter, data));
 		else
@@ -606,7 +788,6 @@
 static void 
 e100_reset_tranceiver(void)
 {
-	unsigned long flags;
 	unsigned short cmd;
 	unsigned short data;
 	int bitCounter;
@@ -619,7 +800,7 @@
 	
 	data |= 0x8000;
 	
-	for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
+	for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
 		e100_send_mdio_bit(GET_BIT(bitCounter, data));
 	}
 }
@@ -706,15 +887,14 @@
 	struct net_device *dev = (struct net_device *)dev_id;
 	unsigned long irqbits = *R_IRQ_MASK2_RD;
  
-	if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
-
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
 		/* acknowledge the eop interrupt */
 
 		*R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
 
 		/* check if one or more complete packets were indeed received */
 
-		while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
+		while (*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
 			/* Take out the buffer and give it to the OS, then
 			 * allocate a new buffer to put a packet in.
 			 */
@@ -747,8 +927,7 @@
 	struct net_local *np = (struct net_local *)dev->priv;
 
 	/* check for a dma0_eop interrupt */
-	if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { 
-		
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { 
 		/* This protects us from concurrent execution of
 		 * our dev->hard_start_xmit function above.
 		 */
@@ -759,7 +938,7 @@
 
 		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
 
-		if(*R_DMA_CH0_FIRST == 0 && tx_skb) {
+		if (*R_DMA_CH0_FIRST == 0 && tx_skb) {
 			np->stats.tx_bytes += tx_skb->len;
 			np->stats.tx_packets++;
 			/* dma is ready with the transmission of the data in tx_skb, so now
@@ -784,19 +963,19 @@
 	unsigned long irqbits = *R_IRQ_MASK0_RD;
 
 	/* check for underrun irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
 		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
 		np->stats.tx_errors++;
 		D(printk("ethernet receiver underrun!\n"));
 	}
 
 	/* check for overrun irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { 
 		update_rx_stats(&np->stats); /* this will ack the irq */
 		D(printk("ethernet receiver overrun!\n"));
 	}
 	/* check for excessive collision irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
 		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
 		np->stats.tx_errors++;
 		D(printk("ethernet excessive collisions!\n"));
@@ -809,11 +988,13 @@
 e100_rx(struct net_device *dev)
 {
 	struct sk_buff *skb;
-	int length=0;
-	int i;
+	int length = 0;
 	struct net_local *np = (struct net_local *)dev->priv;
 	struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc;
 	unsigned char *skb_data_ptr;
+#ifdef ETHDEBUG
+	int i;
+#endif
 
 	if (!led_active && jiffies > led_next_time) {
 		/* light the network leds depending on the current speed. */
@@ -822,6 +1003,7 @@
 		/* Set the earliest time we may clear the LED */
 		led_next_time = jiffies + NET_FLASH_TIME;
 		led_active = 1;
+		mod_timer(&clear_led_timer, jiffies + HZ/10);
 	}
 
 	/* If the packet is broken down in many small packages then merge
@@ -842,7 +1024,7 @@
 	printk("Got a packet of length %d:\n", length);
 	/* dump the first bytes in the packet */
 	skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf);
-	for(i = 0; i < 8; i++) {
+	for (i = 0; i < 8; i++) {
 		printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
 		       skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
 		       skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
@@ -869,7 +1051,7 @@
 
 	/* this loop can be made using max two memcpy's if optimized */
 
-	while(mySaveRxDesc != myNextRxDesc) {
+	while (mySaveRxDesc != myNextRxDesc) {
 		memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
 		       mySaveRxDesc->sw_len);
 		skb_data_ptr += mySaveRxDesc->sw_len;
@@ -946,6 +1128,37 @@
 	return 0;
 }
 
+static int
+e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	/* Maybe default should return -EINVAL instead? */
+	switch (cmd) {
+		case SET_ETH_SPEED_10:                  /* 10 Mbps */
+			e100_set_speed(10);
+			break;
+		case SET_ETH_SPEED_100:                /* 100 Mbps */
+			e100_set_speed(100);
+			break;
+		case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
+			e100_set_speed(0);
+			break;
+		case SET_ETH_DUPLEX_HALF:              /* Hhalf duplex. */
+			e100_set_duplex(half);
+			break;
+		case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
+			e100_set_duplex(full);
+			break;
+		case SET_ETH_DUPLEX_AUTO:             /* Autonegotiate duplex*/
+			e100_set_duplex(autoneg);
+			break;
+		default: /* Auto neg */
+			e100_set_speed(0);
+			e100_set_duplex(autoneg);
+			break;
+	}
+	return 0;
+}
+
 static void
 update_rx_stats(struct net_device_stats *es)
 {
@@ -996,26 +1209,31 @@
 	int num_addr = dev->mc_count;
 	unsigned long int lo_bits;
 	unsigned long int hi_bits;
-	if (num_addr == -1)
+	if (dev->flags & IFF_PROMISC)
 	{
 		/* promiscuous mode */
 		lo_bits = 0xfffffffful;
 		hi_bits = 0xfffffffful;
 
-                /* Enable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, individual, receive);
+		/* Enable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		/* enable all multicasts */
+		lo_bits = 0xfffffffful;
+		hi_bits = 0xfffffffful;
+
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
 	} else if (num_addr == 0) {
 		/* Normal, clear the mc list */
 		lo_bits = 0x00000000ul;
 		hi_bits = 0x00000000ul;
 
-                /* Disable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable);
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
 	} else {
 		/* MC mode, receive normal and MC packets */
 		char hash_ix;
@@ -1057,10 +1275,9 @@
 			}
 			dmi = dmi->next;
 		}
-                /* Disable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable);
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 	}
 	*R_NETWORK_GA_0 = lo_bits;
 	*R_NETWORK_GA_1 = hi_bits;
@@ -1078,6 +1295,7 @@
 		/* Set the earliest time we may clear the LED */
 		led_next_time = jiffies + NET_FLASH_TIME;
 		led_active = 1;
+		mod_timer(&clear_led_timer, jiffies + HZ/10);
 	}
 
 	/* configure the tx dma descriptor */
@@ -1095,16 +1313,13 @@
 static void
 e100_clear_network_leds(unsigned long dummy)
 {
-        if (led_active && jiffies > led_next_time) {
+	if (led_active && jiffies > led_next_time) {
 		e100_set_network_leds(NO_NETWORK_ACTIVITY);
 
 		/* Set the earliest time we may set the LED */
 		led_next_time = jiffies + NET_FLASH_PAUSE;
 		led_active = 0;
 	}
-
-        clear_led_timer.expires = jiffies + HZ/10;
-	add_timer(&clear_led_timer);
 }
 
 static void
@@ -1143,7 +1358,7 @@
 
 	d->init = etrax_ethernet_init;
 
-	if(register_netdev(d) == 0)
+	if (register_netdev(d) == 0)
 		return 0;
 	else
 		return -ENODEV;
diff -urN linux-2.5.3-pre2/arch/cris/drivers/examples/kiobuftest.c linux/arch/cris/drivers/examples/kiobuftest.c
--- linux-2.5.3-pre2/arch/cris/drivers/examples/kiobuftest.c	Fri Apr  6 10:42:55 2001
+++ linux/arch/cris/drivers/examples/kiobuftest.c	Mon Jan 21 17:23:22 2002
@@ -10,6 +10,9 @@
  * Author:  Bjorn Wesen
  *
  * $Log: kiobuftest.c,v $
+ * Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+ * Import of Linux 2.5.1
+ *
  * Revision 1.2  2001/02/27 13:52:50  bjornw
  * malloc.h -> slab.h
  *
diff -urN linux-2.5.3-pre2/arch/cris/drivers/gpio.c linux/arch/cris/drivers/gpio.c
--- linux-2.5.3-pre2/arch/cris/drivers/gpio.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/gpio.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.11 2001/10/30 14:39:12 johana Exp $
+/* $Id: gpio.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $
  *
  * Etrax general port I/O device
  *
@@ -9,6 +9,13 @@
  *             Johan Adolfsson  (read/set directions, write)
  *
  * $Log: gpio.c,v $
+ * Revision 1.2  2001/12/18 13:35:15  bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.12  2001/11/12 19:42:15  pkj
+ * * Corrected return values from gpio_leds_ioctl().
+ * * Fixed compiler warnings.
+ *
  * Revision 1.11  2001/10/30 14:39:12  johana
  * Added D() around gpio_write printk.
  *
@@ -74,7 +81,9 @@
 
 static char gpio_name[] = "etrax gpio";
 
+#if 0
 static wait_queue_head_t *gpio_wq;
+#endif
 
 static int gpio_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg);
@@ -143,7 +152,7 @@
 {
 	/* TODO poll on alarms! */
 #if 0
-        if(!ANYTHING_WANTED) {
+        if (!ANYTHING_WANTED) {
 		D(printk("gpio_select sleeping task\n"));
 	        select_wait(&gpio_wq, table);
 	        return 0;
@@ -160,16 +169,14 @@
 	unsigned char data, clk_mask, data_mask, write_msb;
 	unsigned long flags;
 	ssize_t retval = count;
-	if (verify_area(VERIFY_READ, buf, count))
-	{
+	if (verify_area(VERIFY_READ, buf, count)) {
 		return -EFAULT;
 	}
 	clk_mask = priv->clk_mask;
 	data_mask = priv->data_mask;
 	/* It must have been configured using the IO_CFG_WRITE_MODE */
 	/* Perhaps a better error code? */
-	if (clk_mask == 0 || data_mask == 0) 
-	{
+	if (clk_mask == 0 || data_mask == 0) {
 		return -EPERM;
 	}
 	write_msb = priv->write_msb;
@@ -178,7 +185,7 @@
 		int i;
 		data = *buf++;
 		if (priv->write_msb) {
-			for (i = 7; i>=0;i--) {
+			for (i = 7; i >= 0;i--) {
 				save_flags(flags); cli();
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
@@ -190,7 +197,7 @@
 				restore_flags(flags);
 			}
 		} else {
-			for (i = 0; i<=7;i++) {
+			for (i = 0; i <= 7;i++) {
 				save_flags(flags); cli();
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
@@ -212,13 +219,13 @@
 	struct gpio_private *priv;
 	int p = MINOR(inode->i_rdev);
 
-	if(p >= NUM_PORTS && p != LEDS)
+	if (p >= NUM_PORTS && p != LEDS)
 		return -EINVAL;
 
 	priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), 
 					      GFP_KERNEL);
 
-	if(!priv)
+	if (!priv)
 		return -ENOMEM;
 
 	priv->minor = p;
@@ -254,10 +261,10 @@
 
 	/* unlink from alarmlist and free the private structure */
 
-	if(p == todel) {
+	if (p == todel) {
 		alarmlist = todel->next;
 	} else {
-		while(p->next != todel)
+		while (p->next != todel)
 			p = p->next;
 		p->next = todel->next;
 	}
@@ -280,7 +287,7 @@
 {
 	unsigned long flags;
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
-	if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+	if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
 		return -EINVAL;
 	}
 
@@ -353,7 +360,7 @@
 			if (!((priv->clk_mask & priv->changeable_bits) &&
 			      (priv->data_mask & priv->changeable_bits) &&
 			      (priv->clk_mask & *priv->dir_shadow) &&
-			      (priv->data_mask & *priv->dir_shadow)) )
+			      (priv->data_mask & *priv->dir_shadow)))
 			{
 				priv->clk_mask = 0;
 				priv->data_mask = 0;
@@ -361,7 +368,7 @@
 			}
 			break;
 		default:
-			if(priv->minor == LEDS)
+			if (priv->minor == LEDS)
 				return gpio_leds_ioctl(cmd, arg);
                         else
 				return -EINVAL;
@@ -375,6 +382,7 @@
 {
 	unsigned char green;
 	unsigned char red;
+
 	switch (_IOC_NR(cmd)) {
 		case IO_LEDACTIVE_SET:
 			green = ((unsigned char) arg) & 1;
@@ -382,14 +390,20 @@
 			LED_ACTIVE_SET_G(green);
 			LED_ACTIVE_SET_R(red);
 			break;
-                case IO_LED_SETBIT:                 
-                        LED_BIT_SET(arg);
-                       break;
-                case IO_LED_CLRBIT:
-                        LED_BIT_CLR(arg);
+
+		case IO_LED_SETBIT:                 
+			LED_BIT_SET(arg);
+			break;
+
+		case IO_LED_CLRBIT:
+			LED_BIT_CLR(arg);
+			break;
+
 		default:
 			return -EINVAL;
 	}
+
+	return 0;
 }
 
 struct file_operations gpio_fops = {
@@ -406,30 +420,32 @@
 static __init int
 gpio_init(void)
 {
-	int res,i;
+	extern void init_ioremap(void);
+	int res;
+#if defined (CONFIG_ETRAX_CSP0_LEDS)
+	int i;
+#endif
 
 	/* do the formalities */
 
 	res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
-	if(res < 0) {
+	if (res < 0) {
 		printk(KERN_ERR "gpio: couldn't get a major number.\n");
 		return res;
 	}
 
         /* Clear all leds */
-#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS)         || defined (CONFIG_ETRAX_PB_LEDS) 
-
-        init_ioremap();
-        LED_NETWORK_SET(0);
-        LED_ACTIVE_SET(0);
-        LED_DISK_READ(0);
-        LED_DISK_WRITE(0);        
+#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) 
+	init_ioremap();
+	LED_NETWORK_SET(0);
+	LED_ACTIVE_SET(0);
+	LED_DISK_READ(0);
+	LED_DISK_WRITE(0);        
 
 #if defined (CONFIG_ETRAX_CSP0_LEDS)
-        for( i = 0; i < 32; i ++)
-        {
-            LED_BIT_SET(i);
-        }
+	for (i = 0; i < 32; i++) {
+		LED_BIT_SET(i);
+	}
 #endif
 
 #endif
diff -urN linux-2.5.3-pre2/arch/cris/drivers/i2c.c linux/arch/cris/drivers/i2c.c
--- linux-2.5.3-pre2/arch/cris/drivers/i2c.c	Sat May 19 17:43:05 2001
+++ linux/arch/cris/drivers/i2c.c	Mon Jan 21 17:23:22 2002
@@ -12,6 +12,9 @@
 *!                                 don't use PB_I2C if DS1302 uses same bits,
 *!                                 use PB.
 *! $Log: i2c.c,v $
+*! Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+*! Import of Linux 2.5.1
+*!
 *! Revision 1.7  2001/04/04 13:11:36  markusl
 *! Updated according to review remarks
 *!
@@ -43,7 +46,7 @@
 *! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN
 *!
 *!***************************************************************************/
-/* $Id: i2c.c,v 1.7 2001/04/04 13:11:36 markusl Exp $ */
+/* $Id: i2c.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */
 /****************** INCLUDE FILES SECTION ***********************************/
 
 #include <linux/module.h>
diff -urN linux-2.5.3-pre2/arch/cris/drivers/i2c.h linux/arch/cris/drivers/i2c.h
--- linux-2.5.3-pre2/arch/cris/drivers/i2c.h	Sat May 19 17:43:05 2001
+++ linux/arch/cris/drivers/i2c.h	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: i2c.h,v 1.3 2001/03/19 12:43:01 markusl Exp $ */
+/* $Id: i2c.h,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */
 
 /* High level I2C actions */
 int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
diff -urN linux-2.5.3-pre2/arch/cris/drivers/ide.c linux/arch/cris/drivers/ide.c
--- linux-2.5.3-pre2/arch/cris/drivers/ide.c	Wed Jul  4 11:50:38 2001
+++ linux/arch/cris/drivers/ide.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: ide.c,v 1.19 2001/05/09 12:53:16 johana Exp $
+/* $Id: ide.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  *
  * Etrax specific IDE functions, like init and PIO-mode setting etc.
  * Almost the entire ide.c is used for the rest of the Etrax ATA driver.
@@ -8,6 +8,9 @@
  *             Mikael Starvik     (pio setup stuff)
  *
  * $Log: ide.c,v $
+ * Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+ * Import of Linux 2.5.1
+ *
  * Revision 1.19  2001/05/09 12:53:16  johana
  * Added #include <asm/dma.h>
  *
diff -urN linux-2.5.3-pre2/arch/cris/drivers/lpslave/bintocarr.pl linux/arch/cris/drivers/lpslave/bintocarr.pl
--- linux-2.5.3-pre2/arch/cris/drivers/lpslave/bintocarr.pl	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/drivers/lpslave/bintocarr.pl	Mon Jan 21 17:23:22 2002
@@ -1,5 +1,5 @@
 #!/usr/bin/perl -w
-# $Id: bintocarr.pl,v 1.4 2001/08/08 08:18:13 bjarne Exp $
+# $Id: bintocarr.pl,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
 # Copy of mkjulbin.pl made by Olof
 # convert a binary stdin to a C-file containing a char array of the input
 # first argument is the symbol name
diff -urN linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslave.S linux/arch/cris/drivers/lpslave/e100lpslave.S
--- linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslave.S	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/drivers/lpslave/e100lpslave.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-	;; $Id: e100lpslave.S,v 1.3 2001/06/21 16:55:26 olof Exp $
+	;; $Id: e100lpslave.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
 	;;
 	;; Etrax100 slave network<->parport forwarder
 	;;
diff -urN linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslavenet.c linux/arch/cris/drivers/lpslave/e100lpslavenet.c
--- linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslavenet.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/drivers/lpslave/e100lpslavenet.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: e100lpslavenet.c,v 1.4 2001/06/21 16:55:26 olof Exp $
+/* $Id: e100lpslavenet.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  *
  * e100lpslavenet.c: A network driver for the ETRAX 100LX slave controller.
  *
@@ -7,6 +7,9 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: e100lpslavenet.c,v $
+ * Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+ * Import of Linux 2.5.1
+ *
  * Revision 1.4  2001/06/21 16:55:26  olof
  * Minimized par port setup time to gain bandwidth
  *
diff -urN linux-2.5.3-pre2/arch/cris/drivers/parport.c linux/arch/cris/drivers/parport.c
--- linux-2.5.3-pre2/arch/cris/drivers/parport.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/drivers/parport.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: parport.c,v 1.8 2001/09/26 11:51:52 bjornw Exp $
+/* $Id: parport.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * 
  * Elinux parallel port driver
  * NOTE!
diff -urN linux-2.5.3-pre2/arch/cris/drivers/serial.c linux/arch/cris/drivers/serial.c
--- linux-2.5.3-pre2/arch/cris/drivers/serial.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/serial.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: serial.c,v 1.23 2001/10/30 17:53:26 pkj Exp $
+/* $Id: serial.c,v 1.3 2001/12/19 10:32:35 johana Exp $
  *
  * Serial port driver for the ETRAX 100LX chip
  *
@@ -7,6 +7,34 @@
  *      Many, many authors. Based once upon a time on serial.c for 16x50.
  *
  * $Log: serial.c,v $
+ * Revision 1.3  2001/12/19 10:32:35  johana
+ * Cleaned up write_rs485() - now it works correctly without padding extra
+ * char.
+ * Added sane default initialisation of rs485.
+ * Added #ifdef around dummy variables.
+ *
+ * Revision 1.27  2001/11/29 17:00:41  pkj
+ * 2kB seems to be too small a buffer when using 921600 bps,
+ * so increase it to 4kB (this was already done for the elinux
+ * version of the serial driver).
+ *
+ * Revision 1.26  2001/11/19 14:20:41  pkj
+ * Minor changes to comments and unused code.
+ *
+ * Revision 1.25  2001/11/12 20:03:43  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.24  2001/11/12 15:10:05  pkj
+ * Total redesign of the receiving part of the serial driver.
+ * Uses eight chained descriptors to write to a 4kB buffer.
+ * This data is then serialised into a 2kB buffer. From there it
+ * is copied into the TTY's flip buffers when they become available.
+ * A lot of copying, and the sizes of the buffers might need to be
+ * tweaked, but all in all it should work better than the previous
+ * version, without the need to modify the TTY code in any way.
+ * Also note that erroneous bytes are now correctly marked in the
+ * flag buffers (instead of always marking the first byte).
+ *
  * Revision 1.23  2001/10/30 17:53:26  pkj
  * * Set info->uses_dma to 0 when a port is closed.
  * * Mark the timer1 interrupt as a fast one (SA_INTERRUPT).
@@ -123,12 +151,12 @@
  * Changed %ul to %lu in printf's
  *
  * Revision 1.47  2000/10/18 15:06:53  pkj
- * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and
- * CONFIG_SERIAL_PROC_ENTRY together.
+ * Compile correctly with CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST and
+ * CONFIG_ETRAX_SERIAL_PROC_ENTRY together.
  * Some clean-up of the /proc/serial file.
  *
  * Revision 1.46  2000/10/16 12:59:40  johana
- * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info.
+ * Added CONFIG_ETRAX_SERIAL_PROC_ENTRY for statistics and debug info.
  *
  * Revision 1.45  2000/10/13 17:10:59  pkj
  * Do not flush DMAs while flipping TTY buffers.
@@ -171,7 +199,7 @@
  * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS.
  *
  * Revision 1.36  2000/09/20 13:12:52  johana
- * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS:
+ * Support for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS:
  *   Number of timer ticks between flush of receive fifo (1 tick = 10ms).
  *   Try 0-3 for low latency applications. Approx 5 for high load
  *   applications (e.g. PPP). Maybe this should be more adaptive some day...
@@ -255,7 +283,7 @@
  *
  */
 
-static char *serial_version = "$Revision: 1.23 $";
+static char *serial_version = "$Revision: 1.3 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -272,6 +300,7 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #if (LINUX_VERSION_CODE >= 131343)
 #include <linux/init.h>
 #endif
@@ -302,6 +331,8 @@
 #include "serial_compat.h"
 #endif
 
+#define _INLINE_ inline
+
 static DECLARE_TASK_QUEUE(tq_serial);
 
 struct tty_driver serial_driver, callout_driver;
@@ -313,11 +344,6 @@
 #define SERIAL_TYPE_CALLOUT	2
 #endif
 
-#define DEBUG_LOG(line, string, value)
-
-/* Add an x here to log a lot of timer stuff */
-#define TIMERD(x)
-
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
@@ -337,6 +363,14 @@
 
 #define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
 
+#define SERIAL_RECV_SIZE      4096
+#define SERIAL_DESCR_BUF_SIZE 512
+
+/* Add an x here to log a lot of timer stuff */
+#define TIMERD(x)
+
+#define DEBUG_LOG(line, string, value)
+
 #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
 /* Default number of timer ticks before flushing rx fifo 
  * When using "little data, low latency applications: use 0
@@ -345,8 +379,6 @@
 #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 
 #endif
 
-#define _INLINE_ inline
-
 static void change_speed(struct e100_serial *info);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int rs_write(struct tty_struct * tty, int from_user,
@@ -403,9 +435,9 @@
 static struct e100_serial rs_table[] = {
 	{ DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
 	  R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
-	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW,
+	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
 	  R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
-	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW,
+	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 2,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
           1
@@ -416,9 +448,9 @@
 #ifndef CONFIG_SVINTO_SIM
 	{ DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
 	  R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
-	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW,
+	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
 	  R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
-	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW,
+	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 3 ,
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
           1
@@ -429,9 +461,9 @@
 
 	{ DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4,  /* uses DMA 2 and 3 */
 	  R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
-	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW,
+	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
 	  R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
-	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW,
+	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 0,
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
           1
@@ -442,9 +474,9 @@
 
 	{ DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8,  /* uses DMA 4 and 5 */
 	  R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
-	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW,
+	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
 	  R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
-	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW,
+	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
           1
@@ -462,9 +494,9 @@
 static struct termios *serial_termios[NR_PORTS];
 static struct termios *serial_termios_locked[NR_PORTS];
 
-#ifdef CONFIG_SERIAL_PROC_ENTRY
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
 #define PROCSTAT(x) x
-struct ser_statistics_type{
+struct ser_statistics_type {
 	int overrun_cnt;
 	int early_errors_cnt;
 	int ser_ints_ok_cnt;
@@ -484,7 +516,7 @@
 
 #define PROCSTAT(x)
 
-#endif /* CONFIG_SERIAL_PROC_ENTRY */
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
 
 /* RS-485 */
 #if defined(CONFIG_ETRAX_RS485)
@@ -497,14 +529,20 @@
 /* For now we assume that all bits are on the same port for each serial port */
 
 /* Dummy shadow variables */
+#if !defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
 static unsigned char dummy_ser0 = 0x00;
-static unsigned char dummy_ser1 = 0x00;
-static unsigned char dummy_ser2 = 0x00;
-static unsigned char dummy_ser3 = 0x00;
-
 static unsigned char dummy_dir_ser0 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
+static unsigned char dummy_ser1 = 0x00;
 static unsigned char dummy_dir_ser1 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
+static unsigned char dummy_ser2 = 0x00;
 static unsigned char dummy_dir_ser2 = 0x00;
+#endif
+
+static unsigned char dummy_ser3 = 0x00;
 static unsigned char dummy_dir_ser3 = 0x00;
 
 /* Info needed for each ports extra control/status signals.
@@ -523,52 +561,56 @@
 
 static const struct control_pins e100_modem_pins[NR_PORTS] = 
 {
-/* Ser 0 */
-  {
+	/* Ser 0 */
+	{
 #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
-    R_PORT_PB_DATA,  &port_pb_data_shadow,  &port_pb_dir_shadow,
-    CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
-    CONFIG_ETRAX_SER0_RI_ON_PB_BIT, 
-    CONFIG_ETRAX_SER0_DSR_ON_PB_BIT, 
-    CONFIG_ETRAX_SER0_CD_ON_PB_BIT
+		R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+		CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_RI_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_DSR_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_CD_ON_PB_BIT
 #else
-    &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
-#endif   
-  },
-/* Ser 1 */
-  {
+		&dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 1 */
+	{
 #if defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
-    R_PORT_PB_DATA,  &port_pb_data_shadow,  &port_pb_dir_shadow,
-    CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
-    CONFIG_ETRAX_SER1_RI_ON_PB_BIT, 
-    CONFIG_ETRAX_SER1_DSR_ON_PB_BIT, 
-    CONFIG_ETRAX_SER1_CD_ON_PB_BIT
+		R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+		CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_RI_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_DSR_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_CD_ON_PB_BIT
 #else
-    &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
-#endif   
-  },  
-/* Ser 2 */
-  {
+		&dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 2 */
+	{
 #if defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
-    R_PORT_PA_DATA,  &port_pa_data_shadow,  &port_pa_dir_shadow,
-    CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
-    CONFIG_ETRAX_SER2_RI_ON_PA_BIT, 
-    CONFIG_ETRAX_SER2_DSR_ON_PA_BIT, 
-    CONFIG_ETRAX_SER2_CD_ON_PA_BIT
+		R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow,
+		CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_RI_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_DSR_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_CD_ON_PA_BIT
 #else
-    &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
-#endif   
-  },
-/* Ser 3 */
-  {
-    &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
-  }
+		&dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 3 */
+	{
+		&dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
+	}
 };
 
 #if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA)
 unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT;
 #endif
 
+
 #define E100_RTS_MASK 0x20
 #define E100_CTS_MASK 0x40
 
@@ -840,8 +882,8 @@
 {
 #ifndef CONFIG_SVINTO_SIM
 	/* disable the receiver */
-	info->port[REG_REC_CTRL] = info->rx_ctrl &=
-		~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+	info->port[REG_REC_CTRL] =
+		(info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
 #endif
 }
 
@@ -850,8 +892,8 @@
 {
 #ifndef CONFIG_SVINTO_SIM
 	/* enable the receiver */
-	info->port[REG_REC_CTRL] = info->rx_ctrl |=
-		IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+	info->port[REG_REC_CTRL] =
+		(info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
 #endif
 }
 
@@ -943,13 +985,8 @@
 static int
 e100_write_rs485(struct tty_struct *tty,struct rs485_write *r)
 {
-	int stop_delay;
-	int total, i;
-	int max_j, delay_ms, bits;
-	tcflag_t cflags;
-	int size = (*r).outc_size;
+	int total;
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
-	struct wait_queue wait = { current, NULL };
 
 	/* If we are in RS-485 mode, we need to toggle RTS and disable
 	 * the receiver before initiating a DMA transfer
@@ -975,44 +1012,20 @@
 	 * enable the receiver
 	 */	
 
-	/* wait on transmit shift register */
-	/* All is sent, check if we should wait more before toggling rts */
-	
-	/* calc. number of bits / data byte */
-	cflags = info->tty->termios->c_cflag;
-
-	/* databits + startbit and 1 stopbit */
-	if ((cflags & CSIZE) == CS7)
-		bits = 9;
-	else
-		bits = 10;  
-
-	if (cflags & CSTOPB)     /* 2 stopbits ? */
-		bits++;
-
-	if (cflags & PARENB)     /* parity bit ? */
-		bits++;
-	
-	/* calc timeout */
-	delay_ms = ((bits * size * 1000) / info->baud) + 1;
-	max_j = jiffies + (delay_ms * HZ)/1000 + 10;
-
-	while (jiffies < max_j) {
-		if (info->port[REG_STATUS] &
-		    IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
-			for (i = 0; i < 100; i++)
-				;
-			if (info->port[REG_STATUS] &
-			    IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
-				/* ~25 for loops per usec */
-				stop_delay = 1000000 / info->baud;
-				if (cflags & CSTOPB) 
-					stop_delay *= 2;
-				udelay(stop_delay);
-				break;
-			}
-		}
+	/* Sleep until all sent */
+	tty_wait_until_sent(tty, 0);
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+	/* Now sleep a little more so that shift register is empty */
+	schedule_usleep(info->char_time_usec * 2);
+#else
+	{
+		unsigned int val;
+		/* wait on transmit shift register */
+		do{
+			get_lsr_info(info, &val);
+		}while (!(val & TIOCSER_TEMT));
 	}
+#endif
 
 	e100_rts(info, info->rs485.rts_after_sent);
 	
@@ -1118,7 +1131,7 @@
 	}
 	return;
 #endif
-	/* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
+	/* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
 	*info->oclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
@@ -1168,14 +1181,14 @@
 
 #if defined(CONFIG_ETRAX_RS485)
 		/* Check if we should toggle RTS now */
-		if (info->rs485.enabled)
-		{
+		if (info->rs485.enabled) {
 			/* Make sure fifo is empty */
-			int in_fifo = 0 ;
-			do{
+			int in_fifo = 0;
+
+			do {
 				in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail,
-						    *info->ostatusadr);
-			}  while (in_fifo > 0) ;
+						     *info->ostatusadr);
+			}  while (in_fifo > 0);
 			/* Any way to really check transmitter empty? (TEMT) */
 			/* Control RTS to set to RX mode */
 			e100_rts(info, info->rs485.rts_after_sent); 
@@ -1201,7 +1214,6 @@
 	*info->ocmdadr = 1;       /* dma command start -> R_DMAx_CMD */
 	
 	/* DMA is now running (hopefully) */
-
 }
 
 static void 
@@ -1220,13 +1232,122 @@
 	transmit_chars(info);
 }
 
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static int serial_fast_timer_started = 0;
+static int serial_fast_timer_expired = 0;
+static void flush_timeout_function(unsigned long data);
+#define START_FLUSH_FAST_TIMER(info, string) {\
+  unsigned long timer_flags; \
+  save_flags(timer_flags); \
+  cli(); \
+  if (fast_timers[info->line].function == NULL) { \
+    serial_fast_timer_started++; \
+    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
+    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
+    start_one_shot_timer(&fast_timers[info->line], \
+                         flush_timeout_function, \
+                         (unsigned long)info, \
+                         info->char_time_usec*4, \
+                         string); \
+  } \
+  else { \
+    TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
+  } \
+  restore_flags(timer_flags); \
+}
+
+#else
+#define START_FLUSH_FAST_TIMER(info, string)
+#endif
+
+static int
+add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
+{
+	if (!CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE))
+		return 0;
+
+	info->recv.buf[info->recv.head] = data;
+	info->flag_buf[info->recv.head] = flag;
+	info->recv.head = (info->recv.head + 1) & (SERIAL_RECV_SIZE - 1);
+
+	info->icount.rx++;
+
+	return 1;
+}
+
+static _INLINE_ unsigned int
+copy_descr_data(struct e100_serial *info, unsigned int recvl, unsigned char *buf)
+{
+	unsigned int count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+	unsigned int length = 0;
+
+	 while (length < recvl && count) {
+		if (length + count > recvl)
+			count = recvl - length;
+
+		memcpy(info->recv.buf + info->recv.head, buf + length, count);
+		memset(info->flag_buf + info->recv.head, '\0', count);
+		info->recv.head = (info->recv.head + count) & (SERIAL_RECV_SIZE - 1);
+		length += count;
+
+		count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+	}
+
+	if (length != recvl) { 
+		printk(__FUNCTION__ ": Buffer overflow! %d byte(s) did not fit.\n", recvl - length);
+		PROCSTAT(ser_stat[info->line].overrun_cnt += recvl - length);
+	}
+
+	return length;
+}
+
+static _INLINE_ unsigned int
+copy_all_descr_data(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr;
+	unsigned int recvl;
+	unsigned int ret = 0;
+
+	while (1)
+	{
+		descr = &info->rec_descr[info->cur_rec_descr];
+
+		if (descr == phys_to_virt(*info->idescradr))
+			break;
+
+		if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+			info->cur_rec_descr = 0;
+	
+		/* find out how many bytes were read */
+
+		/* if the eop bit was not set, all data has been received */
+		if (!(descr->status & d_eop)) {
+			recvl = descr->sw_len;
+		} else {
+			/* otherwise we find the amount of data received here */
+			recvl = descr->hw_len;
+		}
+
+		/* Reset the status information */
+		descr->status = 0;
+
+		DEBUG_LOG(info->line, "recvl %lu\n", recvl);
+
+		/* update stats */
+		info->icount.rx += recvl;
+
+		ret += copy_descr_data(info, recvl, phys_to_virt(descr->buf));
+	}
+
+	return ret;
+}
+
 static _INLINE_ void 
 receive_chars(struct e100_serial *info)
 {
 	struct tty_struct *tty;
 	unsigned char rstat;
-	unsigned int recvl;
-	struct etrax_dma_descr *descr;
+	unsigned int old_head;
 
 #ifdef CONFIG_SVINTO_SIM
 	/* No receive in the simulator.  Will probably be when the rest of
@@ -1235,168 +1356,96 @@
 	return;
 #endif
 
-	tty = info->tty;
-
-	/* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
-
-	// ?
+	/* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
 	*info->iclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
 
-	if (!tty) /* something wrong... */
+	tty = info->tty;
+	if (!tty) /* Something wrong... */
 		return;
 
-	descr = &info->rec_descr;
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	e100_enable_serial_data_irq(info);
+#endif	
 
-	/* find out how many bytes were read */
+	if (info->errorcode == ERRCODE_INSERT_BREAK)
+		add_char_and_flag(info, '\0', TTY_BREAK);
 
-	/* if the eop bit was not set, all data has been received */
-	if (!(descr->status & d_eop)) {
-		recvl = descr->sw_len;
-	} else {
-		/* otherwise we find the amount of data received here */
-		recvl = descr->hw_len;
-	}
+	old_head = info->recv.head;
+	
+	if (copy_all_descr_data(info) && info->errorcode == ERRCODE_SET_BREAK)
+		info->flag_buf[old_head] = TTY_BREAK;
 
-	/* read the status register so we can detect errors,
-	 * but we can't really do anything about those errors
-	 * anyway, since we have the DMA in "force eop at error" mode
-	 * the fault characters are not in the buffer anyway.
-	 */
+	info->errorcode = 0;
 
+	/* Read the status register to detect errors */
 	rstat = info->port[REG_STATUS];
 
-	if ((rstat & SER_ERROR_MASK) != 0) {
-		unsigned char data;
-		/* if we got an error, we must reset it by reading the
+	if (rstat & SER_ERROR_MASK) {
+		/* If we got an error, we must reset it by reading the
 		 * data_in field
 		 */
-		data = info->port[REG_DATA];
+		unsigned char data = info->port[REG_DATA];
+
 		PROCSTAT(ser_stat[info->line].errors_cnt++);
-		DEBUG_LOG(info->line, " #dERR: s d 0x%04X\n",
+		DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
 			  ((rstat & SER_ERROR_MASK) << 8) | data);
-		/* Only handle the saved error code, that indicates that we got
-		 * the last character of a break that looks like it's ok, but
-		 * is not
-		 */
 
-		if (info->errorcode == 0) {
-			*tty->flip.flag_buf_ptr = TTY_NORMAL;
-		} else {
-			unsigned char data;
-			data = info->port[REG_DATA];
-			if (info->errorcode & ERRCODE_INSERT) {
-				unsigned char *currbuf;
-				/* Get the current buffer */
-				if (tty->flip.buf_num) {
-					currbuf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-				} else {
-					currbuf = tty->flip.char_buf;
-				}
-				/* We should insert a character in the buffer! */
-				if (recvl == 0) {
-					recvl = 1;
-					DEBUG_LOG(info->line, "insert to %lu\n", recvl);
-				} else {
-					/* Move stuff around.. */
-					DEBUG_LOG(info->line, "#insert to %lu!\n", recvl);
-					if (recvl < TTY_FLIPBUF_SIZE) {
-						int i;
-						/* Move the data 1 step right */
-						i = recvl;
-						while (i) {
-							currbuf[i] = currbuf[i-1];
-							i--;
-						}
-						recvl++;
-					} else {
-						/* We can't move it all! Skip break! */
-						/* TODO: Handle full buffer? */
-						DEBUG_LOG(info->line, "#BRK skipped! %lu!\n", recvl);
-						info->errorcode = 0;
-					}
-				}
-			}
-	    
-			PROCSTAT(ser_stat[info->line].errors_cnt++);
-			DEBUG_LOG(info->line, " #bERR: s d 0x%04X\n",
-				  ((rstat & SER_ERROR_MASK) << 8) | data);
-			*tty->flip.flag_buf_ptr = (info->errorcode & 0xFF);
-			info->errorcode = 0;
-#if 0
-			printk("SERERR: 0x%02X data: 0x%02X\n", rstat & SER_ERROR_MASK, data);
-#endif
-			/* we only ever write errors into the first byte in
-			 * the flip flag buffer, so we dont have to clear it
-			 * all every time
-			 */
-		}
+		if (rstat & SER_PAR_ERR_MASK)
+			add_char_and_flag(info, data, TTY_PARITY);
+		else if (rstat & SER_OVERRUN_MASK)
+			add_char_and_flag(info, data, TTY_OVERRUN);
+		else if (rstat & SER_FRAMING_ERR_MASK)
+			add_char_and_flag(info, data, TTY_FRAME);
 	}
 
-	DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
-	if (recvl) {
-		unsigned char *buf;
-		struct async_icount *icount = &info->icount;
-
-		/* update stats */
-		icount->rx += recvl;
-
-		/* use the flip buffer next in turn to restart DMA into */
-
-		if (tty->flip.buf_num) {
-			buf = tty->flip.char_buf;
-		} else {
-			buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-		}
+	if (!E100_RTS_GET(info) &&
+	    CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) < TTY_THROTTLE_LIMIT)
+		info->tty->driver.throttle(info->tty);
+	
+	START_FLUSH_FAST_TIMER(info, "receive_chars");
 
-		if (buf == phys_to_virt(descr->buf)) {
-			printk("ttyS%d flip-buffer overrun!\n", info->line);
-			icount->overrun++;
-			*tty->flip.flag_buf_ptr = TTY_OVERRUN;
-			/* restart old buffer */
-		} else {
-			descr->buf = virt_to_phys(buf);
+	/* Restart the receiving DMA */
+	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+}
 
-			/* schedule or push a flip of the buffer */
+static _INLINE_ int
+start_recv_dma(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr = info->rec_descr;
+	unsigned char *buf = info->recv.buf + 2*SERIAL_RECV_SIZE;
+        int i;
 
-			info->tty->flip.count = recvl;
+	/* Set up the receiving descriptors */
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+		descr[i].ctrl = d_int;
+		descr[i].buf = virt_to_phys(buf);
+		descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
+		descr[i].hw_len = 0;
+		descr[i].status = 0;
+		descr[i].next = virt_to_phys(&descr[i+1]);
 
-#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
-			/* this includes a check for low-latency */
-			tty_flip_buffer_push(tty);
-#else
-			queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#endif	
-		}
+		buf += SERIAL_DESCR_BUF_SIZE;
 	}
 
-	/* restart the receiving dma */
+	/* Link the last descriptor to the first */
+	descr[i-1].next = virt_to_phys(&descr[0]);
 
-	descr->sw_len = TTY_FLIPBUF_SIZE;
-	descr->ctrl = d_int | d_eol | d_eop;
-	descr->hw_len = 0;
-	descr->status = 0;
+	/* Start with the first descriptor in the list */
+	info->cur_rec_descr = 0;
 
-	*info->ifirstadr = virt_to_phys(descr);
+	/* Start the DMA */
+	*info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
 
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-	e100_enable_serial_data_irq(info);
-#endif	
-	/* input dma should be running now */
-
-	/* unthrottle if we have throttled */
-	if (E100_RTS_GET(info))
-		tty->driver.unthrottle(info->tty);
+	/* Input DMA should be running now */
+	return 1;
 }
 
 static void 
 start_receive(struct e100_serial *info)
 {
-	struct etrax_dma_descr *descr;
-	
 #ifdef CONFIG_SVINTO_SIM
 	/* No receive in the simulator.  Will probably be when the rest of
 	 * the serial interface works, and this piece will just be removed.
@@ -1410,21 +1459,10 @@
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
-	descr = &info->rec_descr;
-	
-	/* start the receiving dma into the flip buffer */
-	
-	descr->ctrl = d_int | d_eol | d_eop;
-	descr->sw_len = TTY_FLIPBUF_SIZE;
-	descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr);
-	descr->hw_len = 0;
-	descr->status = 0;
-	
 	info->tty->flip.count = 0;
 
-	*info->ifirstadr = virt_to_phys(descr);
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-
+	start_recv_dma(info);
+	
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
 	start_flush_timer();
 #endif
@@ -1474,9 +1512,16 @@
 		info = rs_table + i;
 		if (!info->uses_dma) 
 			continue; 
-		/* check for dma_descr (dont need to check for dma_eop in output dma for serial */
+		/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
 		if (ireg & info->irq) {  
 			/* we can send a new dma bunch. make it so. */
+			DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+			/* Read jiffies_usec first, 
+			 * we want this time to be as late as possible
+			 */
+ 			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+			info->last_tx_active_usec = GET_JIFFIES_USEC();
+			info->last_tx_active = jiffies;
 			transmit_chars(info);
 		}
 		
@@ -1524,77 +1569,26 @@
 	}
 }
 
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static int serial_fast_timer_started = 0;
-static int serial_fast_timer_expired = 0;
-static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
-  unsigned long timer_flags; \
-  save_flags(timer_flags); \
-  cli(); \
-  TIMERD(DEBUG_LOG(info->line, "start_timer? %i ", info->line)); \
-  if (fast_timers[info->line].function == NULL) { \
-    serial_fast_timer_started++; \
-    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
-    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
-    start_one_shot_timer(&fast_timers[info->line], \
-                         flush_timeout_function, \
-                         (unsigned long)info, \
-                         info->char_time_usec*4, \
-                         string); \
-  } \
-  else { \
-    /* DEBUG_LOG(info->line, " ## timer %i running ##\n", info->line); */ \
-  } \
-  restore_flags(timer_flags); \
-}
-
-#else
-#define START_FLUSH_FAST_TIMER(info, string)
-#endif
-
-void _INLINE_ check_flush_timeout(struct e100_serial *info)
+static _INLINE_ int
+force_eop_if_needed(struct e100_serial *info)
 {
-	unsigned char rstat;
-	unsigned int magic;
-
-	if (0 /*info->tty->processing_flip*/) {
-		if (!E100_RTS_GET(info)) {
-			int left = (*info->ihwswadr >> 16) - (*info->istatusadr & 0x3F);
-
-			if (left < TTY_THROTTLE_LIMIT)
-				info->tty->driver.throttle(info->tty);
-		}
-
-		PROCSTAT(ser_stat[info->line].processing_flip++);
-		START_FLUSH_FAST_TIMER(info, "flip");
-		return;
-	}
-
 	/* We check data_avail bit to determine if data has 
 	 * arrived since last time
 	 */ 
-	magic = info->fifo_magic;
-#ifdef SERIAL_DEBUG_DATA
-	if (info->fifo_magic || info->fifo_didmagic) {
-		DEBUG_LOG(info->line, "timeout_int: did fifo_magic %03X\n",
-		    (info->fifo_didmagic << 8) | info->fifo_magic);
-	}
-#endif
-	rstat = info->port[REG_STATUS];
+	unsigned char rstat = info->port[REG_STATUS];
+
 	/* error or datavail? */
 	if (rstat & SER_ERROR_MASK) { 
-		/* Some error has occured */
-		/* If there has been valid data, 
-		 * an EOP interrupt will be made automatically.
-		 * If no data, the normal ser_interrupt should be enabled 
-		 * and handle it.
+		/* Some error has occurred. If there has been valid data, an
+		 * EOP interrupt will be made automatically. If no data, the
+		 * normal ser_interrupt should be enabled and handle it.
 		 * So do nothing!
 		 */
 		DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
 		          rstat | (info->line << 8));
-		return;
+		return 0;
 	}
+
 	if (rstat & SER_DATA_AVAIL_MASK) { 
 		/* Ok data, no error, count it */
 		TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
@@ -1602,32 +1596,87 @@
 		/* Read data to clear status flags */
 		(void)info->port[REG_DATA];
 
-		magic++;
+		info->forced_eop = 0;
+		START_FLUSH_FAST_TIMER(info, "magic");
+		return 0;
 	}
 
-	if (magic != info->fifo_magic) {
-		info->fifo_magic = magic;
-		info->fifo_didmagic = 0;
-		START_FLUSH_FAST_TIMER(info, "magic");
-	} else {
-		/* hit the timeout, force an EOP for the input
-		 * dma channel if we haven't already
-		 */
-		if (!info->fifo_didmagic && magic) {
-			info->fifo_didmagic = 1;
-			info->fifo_magic = 0;
-			PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
-			DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
-			TIMERD(DEBUG_LOG(info->line, "timeout magic %i\n", magic));
-			FORCE_EOP(info);
-		}
+	/* hit the timeout, force an EOP for the input
+	 * dma channel if we haven't already
+	 */
+	if (!info->forced_eop) {
+		info->forced_eop = 1;
+		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+		DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+		FORCE_EOP(info);
 	}
-} /* check_flush_timeout */
+
+	return 1;
+}
+
+static _INLINE_ void
+flush_to_flip_buffer(struct e100_serial *info)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned int count = CIRC_CNT_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+	unsigned int length;
+	unsigned long flags;
+
+	if (!count)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	length = tty->flip.count;
+	
+	do {
+		if (length + count > TTY_FLIPBUF_SIZE)
+			count = TTY_FLIPBUF_SIZE - length;
+
+		memcpy(tty->flip.char_buf_ptr + length, info->recv.buf + info->recv.tail, count);
+		memcpy(tty->flip.flag_buf_ptr + length, info->flag_buf + info->recv.tail, count);
+		info->recv.tail = ((info->recv.tail + count) & (SERIAL_RECV_SIZE-1));
+		length += count;
+		
+		count = CIRC_CNT_TO_END(info->recv.head,
+					info->recv.tail,
+					SERIAL_RECV_SIZE);
+	} while (length < TTY_FLIPBUF_SIZE && count);
+
+	tty->flip.count = length;
+
+	restore_flags(flags);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+	/* this includes a check for low-latency */
+	tty_flip_buffer_push(tty);
+#else
+	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+#endif
+
+	/* unthrottle if we have throttled */
+	if (E100_RTS_GET(info) &&
+	    CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) > TTY_THROTTLE_LIMIT)
+		tty->driver.unthrottle(info->tty);
+}
+
+static _INLINE_ void
+check_flush_timeout(struct e100_serial *info)
+{
+	force_eop_if_needed(info);
+
+	flush_to_flip_buffer(info);
+
+	if (CIRC_CNT(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE))
+		START_FLUSH_FAST_TIMER(info, "flip");
+}
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 static void flush_timeout_function(unsigned long data)
 {
 	struct e100_serial *info = (struct e100_serial *)data;
+
 	fast_timers[info->line].function = NULL;
 	serial_fast_timer_expired++;
 	TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
@@ -1681,7 +1730,6 @@
 {
 	struct e100_serial *info;
 	int i;
-	unsigned int magic;
 
 #ifdef CONFIG_SVINTO_SIM
 	return;
@@ -1689,35 +1737,8 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !(info->flags & ASYNC_INITIALIZED))
-			continue;
-
-		/* istatusadr (bit 6-0) hold number of bytes in fifo 
-		 * ihwswadr (bit 31-16) holds number of bytes in dma buffer
-		 * ihwswadr (bit 15-0) specifies size of dma buffer
-		 */
-
-		magic = (*info->istatusadr & 0x3f);
-		magic += ((*info->ihwswadr & 0xffff) - (*info->ihwswadr >> 16));
-
-		/* if magic is equal to fifo_magic (magic in previous
-		 * timeout_interrupt) then no new data has arrived since last
-		 * interrupt and we'll force eop to flush fifo+dma buffers
-		 */
-
-		if (magic != info->fifo_magic) {
-			info->fifo_magic = magic;
-			info->fifo_didmagic = 0;
-		} else {
-			/* hit the timeout, force an EOP for the input
-			 * dma channel if we haven't already
-			 */
-			if (!info->fifo_didmagic && magic) {
-				info->fifo_didmagic = 1;
-				info->fifo_magic = 0;
-				FORCE_EOP(info);
-			}
-		}
+		if (info->uses_dma) 
+			check_flush_timeout(info);
 	}
 
 	/* restart flush timer */
@@ -1770,7 +1791,7 @@
 
 Multiple frame errors with data == 0x00 (B),
 but the part of the break trigs is interpreted as a start bit (and possibly
-som 0 bits followed by a number of 1 bits and a stop bit).
+some 0 bits followed by a number of 1 bits and a stop bit).
 Depending on parity settings etc. this last character can be either
 a fake "valid" char (F) or have a parity error (E).
 
@@ -1805,19 +1826,20 @@
 /*	DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
 	if (rstat & SER_ERROR_MASK) {
 		unsigned char data;
+
 		info->last_rx_active_usec = GET_JIFFIES_USEC();
 		info->last_rx_active = jiffies;
-		/* if we got an error, we must reset it by
-		 * reading the data_in field
+		/* If we got an error, we must reset it by reading the
+		 * data_in field
 		 */
 		data = info->port[REG_DATA];
 
-		if ((data == 0x00) && (rstat & SER_FRAMING_ERR_MASK)) {
-			/* Most likely a break, but we get 
-			 * interrupts over and over again.
+		if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
 			 */
 
-			if (info->break_detected_cnt == 0) {
+			if (!info->break_detected_cnt) {
 				DEBUG_LOG(info->line, "#BRK start\n", 0);
 			}
 			if (rstat & SER_RXD_MASK) {
@@ -1833,46 +1855,46 @@
 			}
 			info->break_detected_cnt++;
 		} else {
-			/* Error doesn't look like a break,
-			 * but could be end of a break 
+			/* The error does not look like a break, but could be
+			 * the end of one
 			 */
 			if (info->break_detected_cnt) {
 				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
 				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				if (info->errorcode == ERRCODE_INSERT_BREAK)
+					add_char_and_flag(info, '\0', TTY_BREAK);
+
+				if (rstat & SER_PAR_ERR_MASK)
+					add_char_and_flag(info, data, TTY_PARITY);
+				else if (rstat & SER_OVERRUN_MASK)
+					add_char_and_flag(info, data, TTY_OVERRUN);
+				else if (rstat & SER_FRAMING_ERR_MASK)
+					add_char_and_flag(info, data, TTY_FRAME);
+				info->errorcode = 0;
 			}
 			info->break_detected_cnt = 0;
 			DEBUG_LOG(info->line, "#iERR s d %04X\n",
 				  ((rstat & SER_ERROR_MASK) << 8) | data);
 		}
 		PROCSTAT(ser_stat[info->line].early_errors_cnt++);
-
-#if 0
-		/* Reset DMA before starting */
-		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-#endif
-	} else { /* it was a valid byte, now let the dma do the rest */
-		unsigned char data;
+	} else { /* It was a valid byte, now let the DMA do the rest */
 		unsigned long curr_time_u = GET_JIFFIES_USEC();
 		unsigned long curr_time = jiffies;
 		
 		if (info->break_detected_cnt) {
-			/* Detect if this character is a new
-			 * valid char or the last char in a
-			 * break sequence:
-			 * If LSBits are 0 and MSBits are high
-			 * AND the time is close to the
-			 * previous interrupt we should discard
-			 * it.
+			/* Detect if this character is a new valid char or the
+			 * last char in a break sequence: If LSBits are 0 and
+			 * MSBits are high AND the time is close to the
+			 * previous interrupt we should discard it.
 			 */
 			long elapsed_usec = 
-			  (curr_time - info->last_rx_active) * (1000000/HZ) + 
-			  curr_time_u - info->last_rx_active_usec;
-			if (elapsed_usec<2*info->char_time_usec) {
+				(curr_time - info->last_rx_active) * (1000000/HZ) + 
+				curr_time_u - info->last_rx_active_usec;
+			if (elapsed_usec < 2*info->char_time_usec) {
 				DEBUG_LOG(info->line, "FBRK %i\n", info->line);
-				/* Report as BREAK (error) and 
-				 * let receive_chars handle it
+				/* Report as BREAK (error) and let
+				 * receive_chars() handle it
 				 */
 				info->errorcode = ERRCODE_SET_BREAK;
 			} else {
@@ -1880,22 +1902,19 @@
 			}
 			DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
 		}
-		/* Reset data_avail by
-		 * reading the data_in field
-		 */
-		data = info->port[REG_DATA];
-		info->break_detected_cnt = 0;
-		info->fifo_magic++; /* Count received chars */
+
 #ifdef SERIAL_DEBUG_INTR
 		printk("** OK, disabling ser_interupts\n");
 #endif
-		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
-		DEBUG_LOG(info->line, " ser_int OK %03X\n",
-		          (info->line << 8) | data);
 		e100_disable_serial_data_irq(info);
+
+		info->break_detected_cnt = 0;
+
+		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
+		DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
 	}
 
-	/* restart the DMA never hurts */
+	/* Restarting the DMA never hurts */
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
 	START_FLUSH_FAST_TIMER(info, "ser_int");
 } /* handle_ser_interrupt */
@@ -1973,7 +1992,7 @@
 {
 	struct e100_serial	*info = (struct e100_serial *) private_;
 	struct tty_struct	*tty;
-	
+
 	tty = info->tty;
 	if (!tty)
 		return;
@@ -1985,43 +2004,46 @@
 startup(struct e100_serial * info)
 {
 	unsigned long flags;
-	unsigned long page;
+	unsigned long xmit_page;
+	unsigned char *recv_page;
+
+	xmit_page = get_zeroed_page(GFP_KERNEL);
+	if (!xmit_page)
+		return -ENOMEM;
 
-	page = get_zeroed_page(GFP_KERNEL);
-	if (!page)
+	recv_page = kmalloc(2 * SERIAL_RECV_SIZE + SERIAL_RECV_DESCRIPTORS * SERIAL_DESCR_BUF_SIZE, GFP_KERNEL);
+	if (!recv_page) {
+		free_page(xmit_page);
 		return -ENOMEM;
+	}
 
 	save_flags(flags); cli();
 
 	/* if it was already initialized, skip this */
-	
+
 	if (info->flags & ASYNC_INITIALIZED) {
-		free_page(page);
 		restore_flags(flags);
+		free_page(xmit_page);
+		kfree(recv_page);
 		return 0;
 	}
-	
+
 	if (info->xmit.buf)
-		free_page(page);
+		free_page(xmit_page);
 	else
-		info->xmit.buf = (unsigned char *) page;
-		
-#ifdef SERIAL_DEBUG_OPEN
-	printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit.buf);
-#endif
-
-	if (info->tty) {
-
-		/* clear the tty flip flag buffer since we will not
-		 * be using it (we only use the first byte..)
-		 */
+		info->xmit.buf = (unsigned char *) xmit_page;
 
-		memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2);
+	if (info->recv.buf)
+		kfree(recv_page);
+	else {
+		info->recv.buf = (unsigned char *) recv_page;
+		info->flag_buf = info->recv.buf + SERIAL_RECV_SIZE;
 	}
 
-	save_flags(flags);
-	cli();
-	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d (xmit_buf 0x%p, recv_buf 0x%p)...\n", info->line, info->xmit.buf, info->recv.buf);
+#endif
+
 #ifdef CONFIG_SVINTO_SIM
 	/* Bits and pieces collected from below.  Better to have them
 	   in one ifdef:ed clause than to mix in a lot of ifdefs,
@@ -2029,7 +2051,8 @@
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	info->xmit.head = info->xmit.tail = 0;
-	
+	info->recv.head = info->recv.tail = 0;
+
 	/* No real action in the simulator, but may set info important
 	   to ioctl. */
 	change_speed(info);
@@ -2039,33 +2062,35 @@
 	 * Clear the FIFO buffers and disable them
 	 * (they will be reenabled in change_speed())
 	 */
-	
+
 	/*
 	 * Reset the DMA channels and make sure their interrupts are cleared
 	 */
-	
+
 	info->uses_dma = 1;
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
-	/* wait until reset cycle is complete */
+	/* Wait until reset cycle is complete */
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-	
+
+	/* Make sure the irqs are cleared */
 	*info->iclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
 	*info->oclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-	
+
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 
-        info->xmit.head = info->xmit.tail = 0;
+	info->xmit.head = info->xmit.tail = 0;
+	info->recv.head = info->recv.tail = 0;
 	
 	/*
 	 * and set the speed and other flags of the serial port
@@ -2085,7 +2110,7 @@
 	e100_enable_txdma_irq(info);
 	e100_enable_rxdma_irq(info);
 
-	info->tr_running = 0;  /* to be sure we dont lock up the transmitter */
+	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
 
 	/* setup the dma input descriptor and start dma */
 	
@@ -2120,7 +2145,7 @@
 	unsigned long flags;
 
 #ifndef CONFIG_SVINTO_SIM	
-	/* shut down the transmitter and receiver  */
+	/* shut down the transmitter and receiver */
 
 	e100_disable_rx(info);
 	info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
@@ -2150,9 +2175,14 @@
 	cli(); /* Disable interrupts */
 	
 	if (info->xmit.buf) {
-		unsigned long pg = (unsigned long) info->xmit.buf;
-		info->xmit.buf = 0;
-		free_page(pg);
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = NULL;
+	}
+
+	if (info->recv.buf) {
+		kfree(info->recv.buf);
+		info->recv.buf = NULL;
+		info->flag_buf = NULL;
 	}
 
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
@@ -2258,11 +2288,11 @@
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
 	unsigned long flags;
 
-	if (info->tr_running
-	    || info->xmit.head == info->xmit.tail
-	    || tty->stopped
-	    || tty->hw_stopped
-	    || !info->xmit.buf)
+	if (info->tr_running ||
+	    info->xmit.head == info->xmit.tail ||
+	    tty->stopped ||
+	    tty->hw_stopped ||
+	    !info->xmit.buf)
 		return;
 
 #ifdef SERIAL_DEBUG_FLOW
@@ -2299,7 +2329,7 @@
 #ifdef CONFIG_SVINTO_SIM
 	/* Really simple.  The output is here and now. */
 	SIMCOUT(buf, count);
-	return;
+	return count;
 #endif
 	save_flags(flags);
 	
@@ -2370,8 +2400,8 @@
 	 * the IRQ's are not running anyway for this port.
 	 */
 	
-	if (info->xmit.head != info->xmit.tail
-	    && !tty->stopped &&
+	if (info->xmit.head != info->xmit.tail &&
+	    !tty->stopped &&
 	    !tty->hw_stopped &&
 	    !info->tr_running) {
 		start_transmit(info);
@@ -2528,20 +2558,20 @@
 	tmp.flags = info->flags;
 	tmp.close_delay = info->close_delay;
 	tmp.closing_wait = info->closing_wait;
-	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
 }
 
 static int
-set_serial_info(struct e100_serial * info,
-		struct serial_struct * new_info)
+set_serial_info(struct e100_serial *info,
+		struct serial_struct *new_info)
 {
 	struct serial_struct new_serial;
 	struct e100_serial old_info;
-	int 			retval = 0;
+	int retval = 0;
 
-	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
 		return -EFAULT;
 
 	old_info = *info;
@@ -2636,6 +2666,7 @@
 char *get_control_state_str(int MLines, char *s)
 {
 	int i = 0;
+
 	s[0]='\0';
 	while (control_state_str[i].str != NULL) {
 		if (MLines & control_state_str[i].state) {
@@ -2805,9 +2836,13 @@
 rs_ioctl(struct tty_struct *tty, struct file * file,
 	 unsigned int cmd, unsigned long arg)
 {
-	int error;
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+#if defined(CONFIG_ETRAX_RS485) || (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+	int error;
+#endif
+#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
 	int retval;
+#endif
 	
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
 	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
@@ -3108,7 +3143,7 @@
 		struct e100_serial *info)
 {
 	DECLARE_WAITQUEUE(wait, current);
-	unsigned long   flags;
+	unsigned long	flags;
 	int		retval;
 	int		do_clocal = 0, extra_count = 0;
 	
@@ -3292,7 +3327,7 @@
 	}
 
 	/*
-	 * If the port is the middle of closing, bail out now
+	 * If the port is in the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
@@ -3346,12 +3381,11 @@
 
 static inline int line_info(char *buf, struct e100_serial *info)
 {
-	char	stat_buf[30], control, status;
+	char	stat_buf[30];
 	int	ret;
-	unsigned long flags;
 
 	ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
-		      info->line, info->port, info->irq);
+		      info->line, (unsigned long)info->port, info->irq);
 
 	if (!info->port || (info->type == PORT_UNKNOWN)) {
 		ret += sprintf(buf+ret, "\n");
@@ -3376,19 +3410,24 @@
 	ret += sprintf(buf+ret, " baud:%d", info->baud);
 
 	ret += sprintf(buf+ret, " tx:%lu rx:%lu",
-		       info->icount.tx, info->icount.rx);
+		       (unsigned long)info->icount.tx,
+		       (unsigned long)info->icount.rx);
 
 	if (info->icount.frame)
-		ret += sprintf(buf+ret, " fe:%lu", info->icount.frame);
+		ret += sprintf(buf+ret, " fe:%lu",
+			       (unsigned long)info->icount.frame);
 	
 	if (info->icount.parity)
-		ret += sprintf(buf+ret, " pe:%lu", info->icount.parity);
+		ret += sprintf(buf+ret, " pe:%lu",
+			       (unsigned long)info->icount.parity);
 	
 	if (info->icount.brk)
-		ret += sprintf(buf+ret, " brk:%lu", info->icount.brk);	
+		ret += sprintf(buf+ret, " brk:%lu",
+			       (unsigned long)info->icount.brk);	
 
 	if (info->icount.overrun)
-		ret += sprintf(buf+ret, " oe:%lu", info->icount.overrun);
+		ret += sprintf(buf+ret, " oe:%lu",
+			       (unsigned long)info->icount.overrun);
 
 	/*
 	 * Last thing is the RS-232 status lines
@@ -3526,8 +3565,7 @@
 		info->tty = 0;
 		info->type = PORT_ETRAX;
 		info->tr_running = 0;
-		info->fifo_magic = 0;
-		info->fifo_didmagic = 0;
+		info->forced_eop = 0;
 		info->flags = 0;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
@@ -3541,8 +3579,21 @@
 		info->normal_termios = serial_driver.init_termios;
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
-		info->xmit.buf = 0;
+		info->xmit.buf = NULL;
 		info->xmit.tail = info->xmit.head = 0;
+		info->recv.buf = NULL;
+		info->recv.tail = info->recv.head = 0;
+		info->flag_buf = NULL;
+		info->last_tx_active_usec = 0;
+		info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+		/* Set sane defaults */
+		info->rs485.rts_on_send = 0;
+		info->rs485.rts_after_sent = 1;
+		info->rs485.delay_rts_before_send = 0;
+		info->rs485.enabled = 0;
+#endif
 
 		if (info->enabled) {
 			printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
diff -urN linux-2.5.3-pre2/arch/cris/drivers/serial.h linux/arch/cris/drivers/serial.h
--- linux-2.5.3-pre2/arch/cris/drivers/serial.h	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/serial.h	Mon Jan 21 17:23:22 2002
@@ -23,81 +23,90 @@
  * For definitions of the flags field, see tty.h
  */
 
+#define SERIAL_RECV_DESCRIPTORS 8
+
 struct e100_serial {
-	int                   baud;
-	volatile u8 *port;             /* R_SERIALx_CTRL */
-	u32 irq;                       /* bitnr in R_IRQ_MASK2 for dmaX_descr */
-
-	volatile u8 *oclrintradr;      /* adr to R_DMA_CHx_CLR_INTR, output */
-	volatile u32 *ofirstadr;       /* adr to R_DMA_CHx_FIRST, output */
-	volatile u8 *ocmdadr;          /* adr to R_DMA_CHx_CMD, output */
-	const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */
-	volatile u32 *ohwswadr;        /* adr to R_DMA_CHx_HWSW, output */
-
-	volatile u8 *iclrintradr;      /* adr to R_DMA_CHx_CLR_INTR, input */
-	volatile u32 *ifirstadr;       /* adr to R_DMA_CHx_FIRST, input */
-	volatile u8 *icmdadr;          /* adr to R_DMA_CHx_CMD, input */
-	const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS, input */
-	volatile u32 *ihwswadr;        /* adr to R_DMA_CHx_HWSW, input */
-
-	int			flags; 		/* defined in tty.h */
-
-	u8           rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
-	u8           tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
-	u8           iseteop; /* bit number for R_SET_EOP for the input dma */
-	int          enabled;    /* Set to 1 if the port is enabled in HW config */
-  
+	int			baud;
+	volatile u8		*port; /* R_SERIALx_CTRL */
+	u32			irq;  /* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+	/* Output registers */
+	volatile u8		*oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32		*ofirstadr;   /* adr to R_DMA_CHx_FIRST */
+	volatile u8		*ocmdadr;     /* adr to R_DMA_CHx_CMD */
+	const volatile u8	*ostatusadr;  /* adr to R_DMA_CHx_STATUS */
+	volatile u32		*ohwswadr;    /* adr to R_DMA_CHx_HWSW */
+	volatile u32		*odescradr;   /* adr to R_DMA_CHx_DESCR */
+
+	/* Input registers */
+	volatile u8		*iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32		*ifirstadr;   /* adr to R_DMA_CHx_FIRST */
+	volatile u8		*icmdadr;     /* adr to R_DMA_CHx_CMD */
+	const volatile u8	*istatusadr;  /* adr to R_DMA_CHx_STATUS */
+	volatile u32		*ihwswadr;    /* adr to R_DMA_CHx_HWSW */
+	volatile u32		*idescradr;   /* adr to R_DMA_CHx_DESCR */
+
+	int			flags;	/* defined in tty.h */
+
+	u8			rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+	u8			tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+	u8			iseteop; /* bit number for R_SET_EOP for the input dma */
+
+	int			enabled; /* Set to 1 if the port is enabled in HW config */
   
-/* end of fields defined in rs_table[] in .c-file */
-	int          uses_dma; /* Set to 1 if DMA should be used */
-	unsigned char           fifo_didmagic; /* a fifo eop has been forced */
+	/* end of fields defined in rs_table[] in .c-file */
 
-	struct etrax_dma_descr tr_descr, rec_descr;
+	int			uses_dma; /* Set to 1 if DMA should be used */
+	unsigned char           forced_eop; /* a fifo eop has been forced */
 
-	int                     fifo_magic; /* fifo amount - bytes left in dma buffer */
+	struct etrax_dma_descr	tr_descr;
+	struct etrax_dma_descr	rec_descr[SERIAL_RECV_DESCRIPTORS];
+	int			cur_rec_descr;
 
-	volatile int            tr_running; /* 1 if output is running */
+	volatile int		tr_running; /* 1 if output is running */
 
-	struct tty_struct 	*tty;
+	struct tty_struct	*tty;
 	int			read_status_mask;
 	int			ignore_status_mask;
 	int			x_char;	/* xon/xoff character */
 	int			close_delay;
-	unsigned short	        closing_wait;
-	unsigned short	        closing_wait2;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
 	unsigned long		event;
 	unsigned long		last_active;
 	int			line;
-	int                     type;  /* PORT_ETRAX */
+	int			type;  /* PORT_ETRAX */
 	int			count;	    /* # of fd on device */
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
 	long			pgrp; /* pgrp of opening process */
-	struct circ_buf         xmit;
+	struct circ_buf		xmit;
+	struct circ_buf		recv;
+	unsigned char		*flag_buf;
 
 	struct tq_struct	tqueue;
-	struct async_icount     icount;   /* error-statistics etc.*/
-	struct termios	        normal_termios;
-	struct termios	        callout_termios;
+	struct async_icount	icount;   /* error-statistics etc.*/
+	struct termios		normal_termios;
+	struct termios		callout_termios;
 #ifdef DECLARE_WAITQUEUE
-	wait_queue_head_t       open_wait;
-        wait_queue_head_t       close_wait;
-#else   
-        struct wait_queue       *open_wait;
-        struct wait_queue       *close_wait;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+#else
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
 #endif  
 
-	unsigned long char_time_usec;       /* The time for 1 char, in usecs */
-	unsigned long last_tx_active_usec;  /* Last tx usec in the jiffies */
-	unsigned long last_tx_active;       /* Last tx time in jiffies */
-	unsigned long last_rx_active_usec;  /* Last rx usec in the jiffies */
-	unsigned long last_rx_active;       /* Last rx time in jiffies */
+	unsigned long		char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long		last_tx_active_usec;  /* Last tx usec in the jiffies */
+	unsigned long		last_tx_active;       /* Last tx time in jiffies */
+	unsigned long		last_rx_active_usec;  /* Last rx usec in the jiffies */
+	unsigned long		last_rx_active;       /* Last rx time in jiffies */
 
-	int break_detected_cnt;
-	int errorcode;
+	int			break_detected_cnt;
+	int			errorcode;
 
 #ifdef CONFIG_RS485
-	struct rs485_control    rs485;  /* RS-485 support */
+	struct rs485_control	rs485;  /* RS-485 support */
 #endif
 };
 
@@ -116,4 +125,4 @@
 
 #endif /* __KERNEL__ */
 
-#endif /* !(_ETRAX_SERIAL_H) */
+#endif /* !_ETRAX_SERIAL_H */
diff -urN linux-2.5.3-pre2/arch/cris/drivers/sync_serial.c linux/arch/cris/drivers/sync_serial.c
--- linux-2.5.3-pre2/arch/cris/drivers/sync_serial.c	Tue May  1 16:04:56 2001
+++ linux/arch/cris/drivers/sync_serial.c	Mon Jan 21 17:23:22 2002
@@ -711,7 +711,7 @@
 {
 	port->out_descr.hw_len = 0;
 	port->out_descr.next = 0;
-	port->out_descr.ctrl = d_int | d_eol | d_eop;
+	port->out_descr.ctrl = d_int | d_eol | d_eop | d_wait;
 	port->out_descr.sw_len = count;
 	port->out_descr.buf = virt_to_phys(port->out_buffer);
 	port->out_descr.status = 0;
diff -urN linux-2.5.3-pre2/arch/cris/drivers/usb-host.c linux/arch/cris/drivers/usb-host.c
--- linux-2.5.3-pre2/arch/cris/drivers/usb-host.c	Sun Jan  6 18:49:21 2002
+++ linux/arch/cris/drivers/usb-host.c	Mon Jan 21 17:23:22 2002
@@ -192,15 +192,15 @@
 static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
 static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
 
-static urb_t *URB_List[NBR_OF_EP_DESC];
+static struct urb *URB_List[NBR_OF_EP_DESC];
 static kmem_cache_t *usb_desc_cache;
 static struct usb_bus *etrax_usb_bus;
 
 static void dump_urb (struct urb *urb);
 static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb (urb_t *urb);
-static void etrax_rh_send_irq(urb_t *urb);
-static void etrax_rh_init_int_timer(urb_t *urb);
+static int etrax_rh_unlink_urb (struct urb *urb);
+static void etrax_rh_send_irq(struct urb *urb);
+static void etrax_rh_init_int_timer(struct urb *urb);
 static void etrax_rh_int_timer_do(unsigned long ptr);
 
 static void etrax_usb_setup_epid(char epid, char devnum, char endpoint,
@@ -210,13 +210,13 @@
 static void etrax_usb_free_epid(char epid);
 static void cleanup_sb(USB_SB_Desc_t *sb);
 
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen);
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen);
+static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen);
+static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen);
 
-static int etrax_usb_submit_ctrl_urb(urb_t *urb);
+static int etrax_usb_submit_ctrl_urb(struct urb *urb);
 
-static int etrax_usb_submit_urb(urb_t *urb);
-static int etrax_usb_unlink_urb(urb_t *urb);
+static int etrax_usb_submit_urb(struct urb *urb);
+static int etrax_usb_unlink_urb(struct urb *urb);
 static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
 static int etrax_usb_allocate_dev(struct usb_device *usb_dev);
 static int etrax_usb_deallocate_dev(struct usb_device *usb_dev);
@@ -225,7 +225,7 @@
 static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
 static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs);
 
-static int etrax_rh_submit_urb (urb_t *urb);
+static int etrax_rh_submit_urb (struct urb *urb);
 
 static int etrax_usb_hc_init(void);
 static void etrax_usb_hc_cleanup(void);
@@ -421,7 +421,7 @@
 }
 
 
-static int etrax_usb_unlink_intr_urb(urb_t *urb)
+static int etrax_usb_unlink_intr_urb(struct urb *urb)
 {
 	struct usb_device *usb_dev = urb->dev;
 	etrax_hc_t *hc = usb_dev->bus->hcpriv;
@@ -512,7 +512,7 @@
 	} while (tmp_ep != first_ep);
 }
 
-static int etrax_usb_submit_intr_urb(urb_t *urb)
+static int etrax_usb_submit_intr_urb(struct urb *urb)
 {
 	USB_EP_Desc_t *tmp_ep;
 	USB_EP_Desc_t *first_ep;
@@ -643,7 +643,7 @@
 
 static int handle_intr_transfer_attn(char epid, int status)
 {
-	urb_t *old_urb;
+	struct urb *old_urb;
 
 	DBFENTER;
 
@@ -696,7 +696,7 @@
 	DBFEXIT;
 }
 
-static int etrax_rh_unlink_urb (urb_t *urb)
+static int etrax_rh_unlink_urb (struct urb *urb)
 {
 	etrax_hc_t *hc;
 	
@@ -713,7 +713,7 @@
 	return 0;
 }
 
-static void etrax_rh_send_irq(urb_t *urb)
+static void etrax_rh_send_irq(struct urb *urb)
 {
 	__u16 data = 0;
 	etrax_hc_t *hc = urb->dev->bus->hcpriv;
@@ -746,7 +746,7 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_rh_init_int_timer(urb_t *urb)
+static void etrax_rh_init_int_timer(struct urb *urb)
 {
 	etrax_hc_t *hc;
 	
@@ -765,12 +765,12 @@
 
 static void etrax_rh_int_timer_do(unsigned long ptr)
 {
-	urb_t *urb;
+	struct urb *urb;
 	etrax_hc_t *hc;
 	
 /*	DBFENTER; */
 	
-	urb = (urb_t*)ptr;
+	urb = (struct urb *)ptr;
 	hc = urb->dev->bus->hcpriv;
 	
 	if (hc->rh.send) {
@@ -904,7 +904,7 @@
 	return -1;
 }
 
-static int etrax_usb_submit_bulk_urb(urb_t *urb)
+static int etrax_usb_submit_bulk_urb(struct urb *urb)
 {
 	char epid;
 	char devnum;
@@ -912,7 +912,7 @@
 	char maxlen;
 	char slow;
 
-	urb_t *tmp_urb;
+	struct urb *tmp_urb;
 	
 	etrax_urb_priv_t *urb_priv;
 	unsigned long flags;
@@ -962,7 +962,7 @@
 	return 0;
 }
 
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen)
+static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen)
 {
 	USB_SB_Desc_t *sb_desc_1;
 
@@ -1080,7 +1080,7 @@
 
 static int handle_bulk_transfer_attn(char epid, int status)
 {
-	urb_t *old_urb;
+	struct urb *old_urb;
 	etrax_urb_priv_t *hc_priv;
 	unsigned long flags;
 
@@ -1159,7 +1159,7 @@
 
 /* ---------------------------------------------------------------------------- */
 
-static int etrax_usb_submit_ctrl_urb(urb_t *urb)
+static int etrax_usb_submit_ctrl_urb(struct urb *urb)
 {
 	char epid;
 	char devnum;
@@ -1167,7 +1167,7 @@
 	char maxlen;
 	char slow;
 
-	urb_t *tmp_urb;
+	struct urb *tmp_urb;
 	
 	etrax_urb_priv_t *urb_priv;
 	unsigned long flags;
@@ -1217,7 +1217,7 @@
 	return 0;
 }
 
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen)
+static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen)
 {
 	USB_SB_Desc_t *sb_desc_1;
 	USB_SB_Desc_t *sb_desc_2;
@@ -1358,7 +1358,7 @@
 	DBFEXIT;
 }
 
-static int etrax_usb_submit_urb(urb_t *urb)
+static int etrax_usb_submit_urb(struct urb *urb)
 {
 	etrax_hc_t *hc;
 	int rval = -EINVAL;
@@ -1403,7 +1403,7 @@
 	return rval;
 }
 
-static int etrax_usb_unlink_urb(urb_t *urb)
+static int etrax_usb_unlink_urb(struct urb *urb)
 {
 	etrax_hc_t *hc = urb->dev->bus->hcpriv;
 	int epid;
@@ -1448,7 +1448,7 @@
 	cli();
 	
 	for (epid = 0; epid < 32; epid++) {
-		urb_t *u = URB_List[epid];
+		struct urb *u = URB_List[epid];
 		pos = 0;
 
 		for (; u; u = u->next) {
@@ -1474,7 +1474,7 @@
 					URB_List[epid] = u->next;
 				
 				} else {
-					urb_t *up;
+					struct urb *up;
 					for (up = URB_List[epid]; up->next != u; up = up->next);
 					up->next = u->next;
 				}
@@ -1522,7 +1522,7 @@
 	etrax_hc_t *hc = (etrax_hc_t *)vhc;
 	int epid;
 	char eol;
-	urb_t *urb;
+	struct urb *urb;
 	USB_EP_Desc_t *tmp_ep;
 	USB_SB_Desc_t *tmp_sb;
 	
@@ -1551,7 +1551,7 @@
 static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
 {
 	int epid = 0;
-	urb_t *urb;
+	struct urb *urb;
 	etrax_urb_priv_t *urb_priv;
 		
 	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
@@ -1649,7 +1649,7 @@
 
 static int handle_control_transfer_attn(char epid, int status)
 {
-	urb_t *old_urb;
+	struct urb *old_urb;
 	etrax_urb_priv_t *hc_priv;	
 
 	DBFENTER;
@@ -1710,7 +1710,7 @@
 static void etrax_usb_hc_intr_bottom_half(void *data)
 {
 	struct usb_reg_context *reg = (struct usb_reg_context *)data;
-	urb_t *old_urb;
+	struct urb *old_urb;
 	
 	int error_code;
 	int epid;
@@ -1976,7 +1976,7 @@
 	DBFEXIT;
 }
 
-static int etrax_rh_submit_urb(urb_t *urb)
+static int etrax_rh_submit_urb(struct urb *urb)
 {
 	struct usb_device *usb_dev = urb->dev;
 	etrax_hc_t *hc = usb_dev->bus->hcpriv;
diff -urN linux-2.5.3-pre2/arch/cris/kernel/Makefile linux/arch/cris/kernel/Makefile
--- linux-2.5.3-pre2/arch/cris/kernel/Makefile	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/Makefile	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.9 2001/10/22 13:10:21 pkj Exp $
+# $Id: Makefile,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
 #
 # Makefile for the linux kernel.
 #
diff -urN linux-2.5.3-pre2/arch/cris/kernel/debugport.c linux/arch/cris/kernel/debugport.c
--- linux-2.5.3-pre2/arch/cris/kernel/debugport.c	Tue May  1 16:04:56 2001
+++ linux/arch/cris/kernel/debugport.c	Mon Jan 21 17:23:22 2002
@@ -12,6 +12,9 @@
  *    init_etrax_debug()
  *
  * $Log: debugport.c,v $
+ * Revision 1.2  2002/01/21 15:21:50  bjornw
+ * Update for kdev_t changes
+ *
  * Revision 1.6  2001/04/17 13:58:39  orjanf
  * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB.
  *
@@ -214,7 +217,7 @@
 static kdev_t 
 console_device(struct console *c)
 {
-         return MKDEV(TTY_MAJOR, 64 + c->index);
+         return mk_kdev(TTY_MAJOR, 64 + c->index);
 }
 
 static int __init 
@@ -224,17 +227,16 @@
 }
 
 static struct console sercons = {
-        "ttyS",
-        console_write,
-        NULL,
-        console_device,
-        NULL,
-	NULL,
-	console_setup,
-	CON_PRINTBUFFER,
-	DEBUG_PORT_IDX,
-	0,
-	NULL
+        name:    "ttyS",
+        write:   console_write,
+        read:    NULL,
+        device:  console_device,
+        unblank: NULL,
+	setup:   console_setup,
+	flags:   CON_PRINTBUFFER,
+	index:   DEBUG_PORT_IDX,
+	cflag:   0,
+	next:    NULL
 };
 
 /*
diff -urN linux-2.5.3-pre2/arch/cris/kernel/entry.S linux/arch/cris/kernel/entry.S
--- linux-2.5.3-pre2/arch/cris/kernel/entry.S	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/entry.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.35 2001/10/30 17:10:15 bjornw Exp $
+/* $Id: entry.S,v 1.3 2002/01/21 15:22:20 bjornw Exp $
  *
  *  linux/arch/cris/entry.S
  *
@@ -7,6 +7,18 @@
  *  Authors:	Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: entry.S,v $
+ *  Revision 1.3  2002/01/21 15:22:20  bjornw
+ *  NICE_DOGGY fix from 2.4 arch/cris
+ *
+ *  Revision 1.37  2001/12/07 17:03:55  bjornw
+ *  Call a c-hook called watchdog_bite_hook instead of show_registers directly
+ *
+ *  Revision 1.36  2001/11/22 13:36:36  bjornw
+ *  * In ret_from_intr, check regs->dccr for usermode reentrance instead of
+ *    DCCR explicitely (because the latter might not reflect current reality)
+ *  * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before
+ *    since $r9 is call-clobbered and is potentially needed afterwards
+ *
  *  Revision 1.35  2001/10/30 17:10:15  bjornw
  *  Add some syscalls
  *
@@ -217,8 +229,11 @@
 	
 ret_from_intr:
 	;; check for resched only if we're going back to user-mode
-
-	move	$ccr, $r0
+	;; this test matches the user_regs(regs) macro
+	;; we cannot simply test $dccr, because that does not necessarily
+	;; reflect what mode we'll return into.
+	
+	move.d	[$sp + LDCCR], $r0; regs->dccr
 	btstq	8, $r0		; U-flag
 	bpl	_Rexit		; go back directly
 	nop
@@ -468,8 +483,6 @@
 	moveq	1, $r10
 	push	$r10		; frametype == 1, BUSFAULT frame type
 
-	moveq	0, $r9		; busfault is equivalent to an irq
-		
 	move.d	$sp, $r10	; pt_regs argument to handle_mmu_bus_fault
 		
 	jsr	handle_mmu_bus_fault  ; in arch/cris/mm/fault.c
@@ -479,6 +492,8 @@
 	;; process due to a SEGV, scheduled due to a page blocking or
 	;; whatever.
 
+	moveq	0, $r9		; busfault is equivalent to an irq
+		
 	ba	ret_from_intr
 	nop
 		
@@ -559,6 +574,17 @@
 ;; We'll see this in ksymoops dumps.
 Watchdog_bite:
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+       ;; We just restart the watchdog here to be sure we dont get
+       ;; hit while printing the watchdogmsg below
+       ;; This restart is compatible with the rest of the C-code, so
+       ;; the C-code can keep restarting the watchdog after this point.
+       ;; The non-NICE_DOGGY code below though, disables the possibility
+       ;; to restart since it changes the watchdog key, to avoid any
+       ;; buggy loops etc. keeping the watchdog alive after this.
+       jsr     reset_watchdog
+#else
+
 ;; We need to extend the 3.3ms after the NMI at watchdog bite, so we have
 ;; time for an oops-dump over a 115k2 serial wire.  Another 100ms should do.
 
@@ -576,6 +602,8 @@
 		| IO_STATE (R_WATCHDOG, enable, start), $r10
 	move.d	$r10, [$r11]
 
+#endif
+	
 ;; Note that we don't do "setf m" here (or after two necessary NOPs),
 ;; since *not* doing that saves us from re-entrancy checks.  We don't want
 ;; to get here again due to possible subsequent NMIs; we want the watchdog
@@ -585,7 +613,7 @@
 	jsr	printk
 
 	move.d	$sp, $r10
-	jsr	show_registers
+	jsr	watchdog_bite_hook
 
 ;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps
 ;; rather than "spurious_interrupt".
diff -urN linux-2.5.3-pre2/arch/cris/kernel/head.S linux/arch/cris/kernel/head.S
--- linux-2.5.3-pre2/arch/cris/kernel/head.S	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/head.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.41 2001/10/29 14:55:58 pkj Exp $
+/* $Id: head.S,v 1.2 2001/12/18 13:35:19 bjornw Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,15 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.2  2001/12/18 13:35:19  bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.43  2001/11/08 15:09:43  starvik
+ * Only start MII clock if Ethernet is configured
+ *
+ * Revision 1.42  2001/11/08 14:37:34  starvik
+ * Start MII clock early to make sure that it is running at tranceiver reset
+ *
  * Revision 1.41  2001/10/29 14:55:58  pkj
  * Corrected pa$r0 to par0.
  *
@@ -156,7 +165,10 @@
 
 #define CRAMFS_MAGIC 0x28cd3d45
 #define RAM_INIT_MAGIC 0x56902387
-		
+
+#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
+                             IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
+				
 	;; exported symbols
 		
 	.globl	etrax_irv
@@ -301,6 +313,12 @@
 	;; after init.
 	.section ".text.init"
 _inflash:
+#ifdef CONFIG_ETRAX_ETHERNET	
+	;; Start MII clock to make sure it is running when tranceiver is reset
+	move.d START_ETHERNET_CLOCK, $r0
+	move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+		
 	;; We need to initialze DRAM registers before we start using the DRAM
 
 	cmp.d	RAM_INIT_MAGIC, $r8	; Already initialized?
diff -urN linux-2.5.3-pre2/arch/cris/kernel/irq.c linux/arch/cris/kernel/irq.c
--- linux-2.5.3-pre2/arch/cris/kernel/irq.c	Wed Nov 28 13:22:25 2001
+++ linux/arch/cris/kernel/irq.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.17 2001/07/25 16:08:01 bjornw Exp $
+/* $Id: irq.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
  *
  *	linux/arch/cris/kernel/irq.c
  *
@@ -138,7 +138,7 @@
 /* IRQ0 and 1 are special traps */
 void hwbreakpoint(void);
 void IRQ1_interrupt(void);
-BUILD_IRQ(2, 0x04)             /* the timer interrupt */
+BUILD_TIMER_IRQ(2, 0x04)       /* the timer interrupt is somewhat special */
 BUILD_IRQ(3, 0x08)
 BUILD_IRQ(4, 0x10)
 BUILD_IRQ(5, 0x20)
diff -urN linux-2.5.3-pre2/arch/cris/kernel/kgdb.c linux/arch/cris/kernel/kgdb.c
--- linux-2.5.3-pre2/arch/cris/kernel/kgdb.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/kgdb.c	Mon Jan 21 17:23:22 2002
@@ -18,6 +18,9 @@
 *! Jul 21 1999  Bjorn Wesen     eLinux port
 *!
 *! $Log: kgdb.c,v $
+*! Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+*! Import of Linux 2.5.1
+*!
 *! Revision 1.6  2001/10/09 13:10:03  matsfg
 *! Added $ on registers and removed some underscores
 *!
@@ -55,7 +58,7 @@
 *!
 *!---------------------------------------------------------------------------
 *!
-*! $Id: kgdb.c,v 1.6 2001/10/09 13:10:03 matsfg Exp $
+*! $Id: kgdb.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
 *!
 *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
 *!
diff -urN linux-2.5.3-pre2/arch/cris/kernel/ksyms.c linux/arch/cris/kernel/ksyms.c
--- linux-2.5.3-pre2/arch/cris/kernel/ksyms.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/ksyms.c	Mon Jan 21 17:23:22 2002
@@ -23,34 +23,45 @@
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern unsigned long get_cmos_time(void);
+extern void __Udiv(void);
 extern void __ashrdi3(void);
 extern void iounmap(void *addr);
 
-/* platform dependent support */
-
+/* Platform dependent support */
 EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
 EXPORT_SYMBOL(kernel_thread);
 EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(loops_per_usec);
 
+/* String functions */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(strtok);
 EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL(strstr);
-
+EXPORT_SYMBOL(strcpy);
 EXPORT_SYMBOL(strchr);
 EXPORT_SYMBOL(strcmp);
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strncat);
 EXPORT_SYMBOL(strncmp);
+
+/* Math functions */
+EXPORT_SYMBOL(__Udiv);
 EXPORT_SYMBOL(__ashrdi3);
 
+/* Memory functions */
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
 
-/* export shadow registers for the CPU I/O pins */
+/* Semaphore functions */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
 
+/* Export shadow registers for the CPU I/O pins */
 EXPORT_SYMBOL(genconfig_shadow);
 EXPORT_SYMBOL(port_pa_data_shadow);
 EXPORT_SYMBOL(port_pa_dir_shadow);
@@ -59,8 +70,7 @@
 EXPORT_SYMBOL(port_pb_config_shadow);
 EXPORT_SYMBOL(port_g_data_shadow);
 
-/* other stuff */
-
+/* Userspace access functions */
 EXPORT_SYMBOL(strncpy_from_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(__generic_copy_from_user);
@@ -71,8 +81,8 @@
 
 #undef memcpy
 #undef memset
-extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memset(void *, int, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memset);
 
diff -urN linux-2.5.3-pre2/arch/cris/kernel/process.c linux/arch/cris/kernel/process.c
--- linux-2.5.3-pre2/arch/cris/kernel/process.c	Thu Dec 27 08:21:28 2001
+++ linux/arch/cris/kernel/process.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.20 2001/10/03 08:21:39 jonashg Exp $
+/* $Id: process.c,v 1.3 2002/01/21 15:22:49 bjornw Exp $
  * 
  *  linux/arch/cris/kernel/process.c
  *
@@ -8,6 +8,15 @@
  *  Authors:   Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: process.c,v $
+ *  Revision 1.3  2002/01/21 15:22:49  bjornw
+ *  current->counter is gone
+ *
+ *  Revision 1.22  2001/11/13 09:40:43  orjanf
+ *  Added dump_fpu (needed for core dumps).
+ *
+ *  Revision 1.21  2001/11/12 18:26:21  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.20  2001/10/03 08:21:39  jonashg
  *  cause_of_death does not exist if CONFIG_SVINTO_SIM is defined.
  *
@@ -57,6 +66,7 @@
 #include <linux/slab.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/elfcore.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
@@ -77,7 +87,6 @@
  * setup.
  */
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
@@ -135,14 +144,15 @@
 	 * code to know about it than the watchdog handler in entry.S and
 	 * this code, implementing hard reset through the watchdog.
 	 */
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 	extern int cause_of_death;
+#endif
 
 	printk("*** HARD RESET ***\n");
 	cli();
 
 #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 	cause_of_death = 0xbedead;
-
 #else
 	/* Since we dont plan to keep on reseting the watchdog,
 	   the key can be arbitrary hence three */
@@ -243,9 +253,10 @@
  */
 void dump_thread(struct pt_regs * regs, struct user * dump)
 {
-	int i;
 #if 0
-/* changed the size calculations - should hopefully work better. lbt */
+	int i;
+
+	/* changed the size calculations - should hopefully work better. lbt */
 	dump->magic = CMAGIC;
 	dump->start_code = 0;
 	dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
@@ -263,6 +274,12 @@
 
 	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
 #endif 
+}
+
+/* Fill in the fpu structure for a core dump. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+        return 0;
 }
 
 /* 
diff -urN linux-2.5.3-pre2/arch/cris/kernel/ptrace.c linux/arch/cris/kernel/ptrace.c
--- linux-2.5.3-pre2/arch/cris/kernel/ptrace.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/kernel/ptrace.c	Mon Jan 21 17:23:22 2002
@@ -8,6 +8,12 @@
  * Authors:   Bjorn Wesen
  *
  * $Log: ptrace.c,v $
+ * Revision 1.2  2001/12/18 13:35:20  bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.8  2001/11/12 18:26:21  pkj
+ * Fixed compiler warnings.
+ *
  * Revision 1.7  2001/09/26 11:53:49  bjornw
  * PTRACE_DETACH works more simple in 2.4.10
  *
@@ -74,8 +80,6 @@
 static inline int put_reg(struct task_struct *task, unsigned int regno,
 			  unsigned long data)
 {
-	unsigned long *addr;
-
 	if (regno == PT_USP)
 		task->thread.usp = data;
 	else if (regno < PT_MAX)
@@ -207,9 +211,7 @@
 			break;
 
 		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
-		case PTRACE_CONT: { /* restart after signal. */
-			long tmp;
-
+		case PTRACE_CONT: /* restart after signal. */
 			ret = -EIO;
 			if ((unsigned long) data > _NSIG)
 				break;
@@ -222,16 +224,13 @@
 			wake_up_process(child);
 			ret = 0;
 			break;
-		}
 
 /*
  * make the child exit.  Best I can do is send it a sigkill. 
  * perhaps it should be put in the status that it wants to 
  * exit.
  */
-		case PTRACE_KILL: {
-			long tmp;
-
+		case PTRACE_KILL:
 			ret = 0;
 			if (child->state == TASK_ZOMBIE) /* already dead */
 				break;
@@ -239,11 +238,8 @@
 			/* TODO: make sure any pending breakpoint is killed */
 			wake_up_process(child);
 			break;
-		}
-
-		case PTRACE_SINGLESTEP: {  /* set the trap flag. */
-			long tmp;
 
+		case PTRACE_SINGLESTEP: /* set the trap flag. */
 			ret = -EIO;
 			if ((unsigned long) data > _NSIG)
 				break;
@@ -256,7 +252,6 @@
 			wake_up_process(child);
 			ret = 0;
 			break;
-		}
 
 		case PTRACE_DETACH:
 			ret = ptrace_detach(child, data);
diff -urN linux-2.5.3-pre2/arch/cris/kernel/setup.c linux/arch/cris/kernel/setup.c
--- linux-2.5.3-pre2/arch/cris/kernel/setup.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/setup.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.22 2001/10/23 17:42:58 pkj Exp $
+/* $Id: setup.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
  *
  *  linux/arch/cris/kernel/setup.c
  *
@@ -26,10 +26,12 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
+#include <linux/seq_file.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include <asm/pgtable.h>
 #include <asm/types.h>
 #include <asm/svinto.h>
 
@@ -72,10 +74,10 @@
 void __init 
 setup_arch(char **cmdline_p)
 {
-        unsigned long bootmap_size;
+	extern void init_etrax_debug(void);
+	unsigned long bootmap_size;
 	unsigned long start_pfn, max_pfn;
 	unsigned long memory_start;
-	extern void console_print_etrax(const char *b);
 
  	/* register an initial console printing routine for printk's */
 
@@ -87,12 +89,12 @@
 
 	if(romfs_in_flash || !romfs_length) {
 		/* if we have the romfs in flash, or if there is no rom filesystem,
-		 * our free area starts directly after the BSS 
+		 * our free area starts directly after the BSS
 		 */
 		memory_start = (unsigned long) &_end;
 	} else {
 		/* otherwise the free area starts after the ROM filesystem */
-		printk("ROM fs in RAM, size %d bytes\n", romfs_length);
+		printk("ROM fs in RAM, size %lu bytes\n", romfs_length);
 		memory_start = romfs_start + romfs_length;
 	}
 
@@ -193,7 +195,7 @@
 #define HAS_ATA		0x0020
 #define HAS_USB		0x0040
 #define HAS_IRQ_BUG	0x0080
-#define HAS_MMU_BUG     0x0100
+#define HAS_MMU_BUG	0x0100
 
 static struct cpu_info {
 	char *model;
@@ -213,50 +215,27 @@
 	{ "ETRAX 100",       8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
 	{ "ETRAX 100",       8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
 	{ "ETRAX 100LX",     8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG },
-	{ "ETRAX 100LX v2",  8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
+	{ "ETRAX 100LX v2",  8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU  },
 	{ "Unknown",         0, 0 }  /* This entry MUST be the last */
 };
 
-/*
- * get_cpuinfo - Get information on one CPU for use by the procfs.
- *
- *	Prints info on the next CPU into buffer.  Beware, doesn't check for
- *	buffer overflow.  Current implementation of procfs assumes that the
- *	resulting data is <= 1K.
- *
- *	BUFFER is PAGE_SIZE - 1K bytes long.
- *
- * Args:
- *	buffer	-- you guessed it, the data buffer
- *	cpu_np	-- Input: next cpu to get (start at 0).  Output: Updated.
- *
- *	Returns number of bytes written to buffer.
- */
-int get_cpuinfo(char *buffer, unsigned *cpu_np)
+static int show_cpuinfo(struct seq_file *m, void *v)
 {
-	int revision;
- 	struct cpu_info *info;
-	unsigned n;
+	unsigned long revision;
+	struct cpu_info *info;
 
 	/* read the version register in the CPU and print some stuff */
 
 	revision = rdvr();
 
-	if (revision < 0 || revision >= sizeof cpu_info/sizeof *cpu_info) {
+	if (revision >= sizeof cpu_info/sizeof *cpu_info)
 		info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1];
-	} else
+	else
 		info = &cpu_info[revision];
 
-  	/* No SMP at the moment, so just toggle 0/1 */
-	n = *cpu_np;
-	*cpu_np = 1;
-	if (n != 0) {
-		return (0);
-	}
-
-	return sprintf(buffer,
+	return seq_printf(m,
 		       "cpu\t\t: CRIS\n"
-		       "cpu revision\t: %d\n"
+		       "cpu revision\t: %lu\n"
 		       "cpu model\t: %s\n"
 		       "cache size\t: %d kB\n"
 		       "fpu\t\t: %s\n"
@@ -283,4 +262,28 @@
 		       (loops_per_jiffy * HZ + 500) / 500000,
 		       ((loops_per_jiffy * HZ + 500) / 5000) % 100);
 }
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	/* We only got one CPU... */
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+	start:  c_start,
+	next:   c_next,
+	stop:   c_stop,
+	show:   show_cpuinfo,
+};
+
 #endif /* CONFIG_PROC_FS */
diff -urN linux-2.5.3-pre2/arch/cris/kernel/shadows.c linux/arch/cris/kernel/shadows.c
--- linux-2.5.3-pre2/arch/cris/kernel/shadows.c	Tue May  1 16:04:56 2001
+++ linux/arch/cris/kernel/shadows.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: shadows.c,v 1.2 2001/03/15 14:25:16 bjornw Exp $
+/* $Id: shadows.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * 
  * Various shadow registers. Defines for these are in include/asm-etrax100/io.h
  */
diff -urN linux-2.5.3-pre2/arch/cris/kernel/sys_cris.c linux/arch/cris/kernel/sys_cris.c
--- linux-2.5.3-pre2/arch/cris/kernel/sys_cris.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/kernel/sys_cris.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: sys_cris.c,v 1.10 2001/06/27 21:16:15 hp Exp $
+/* $Id: sys_cris.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  *
  * linux/arch/cris/kernel/sys_cris.c
  *
diff -urN linux-2.5.3-pre2/arch/cris/kernel/time.c linux/arch/cris/kernel/time.c
--- linux-2.5.3-pre2/arch/cris/kernel/time.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/time.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.9 2001/10/25 10:26:37 johana Exp $
+/* $Id: time.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
  *
  *  linux/arch/cris/kernel/time.c
  *
@@ -18,6 +18,7 @@
  * Linux/CRIS specific code:
  *
  * Authors:    Bjorn Wesen
+ *             Johan Adolfsson  
  *
  */
 
@@ -61,6 +62,7 @@
 static unsigned long do_slow_gettimeoffset(void)
 {
 	unsigned long count;
+	unsigned long usec_count = 0;
 
 	static unsigned long count_p = LATCH;    /* for the first call after boot */
 	static unsigned long jiffies_p = 0;
@@ -93,16 +95,20 @@
  */
 	if( jiffies_t == jiffies_p ) {
 		if( count > count_p ) {
+			/* Timer wrapped */
+			count = count_p;
+			usec_count = 1000000/CLOCK_TICK_RATE/2;
 		}
 	} else
 		jiffies_p = jiffies_t;
-
         count_p = count;
-
+	/* Convert timer value to usec using table lookup */
+	usec_count += cris_timer0_value_us[count];
+#if 0
 	count = ((LATCH-1) - count) * TICK_SIZE;
 	count = (count + LATCH/2) / LATCH;
-
-	return count;
+#endif
+	return usec_count;
 }
 
 static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
@@ -160,9 +166,8 @@
 {
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
-	unsigned char save_control, save_freq_select;
 
-	printk("set_rtc_mmss(%d)\n", nowtime);
+	printk("set_rtc_mmss(%lu)\n", nowtime);
 
 	if(!have_rtc)
 		return 0;
@@ -225,7 +230,9 @@
 /* right now, starting the watchdog is the same as resetting it */
 #define start_watchdog reset_watchdog
 
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 static int watchdog_key = 0;  /* arbitrary number */
+#endif
 
 /* number of pages to consider "out of memory". it is normal that the memory
  * is used though, so put this really low.
@@ -306,12 +313,12 @@
 	if ((time_status & STA_UNSYNC) == 0 &&
 	    xtime.tv_sec > last_rtc_update + 660 &&
 	    xtime.tv_usec > 500000 - (tick >> 1) &&
-	    xtime.tv_usec < 500000 + (tick >> 1))
+	    xtime.tv_usec < 500000 + (tick >> 1)) {
 		if (set_rtc_mmss(xtime.tv_sec) == 0)
 			last_rtc_update = xtime.tv_sec;
 		else
 			last_rtc_update = xtime.tv_sec - 600;
-	
+	}
 }
 
 #if 0
@@ -322,6 +329,7 @@
 {
 	unsigned long flags;
 	unsigned int newjiff;
+
 	save_flags(flags);
 	cli();
 	newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); 
@@ -337,7 +345,6 @@
 get_cmos_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
-	int i;
 
 	sec = CMOS_READ(RTC_SECONDS);
 	min = CMOS_READ(RTC_MINUTES);
diff -urN linux-2.5.3-pre2/arch/cris/kernel/traps.c linux/arch/cris/kernel/traps.c
--- linux-2.5.3-pre2/arch/cris/kernel/traps.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/traps.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.15 2001/07/18 14:02:37 bjornw Exp $
+/* $Id: traps.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
  *
  *  linux/arch/cris/traps.c
  *
@@ -30,6 +30,55 @@
 
 int kstack_depth_to_print = 24;
 
+void show_trace(unsigned long * stack)
+{
+	unsigned long addr, module_start, module_end;
+	extern char _stext, _etext;
+	int i;
+
+        printk("\nCall Trace: ");
+
+        i = 1;
+        module_start = VMALLOC_START;
+        module_end = VMALLOC_END;
+
+        while (((long) stack & (THREAD_SIZE-1)) != 0) {
+		if (__get_user (addr, stack)) {
+			/* This message matches "failing address" marked
+			   s390 in ksymoops, so lines containing it will
+			   not be filtered out by ksymoops.  */
+			printk ("Failing address 0x%lx\n", (unsigned long)stack);
+			break;
+		}
+		stack++;
+
+                /*
+                 * If the address is either in the text segment of the
+                 * kernel, or in the region which contains vmalloc'ed
+                 * memory, it *may* be the address of a calling
+                 * routine; if so, print it so that someone tracing
+                 * down the cause of the crash will be able to figure
+                 * out the call path that was taken.
+                 */
+                if (((addr >= (unsigned long) &_stext) &&
+                     (addr <= (unsigned long) &_etext)) ||
+                    ((addr >= module_start) && (addr <= module_end))) {
+                        if (i && ((i % 8) == 0))
+                                printk("\n       ");
+                        printk("[<%08lx>] ", addr);
+                        i++;
+                }
+        }
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/* TODO, this is not really useful since its called from
+	 * SysRq-T and we don't have a keyboard.. :) 
+	 */
+}
+
+
 /*
  * These constants are for searching for possible module text
  * segments. MODULE_RANGE is a guess of how much space is likely
@@ -48,9 +97,8 @@
 void 
 show_stack(unsigned long *sp)
 {
-        unsigned long *stack, addr, module_start, module_end;
+        unsigned long *stack, addr;
         int i;
-	extern char _stext, _etext;
 
 	/*
 	 * debugging aid: "show_stack(NULL);" prints a
@@ -62,7 +110,7 @@
 
         stack = sp;
 
-	printk("\nStack from %08lx:\n       ", stack);
+	printk("\nStack from %08lx:\n       ", (unsigned long)stack);
         for(i = 0; i < kstack_depth_to_print; i++) {
                 if (((long) stack & (THREAD_SIZE-1)) == 0)
                         break;
@@ -72,45 +120,13 @@
 			/* This message matches "failing address" marked
 			   s390 in ksymoops, so lines containing it will
 			   not be filtered out by ksymoops.  */
-			printk ("Failing address 0x%lx\n", stack);
+			printk ("Failing address 0x%lx\n", (unsigned long)stack);
 			break;
 		}
 		stack++;
 		printk("%08lx ", addr);
         }
-
-        printk("\nCall Trace: ");
-        stack = sp;
-        i = 1;
-        module_start = VMALLOC_START;
-        module_end = VMALLOC_END;
-        while (((long) stack & (THREAD_SIZE-1)) != 0) {
-		if (__get_user (addr, stack)) {
-			/* This message matches "failing address" marked
-			   s390 in ksymoops, so lines containing it will
-			   not be filtered out by ksymoops.  */
-			printk ("Failing address 0x%lx\n", stack);
-			break;
-		}
-		stack++;
-
-                /*
-                 * If the address is either in the text segment of the
-                 * kernel, or in the region which contains vmalloc'ed
-                 * memory, it *may* be the address of a calling
-                 * routine; if so, print it so that someone tracing
-                 * down the cause of the crash will be able to figure
-                 * out the call path that was taken.
-                 */
-                if (((addr >= (unsigned long) &_stext) &&
-                     (addr <= (unsigned long) &_etext)) ||
-                    ((addr >= module_start) && (addr <= module_end))) {
-                        if (i && ((i % 8) == 0))
-                                printk("\n       ");
-                        printk("[<%08lx>] ", addr);
-                        i++;
-                }
-        }
+	show_trace(sp);
 }
 
 #if 0
@@ -148,7 +164,7 @@
 	       regs->r8, regs->r9, regs->r10, regs->r11);
 	printk("r12: %08lx r13: %08lx oR10: %08lx\n",
 	       regs->r12, regs->r13, regs->orig_r10);
-	printk("R_MMU_CAUSE: %08lx\n", *R_MMU_CAUSE);
+	printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
 	printk("Process %s (pid: %d, stackpage=%08lx)\n",
 	       current->comm, current->pid, (unsigned long)current);
 
@@ -195,25 +211,56 @@
         }
 }
 
+/* Called from entry.S when the watchdog has bitten
+ * We print out something resembling an oops dump, and if
+ * we have the nice doggy development flag set, we halt here
+ * instead of rebooting.
+ */
+
+void
+watchdog_bite_hook(struct pt_regs *regs)
+{
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	cli();
+	stop_watchdog();
+	show_registers(regs);
+	while(1) /* nothing */;
+#else
+	show_registers(regs);
+#endif	
+}
+
+/* This is normally the 'Oops' routine */
+
 void 
 die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
+	extern void reset_watchdog(void);
+	extern void stop_watchdog(void);
+
 	if(user_mode(regs))
 		return;
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	/* This printout might take too long and trigger the 
+	 * watchdog normally. If we're in the nice doggy
+	 * development mode, stop the watchdog during printout.
+	 */
 	stop_watchdog();
+#endif
 
 	printk("%s: %04lx\n", str, err & 0xffff);
 
 	show_registers(regs);
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
 	reset_watchdog();
-
+#endif
 	do_exit(SIGSEGV);
 }
 
 void __init 
 trap_init(void)
 {
-  /* Nothing needs to be done */
+	/* Nothing needs to be done */
 }
diff -urN linux-2.5.3-pre2/arch/cris/lib/checksum.S linux/arch/cris/lib/checksum.S
--- linux-2.5.3-pre2/arch/cris/lib/checksum.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/lib/checksum.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: checksum.S,v 1.6 2001/10/01 14:47:35 bjornw Exp $
+/* $Id: checksum.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * A fast checksum routine using movem
  * Copyright (c) 1998-2001 Axis Communications AB
  *
diff -urN linux-2.5.3-pre2/arch/cris/lib/checksumcopy.S linux/arch/cris/lib/checksumcopy.S
--- linux-2.5.3-pre2/arch/cris/lib/checksumcopy.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/lib/checksumcopy.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: checksumcopy.S,v 1.7 2001/10/01 14:47:35 bjornw Exp $
+/* $Id: checksumcopy.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * A fast checksum+copy routine using movem
  * Copyright (c) 1998, 2001 Axis Communications AB
  *
diff -urN linux-2.5.3-pre2/arch/cris/lib/dmacopy.c linux/arch/cris/lib/dmacopy.c
--- linux-2.5.3-pre2/arch/cris/lib/dmacopy.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/cris/lib/dmacopy.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ 
+/* $Id: dmacopy.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ 
  *
  * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
  */
diff -urN linux-2.5.3-pre2/arch/cris/lib/dram_init.S linux/arch/cris/lib/dram_init.S
--- linux-2.5.3-pre2/arch/cris/lib/dram_init.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/lib/dram_init.S	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: dram_init.S,v 1.10 2001/10/04 12:00:21 martinnn Exp $
+/* $Id: dram_init.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * 
  * DRAM/SDRAM initialization - alter with care
  * This file is intended to be included from other assembler files
@@ -11,6 +11,9 @@
  * Authors:  Mikael Starvik (starvik@axis.com)	
  * 
  * $Log: dram_init.S,v $
+ * Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+ * Import of Linux 2.5.1
+ *
  * Revision 1.10  2001/10/04 12:00:21  martinnn
  * Added missing underscores.
  *
diff -urN linux-2.5.3-pre2/arch/cris/lib/hw_settings.S linux/arch/cris/lib/hw_settings.S
--- linux-2.5.3-pre2/arch/cris/lib/hw_settings.S	Wed Jul  4 11:50:39 2001
+++ linux/arch/cris/lib/hw_settings.S	Mon Jan 21 17:23:22 2002
@@ -1,5 +1,5 @@
 /*
- * $Id: hw_settings.S,v 1.3 2001/04/21 17:02:46 bjornw Exp $
+ * $Id: hw_settings.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  * 
  * This table is used by some tools to extract hardware parameters.
  * The table should be included in the kernel and the decompressor.
diff -urN linux-2.5.3-pre2/arch/cris/lib/old_checksum.c linux/arch/cris/lib/old_checksum.c
--- linux-2.5.3-pre2/arch/cris/lib/old_checksum.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/cris/lib/old_checksum.c	Mon Jan 21 17:23:22 2002
@@ -1,4 +1,4 @@
-/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+/* $Id: old_checksum.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
  *
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
diff -urN linux-2.5.3-pre2/arch/cris/mm/extable.c linux/arch/cris/mm/extable.c
--- linux-2.5.3-pre2/arch/cris/mm/extable.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/mm/extable.c	Mon Jan 21 17:23:22 2002
@@ -2,6 +2,9 @@
  * linux/arch/cris/mm/extable.c
  *
  * $Log: extable.c,v $
+ * Revision 1.1.1.1  2001/12/17 13:59:27  bjornw
+ * Import of Linux 2.5.1
+ *
  * Revision 1.3  2001/09/27 13:52:40  bjornw
  * Harmonize underscore-ness with other parts
  *
diff -urN linux-2.5.3-pre2/arch/cris/mm/fault.c linux/arch/cris/mm/fault.c
--- linux-2.5.3-pre2/arch/cris/mm/fault.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/mm/fault.c	Mon Jan 21 17:23:22 2002
@@ -6,6 +6,20 @@
  *  Authors:  Bjorn Wesen 
  * 
  *  $Log: fault.c,v $
+ *  Revision 1.2  2001/12/18 13:35:22  bjornw
+ *  Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ *  Revision 1.20  2001/11/22 13:34:06  bjornw
+ *  * Bug workaround (LX TR89): force a rerun of the whole of an interrupted
+ *    unaligned write, because the second half of the write will be corrupted
+ *    otherwise. Affected unaligned writes spanning not-yet mapped pages.
+ *  * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss
+ *    was due to a read or a write (before we didn't know this until the next
+ *    restart of the interrupted instruction, thus wasting one fault-irq)
+ *
+ *  Revision 1.19  2001/11/12 19:02:10  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.18  2001/07/18 22:14:32  bjornw
  *  Enable interrupts in the bulk of do_page_fault
  *
@@ -78,7 +92,14 @@
 			      int error_code);
 
 /* debug of low-level TLB reload */
+#undef DEBUG
+
+#ifdef DEBUG
+#define D(x) x
+#else
 #define D(x)
+#endif
+
 /* debug of higher-level faults */
 #define DPG(x)
 
@@ -94,9 +115,12 @@
 handle_mmu_bus_fault(struct pt_regs *regs)
 {
 	int cause, select;
+#ifdef DEBUG
 	int index;
 	int page_id;
-	int miss, we, acc, inv;  
+	int acc, inv;
+#endif
+	int miss, we, writeac;
 	pmd_t *pmd;
 	pte_t pte;
 	int errcode;
@@ -106,75 +130,83 @@
 	select = *R_TLB_SELECT;
 
 	address = cause & PAGE_MASK; /* get faulting address */
-	
-	D(page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause));
-	D(acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause));
-	D(inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause));  
-	D(index  =  IO_EXTRACT(R_TLB_SELECT, index,     select));
+
+#ifdef DEBUG
+	page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause);
+	acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause);
+	inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause);  
+	index   = IO_EXTRACT(R_TLB_SELECT, index,     select);
+#endif
 	miss    = IO_EXTRACT(R_MMU_CAUSE,  miss_excp, cause);
 	we      = IO_EXTRACT(R_MMU_CAUSE,  we_excp,   cause);
+	writeac = IO_EXTRACT(R_MMU_CAUSE,  wr_rd,     cause);
+
+	/* ETRAX 100LX TR89 bugfix: if the second half of an unaligned
+	 * write causes a MMU-fault, it will not be restarted correctly.
+	 * This could happen if a write crosses a page-boundary and the
+	 * second page is not yet COW'ed or even loaded. The workaround
+	 * is to clear the unaligned bit in the CPU status record, so 
+	 * that the CPU will rerun both the first and second halves of
+	 * the instruction. This will not have any sideeffects unless
+	 * the first half goes to any device or memory that can't be
+	 * written twice, and which is mapped through the MMU.
+	 *
+	 * We only need to do this for writes.
+	 */
+
+	if(writeac)
+		regs->csrinstr &= ~(1 << 5);
 	
-	/* Note: the reason we don't set errcode's r/w flag here
-	 * using the 'we' flag, is because the latter is only given
-	 * if there is a write-protection exception, not given as a
-	 * general r/w access mode flag. It is currently not possible
-	 * to get this from the MMU (TODO: check if this is the case
-	 * for LXv2).
-	 * 
-	 * The page-fault code won't care, but there will be two page-
-	 * faults instead of one for the case of a write to a non-tabled
-	 * page (miss, then write-protection).
+	/* Set errcode's R/W flag according to the mode which caused the
+	 * fault
 	 */
 
-	errcode = 0;
+	errcode = writeac << 1;
 
-	D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, "
-		 "idx %d pid %d\n",
+	D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n",
 		 regs->irp, address, miss, inv, we, acc, index, page_id));
 
 	/* for a miss, we need to reload the TLB entry */
 
-	if(miss) {
-
+	if (miss) {
 		/* see if the pte exists at all
 		 * refer through current_pgd, dont use mm->pgd
 		 */
-		
+
 		pmd = (pmd_t *)(current_pgd + pgd_index(address));
-		if(pmd_none(*pmd))
+		if (pmd_none(*pmd))
 			goto dofault;
-		if(pmd_bad(*pmd)) {
-			printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd);
+		if (pmd_bad(*pmd)) {
+			printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd);
 			pmd_clear(pmd);
 			return;
 		}
 		pte = *pte_offset(pmd, address);
-		if(!pte_present(pte))
+		if (!pte_present(pte))
 			goto dofault;
-		
-		D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte)));
-		D(
-		  {
-			  if(pte_val(pte) & _PAGE_SILENT_WRITE)
-				  printk("Silent-W ");
-			  if(pte_val(pte) & _PAGE_KERNEL)
-				  printk("Kernel ");
-			  if(pte_val(pte) & _PAGE_SILENT_READ)
-				  printk("Silent-R ");
-			  if(pte_val(pte) & _PAGE_GLOBAL)
-				  printk("Global ");
-			  if(pte_val(pte) & _PAGE_PRESENT)
-				  printk("Present ");
-			  if(pte_val(pte) & _PAGE_ACCESSED)
-				  printk("Accessed ");
-			  if(pte_val(pte) & _PAGE_MODIFIED)
-				  printk("Modified ");
-			  if(pte_val(pte) & _PAGE_READ)
-				  printk("Readable ");
-			  if(pte_val(pte) & _PAGE_WRITE)
-				  printk("Writeable ");
-			  printk("\n");
-		  });
+
+#ifdef DEBUG
+		printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte));
+		if (pte_val(pte) & _PAGE_SILENT_WRITE)
+			printk("Silent-W ");
+		if (pte_val(pte) & _PAGE_KERNEL)
+			printk("Kernel ");
+		if (pte_val(pte) & _PAGE_SILENT_READ)
+			printk("Silent-R ");
+		if (pte_val(pte) & _PAGE_GLOBAL)
+			printk("Global ");
+		if (pte_val(pte) & _PAGE_PRESENT)
+			printk("Present ");
+		if (pte_val(pte) & _PAGE_ACCESSED)
+			printk("Accessed ");
+		if (pte_val(pte) & _PAGE_MODIFIED)
+			printk("Modified ");
+		if (pte_val(pte) & _PAGE_READ)
+			printk("Readable ");
+		if (pte_val(pte) & _PAGE_WRITE)
+			printk("Writeable ");
+		printk("\n");
+#endif
 
 		/* load up the chosen TLB entry
 		 * this assumes the pte format is the same as the TLB_LO layout.
@@ -189,10 +221,10 @@
 	} 
 
 	errcode = 1 | (we << 1);
-	
+
  dofault:
 	/* leave it to the MM system fault handler below */
-	D(printk("do_page_fault %p errcode %d\n", address, errcode));
+	D(printk("do_page_fault %lx errcode %d\n", address, errcode));
 	do_page_fault(address, regs, errcode);
 }
 
@@ -221,20 +253,19 @@
 	struct mm_struct *mm;
 	struct vm_area_struct * vma;
 	int writeaccess;
-	int fault;
 	unsigned long fixup;
 	siginfo_t info;
 
 	tsk = current;
 
-        /*
-         * We fault-in kernel-space virtual memory on-demand. The
-         * 'reference' page table is init_mm.pgd.
-         *
-         * NOTE! We MUST NOT take any locks for this case. We may
-         * be in an interrupt or a critical region, and should
-         * only copy the information from the master page table,
-         * nothing more.
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
 	 *
 	 * NOTE2: This is done so that, when updating the vmalloc
 	 * mappings we don't have to walk all processes pgdirs and
@@ -243,13 +274,13 @@
 	 * bit set so sometimes the TLB can use a lingering entry.
 	 *
 	 * This verifies that the fault happens in kernel space
-         * and that the fault was not a protection error (error_code & 1).
-         */
+	 * and that the fault was not a protection error (error_code & 1).
+	 */
 
-        if (address >= VMALLOC_START &&
+	if (address >= VMALLOC_START &&
 	    !(error_code & 1) &&
 	    !user_mode(regs))
-                goto vmalloc_fault;
+		goto vmalloc_fault;
 
 	/* we can and should enable interrupts at this point */
 	sti();
@@ -312,28 +343,27 @@
 	 */
 
 	switch (handle_mm_fault(mm, vma, address, writeaccess)) {
-        case 1:
-                tsk->min_flt++;
-                break;
-        case 2:
-                tsk->maj_flt++;
-                break;
-        case 0:
-                goto do_sigbus;
-        default:
-                goto out_of_memory;
+	case 1:
+		tsk->min_flt++;
+		break;
+	case 2:
+		tsk->maj_flt++;
+		break;
+	case 0:
+		goto do_sigbus;
+	default:
+		goto out_of_memory;
 	}
 
 	up_read(&mm->mmap_sem);
 	return;
-	
+
 	/*
 	 * Something tried to access memory that isn't in our memory map..
 	 * Fix it, but check if it's kernel or user first..
 	 */
 
  bad_area:
-
 	up_read(&mm->mmap_sem);
 
  bad_area_nosemaphore:
@@ -361,10 +391,10 @@
 	 *  code)
 	 */
 
-        if ((fixup = search_exception_table(regs->irp)) != 0) {
+	if ((fixup = search_exception_table(regs->irp)) != 0) {
 		/* Adjust the instruction pointer in the stackframe */
 
-                regs->irp = fixup;
+		regs->irp = fixup;
 
 		/* We do not want to return by restoring the CPU-state
 		 * anymore, so switch frame-types (see ptrace.h)
@@ -372,9 +402,9 @@
 
 		regs->frametype = CRIS_FRAME_NORMAL;
 
-		D(printk("doing fixup to 0x%x\n", fixup));
-                return;
-        }
+		D(printk("doing fixup to 0x%lx\n", fixup));
+		return;
+	}
 
 	/*
 	 * Oops. The kernel tried to access some bad page. We'll have to
@@ -397,9 +427,9 @@
 	 */
 
  out_of_memory:
-        up_read(&mm->mmap_sem);
+	up_read(&mm->mmap_sem);
 	printk("VM: killing process %s\n", tsk->comm);
-	if(user_mode(regs))
+	if (user_mode(regs))
 		do_exit(SIGKILL);
 	goto no_context;
 
@@ -407,40 +437,40 @@
 	up_read(&mm->mmap_sem);
 
 	/*
-         * Send a sigbus, regardless of whether we were in kernel
-         * or user mode.
-         */
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
 	info.si_code = SIGBUS;
 	info.si_errno = 0;
 	info.si_code = BUS_ADRERR;
 	info.si_addr = (void *)address;
 	force_sig_info(SIGBUS, &info, tsk);
-	
-        /* Kernel mode? Handle exceptions or die */
-        if (!user_mode(regs))
-                goto no_context;
-        return;
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!user_mode(regs))
+		goto no_context;
+	return;
 
 vmalloc_fault:
-        {
-                /*
-                 * Synchronize this task's top level page-table
-                 * with the 'reference' page table.
+	{
+		/*
+		 * Synchronize this task's top level page-table
+		 * with the 'reference' page table.
 		 *
 		 * Use current_pgd instead of tsk->active_mm->pgd
 		 * since the latter might be unavailable if this
 		 * code is executed in a misfortunately run irq
 		 * (like inside schedule() between switch_mm and
 		 *  switch_to...).
-                 */
+		 */
 
-                int offset = pgd_index(address);
-                pgd_t *pgd, *pgd_k;
-                pmd_t *pmd, *pmd_k;
+		int offset = pgd_index(address);
+		pgd_t *pgd, *pgd_k;
+		pmd_t *pmd, *pmd_k;
 		pte_t *pte_k;
 
-                pgd = current_pgd + offset;
-                pgd_k = init_mm.pgd + offset;
+		pgd = (pgd_t *)current_pgd + offset;
+		pgd_k = init_mm.pgd + offset;
 
 		/* Since we're two-level, we don't need to do both
 		 * set_pgd and set_pmd (they do the same thing). If
@@ -454,13 +484,13 @@
 		 * it exists.
 		 */
 
-                pmd = pmd_offset(pgd, address);
-                pmd_k = pmd_offset(pgd_k, address);
+		pmd = pmd_offset(pgd, address);
+		pmd_k = pmd_offset(pgd_k, address);
 
-                if (!pmd_present(*pmd_k))
-                        goto bad_area_nosemaphore;
+		if (!pmd_present(*pmd_k))
+			goto bad_area_nosemaphore;
 
-                set_pmd(pmd, *pmd_k);
+		set_pmd(pmd, *pmd_k);
 
 		/* Make sure the actual PTE exists as well to
 		 * catch kernel vmalloc-area accesses to non-mapped
@@ -468,10 +498,10 @@
 		 * silently loop forever.
 		 */
 
-                pte_k = pte_offset(pmd_k, address);
-                if (!pte_present(*pte_k))
-                        goto no_context;
+		pte_k = pte_offset(pmd_k, address);
+		if (!pte_present(*pte_k))
+			goto no_context;
 
-                return;
-        }
+		return;
+	}
 }
diff -urN linux-2.5.3-pre2/arch/cris/mm/init.c linux/arch/cris/mm/init.c
--- linux-2.5.3-pre2/arch/cris/mm/init.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/mm/init.c	Mon Jan 21 17:23:22 2002
@@ -7,6 +7,15 @@
  *  Authors:  Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: init.c,v $
+ *  Revision 1.2  2001/12/18 13:35:22  bjornw
+ *  Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ *  Revision 1.31  2001/11/13 16:22:00  bjornw
+ *  Skip calculating totalram and sharedram in si_meminfo
+ *
+ *  Revision 1.30  2001/11/12 19:02:10  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.29  2001/07/25 16:09:50  bjornw
  *  val->sharedram will stay 0
  *
@@ -459,29 +468,18 @@
                 free_page(addr);
                 totalram_pages++;
         }
-        printk ("Freeing unused kernel memory: %dk freed\n", 
+        printk ("Freeing unused kernel memory: %luk freed\n", 
 		(&__init_end - &__init_begin) >> 10);
 }
 
 void 
 si_meminfo(struct sysinfo *val)
 {
-	int i;
-
-	i = max_mapnr;
-	val->totalram = 0;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	while (i-- > 0)  {
-		if (PageReserved(mem_map+i))
-			continue;
-		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
-			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
-	}
-	val->mem_unit = PAGE_SIZE;
-	val->totalhigh = 0;
-	val->freehigh = 0;
+        val->totalram = totalram_pages;
+        val->sharedram = 0;
+        val->freeram = nr_free_pages();
+        val->bufferram = atomic_read(&buffermem_pages);
+        val->totalhigh = 0;
+        val->freehigh = 0;
+        val->mem_unit = PAGE_SIZE;
 }
diff -urN linux-2.5.3-pre2/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.5.3-pre2/arch/i386/defconfig	Mon Jan 21 17:22:32 2002
+++ linux/arch/i386/defconfig	Mon Jan 21 17:23:22 2002
@@ -491,8 +491,6 @@
 # CONFIG_PCMCIA_XIRTULIP is not set
 CONFIG_NET_PCMCIA_RADIO=y
 CONFIG_PCMCIA_RAYCS=y
-# CONFIG_PCMCIA_NETWAVE is not set
-# CONFIG_PCMCIA_WAVELAN is not set
 # CONFIG_AIRONET4500_CS is not set
 
 #
@@ -738,6 +736,7 @@
 # USB Host Controller Drivers
 #
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
 CONFIG_USB_UHCI_ALT=y
 # CONFIG_USB_OHCI is not set
 
diff -urN linux-2.5.3-pre2/arch/i386/kernel/i8259.c linux/arch/i386/kernel/i8259.c
--- linux-2.5.3-pre2/arch/i386/kernel/i8259.c	Mon Sep 17 23:03:09 2001
+++ linux/arch/i386/kernel/i8259.c	Mon Jan 21 17:23:22 2002
@@ -79,6 +79,7 @@
  * through the ICC by us (IPIs)
  */
 #ifdef CONFIG_SMP
+BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR)
 BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)
 BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR)
 BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR)
@@ -472,6 +473,9 @@
 	 * IPI, driven by wakeup.
 	 */
 	set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
+
+	/* IPI for task migration */
+	set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt);
 
 	/* IPI for invalidation */
 	set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
diff -urN linux-2.5.3-pre2/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- linux-2.5.3-pre2/arch/i386/kernel/smp.c	Mon Jan  7 12:55:17 2002
+++ linux/arch/i386/kernel/smp.c	Mon Jan 21 17:23:22 2002
@@ -485,6 +485,35 @@
 	do_flush_tlb_all_local();
 }
 
+static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED;
+static task_t *new_task;
+
+/*
+ * This function sends a 'task migration' IPI to another CPU.
+ * Must be called from syscall contexts, with interrupts *enabled*.
+ */
+void smp_migrate_task(int cpu, task_t *p)
+{
+	/*
+	 * The target CPU will unlock the migration spinlock:
+	 */
+	spin_lock(&migration_lock);
+	new_task = p;
+	send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR);
+}
+
+/*
+ * Task migration callback.
+ */
+asmlinkage void smp_task_migration_interrupt(void)
+{
+	task_t *p;
+
+	ack_APIC_irq();
+	p = new_task;
+	spin_unlock(&migration_lock);
+	sched_task_migrated(p);
+}
 /*
  * this function sends a 'reschedule' IPI to another CPU.
  * it goes straight through and wastes no time serializing
diff -urN linux-2.5.3-pre2/arch/i386/kernel/smpboot.c linux/arch/i386/kernel/smpboot.c
--- linux-2.5.3-pre2/arch/i386/kernel/smpboot.c	Mon Jan  7 12:55:17 2002
+++ linux/arch/i386/kernel/smpboot.c	Mon Jan 21 17:23:22 2002
@@ -462,6 +462,7 @@
 	 * things done here to the most necessary things.
 	 */
 	cpu_init();
+	init_idle();
 	smp_callin();
 	while (!atomic_read(&smp_commenced))
 		rep_nop();
@@ -470,8 +471,8 @@
 	 * the local TLBs too.
 	 */
 	local_flush_tlb();
+	idle_startup_done();
 
-	init_idle();
 	return cpu_idle();
 }
 
diff -urN linux-2.5.3-pre2/drivers/block/ps2esdi.c linux/drivers/block/ps2esdi.c
--- linux-2.5.3-pre2/drivers/block/ps2esdi.c	Mon Dec 31 17:56:19 2001
+++ linux/drivers/block/ps2esdi.c	Mon Jan 21 17:23:22 2002
@@ -422,7 +422,7 @@
 	blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 128);
 
 	for (i = 0; i < ps2esdi_drives; i++) {
-		register_disk(&ps2esdi_gendisk,MKDEV(MAJOR_NR,i<<6),1<<6,
+		register_disk(&ps2esdi_gendisk,mk_kdev(MAJOR_NR,i<<6),1<<6,
 				&ps2esdi_fops,
 				ps2esdi_info[i].head * ps2esdi_info[i].sect *
 				ps2esdi_info[i].cyl);
@@ -466,7 +466,7 @@
 #if 0
 	printk("%s:got request. device : %d minor : %d command : %d  sector : %ld count : %ld, buffer: %p\n",
 	       DEVICE_NAME,
-	       CURRENT_DEV, MINOR(CURRENT->rq_dev),
+	       CURRENT_DEV, minor(CURRENT->rq_dev),
 	       CURRENT->cmd, CURRENT->sector,
 	       CURRENT->current_nr_sectors, CURRENT->buffer);
 #endif
@@ -481,12 +481,12 @@
 	}			/* check for above 16Mb dmas */
 	else if ((CURRENT_DEV < ps2esdi_drives) &&
 	    (CURRENT->sector + CURRENT->current_nr_sectors <=
-	     ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects) &&
+	     ps2esdi[minor(CURRENT->rq_dev)].nr_sects) &&
 	    	CURRENT->flags & REQ_CMD) {
 #if 0
 		printk("%s:got request. device : %d minor : %d command : %d  sector : %ld count : %ld\n",
 		       DEVICE_NAME,
-		       CURRENT_DEV, MINOR(CURRENT->rq_dev),
+		       CURRENT_DEV, minor(CURRENT->rq_dev),
 		       CURRENT->cmd, CURRENT->sector,
 		       CURRENT->current_nr_sectors);
 #endif
@@ -510,7 +510,7 @@
 	/* is request is valid */ 
 	else {
 		printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives,
-		       CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects);
+		       CURRENT->sector, ps2esdi[minor(CURRENT->rq_dev)].nr_sects);
 		end_request(FAIL);
 	}
 
@@ -849,7 +849,7 @@
 	switch (int_ret_code & 0x0f) {
 	case INT_TRANSFER_REQ:
 		ps2esdi_prep_dma(CURRENT->buffer, CURRENT->current_nr_sectors,
-		    (CURRENT->cmd == READ)
+		    (rq_data_dir(CURRENT) == READ)
 		    ? MCA_DMA_MODE_16 | MCA_DMA_MODE_WRITE | MCA_DMA_MODE_XFER
 		    : MCA_DMA_MODE_16 | MCA_DMA_MODE_READ);
 		outb(CTRL_ENABLE_DMA | CTRL_ENABLE_INTR, ESDI_CONTROL);
diff -urN linux-2.5.3-pre2/drivers/ieee1394/Config.in linux/drivers/ieee1394/Config.in
--- linux-2.5.3-pre2/drivers/ieee1394/Config.in	Thu Jul 19 17:48:15 2001
+++ linux/drivers/ieee1394/Config.in	Mon Jan 21 17:23:22 2002
@@ -19,6 +19,7 @@
 	comment "Protocol Drivers"
 	dep_tristate '  OHCI-1394 Video support' CONFIG_IEEE1394_VIDEO1394 $CONFIG_IEEE1394_OHCI1394
 	dep_tristate '  SBP-2 support (Harddisks etc.)' CONFIG_IEEE1394_SBP2 $CONFIG_SCSI $CONFIG_IEEE1394
+	dep_tristate '  OHCI-DV I/O support' CONFIG_IEEE1394_DV1394 $CONFIG_IEEE1394_OHCI1394
 	dep_tristate '  Raw IEEE1394 I/O support' CONFIG_IEEE1394_RAWIO $CONFIG_IEEE1394
 
 	bool 'Excessive debugging output' CONFIG_IEEE1394_VERBOSEDEBUG
diff -urN linux-2.5.3-pre2/drivers/ieee1394/Makefile linux/drivers/ieee1394/Makefile
--- linux-2.5.3-pre2/drivers/ieee1394/Makefile	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/Makefile	Mon Jan 21 17:23:22 2002
@@ -16,6 +16,7 @@
 obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o
 obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o
 obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o
+obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o
 
 include $(TOPDIR)/Rules.make
 
diff -urN linux-2.5.3-pre2/drivers/ieee1394/csr.c linux/drivers/ieee1394/csr.c
--- linux-2.5.3-pre2/drivers/ieee1394/csr.c	Thu Jul 19 17:48:15 2001
+++ linux/drivers/ieee1394/csr.c	Mon Jan 21 17:23:22 2002
@@ -70,7 +70,7 @@
 {
         host->csr.lock = SPIN_LOCK_UNLOCKED;
 
-        host->csr.rom_size = host->template->get_rom(host, &host->csr.rom);
+        host->csr.rom_size = host->ops->get_rom(host, &host->csr.rom);
 
         host->csr.state                 = 0;
         host->csr.node_ids              = 0;
@@ -152,7 +152,7 @@
         case CSR_CYCLE_TIME:
                 oldcycle = host->csr.cycle_time;
                 host->csr.cycle_time =
-                        host->template->devctl(host, GET_CYCLE_COUNTER, 0);
+                        host->ops->devctl(host, GET_CYCLE_COUNTER, 0);
 
                 if (oldcycle > host->csr.cycle_time) {
                         /* cycle time wrapped around */
@@ -163,7 +163,7 @@
         case CSR_BUS_TIME:
                 oldcycle = host->csr.cycle_time;
                 host->csr.cycle_time =
-                        host->template->devctl(host, GET_CYCLE_COUNTER, 0);
+                        host->ops->devctl(host, GET_CYCLE_COUNTER, 0);
 
                 if (oldcycle > host->csr.cycle_time) {
                         /* cycle time wrapped around */
@@ -181,32 +181,32 @@
                 return RCODE_ADDRESS_ERROR;
 
         case CSR_BUS_MANAGER_ID:
-                if (host->template->hw_csr_reg)
-                        ret = host->template->hw_csr_reg(host, 0, 0, 0);
+                if (host->ops->hw_csr_reg)
+                        ret = host->ops->hw_csr_reg(host, 0, 0, 0);
                 else
                         ret = host->csr.bus_manager_id;
 
                 *(buf++) = cpu_to_be32(ret);
                 out;
         case CSR_BANDWIDTH_AVAILABLE:
-                if (host->template->hw_csr_reg)
-                        ret = host->template->hw_csr_reg(host, 1, 0, 0);
+                if (host->ops->hw_csr_reg)
+                        ret = host->ops->hw_csr_reg(host, 1, 0, 0);
                 else
                         ret = host->csr.bandwidth_available;
 
                 *(buf++) = cpu_to_be32(ret);
                 out;
         case CSR_CHANNELS_AVAILABLE_HI:
-                if (host->template->hw_csr_reg)
-                        ret = host->template->hw_csr_reg(host, 2, 0, 0);
+                if (host->ops->hw_csr_reg)
+                        ret = host->ops->hw_csr_reg(host, 2, 0, 0);
                 else
                         ret = host->csr.channels_available_hi;
 
                 *(buf++) = cpu_to_be32(ret);
                 out;
         case CSR_CHANNELS_AVAILABLE_LO:
-                if (host->template->hw_csr_reg)
-                        ret = host->template->hw_csr_reg(host, 3, 0, 0);
+                if (host->ops->hw_csr_reg)
+                        ret = host->ops->hw_csr_reg(host, 3, 0, 0);
                 else
                         ret = host->csr.channels_available_lo;
 
@@ -244,7 +244,7 @@
                 host->csr.node_ids &= NODE_MASK << 16;
                 host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16);
                 host->node_id = host->csr.node_ids >> 16;
-                host->template->devctl(host, SET_BUS_ID, host->node_id >> 6);
+                host->ops->devctl(host, SET_BUS_ID, host->node_id >> 6);
                 out;
 
         case CSR_RESET_START:
@@ -269,7 +269,7 @@
         case CSR_CYCLE_TIME:
                 /* should only be set by cycle start packet, automatically */
                 host->csr.cycle_time = be32_to_cpu(*data);
-                host->template->devctl(host, SET_CYCLE_COUNTER,
+                host->ops->devctl(host, SET_CYCLE_COUNTER,
                                        be32_to_cpu(*(data++)));
                 out;
         case CSR_BUS_TIME:
@@ -318,10 +318,10 @@
         data = be32_to_cpu(data);
         arg = be32_to_cpu(arg);
 
-        if (host->template->hw_csr_reg) {
+        if (host->ops->hw_csr_reg) {
                 quadlet_t old;
 
-                old = host->template->
+                old = host->ops->
                         hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
                                    data, arg);
 
diff -urN linux-2.5.3-pre2/drivers/ieee1394/dv1394-private.h linux/drivers/ieee1394/dv1394-private.h
--- linux-2.5.3-pre2/drivers/ieee1394/dv1394-private.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ieee1394/dv1394-private.h	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,606 @@
+/*
+ * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *   video1394.h - driver for OHCI 1394 boards
+ *   Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *                          Peter Schlaile <udbz@rz.uni-karlsruhe.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_PRIVATE_H
+#define _DV_1394_PRIVATE_H
+
+#include "ieee1394.h"
+#include <linux/pci.h>
+#include <asm/scatterlist.h>
+
+/* data structures private to the dv1394 driver */
+/* none of this is exposed to user-space */
+
+
+/* 
+   the 8-byte CIP (Common Isochronous Packet) header that precedes
+   each packet of DV data.
+
+   See the IEC 61883 standard. 
+*/
+
+struct CIP_header { unsigned char b[8]; };
+
+static inline void fill_cip_header(struct CIP_header *cip,
+				   unsigned char source_node_id,
+				   unsigned long counter,
+				   enum pal_or_ntsc format,
+				   unsigned long timestamp)
+{
+	cip->b[0] = source_node_id;
+	cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */
+	cip->b[2] = 0x00;
+	cip->b[3] = counter;
+
+	cip->b[4] = 0x80; /* const */
+
+	switch(format) {
+	case DV1394_PAL:
+		cip->b[5] = 0x80;
+		break;
+	case DV1394_NTSC:
+		cip->b[5] = 0x00;
+		break;
+	}
+
+	cip->b[6] = timestamp >> 8;
+	cip->b[7] = timestamp & 0xFF;
+}
+
+
+
+/* 
+   DMA commands used to program the OHCI's DMA engine
+
+   See the Texas Instruments OHCI 1394 chipset documentation. 
+*/
+
+struct output_more_immediate { u32 q[8]; };
+struct output_more { u32 q[4]; };
+struct output_last { u32 q[4]; };
+struct input_more { u32 q[4]; };
+struct input_last { u32 q[4]; };
+
+/* outputs */
+
+static inline void fill_output_more_immediate(struct output_more_immediate *omi,
+					      unsigned char tag,
+					      unsigned char channel,
+					      unsigned char sync_tag,
+					      unsigned int  payload_size)
+{
+	omi->q[0] = 0x02000000 | 8 ; /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */
+	omi->q[1] = 0;
+	omi->q[2] = 0;
+	omi->q[3] = 0;
+	
+	/* IT packet header */
+	omi->q[4] = (0x0 << 16)  /* DMA_SPEED_100 */
+			| (tag << 14)
+			| (channel << 8)
+			| (TCODE_ISO_DATA << 4) 
+			| (sync_tag);
+
+	omi->q[5] = payload_size << 16;
+	omi->q[5] |= (0x7F << 8) | 0xA0; /* reserved field; mimic behavior of my Sony DSR-40 */
+	
+	omi->q[6] = 0;
+	omi->q[7] = 0;
+}
+
+static inline void fill_output_more(struct output_more *om,
+				    unsigned int data_size,
+				    unsigned long data_phys_addr)
+{
+	om->q[0] = 0; /* OUTPUT_MORE */
+	om->q[0] |= data_size;
+
+	om->q[1] = data_phys_addr;
+	om->q[2] = 0;
+	om->q[3] = 0;
+}
+
+static inline void fill_output_last(struct output_last *ol,
+				    int want_timestamp,
+				    int want_interrupt,
+				    unsigned int data_size,
+				    unsigned long data_phys_addr)
+{
+	ol->q[0] = 0;
+	ol->q[0] |= 1 << 28; /* OUTPUT_LAST */
+
+	if(want_timestamp) /* controller will update timestamp at DMA time */
+		ol->q[0] |= 1 << 27;
+
+	if(want_interrupt)
+		ol->q[0] |= 3 << 20;
+
+	ol->q[0] |= 3 << 18; /* must take branch */
+	ol->q[0] |= data_size;
+	
+	ol->q[1] = data_phys_addr;
+	ol->q[2] = 0;
+	ol->q[3] = 0;
+}
+
+/* inputs */
+
+static inline void fill_input_more(struct input_more *im,
+				   int want_interrupt,
+				   unsigned int data_size,
+				   unsigned long data_phys_addr)
+{
+	im->q[0] =  2 << 28; /* INPUT_MORE */
+	im->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */
+	if (want_interrupt)
+		im->q[0] |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */
+	im->q[0] |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */
+	                       /* disable wait on sync field, not used in DV :-( */
+       	im->q[0] |= data_size;
+
+	im->q[1] = data_phys_addr;
+	im->q[2] = 0; /* branchAddress and Z not use in packet-per-buffer mode */
+	im->q[3] = 0; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+ 
+static inline void fill_input_last(struct input_last *il,
+				    unsigned int data_size,
+				    unsigned long data_phys_addr)
+{
+	il->q[0] =  3 << 28; /* INPUT_LAST */
+	il->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */
+	il->q[0] |= 3 << 20; /* enable interrupts */
+	il->q[0] |= 0xC << 16; /* enable branch to address */
+	                       /* disable wait on sync field, not used in DV :-( */
+	il->q[0] |= data_size;
+
+	il->q[1] = data_phys_addr;
+	il->q[2] = 1; /* branchAddress (filled in later) and Z = 1 descriptor in next block */
+	il->q[3] = data_size; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+
+
+
+/* 
+   A "DMA descriptor block" consists of several contiguous DMA commands.
+   struct DMA_descriptor_block encapsulates all of the commands necessary 
+   to send one packet of DV data. 
+   
+   There are three different types of these blocks:
+
+        1) command to send an empty packet (CIP header only, no DV data):
+
+	    OUTPUT_MORE-Immediate <-- contains the iso header in-line
+	    OUTPUT_LAST           <-- points to the CIP header
+
+	2) command to send a full packet when the DV data payload does NOT
+	   cross a page boundary:
+
+	    OUTPUT_MORE-Immediate <-- contains the iso header in-line
+	    OUTPUT_MORE           <-- points to the CIP header
+	    OUTPUT_LAST           <-- points to entire DV data payload
+
+	3) command to send a full packet when the DV payload DOES cross
+	   a page boundary:
+
+	    OUTPUT_MORE-Immediate <-- contains the iso header in-line
+	    OUTPUT_MORE           <-- points to the CIP header
+	    OUTPUT_MORE           <-- points to first part of DV data payload
+	    OUTPUT_LAST           <-- points to second part of DV data payload
+
+   This struct describes all three block types using unions.
+
+   !!! It is vital that an even number of these descriptor blocks fit on one
+   page of memory, since a block cannot cross a page boundary !!!
+
+ */
+
+struct DMA_descriptor_block {
+
+	union {
+		struct {
+			/*  iso header, common to all output block types */
+			struct output_more_immediate omi; 
+			
+			union {
+				/* empty packet */
+				struct {
+					struct output_last ol;  /* CIP header */
+				} empty;
+			
+				/* full packet */
+				struct {
+					struct output_more om;  /* CIP header */
+					
+					union {
+				               /* payload does not cross page boundary */
+						struct {
+							struct output_last ol;  /* data payload */
+						} nocross;
+						
+				               /* payload crosses page boundary */
+						struct {
+							struct output_more om;  /* data payload */
+							struct output_last ol;  /* data payload */
+						} cross;
+					} u;
+					
+				} full;
+			} u;
+		} out;
+
+		struct {
+			struct input_last il; 
+		} in;
+
+	} u;
+
+	/* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0 
+	   by padding out to 128 bytes */
+	u32 __pad__[12]; 
+};
+
+
+/* struct frame contains all data associated with one frame in the
+   ringbuffer these are allocated when the DMA context is initialized
+   do_dv1394_init().  They are re-used after the card finishes
+   transmitting the frame. */
+
+struct video_card; /* forward declaration */
+
+struct frame {
+
+	/* points to the struct video_card that owns this frame */
+	struct video_card *video;
+
+	/* index of this frame in video_card->frames[] */
+	unsigned int frame_num;
+
+	/* FRAME_CLEAR - DMA program not set up, waiting for data 
+	   FRAME_READY - DMA program written, ready to transmit
+
+	   Changes to these should be locked against the interrupt
+	*/
+	enum {
+		FRAME_CLEAR = 0,
+		FRAME_READY
+	} state;
+	
+	/* whether this frame has been DMA'ed already; used only from
+	   the IRQ handler to determine whether the frame can be reset */
+	int done;
+
+
+	/* kernel virtual pointer to the start of this frame's data in
+	   the user ringbuffer. Use only for CPU access; to get the DMA
+	   bus address you must go through the video->user_dma mapping */
+	unsigned long data; 
+
+	/* Max # of packets per frame */
+        #define MAX_PACKETS 320
+
+
+	/* a PAGE_SIZE memory pool for allocating CIP headers
+	   !header_pool must be aligned to PAGE_SIZE! */
+	struct CIP_header *header_pool;
+	dma_addr_t         header_pool_dma;
+
+	
+	/* a physically contiguous memory pool for allocating DMA
+	   descriptor blocks; usually around 64KB in size
+	   !descriptor_pool must be aligned to PAGE_SIZE! */
+	struct DMA_descriptor_block *descriptor_pool;
+	dma_addr_t                   descriptor_pool_dma;
+	unsigned long                descriptor_pool_size;
+
+
+	/* # of packets allocated for this frame */
+	unsigned int n_packets;
+
+
+	/* below are several pointers (kernel virtual addresses, not
+	   DMA bus addresses) to parts of the DMA program.  These are
+	   set each time the DMA program is written in
+	   frame_prepare(). They are used later on, e.g. from the
+	   interrupt handler, to check the status of the frame */
+
+	/* points to status/timestamp field of first DMA packet */
+	/* (we'll check it later to monitor timestamp accuracy) */
+	u32 *frame_begin_timestamp;
+
+	/* the timestamp we assigned to the first packet in the frame */
+	u32 assigned_timestamp;
+
+	/* pointer to the first packet's CIP header (where the timestamp goes) */
+	struct CIP_header *cip_syt1;
+	
+	/* pointer to the second packet's CIP header
+	   (only set if the first packet was empty) */
+	struct CIP_header *cip_syt2;
+
+	/* in order to figure out what caused an interrupt,
+	   store pointers to the status fields of the two packets
+	   that can cause interrupts. We'll check these from the
+	   interrupt handler.
+	*/
+	u32 *mid_frame_timestamp;
+	u32 *frame_end_timestamp;
+
+	/* branch address field of final packet. This is effectively
+	   the "tail" in the chain of DMA descriptor blocks.
+	   We will fill it with the address of the first DMA descriptor
+	   block in the subsequent frame, once it is ready.
+	*/
+	u32 *frame_end_branch;
+
+	/* the number of descriptors in the first descriptor block
+	   of the frame. Needed to start DMA */
+	int first_n_descriptors;
+};
+
+
+struct packet {
+	u16	timestamp;
+	u16	invalid;
+	u16	iso_header;
+	u16	data_length;
+	u32	cip_h1;
+	u32	cip_h2;
+	unsigned char data[480];
+	unsigned char padding[16]; /* force struct size =512 for page alignment */
+};
+
+
+/* allocate/free a frame */
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video);
+static void frame_delete(struct frame *f);
+
+/* reset f so that it can be used again */
+static void frame_reset(struct frame *f);
+
+
+/* structure for bookkeeping of a large non-physically-contiguous DMA buffer */
+
+struct dma_region {
+	unsigned int n_pages;
+	unsigned int n_dma_pages;
+	struct scatterlist *sglist;
+};
+
+/* return the DMA bus address of the byte with the given offset
+   relative to the beginning of the dma_region */
+
+static inline dma_addr_t dma_offset_to_bus(struct dma_region *dma, unsigned long offset)
+{
+	int i;
+	struct scatterlist *sg;
+	
+	for(i = 0, sg = &dma->sglist[0]; i < dma->n_dma_pages; i++, sg++) {
+		if(offset < sg_dma_len(sg)) {
+			return sg_dma_address(sg) + offset;
+		} 
+		offset -= sg_dma_len(sg);
+	}
+	
+	printk(KERN_ERR "dv1394: dma_offset_to_bus failed for offset %lu!\n", offset);
+	return 0;
+}
+
+
+/* struct video_card contains all data associated with one instance
+   of the dv1394 driver 
+*/
+
+struct video_card {
+
+	/* ohci card to which this instance corresponds */
+	struct ti_ohci *ohci;
+
+	/* OHCI card id; the link between the VFS inode and a specific video_card
+	   (essentially the device minor number) */
+	int id;
+
+	/* entry in dv1394_cards */
+	struct list_head list;
+
+	/* handle to /dev/ieee1394/dv/N, NULL if devfs not in use */
+	devfs_handle_t devfs_handle;
+
+	/* OHCI card IT DMA context number, -1 if not in use */
+	int ohci_it_ctx;
+
+	/* register offsets for current IT DMA context, 0 if not in use */
+	u32 ohci_IsoXmitContextControlSet;
+	u32 ohci_IsoXmitContextControlClear;
+	u32 ohci_IsoXmitCommandPtr;
+	
+	/* OHCI card IR DMA context number, -1 if not in use */
+	int ohci_ir_ctx;
+
+	/* register offsets for current IR DMA context, 0 if not in use */
+	u32 ohci_IsoRcvContextControlSet;
+	u32 ohci_IsoRcvContextControlClear;
+	u32 ohci_IsoRcvCommandPtr;
+	u32 ohci_IsoRcvContextMatch;
+	
+	
+	/* CONCURRENCY CONTROL */
+	
+	/* there are THREE levels of locking associated with video_card. */
+
+	/*
+	   1) the 'open' flag - this prevents more than one process from
+	   opening the device. (the driver currently assumes only one opener).
+	   This is a regular int, but use test_and_set_bit() (on bit zero) 
+	   for atomicity.
+	 */
+	int open;
+
+	/* 
+	   2) the spinlock - this provides mutual exclusion between the interrupt
+	   handler and process-context operations. Generally you must take the
+	   spinlock under the following conditions:
+	     1) DMA (and hence the interrupt handler) may be running
+	     AND
+	     2) you need to operate on the video_card, especially active_frame
+
+	     It is OK to play with video_card without taking the spinlock if
+	     you are certain that DMA is not running. Even if DMA is running,
+	     it is OK to *read* active_frame with the lock, then drop it
+	     immediately. This is safe because the interrupt handler will never
+	     advance active_frame onto a frame that is not READY (and the spinlock
+	     must be held while marking a frame READY).
+	 */
+	spinlock_t spinlock;
+
+	/*
+	  3) the sleeping semaphore 'sem' - this is used from process context only,
+	  to serialize various operations on the video_card. Even though only one
+	  open() is allowed, we still need to prevent multiple threads of execution
+	  from entering calls like read, write, ioctl, etc.
+
+	  I honestly can't think of a good reason to use dv1394 from several threads
+	  at once, but we need to serialize anyway to prevent oopses =).
+
+	  NOTE: if you need both spinlock and sem, take sem first to avoid deadlock!
+	 */
+	struct semaphore sem;
+
+	/* people waiting for buffer space, please form a line here... */
+	wait_queue_head_t waitq;
+
+	/* support asynchronous I/O signals (SIGIO) */
+	struct fasync_struct *fasync;
+	
+	/* the large, non-contiguous (rvmalloc()) ringbuffer for DV
+           data, exposed to user-space via mmap() */
+	unsigned char     *user_buf;
+	unsigned long      user_buf_size;
+	struct dma_region  user_dma;
+	
+	/* next byte in the ringbuffer that a write() call will fill */
+	size_t write_off;
+
+	struct frame *frames[DV1394_MAX_FRAMES];
+	
+	/* n_frames also serves as an indicator that this struct video_card is
+	   intialized and ready to run DMA buffers */
+
+	int n_frames;
+
+	/* this is the frame that is currently "owned" by the OHCI DMA controller
+	   (set to -1 iff DMA is not running) 
+
+	   ! must lock against the interrupt handler when accessing it !
+
+	   RULES:
+
+	       Only the interrupt handler may change active_frame if DMA
+	          is running; if not, process may change it
+
+	       If the next frame is READY, the interrupt handler will advance
+	       active_frame when the current frame is finished.
+
+	       If the next frame is CLEAR, the interrupt handler will re-transmit
+	       the current frame, and the dropped_frames counter will be  incremented.
+
+	       The interrupt handler will NEVER advance active_frame to a
+	       frame that is not READY.
+	       
+	*/
+	int active_frame;
+	int first_run;
+
+	/* the same locking rules apply to these three fields also: */
+
+	/* altered ONLY from process context. Must check first_clear_frame->state;
+	   if it's READY, that means the ringbuffer is full with READY frames;
+	   if it's CLEAR, that means one or more ringbuffer frames are CLEAR */
+	unsigned int first_clear_frame; 
+
+	/* altered both by process and interrupt */
+	unsigned int n_clear_frames;   
+
+	/* only altered by the interrupt */
+	unsigned int dropped_frames;
+
+
+
+	/* the CIP accumulator and continuity counter are properties
+	   of the DMA stream as a whole (not a single frame), so they
+	   are stored here in the video_card */
+
+	unsigned long cip_accum;
+	unsigned long cip_n, cip_d;
+	unsigned int syt_offset;
+	unsigned int continuity_counter;
+
+	enum pal_or_ntsc pal_or_ntsc;
+
+	/* redundant, but simplifies the code somewhat */
+	unsigned int frame_size; /* in bytes */
+
+	/* the isochronous channel to use, -1 if video card is inactive */
+	int channel;
+
+	
+	/* physically contiguous packet ringbuffer for receive */
+#define MAX_PACKET_BUFFER 30
+	struct packet *packet_buffer;
+	dma_addr_t     packet_buffer_dma;
+	unsigned long  packet_buffer_size;
+	
+	unsigned int current_packet;
+	int first_frame; 	/* received first start frame marker? */
+};
+
+/* 
+   if the video_card is not initialized, then the ONLY fields that are valid are:
+   ohci
+   open
+   n_frames
+*/
+
+static inline int video_card_initialized(struct video_card *v)
+{
+	return v->n_frames > 0;
+}
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init);
+static int do_dv1394_init_default(struct video_card *video);
+static int do_dv1394_shutdown(struct video_card *video, int free_user_buf);
+
+
+/* NTSC empty packet rate accurate to within 0.01%, 
+   calibrated against a Sony DSR-40 DVCAM deck */
+
+#define CIP_N_NTSC   68000000
+#define CIP_D_NTSC 1000000000
+
+#define CIP_N_PAL  1
+#define CIP_D_PAL 16
+
+#endif /* _DV_1394_PRIVATE_H */
+
diff -urN linux-2.5.3-pre2/drivers/ieee1394/dv1394.c linux/drivers/ieee1394/dv1394.c
--- linux-2.5.3-pre2/drivers/ieee1394/dv1394.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ieee1394/dv1394.c	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,2722 @@
+/*
+ * dv1394.c - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *  video1394.c - video driver for OHCI 1394 boards
+ *  Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.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
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+  OVERVIEW
+
+  I designed dv1394 as a "pipe" that you can use to shoot DV onto a
+  FireWire bus. In transmission mode, dv1394 does the following:
+
+   1. accepts contiguous frames of DV data from user-space, via write()
+      or mmap() (see dv1394.h for the complete API)
+   2. wraps IEC 61883 packets around the DV data, inserting
+      empty synchronization packets as necessary
+   3. assigns accurate SYT timestamps to the outgoing packets
+   4. shoots them out using the OHCI card's IT DMA engine
+
+   Thanks to Dan Dennedy, we now have a receive mode that does the following:
+
+   1. accepts raw IEC 61883 packets from the OHCI card
+   2. re-assembles the DV data payloads into contiguous frames,
+      discarding empty packets
+   3. sends the DV data to user-space via read() or mmap()
+*/
+
+/*
+  TODO:
+
+  - expose xmit and recv as separate devices
+
+  - tunable frame-drop behavior: either loop last frame, or halt transmission
+  
+  - use a scatter/gather buffer for DMA programs (f->descriptor_pool)
+    so that we don't rely on allocating 64KB of contiguous kernel memory
+    via pci_alloc_consistent()
+    
+  DONE:
+  - safely obtain and release ISO Tx channels in cooperation with OHCI driver
+  - map received DIF blocks to their proper location in DV frame (ensure
+    recovery if dropped packet)
+  - handle bus resets gracefully (OHCI card seems to take care of this itself(!))
+  - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings
+  - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk
+  - added wmb() and mb() to places where PCI read/write ordering needs to be enforced
+  - set video->id correctly
+  - store video_cards in an array indexed by OHCI card ID, rather than a list
+  - implement DMA context allocation to cooperate with other users of the OHCI
+  - fix all XXX showstoppers
+  - disable IR/IT DMA interrupts on shutdown
+  - flush pci writes to the card by issuing a read
+  - devfs and character device dispatching (* needs testing with Linux 2.2.x)
+  - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!)
+  - keep all video_cards in a list (for open() via chardev), set file->private_data = video
+  - dv1394_poll should indicate POLLIN when receiving buffers are available
+  - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal
+
+*/
+     
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/tqueue.h>
+#include <linux/delay.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"	
+#include "dv1394.h"
+#include "dv1394-private.h"
+
+#include "ohci1394.h"
+
+#ifndef virt_to_page
+#define virt_to_page(x) MAP_NR(x)
+#endif
+
+#ifndef vmalloc_32
+#define vmalloc_32(x) vmalloc(x)
+#endif
+
+
+/* DEBUG LEVELS:
+   0 - no debugging messages
+   1 - some debugging messages, but none during DMA frame transmission
+   2 - lots of messages, including during DMA frame transmission
+       (will cause undeflows if your machine is too slow!)
+*/
+
+#define DV1394_DEBUG_LEVEL 0
+
+/* for debugging use ONLY: allow more than one open() of the device */
+/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
+
+#if DV1394_DEBUG_LEVEL >= 2
+#define irq_printk( args... ) printk( args )
+#else
+#define irq_printk( args... )
+#endif
+
+#if DV1394_DEBUG_LEVEL >= 1
+#define debug_printk( args... ) printk( args)
+#else
+#define debug_printk( args... )
+#endif
+
+/* issue a dummy PCI read to force the preceding write
+   to be posted to the PCI bus immediately */
+
+static inline void flush_pci_write(struct ti_ohci *ohci)
+{
+	mb();
+	reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void irq_handler(int card, quadlet_t isoRecvIntEvent, 
+			quadlet_t isoXmitIntEvent, void *data);
+
+
+/* GLOBAL DATA */
+
+/* list of all video_cards */
+static LIST_HEAD(dv1394_cards);
+static spinlock_t dv1394_cards_lock = SPIN_LOCK_UNLOCKED;
+
+static struct hpsb_highlevel *hl_handle; /* = NULL; */
+
+static devfs_handle_t dv1394_devfs_handle;
+
+/* translate from a struct file* to the corresponding struct video_card* */
+
+static inline struct video_card* file_to_video_card(struct file *file)
+{
+	return (struct video_card*) file->private_data;
+}
+
+
+/* Taken from bttv.c */
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+#define MDEBUG(x)	do { } while(0)		/* Debug memory management */
+
+/* [DaveM] I've recoded most of this so that:
+ * 1) It's easier to tell what is happening
+ * 2) It's more portable, especially for translating things
+ *    out of vmalloc mapped areas in the kernel.
+ * 3) Less unnecessary translations happen.
+ *
+ * The code used to assume that the kernel vmalloc mappings
+ * existed in the page tables of every process, this is simply
+ * not guarenteed.  We now use pgd_offset_k which is the
+ * defined way to get at the kernel page tables.
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define page_address(x)	(x)
+#endif
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline struct page *uvirt_to_page(pgd_t *pgd, unsigned long adr)
+{
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+	struct page *ret = NULL;
+	
+	if (!pgd_none(*pgd)) {
+                pmd = pmd_offset(pgd, adr);
+                if (!pmd_none(*pmd)) {
+                        ptep = pte_offset(pmd, adr);
+                        pte = *ptep;
+                        if(pte_present(pte))
+				ret = pte_page(pte);
+                }
+        }
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved, and for
+ * handling page faults on the rvmalloc()ed buffer
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr) 
+{
+        unsigned long va, kva, ret;
+
+        va = VMALLOC_VMADDR(adr);
+	kva = (unsigned long) page_address(uvirt_to_page(pgd_offset_k(va), va));
+	kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+	ret = __pa(kva);
+        MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
+        return ret;
+}
+
+static void * rvmalloc(unsigned long size)
+{
+	void * mem;
+	unsigned long adr, page;
+        
+	mem=vmalloc_32(size);
+	if (mem) {
+		memset(mem, 0, size); /* Clear the ram out, 
+					 no junk to the user */
+	        adr=(unsigned long) mem;
+		while (size > 0) {
+	                page = kvirt_to_pa(adr);
+			mem_map_reserve(virt_to_page(__va(page)));
+			adr+=PAGE_SIZE;
+			size-=PAGE_SIZE;
+		}
+	}
+	return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+        unsigned long adr, page;
+        
+	if (mem) {
+	        adr=(unsigned long) mem;
+		while (size > 0) {
+	                page = kvirt_to_pa(adr);
+			mem_map_unreserve(virt_to_page(__va(page)));
+			adr+=PAGE_SIZE;
+			size-=PAGE_SIZE;
+		}
+		vfree(mem);
+	}
+}
+
+/***********************************/
+/* END Memory management functions */
+/***********************************/
+
+
+/*** FRAME METHODS *********************************************************/
+
+static void frame_reset(struct frame *f)
+{
+	f->state = FRAME_CLEAR;
+	f->done = 0;
+	f->n_packets = 0;
+	f->frame_begin_timestamp = NULL;
+	f->assigned_timestamp = 0;
+	f->cip_syt1 = NULL;
+	f->cip_syt2 = NULL;
+	f->mid_frame_timestamp = NULL;
+	f->frame_end_timestamp = NULL;
+	f->frame_end_branch = NULL;
+}
+
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video)
+{
+	struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL);
+	if(!f)
+		return NULL;
+
+	f->video = video;
+	f->frame_num = frame_num;
+
+	f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma);
+	if(!f->header_pool) {
+		printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n");
+		kfree(f);
+		return NULL;
+	}
+
+	debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+		     (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE);
+	
+	f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block);
+	/* make it an even # of pages */
+	f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE);
+
+	f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev,
+						  f->descriptor_pool_size,
+						  &f->descriptor_pool_dma);
+	if(!f->descriptor_pool) {
+		pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+		kfree(f);
+		return NULL;
+	}
+	
+	debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+		     (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size);
+	
+	f->data = 0;
+	frame_reset(f);
+
+	return f;
+}
+
+static void frame_delete(struct frame *f)
+{
+	pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+	pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma);
+	kfree(f);
+}
+
+
+
+
+/* 
+   frame_prepare() - build the DMA program for transmitting
+   
+   Frame_prepare() must be called OUTSIDE the video->spinlock.
+   However, frame_prepare() must still be serialized, so
+   it should be called WITH the video->sem taken.
+ */
+
+static void frame_prepare(struct video_card *video, unsigned int this_frame)
+{
+	struct frame *f = video->frames[this_frame];
+	int last_frame;
+
+	struct DMA_descriptor_block *block;
+	dma_addr_t block_dma;
+	struct CIP_header *cip;
+	dma_addr_t cip_dma;
+	
+	unsigned int n_descriptors, full_packets, packets_per_frame, payload_size;
+
+	/* these flags denote packets that need special attention */
+	int empty_packet, first_packet, last_packet, mid_packet;
+
+	u32 *branch_address, *last_branch_address = NULL;
+	unsigned long data_p;
+	int first_packet_empty = 0;
+	u32 cycleTimer, ct_sec, ct_cyc, ct_off;
+	unsigned long irq_flags;
+
+	irq_printk("frame_prepare( %d ) ---------------------\n", this_frame);
+	
+	full_packets = 0;
+
+
+
+	if(video->pal_or_ntsc == DV1394_PAL)
+		packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME;
+	else
+		packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME;
+
+	while( full_packets < packets_per_frame ) {
+		empty_packet = first_packet = last_packet = mid_packet = 0;
+
+		data_p = f->data + full_packets * 480;
+
+		/************************************************/
+		/* allocate a descriptor block and a CIP header */
+		/************************************************/
+
+		/* note: these should NOT cross a page boundary (DMA restriction) */
+
+		if(f->n_packets >= MAX_PACKETS) {
+			printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n");
+			return;
+		}
+
+		/* the block surely won't cross a page boundary, 
+		   since an even number of descriptor_blocks fit on a page */
+		block = &(f->descriptor_pool[f->n_packets]);
+
+		/* DMA address of the block = offset of block relative
+		    to the kernel base address of the descriptor pool
+		    + DMA base address of the descriptor pool */
+		block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+		
+
+		/* the whole CIP pool fits on one page, so no worries about boundaries */
+		if( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool) 
+		    > PAGE_SIZE) {
+			printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n");
+			return;
+		}
+
+		cip = &(f->header_pool[f->n_packets]);
+		
+		/* DMA address of the CIP header = offset of cip
+		   relative to kernel base address of the header pool
+		   + DMA base address of the header pool */
+		cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma;
+		
+		/* is this an empty packet? */
+
+		if(video->cip_accum > video->cip_d) {
+			empty_packet = 1;
+			payload_size = 8;
+			video->cip_accum -= video->cip_d;
+		} else {
+			payload_size = 488;
+			video->cip_accum += video->cip_n;
+		}
+
+		/* there are three important packets each frame:
+
+		   the first packet in the frame - we ask the card to record the timestamp when
+		                                   this packet is actually sent, so we can monitor
+						   how accurate our timestamps are. Also, the first
+						   packet serves as a semaphore to let us know that
+						   it's OK to free the *previous* frame's DMA buffer
+
+		   the last packet in the frame -  this packet is used to detect buffer underflows.
+		                                   if this is the last ready frame, the last DMA block
+						   will have a branch back to the beginning of the frame
+						   (so that the card will re-send the frame on underflow).
+						   if this branch gets taken, we know that at least one
+						   frame has been dropped. When the next frame is ready,
+						   the branch is pointed to its first packet, and the
+						   semaphore is disabled.
+
+		   a "mid" packet slightly before the end of the frame - this packet should trigger
+		                   an interrupt so we can go and assign a timestamp to the first packet
+				   in the next frame. We don't use the very last packet in the frame
+				   for this purpose, because that would leave very little time to set
+				   the timestamp before DMA starts on the next frame.
+		*/
+		
+		if(f->n_packets == 0) {
+			first_packet = 1;
+		} else if ( full_packets == (packets_per_frame-1) ) {
+			last_packet = 1;
+		} else if (f->n_packets == packets_per_frame) {
+			mid_packet = 1;
+		}
+		
+
+		/********************/
+		/* setup CIP header */
+		/********************/
+
+		/* the timestamp will be written later from the
+		   mid-frame interrupt handler. For now we just
+		   store the address of the CIP header(s) that
+		   need a timestamp. */
+
+		/* first packet in the frame needs a timestamp */
+		if(first_packet) {
+			f->cip_syt1 = cip;
+			if(empty_packet)
+				first_packet_empty = 1;
+
+		} else if(first_packet_empty && (f->n_packets == 1) ) {
+			/* if the first packet was empty, the second
+			   packet's CIP header also needs a timestamp */
+			f->cip_syt2 = cip;
+		}
+
+		fill_cip_header(cip,
+				/* the node ID number of the OHCI card */
+				reg_read(video->ohci, OHCI1394_NodeID) & 0x3F,
+				video->continuity_counter, 
+				video->pal_or_ntsc,
+				0xFFFF /* the timestamp is filled in later */);
+		
+		/* advance counter, only for full packets */
+		if( ! empty_packet )
+			video->continuity_counter++;
+
+		/******************************/
+		/* setup DMA descriptor block */
+		/******************************/
+
+		/* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */
+		fill_output_more_immediate( &(block->u.out.omi),
+					    /* tag - what is this??? */ 1,
+					    video->channel,
+					    /* sync tag - what is this??? */ 0,
+					    payload_size);
+
+		if(empty_packet) {
+			/* second descriptor - OUTPUT_LAST for CIP header */
+			fill_output_last( &(block->u.out.u.empty.ol),
+
+					  /* want completion status on all interesting packets */
+					  (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+					  /* want interrupts on all interesting packets */
+					  (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+					  sizeof(struct CIP_header), /* data size */
+					  cip_dma);
+			
+			if(first_packet)
+				f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]);
+			else if(mid_packet)
+				f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]);
+			else if(last_packet) {
+				f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]);
+				f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]);
+			}
+
+			branch_address = &(block->u.out.u.empty.ol.q[2]);
+			n_descriptors = 3;
+			if(first_packet)
+				f->first_n_descriptors = n_descriptors;
+
+		} else { /* full packet */
+
+			/* second descriptor - OUTPUT_MORE for CIP header */
+			fill_output_more( &(block->u.out.u.full.om),
+					  sizeof(struct CIP_header), /* data size */
+					  cip_dma);
+
+			
+			/* third (and possibly fourth) descriptor - for DV data */
+			/* the 480-byte payload can cross a page boundary; if so,
+			   we need to split it into two DMA descriptors */
+
+			/* does the 480-byte data payload cross a page boundary? */
+			if( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) {
+
+				/* page boundary crossed */
+
+				fill_output_more( &(block->u.out.u.full.u.cross.om),
+						  /* data size - how much of data_p fits on the first page */
+						  PAGE_SIZE - (data_p % PAGE_SIZE),
+
+						  /* DMA address of data_p */
+						  dma_offset_to_bus(&f->video->user_dma,
+								    data_p - (unsigned long) f->video->user_buf));
+
+				fill_output_last( &(block->u.out.u.full.u.cross.ol),
+					  
+						  /* want completion status on all interesting packets */
+						  (first_packet || mid_packet || last_packet) ? 1 : 0, 
+
+						  /* want interrupt on all interesting packets */
+						  (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+						  /* data size - remaining portion of data_p */
+						  480 - (PAGE_SIZE - (data_p % PAGE_SIZE)),
+
+						  /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */
+						  dma_offset_to_bus(&f->video->user_dma,
+								    data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) f->video->user_buf));
+
+				if(first_packet)
+					f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+				else if(mid_packet)
+					f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+				else if(last_packet) {
+					f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+					f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]);
+				}
+
+				branch_address = &(block->u.out.u.full.u.cross.ol.q[2]);
+
+				n_descriptors = 5;
+				if(first_packet)
+					f->first_n_descriptors = n_descriptors;
+				
+				full_packets++;
+
+			} else {
+				/* fits on one page */
+
+				fill_output_last( &(block->u.out.u.full.u.nocross.ol),
+					  
+						  /* want completion status on all interesting packets */
+						  (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+						  /* want interrupt on all interesting packets */
+						  (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+						  480, /* data size (480 bytes of DV data) */
+
+						  
+						  /* DMA address of data_p */
+						  dma_offset_to_bus(&f->video->user_dma,
+								    data_p - (unsigned long) f->video->user_buf));
+				
+				if(first_packet)
+					f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+				else if(mid_packet)
+					f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+				else if(last_packet) {
+					f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+					f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]);
+				}
+
+				branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]);
+
+				n_descriptors = 4;
+				if(first_packet)
+					f->first_n_descriptors = n_descriptors;
+
+				full_packets++;
+			}
+		}
+		
+		/* link this descriptor block into the DMA program by filling in 
+		   the branch address of the previous block */
+
+		/* note: we are not linked into the active DMA chain yet */
+
+		if(last_branch_address) {
+			*(last_branch_address) = block_dma | n_descriptors; 
+		}
+
+		last_branch_address = branch_address;
+
+
+		f->n_packets++;
+		
+	}
+
+	/* when we first assemble a new frame, set the final branch 
+	   to loop back up to the top */
+	*(f->frame_end_branch) = f->descriptor_pool_dma | f->first_n_descriptors;
+
+
+	/* make the latest version of the frame buffer visible to the PCI card */
+	/* could optimize this by only syncing the pages associated with this frame */
+	pci_dma_sync_sg(video->ohci->dev,
+			&video->user_dma.sglist[0],
+			video->user_dma.n_dma_pages,
+			PCI_DMA_TODEVICE);
+
+	/* lock against DMA interrupt */
+	spin_lock_irqsave(&video->spinlock, irq_flags);
+
+	f->state = FRAME_READY;
+
+	video->n_clear_frames--;
+
+	last_frame = video->first_clear_frame - 1;
+	if(last_frame == -1)
+		last_frame = video->n_frames-1;
+
+	video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+
+	irq_printk("   frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n",
+		   this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame);
+
+	irq_printk("   begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n",
+		   (unsigned long) f->frame_begin_timestamp, 
+		   (unsigned long) f->mid_frame_timestamp, 
+		   (unsigned long) f->frame_end_timestamp, 
+		   (unsigned long) f->frame_end_branch);
+	
+	if(video->active_frame != -1) {
+
+		/* if DMA is already active, we are almost done */
+		/* just link us onto the active DMA chain */
+		if(video->frames[last_frame]->frame_end_branch) {
+
+			/* point the previous frame's tail to this frame's head */
+			*(video->frames[last_frame]->frame_end_branch) = f->descriptor_pool_dma | f->first_n_descriptors; 
+
+			/* this write MUST precede the next one, or we could silently drop frames */
+			wmb();
+			
+			/* disable the want_status semaphore on the last packet */
+			*(video->frames[last_frame]->frame_end_branch - 2) &= 0xF7CFFFFF;
+
+			/* flush these writes to memory ASAP */
+			flush_pci_write(video->ohci);
+
+			/* NOTE:
+			   ideally the writes should be "atomic": if
+			   the OHCI card reads the want_status flag in
+			   between them, we'll falsely report a
+			   dropped frame. Hopefully this window is too
+			   small to really matter, and the consequence
+			   is rather harmless. */
+			
+
+			irq_printk("     new frame %d linked onto DMA chain\n", this_frame);
+
+		} else {
+			printk(KERN_ERR "dv1394: last frame not ready???\n");
+		}
+
+	} else {
+		
+		u32 transmit_sec, transmit_cyc;
+		u32 ts_cyc, ts_off;
+
+		/* DMA is stopped, so this is the very first frame */
+		video->active_frame = this_frame;
+		
+	        /* set CommandPtr to address and size of first descriptor block */
+		reg_write(video->ohci, video->ohci_IsoXmitCommandPtr,
+			  video->frames[video->active_frame]->descriptor_pool_dma |
+			  f->first_n_descriptors);
+
+		/* assign a timestamp based on the current cycle time...
+		   We'll tell the card to begin DMA 100 cycles from now,
+		   and assign a timestamp 103 cycles from now */
+
+		cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer);
+
+		ct_sec = cycleTimer >> 25;
+		ct_cyc = (cycleTimer >> 12) & 0x1FFF;
+		ct_off = cycleTimer & 0xFFF;
+
+		transmit_sec = ct_sec;
+		transmit_cyc = ct_cyc + 100;
+
+		transmit_sec += transmit_cyc/8000;
+		transmit_cyc %= 8000;
+		
+		ts_off = ct_off;
+		ts_cyc = transmit_cyc + 3;
+		ts_cyc %= 8000;
+
+		f->assigned_timestamp = (ts_cyc&0xF) << 12;
+
+		/* now actually write the timestamp into the appropriate CIP headers */
+		if(f->cip_syt1) {
+			f->cip_syt1->b[6] = f->assigned_timestamp >> 8;
+			f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF;
+		}
+		if(f->cip_syt2) {
+			f->cip_syt2->b[6] = f->assigned_timestamp >> 8;
+			f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF;
+		}
+		
+		/* --- start DMA --- */
+
+		/* clear all bits in ContextControl register */
+
+		reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF);
+		wmb();
+
+		/* the OHCI card has the ability to start ISO transmission on a
+		   particular cycle (start-on-cycle). This way we can ensure that
+		   the first DV frame will have an accurate timestamp.
+		   
+		   However, start-on-cycle only appears to work if the OHCI card 
+		   is cycle master! Since the consequences of messing up the first
+		   timestamp are minimal*, just disable start-on-cycle for now.
+
+		   * my DV deck drops the first few frames before it "locks in;"
+		     so the first frame having an incorrect timestamp is inconsequential.
+		*/
+
+#if 0
+		reg_write(video->ohci, video->ohci_IsoXmitContextControlSet,
+			  (1 << 31) /* enable start-on-cycle */
+			  | ( (transmit_sec & 0x3) << 29)
+			  | (transmit_cyc << 16));
+		wmb();
+#endif
+
+		
+
+		/* set the 'run' bit */
+		reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000);
+		flush_pci_write(video->ohci);
+		
+		/* --- DMA should be running now --- */
+
+		debug_printk("    Cycle = %4u ContextControl = %08x CmdPtr = %08x\n",
+			     (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF,
+			     reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+			     reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
+
+		debug_printk("    DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n",
+			     ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF);
+
+#if DV1394_DEBUG_LEVEL >= 2
+		{
+			/* check if DMA is really running */
+			int i = 0;
+			while(i < 20) {
+				mb();
+				mdelay(1);
+				if(reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) {
+					printk("DMA ACTIVE after %d msec\n", i);
+					break;
+				}
+				i++;
+			}
+
+			printk("set = %08x, cmdPtr = %08x\n", 
+			       reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+			       reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
+			       );
+			
+			if( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) &  (1 << 10)) ) {
+				printk("DMA did NOT go active after 20ms, event = %x\n", 
+				       reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F);
+			} else
+				printk("DMA is RUNNING!\n");
+		}
+#endif
+		
+	}
+
+
+	spin_unlock_irqrestore(&video->spinlock, irq_flags);
+}
+
+
+
+/*** RECEIVE FUNCTIONS *****************************************************/
+
+/* 
+	frame method put_packet
+
+	map and copy the packet data to its location in the frame 
+	based upon DIF section and sequence 
+*/
+
+static void inline
+frame_put_packet (struct frame *f, struct packet *p)
+{
+	int section_type = p->data[0] >> 5;           /* section type is in bits 5 - 7 */
+	int dif_sequence = p->data[1] >> 4;           /* dif sequence number is in bits 4 - 7 */
+	int dif_block = p->data[2];
+
+	switch (section_type) {
+	case 0:           /* 1 Header block */
+	        memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480);
+	        break;
+	
+	case 1:           /* 2 Subcode blocks */
+	        memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480);
+	        break;
+	
+	case 2:           /* 3 VAUX blocks */
+	        memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480);
+	        break;
+	
+	case 3:           /* 9 Audio blocks interleaved with video */
+	        memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480);
+	        break;
+	
+	case 4:           /* 135 Video blocks interleaved with audio */
+	        memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480);
+	        break;
+	
+	default:           /* we can not handle any other data */
+	        break;
+	}
+}
+
+
+static void start_dma_receive(struct video_card *video, struct frame *frame)
+{
+	/* reset iso recv control register */
+	reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF);
+	wmb();
+	
+	/* clear bufferFill, set isochHeader and speed (0=100) */
+	reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000);
+
+	/* match on all tags, listen on channel */
+	reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel);
+	
+	/* address and first descriptor block + Z=1 */
+	reg_write(video->ohci, video->ohci_IsoRcvCommandPtr,             
+		  frame->descriptor_pool_dma | 1); /* Z=1 */
+	wmb();
+	
+	/* run */
+	reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000);
+	flush_pci_write(video->ohci);
+	
+	debug_printk("dv1394: DMA started\n");
+
+#if DV1394_DEBUG_LEVEL >= 2
+	{
+		int i;
+	
+		for(i = 0; i < 1000; ++i) {
+			mdelay(1);
+			if(reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) {
+				printk("DMA ACTIVE after %d msec\n", i);
+				break;
+			}
+		}
+		if( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) &  (1 << 11) ) {
+			printk("DEAD, event = %x\n", 
+			       reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
+		} else
+			printk("RUNNING!\n");
+	}
+#endif
+}
+
+
+/* 
+   receive_packets() - build the DMA program for receiving
+*/
+
+static void receive_packets(struct video_card *video, struct frame *f)
+{
+	struct DMA_descriptor_block *block = NULL;
+	dma_addr_t block_dma = 0;
+	struct packet *data = NULL;
+	dma_addr_t data_dma = 0;
+	u32 *last_branch_address = NULL;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&video->spinlock, irq_flags);
+
+	video->n_clear_frames = 0;
+	video->first_clear_frame = -1;
+
+	for (video->current_packet = 0; video->current_packet < MAX_PACKET_BUFFER; ++video->current_packet) {
+		/* locate a descriptor block and packet from the buffer */
+		block = &(f->descriptor_pool[video->current_packet]);
+		block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+		
+		data = &(video->packet_buffer[video->current_packet]);
+		data_dma = ((unsigned long) data - (unsigned long) video->packet_buffer) + video->packet_buffer_dma;
+		
+		/* setup DMA descriptor block */
+		fill_input_last( &(block->u.in.il), 512, data_dma);
+
+		/* link descriptors */
+		last_branch_address = f->frame_end_branch;
+
+		if (last_branch_address)
+			*(last_branch_address) = block_dma | 1; /* set Z=1 */
+
+		f->frame_end_branch = &(block->u.in.il.q[2]);
+	}
+	
+	/* loop tail to head */
+	if (f->frame_end_branch)
+		*(f->frame_end_branch) =  f->descriptor_pool_dma | 1; /* set Z=1 */
+
+	spin_unlock_irqrestore(&video->spinlock, irq_flags);
+
+	if (video->first_run) {
+		/* start DMA once all of the frames are READY */
+		video->first_run = 0;
+		video->current_packet = 0;
+		video->active_frame = f->frame_num;
+		start_dma_receive(video, f);
+	} 
+	else if( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) &  (1 << 11) ) {
+		debug_printk("DEAD, event = %x\n", 
+			     reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
+
+		/* wake */
+		reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
+	}
+}
+
+
+
+/*** MANAGEMENT FUNCTIONS **************************************************/
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init)
+{
+	unsigned long flags, new_buf_size;
+	int i;
+	u64 chan_mask;
+	int retval = -EINVAL;
+
+	if(init->api_version != DV1394_API_VERSION)
+		goto err;
+	
+	/* first sanitize all the parameters */
+	if( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
+		goto err;
+
+	if( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
+		goto err;
+
+	if( (init->syt_offset == 0) || (init->syt_offset > 50) )
+		/* default SYT offset is 3 cycles */
+		init->syt_offset = 3;
+
+	if( (init->channel > 63) || (init->channel < 0) )
+		init->channel = 63;
+
+	chan_mask = (u64)1 << init->channel;
+
+	/* calculate what size DMA buffer is needed */
+	if(init->format == DV1394_NTSC)
+		new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames;
+	else
+		new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames;
+
+	/* round up to PAGE_SIZE */
+	if(new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
+
+	/* don't allow the user to allocate the DMA buffer more than once */
+	if( (video->user_buf) &&
+	    (video->user_buf_size != new_buf_size) ) {
+		goto err;
+	}
+	
+	/* shutdown the card if it's currently active */
+	/* (the card should not be reset if the parameters are screwy) */
+	if( video_card_initialized(video) )
+		do_dv1394_shutdown(video, 0);
+
+
+	/* try to claim the ISO channel */
+	spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+	if(video->ohci->ISO_channel_usage & chan_mask) {
+		spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+		retval = -EBUSY;
+		goto err;
+	}
+	video->ohci->ISO_channel_usage |= chan_mask;
+	spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+
+	video->channel = init->channel;
+
+	
+	/* find and claim DMA contexts on the OHCI card */
+	
+	if(video->ohci_it_ctx == -1) {
+
+		for(i = 0; i < video->ohci->nb_iso_xmit_ctx; i++) {
+
+			if(! test_and_set_bit(i, &video->ohci->it_ctx_usage)) {
+				video->ohci_it_ctx = i;
+				debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
+				break;
+			}
+		}
+				
+		if(i == video->ohci->nb_iso_xmit_ctx) {
+			printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
+			retval = -EBUSY;
+			goto err_ctx;
+		}
+	}
+	
+
+	if(video->ohci_ir_ctx == -1) {
+		for(i = 0; i < video->ohci->nb_iso_rcv_ctx; i++) {
+
+			if(! test_and_set_bit(i, &video->ohci->ir_ctx_usage)) {
+				video->ohci_ir_ctx = i;
+				debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
+				break;
+			}
+		}
+				
+		if(i == video->ohci->nb_iso_rcv_ctx) {
+			printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
+			retval = -EBUSY;
+			goto err_ctx;
+		}
+	}
+
+	
+	/* allocate struct frames */
+	for(i = 0; i < init->n_frames; i++) {
+		video->frames[i] = frame_new(i, video);
+
+		if(!video->frames[i]) {
+			printk(KERN_ERR "dv1394: Cannot allocate frame structs\n");
+			retval = -ENOMEM;
+			goto err_frames;
+		}
+	}
+
+	/* initialize misc. fields of video */
+	video->n_frames = init->n_frames;
+	video->pal_or_ntsc = init->format;
+	
+
+	video->cip_accum = 0;
+	video->continuity_counter = 0;
+
+	video->active_frame = -1;
+	video->first_clear_frame = 0;
+	video->n_clear_frames = video->n_frames;
+	video->dropped_frames = 0;
+
+	video->write_off = 0;
+
+	video->first_run = 1;
+	video->current_packet = -1;
+	video->first_frame = 0;
+
+
+	if(video->pal_or_ntsc == DV1394_NTSC) {
+		video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
+		video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
+		video->frame_size = DV1394_NTSC_FRAME_SIZE;
+	} else {
+		video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL;
+		video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL;
+		video->frame_size = DV1394_PAL_FRAME_SIZE;
+	}
+
+	video->syt_offset = init->syt_offset;
+
+	if(video->user_buf == NULL) {
+		unsigned int i;
+		
+		/* allocate the ringbuffer */
+		video->user_buf = rvmalloc(new_buf_size);
+		if(!video->user_buf) {
+			printk(KERN_ERR "dv1394: Cannot allocate frame buffers\n");
+			goto err_frames;
+		}
+		video->user_buf_size = new_buf_size;
+
+		/* allocate the sglist to hold the DMA addresses */
+		video->user_dma.n_pages = video->user_buf_size / PAGE_SIZE;
+		video->user_dma.sglist = kmalloc(video->user_dma.n_pages * sizeof(struct scatterlist), GFP_KERNEL);
+		if(!video->user_dma.sglist) {
+			printk(KERN_ERR "dv1394: Cannot allocate sglist for user buffer\n");
+			goto err_user_buf;
+		}
+
+		/* initialize all fields of all sglist entries to zero
+		   (new requirement due to PCI changes in 2.4.13) */
+
+		memset(video->user_dma.sglist, 0, video->user_dma.n_pages * sizeof(struct scatterlist));
+
+		
+		/* fill the sglist with the kernel addresses of pages in the non-contiguous buffer */
+		for(i = 0; i < video->user_dma.n_pages; i++) {
+			unsigned long va = VMALLOC_VMADDR( (unsigned long) video->user_buf + i * PAGE_SIZE );
+			
+			video->user_dma.sglist[i].page = uvirt_to_page(pgd_offset_k(va), va);
+			video->user_dma.sglist[i].length = PAGE_SIZE;
+		}
+		
+		/* map the buffer in the IOMMU */
+		/* the user_data buffer only allows DMA *to* the card for transmission;
+		   incoming DV data comes through the packet_buffer first, and then is copied to user_data */
+		video->user_dma.n_dma_pages = pci_map_sg(video->ohci->dev,
+							 &video->user_dma.sglist[0],
+							 video->user_dma.n_pages,
+							 PCI_DMA_TODEVICE);
+		if(video->user_dma.n_dma_pages == 0) {
+			printk(KERN_ERR "dv1394: Error mapping user buffer to the IOMMU\n");
+			goto err_user_buf;
+		}
+		
+		debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n", 
+			     video->n_frames, video->user_dma.n_pages,
+			     video->user_dma.n_dma_pages, video->user_buf_size);
+	}
+	
+	/* set up the frame->data pointers */
+	for(i = 0; i < video->n_frames; i++)
+		video->frames[i]->data = (unsigned long) video->user_buf + i * video->frame_size;
+
+	/* allocate packet buffers */
+	video->packet_buffer_size = sizeof(struct packet) * MAX_PACKET_BUFFER;
+	if (video->packet_buffer_size % PAGE_SIZE)
+		video->packet_buffer_size += PAGE_SIZE - (video->packet_buffer_size % PAGE_SIZE);
+
+	
+	video->packet_buffer = kmalloc(video->packet_buffer_size, GFP_KERNEL);
+	
+	if(!video->packet_buffer) {
+		printk(KERN_ERR "dv1394: Cannot allocate packet buffers");
+		retval = -ENOMEM;
+		goto err_user_buf;
+	}
+
+	/* map the packet buffer into the IOMMU */
+	video->packet_buffer_dma = pci_map_single(video->ohci->dev,
+						  video->packet_buffer,
+						  video->packet_buffer_size,
+						  PCI_DMA_FROMDEVICE);
+	if(!video->packet_buffer_dma) {
+		printk(KERN_ERR "dv1394: Cannot map packet buffer to IOMMU");
+		kfree(video->packet_buffer);
+		video->packet_buffer = NULL;
+		retval = -ENOMEM;
+		goto err_user_buf;
+	}
+
+	debug_printk("dv1394: Allocated %d packet buffers for receive, total %lu bytes\n", 
+		     MAX_PACKET_BUFFER, video->packet_buffer_size);
+
+
+	/* set up register offsets for IT context */
+	/* IT DMA context registers are spaced 16 bytes apart */
+	video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx;
+	video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx;
+	video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx;
+
+	/* enable interrupts for IT context */
+	reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx));
+	debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx);
+
+	/* set up register offsets for IR context */
+	/* IR DMA context registers are spaced 32 bytes apart */
+	video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx;
+	video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx;
+	video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx;
+	video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx;
+
+	/* enable interrupts for IR context */
+	reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) );
+	debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx);
+	
+	return 0;
+
+ err_user_buf:
+	if(video->user_buf) {
+		if(video->user_dma.sglist) {
+			if(video->user_dma.n_dma_pages > 0) {
+				/* unmap it from the IOMMU */
+				pci_unmap_sg(video->ohci->dev,
+					     video->user_dma.sglist,
+					     video->user_dma.n_pages,
+					     PCI_DMA_TODEVICE);
+				video->user_dma.n_dma_pages = 0;
+			}
+			kfree(video->user_dma.sglist);
+			video->user_dma.sglist = NULL;
+			video->user_dma.n_pages = 0;
+		}
+		rvfree(video->user_buf, video->user_buf_size);
+		video->user_buf = NULL;
+		video->user_buf_size = 0;
+	}
+		
+ err_frames:
+	for(i = 0; i < DV1394_MAX_FRAMES; i++) {
+		if(video->frames[i])
+			frame_delete(video->frames[i]);
+	}	
+	video->n_frames = 0;
+
+ err_ctx:
+	if(video->ohci_it_ctx != -1) {
+		clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage);
+		video->ohci_it_ctx = -1;
+	}
+	if(video->ohci_ir_ctx != -1) {
+		clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage);
+		video->ohci_ir_ctx = -1;
+	}
+	
+	spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+	video->ohci->ISO_channel_usage &= ~chan_mask;
+	spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+ err:
+	return retval;
+}
+
+/* if the user doesn't bother to call ioctl(INIT) before starting
+   mmap() or read()/write(), just give him some default values */
+
+static int do_dv1394_init_default(struct video_card *video)
+{
+	struct dv1394_init init;
+
+	init.api_version = DV1394_API_VERSION;
+	init.channel = 63;
+	init.n_frames = 2;
+	/* the following are now set via proc_fs */
+	init.format = video->pal_or_ntsc;
+	init.cip_n = video->cip_n; 
+	init.cip_d = video->cip_d;
+	init.syt_offset = video->syt_offset;
+
+	return do_dv1394_init(video, &init);
+}
+
+/* do NOT call from interrupt context */
+static void stop_dma(struct video_card *video)
+{
+	unsigned long flags;
+	int i;
+	
+	/* no interrupts */
+	spin_lock_irqsave(&video->spinlock, flags);
+
+	/* stop DMA if in progress */
+	if( (video->active_frame != -1) ||
+	    (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
+	    (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) &  (1 << 10)) ) {
+
+		/* clear the .run bits */
+		reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
+		reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
+		flush_pci_write(video->ohci);
+		
+		video->active_frame = -1;
+		video->first_run = 1;
+
+		
+		/* wait until DMA really stops */
+		i = 0;
+		while(i < 1000) {
+				
+			/* wait 0.1 millisecond */
+			udelay(100); 
+			
+			if( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
+			    (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear)  & (1 << 10)) ) {
+				/* still active */
+				mb();
+			} else {
+				debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10);
+				break;
+			}
+				
+			i++;
+		}
+			
+		if(i == 1000) {
+			printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10);
+		}
+	}
+
+	spin_unlock_irqrestore(&video->spinlock, flags);
+}
+
+
+
+static int do_dv1394_shutdown(struct video_card *video, int free_user_buf)
+{
+	int i;
+
+	debug_printk("dv1394: shutdown...\n");
+
+	/* stop DMA if in progress */
+	stop_dma(video);
+	
+	/* release the ISO channel */
+	if(video->channel != -1) {
+		u64 chan_mask;
+		unsigned long flags;
+		
+		chan_mask = (u64)1 << video->channel;
+		
+		spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+		video->ohci->ISO_channel_usage &= ~(chan_mask);
+		spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+		
+		video->channel = -1;
+	}
+
+	/* release the DMA contexts */
+	if(video->ohci_it_ctx != -1) {
+		video->ohci_IsoXmitContextControlSet = 0;
+		video->ohci_IsoXmitContextControlClear = 0;
+		video->ohci_IsoXmitCommandPtr = 0;
+
+		/* disable interrupts for IT context */
+		reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx));
+		
+		clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage);
+		debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx);
+		video->ohci_it_ctx = -1;
+	}
+
+	if(video->ohci_ir_ctx != -1) {
+		video->ohci_IsoRcvContextControlSet = 0;
+		video->ohci_IsoRcvContextControlClear = 0;
+		video->ohci_IsoRcvCommandPtr = 0;
+		video->ohci_IsoRcvContextMatch = 0;
+
+		/* disable interrupts for IR context */
+		reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx));
+
+		clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage);
+		debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx);
+		video->ohci_ir_ctx = -1;
+	}
+
+	/* free the frame structs */
+	for(i = 0; i < DV1394_MAX_FRAMES; i++) {
+		if(video->frames[i])
+			frame_delete(video->frames[i]);
+		video->frames[i] = NULL;
+	}
+
+	video->n_frames = 0;
+		
+	/* we can't free the DMA buffer unless it is guaranteed that
+	   no more user-space mappings exist */
+	
+	if(free_user_buf && video->user_buf) {
+		if(video->user_dma.sglist) {
+			if(video->user_dma.n_dma_pages > 0) {
+				/* unmap it from the IOMMU */
+				pci_unmap_sg(video->ohci->dev,
+					     video->user_dma.sglist,
+					     video->user_dma.n_pages,
+					     PCI_DMA_TODEVICE);
+				video->user_dma.n_dma_pages = 0;
+			}
+			kfree(video->user_dma.sglist);
+			video->user_dma.sglist = NULL;
+			video->user_dma.n_pages = 0;
+		}
+		rvfree(video->user_buf, video->user_buf_size);
+		video->user_buf = NULL;
+		video->user_buf_size = 0;
+	}
+	
+	if (video->packet_buffer) {
+		pci_unmap_single(video->ohci->dev,
+				 video->packet_buffer_dma,
+				 video->packet_buffer_size,
+				 PCI_DMA_FROMDEVICE);
+		kfree(video->packet_buffer);
+		video->packet_buffer = NULL;
+		video->packet_buffer_size = 0;
+	}
+
+	debug_printk("dv1394: shutdown complete\n");
+
+	return 0;
+}
+
+
+
+/*
+       **********************************
+       *** MMAP() THEORY OF OPERATION ***
+       **********************************
+
+        The ringbuffer cannot be re-allocated or freed while
+        a user program maintains a mapping of it. (note that a mapping
+	can persist even after the device fd is closed!)
+
+	So, only let the user process allocate the DMA buffer once.
+	To resize or deallocate it, you must close the device file
+	and open it again.
+
+	Previously Dan M. hacked out a scheme that allowed the DMA
+	buffer to change by forcefully unmapping it from the user's
+	address space. It was prone to error because it's very hard to
+	track all the places the buffer could have been mapped (we
+	would have had to walk the vma list of every process in the
+	system to be sure we found all the mappings!). Instead, we
+	force the user to choose one buffer size and stick with
+	it. This small sacrifice is worth the huge reduction in
+	error-prone code in dv1394.
+
+	Note: dv1394_mmap does no page table manipulation. The page
+	table entries are created by the dv1394_nopage() handler as
+	page faults are taken by the user.
+*/
+
+static struct page * dv1394_nopage(struct vm_area_struct * area, unsigned long address, int write_access)
+{
+	unsigned long offset;
+	unsigned long page, kernel_virt_addr;
+	struct page *ret = NOPAGE_SIGBUS;
+
+	struct video_card *video = (struct video_card*) area->vm_private_data;
+	
+	/* guard against process-context operations and the interrupt */
+	/* (by definition page faults are taken in interrupt context) */
+	spin_lock(&video->spinlock);
+
+	if(!video->user_buf)
+		goto out;
+
+	if( (address < (unsigned long) area->vm_start) ||
+	    (address > (unsigned long) area->vm_start + video->user_buf_size) )
+		goto out;
+
+	offset = address - area->vm_start;
+	kernel_virt_addr = (unsigned long) video->user_buf + offset;
+
+	page = kvirt_to_pa(kernel_virt_addr);
+	
+	ret = virt_to_page(__va(page));
+	get_page(ret);
+
+ out:
+	spin_unlock(&video->spinlock);
+	return ret;
+}
+
+static struct vm_operations_struct dv1394_vm_ops = {
+	nopage: dv1394_nopage
+};
+
+/*
+  dv1394_mmap does no page table manipulation. The page table entries
+  are created by the dv1394_nopage() handler as page faults are taken
+  by the user.
+*/
+
+int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct video_card *video = file_to_video_card(file);
+	unsigned long size;
+	int res = -EINVAL;
+
+	/* serialize mmap */
+	down(&video->sem);
+
+	if( ! video_card_initialized(video) ) {
+		res = do_dv1394_init_default(video);
+		if(res)
+			goto err;
+	}
+
+	/* region must be page-aligned */
+	if(vma->vm_pgoff != 0)
+		goto err;
+	
+	/* check the size the user is trying to map */
+	size = vma->vm_end - vma->vm_start;
+	if(size > video->user_buf_size)
+		goto err;
+
+	/* 
+	   we don't actually mess with the page tables here.
+	   (nopage() takes care of that from the page fault handler)
+	   Just set up the vma->vm_ops.
+	*/
+
+        vma->vm_ops = &dv1394_vm_ops;
+	vma->vm_private_data = video;
+	vma->vm_file = file;
+
+	/* don't try to swap this out =) */
+	vma->vm_flags |= VM_RESERVED;
+
+	up(&video->sem);
+	return 0;
+ err:
+	up(&video->sem);
+	return res;
+}
+
+
+/*** DEVICE FILE INTERFACE *************************************************/
+
+/* no need to serialize, multiple threads OK */
+static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct video_card *video = file_to_video_card(file);
+	unsigned int mask = 0;
+	unsigned long flags;
+
+	poll_wait(file, &video->waitq, wait);
+
+	spin_lock_irqsave(&video->spinlock, flags);
+	if( video->n_frames == 0 ) {
+
+	} else if( video->active_frame == -1 ) {
+		/* nothing going on */
+		mask |= POLLOUT;
+	} else {
+		/* any clear/ready buffers? */
+		if(video->n_clear_frames >0)
+			mask |= POLLOUT | POLLIN;
+	}
+	spin_unlock_irqrestore(&video->spinlock, flags);
+
+	return mask;
+}
+
+static int dv1394_fasync(int fd, struct file *file, int on)
+{
+	/* I just copied this code verbatim from Alan Cox's mouse driver example
+	   (linux/Documentation/DocBook/) */
+	
+	struct video_card *video = file_to_video_card(file);
+	
+	int retval = fasync_helper(fd, file, on, &video->fasync);
+	 
+	if (retval < 0)
+		return retval;
+        return 0;
+}
+
+static ssize_t dv1394_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct video_card *video = file_to_video_card(file);
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	size_t cnt;
+	unsigned long flags;
+	int target_frame;
+
+	/* serialize this to prevent multi-threaded mayhem */
+	if(file->f_flags & O_NONBLOCK) {
+		if(down_trylock(&video->sem))
+			return -EAGAIN;
+	} else {
+		if(down_interruptible(&video->sem))
+			return -ERESTARTSYS;
+	}
+
+	if( !video_card_initialized(video) ) {
+		ret = do_dv1394_init_default(video);
+		if(ret) {
+			up(&video->sem);
+			return ret;
+		}
+	}
+
+	ret = 0;
+	add_wait_queue(&video->waitq, &wait);
+	
+	while(count > 0) {
+
+		/* must set TASK_INTERRUPTIBLE *before* checking for free
+		   buffers; otherwise we could miss a wakeup if the interrupt
+		   fires between the check and the schedule() */
+		
+		set_current_state(TASK_INTERRUPTIBLE);
+		
+		spin_lock_irqsave(&video->spinlock, flags);
+		
+		target_frame = video->first_clear_frame;
+		
+		spin_unlock_irqrestore(&video->spinlock, flags);
+
+		if(video->frames[target_frame]->state == FRAME_CLEAR) {
+
+			/* how much room is left in the target frame buffer */
+			cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
+
+		} else {
+			/* buffer is already used */
+			cnt = 0;
+		}
+
+		if(cnt > count)
+			cnt = count;
+
+		if (cnt <= 0) { 
+			/* no room left, gotta wait */
+			if(file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+
+			schedule();
+			
+			continue; /* start over from 'while(count > 0)...' */
+		}
+
+		if(copy_from_user(video->user_buf + video->write_off, buffer, cnt)) {
+			if(!ret)
+				ret = -EFAULT;
+			break;
+		}
+
+		video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if(video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames))
+				frame_prepare(video, target_frame);
+	}
+	
+	remove_wait_queue(&video->waitq, &wait);
+	set_current_state(TASK_RUNNING);
+	up(&video->sem);
+	return ret;
+}
+
+
+static ssize_t dv1394_read(struct file *file,  char *buffer, size_t count, loff_t *ppos)
+{
+	struct video_card *video = file_to_video_card(file);
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	size_t cnt;
+	unsigned long flags;
+	int target_frame;
+
+	/* serialize this to prevent multi-threaded mayhem */
+	if(file->f_flags & O_NONBLOCK) {
+		if(down_trylock(&video->sem))
+			return -EAGAIN;
+	} else {
+		if(down_interruptible(&video->sem))
+			return -ERESTARTSYS;
+	}
+
+	if( !video_card_initialized(video) ) {
+		ret = do_dv1394_init_default(video);
+		if(ret) {
+			up(&video->sem);
+			return ret;
+		}
+		receive_packets(video, video->frames[video->first_clear_frame]);
+	}
+
+	ret = 0;
+	add_wait_queue(&video->waitq, &wait);
+
+	while(count > 0) {
+
+		/* must set TASK_INTERRUPTIBLE *before* checking for free
+		   buffers; otherwise we could miss a wakeup if the interrupt
+		   fires between the check and the schedule() */
+		
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&video->spinlock, flags);
+
+		target_frame = video->first_clear_frame;
+
+		spin_unlock_irqrestore(&video->spinlock, flags);
+
+		if(target_frame >= 0 &&
+			video->n_clear_frames > 0 &&
+			video->frames[target_frame]->state == FRAME_CLEAR) {
+
+			/* how much room is left in the target frame buffer */
+			cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
+
+		} else {
+			/* buffer is already used */
+			cnt = 0;
+		}
+
+		if(cnt > count)
+			cnt = count;
+
+		if (cnt <= 0) { 
+			/* no room left, gotta wait */
+			if(file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+
+			schedule();
+			
+			continue; /* start over from 'while(count > 0)...' */
+		}
+
+		if(copy_to_user(buffer, video->user_buf + video->write_off, cnt)) {
+				if(!ret)
+					ret = -EFAULT;
+				break;
+		}
+
+		video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if(video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) {
+			spin_lock_irqsave(&video->spinlock, flags);
+			video->n_clear_frames--;
+			video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+			spin_unlock_irqrestore(&video->spinlock, flags);
+		}
+	}
+	
+	remove_wait_queue(&video->waitq, &wait);
+	set_current_state(TASK_RUNNING);
+	up(&video->sem);
+	return ret;
+}
+
+
+/*** DEVICE IOCTL INTERFACE ************************************************/
+
+/* I *think* the VFS serializes ioctl() for us, so we don't have to worry
+   about situations like having two threads in here at once... */
+
+static int dv1394_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, unsigned long arg)
+{
+	struct video_card *video = file_to_video_card(file);
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	DECLARE_WAITQUEUE(wait, current);
+
+	/* serialize this to prevent multi-threaded mayhem */
+	if(file->f_flags & O_NONBLOCK) {
+		if(down_trylock(&video->sem))
+			return -EAGAIN;
+	} else {
+		if(down_interruptible(&video->sem))
+			return -ERESTARTSYS;
+	}
+
+	switch(cmd)
+	{
+	case DV1394_SUBMIT_FRAMES: {
+		unsigned int n_submit;
+
+		if( !video_card_initialized(video) ) {
+			ret = do_dv1394_init_default(video);
+			if(ret)
+				goto out;
+		}
+
+		n_submit = (unsigned int) arg;
+
+		if(n_submit > video->n_frames) {
+			ret = -EINVAL;
+			goto out;
+		}
+			
+		while(n_submit > 0) {
+
+			add_wait_queue(&video->waitq, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+				
+			spin_lock_irqsave(&video->spinlock, flags);
+
+			/* wait until video->first_clear_frame is really CLEAR */
+			while(video->frames[video->first_clear_frame]->state != FRAME_CLEAR) {
+
+				spin_unlock_irqrestore(&video->spinlock, flags);
+					
+				if(signal_pending(current)) {
+					remove_wait_queue(&video->waitq, &wait);
+					set_current_state(TASK_RUNNING);
+					ret = -EINTR;
+					goto out;
+				}
+
+				schedule();
+				set_current_state(TASK_INTERRUPTIBLE);
+				
+				spin_lock_irqsave(&video->spinlock, flags);
+			}
+			spin_unlock_irqrestore(&video->spinlock, flags);
+
+			remove_wait_queue(&video->waitq, &wait);
+			set_current_state(TASK_RUNNING);
+				
+			frame_prepare(video, video->first_clear_frame);
+
+			n_submit--;
+		}
+
+		ret = 0;
+		break;
+	}
+
+	case DV1394_WAIT_FRAMES: {
+		unsigned int n_wait;
+
+		if( !video_card_initialized(video) ) {
+			ret = -EINVAL;
+			goto out;
+		}
+		
+		n_wait = (unsigned int) arg;
+
+		/* since we re-run the last frame on underflow, we will
+		   never actually have n_frames clear frames; at most only
+		   n_frames - 1 */
+
+		if(n_wait > (video->n_frames-1) ) {
+			ret = -EINVAL;
+			goto out;
+		}
+			
+		add_wait_queue(&video->waitq, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		
+		spin_lock_irqsave(&video->spinlock, flags);
+
+		while(video->n_clear_frames < n_wait) {
+			
+			spin_unlock_irqrestore(&video->spinlock, flags);
+					
+			if(signal_pending(current)) {
+				remove_wait_queue(&video->waitq, &wait);
+				set_current_state(TASK_RUNNING);
+				ret = -EINTR;
+				goto out;
+			}
+
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+			
+			spin_lock_irqsave(&video->spinlock, flags);
+		}
+
+		spin_unlock_irqrestore(&video->spinlock, flags);
+
+		remove_wait_queue(&video->waitq, &wait);
+		set_current_state(TASK_RUNNING);
+		ret = 0;
+		break;
+	}
+
+	case DV1394_RECEIVE_FRAMES: {
+		unsigned int n_recv;
+
+		if( !video_card_initialized(video) ) {
+			ret = -EINVAL;
+			goto out;
+		}
+		
+		n_recv = (unsigned int) arg;
+
+		/* at least one frame must be active */
+		if(n_recv > (video->n_frames-1) ) {
+			ret = -EINVAL;
+			goto out;
+		}
+			
+		spin_lock_irqsave(&video->spinlock, flags);
+
+		/* release the clear frames */
+		video->n_clear_frames -= n_recv;
+
+		/* advance the clear frame cursor */
+		video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames;
+
+		/* reset dropped_frames */
+		video->dropped_frames = 0;
+			
+		spin_unlock_irqrestore(&video->spinlock, flags);
+
+		ret = 0;
+		break;
+	}
+
+	case DV1394_START_RECEIVE: {
+
+		if( !video_card_initialized(video) ) {
+			ret = do_dv1394_init_default(video);
+			if(ret)
+				goto out;
+		}
+	
+		receive_packets(video, video->frames[video->first_clear_frame]);
+
+		ret = 0;
+		break;
+	}
+
+	case DV1394_INIT: {
+		struct dv1394_init init;
+		if(arg == (unsigned long) NULL) {
+			ret = do_dv1394_init_default(video);
+		} else {
+			if(copy_from_user(&init, (void*)arg, sizeof(init))) {
+				ret = -EFAULT;
+				goto out;
+			}
+			ret = do_dv1394_init(video, &init);
+		}
+		break;
+	}
+
+	case DV1394_SHUTDOWN:
+		ret = do_dv1394_shutdown(video, 0);
+		break;
+
+
+        case DV1394_GET_STATUS: {
+		struct dv1394_status status;
+
+		if( !video_card_initialized(video) ) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		status.init.api_version = DV1394_API_VERSION;
+		status.init.channel = video->channel;
+		status.init.n_frames = video->n_frames;
+		status.init.format = video->pal_or_ntsc;
+		status.init.cip_n = video->cip_n;
+		status.init.cip_d = video->cip_d;
+		status.init.syt_offset = video->syt_offset;
+
+		status.first_clear_frame = video->first_clear_frame;
+
+		/* the rest of the fields need to be locked against the interrupt */
+		spin_lock_irqsave(&video->spinlock, flags);
+
+		status.active_frame = video->active_frame;
+		status.n_clear_frames = video->n_clear_frames;
+
+		status.dropped_frames = video->dropped_frames;
+
+		/* reset dropped_frames */
+		video->dropped_frames = 0;
+			
+		spin_unlock_irqrestore(&video->spinlock, flags);
+
+		if(copy_to_user((void*)arg, &status, sizeof(status))) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		ret = 0;
+		break;
+	}
+
+	default:
+		break;
+	}
+
+ out:
+	up(&video->sem);
+	return ret;
+}
+
+
+
+/*** DEVICE FILE INTERFACE CONTINUED ***************************************/
+
+static int dv1394_open(struct inode *inode, struct file *file)
+{
+	struct video_card *video = NULL;
+
+	/* if the device was opened through devfs, then file->private_data
+	   has already been set to video by devfs */
+	if(file->private_data) {
+		video = (struct video_card*) file->private_data;
+		
+	} else {
+		/* look up the card by ID */
+		
+		struct list_head *lh;
+		unsigned long flags;
+		
+		spin_lock_irqsave(&dv1394_cards_lock, flags);
+		if(!list_empty(&dv1394_cards)) {
+			struct video_card *p;
+			list_for_each(lh, &dv1394_cards) {
+				p = list_entry(lh, struct video_card, list);
+				if(p->id == ieee1394_file_to_instance(file)) {
+					video = p;
+					break;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+		if(!video) {
+			debug_printk("dv1394: OHCI card %d not found", ieee1394_file_to_instance(file));
+			return -ENODEV;
+		}
+		
+		file->private_data = (void*) video;
+	}
+	
+#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN
+
+	if( test_and_set_bit(0, &video->open) ) {
+		/* video is already open by someone else */
+		return -EBUSY;
+ 	}
+
+#endif
+
+	V22_COMPAT_MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+static int dv1394_release(struct inode *inode, struct file *file)
+{
+	struct video_card *video = file_to_video_card(file);
+
+	/* OK to free the DMA buffer, no more mappings can exist */
+	do_dv1394_shutdown(video, 1);
+
+	/* clean up async I/O users */
+	dv1394_fasync(-1, file, 0);
+	
+	/* give someone else a turn */
+	clear_bit(0, &video->open);
+
+	V22_COMPAT_MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+/*** PROC_FS INTERFACE ******************************************************/
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *dv1394_procfs_entry;
+
+static int dv1394_procfs_read( char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	struct video_card *video = (struct video_card*) data;
+
+	V22_COMPAT_MOD_INC_USE_COUNT;
+	snprintf( page, count, 
+		"\
+dv1394 settings for host %d:\n\
+----------------------------\n\
+format=%s\n\
+cip_n=%lu\n\
+cip_d=%lu\n\
+syt_offset=%u\n",
+		video->id,
+		(video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"),
+		video->cip_n, video->cip_d, video->syt_offset );
+	V22_COMPAT_MOD_DEC_USE_COUNT;
+	return strlen(page);
+}
+#endif /* CONFIG_PROC_FS */
+
+/* lifted from the stallion.c driver */
+#undef  TOLOWER
+#define TOLOWER(x)      ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
+static unsigned long atol(char *str)
+{
+	unsigned long   val;
+	int             base, c;
+	char            *sp;
+
+	val = 0;
+	sp = str;
+	if ((*sp == '0') && (*(sp+1) == 'x')) {
+		base = 16;
+		sp += 2;
+	} else if (*sp == '0') {
+		base = 8;
+		sp++;
+	} else {
+		base = 10;
+	}
+
+	for (; (*sp != 0); sp++) {
+		c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0');
+		if ((c < 0) || (c >= base)) {
+			printk(KERN_ERR "dv1394: atol() invalid argument %s\n", str);
+			val = 0;
+			break;
+		}
+		val = (val * base) + c;
+	}
+	return(val);
+}
+
+#ifdef CONFIG_PROC_FS
+static int dv1394_procfs_write( struct file *file,
+			const char *buffer, unsigned long count, void *data)
+{
+	int len = 0;
+	char new_value[64];
+	char *pos;
+	struct video_card *video = (struct video_card*) data;
+	
+	V22_COMPAT_MOD_INC_USE_COUNT;
+	
+	if (count > 64)
+		len = 64;
+	else
+		len = count;
+				
+	if (copy_from_user( new_value, buffer, len)) {
+		V22_COMPAT_MOD_DEC_USE_COUNT;
+		return -EFAULT;
+	}
+	
+	pos = strchr(new_value, '=');
+	if (pos != NULL) {
+		int val_len = len - (pos-new_value) - 1;
+		char buf[64];
+		memset(buf, 0, 64);
+		strncpy(buf, pos+1, val_len);
+		if (buf[val_len-1] == '\n') buf[val_len-1] = 0;
+		
+		if (strnicmp( new_value, "format", (pos-new_value)) == 0) {
+			if (strnicmp( buf, "NTSC", val_len) == 0)
+				video->pal_or_ntsc = DV1394_NTSC;
+			else if (strnicmp( buf, "PAL", val_len) == 0)
+				video->pal_or_ntsc = DV1394_PAL;
+				
+		} else if (strnicmp( new_value, "cip_n", (pos-new_value)) == 0) {
+			video->cip_n = atol(buf);
+		} else if (strnicmp( new_value, "cip_d", (pos-new_value)) == 0) {
+			video->cip_d = atol(buf);
+		} else if (strnicmp( new_value, "syt_offset", (pos-new_value)) == 0) {
+			video->syt_offset = atol(buf);
+		}
+	}
+	
+	V22_COMPAT_MOD_DEC_USE_COUNT;
+	return len;
+}
+
+static int dv1394_procfs_add_entry(struct video_card *video)
+{
+	struct proc_dir_entry *procfs_entry = NULL;
+	char buf[16];
+	
+	snprintf(buf, sizeof(buf), "%d", video->id);
+
+	procfs_entry = create_proc_entry( buf, 0666, dv1394_procfs_entry);
+	if (procfs_entry == NULL) {
+		printk(KERN_ERR "dv1394: unable to create /proc/bus/ieee1394/dv/X\n");
+		return -ENOMEM;
+	}
+	procfs_entry->owner = THIS_MODULE;
+	procfs_entry->data = video;
+	procfs_entry->read_proc = dv1394_procfs_read;
+	procfs_entry->write_proc = dv1394_procfs_write;
+	
+	return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*** DEVICE DRIVER HANDLERS ************************************************/
+
+static void irq_handler(int card, quadlet_t isoRecvIntEvent, 
+			quadlet_t isoXmitIntEvent, void *data)
+{
+	int wake = 0;
+	struct video_card *video = (struct video_card*) data;
+
+	irq_printk("INTERRUPT! Video = %08lx Iso event Recv: %08x Xmit: %08x\n",
+		   (unsigned long) video, isoRecvIntEvent, isoXmitIntEvent);
+	irq_printk("ContextControl = %08x, CommandPtr = %08x\n", 
+	       reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+	       reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
+	       );
+
+	
+	if( (video->ohci_it_ctx != -1) &&
+	    (isoXmitIntEvent & (1 << video->ohci_it_ctx)) &&
+	    (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
+
+		struct frame *f;
+		unsigned int frame, i;
+
+		spin_lock(&video->spinlock);
+		if(video->active_frame == -1)
+			frame = 0;
+		else
+			frame = video->active_frame;
+
+		/* check all the DMA-able frames */
+		for(i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) {
+
+			irq_printk("IRQ checking frame %d...", frame);
+			f = video->frames[frame];
+			if(f->state != FRAME_READY) {
+				irq_printk("clear, skipping\n");
+				/* we don't own this frame */
+				continue;
+			}
+
+			irq_printk("DMA\n");
+
+			/* check the frame begin semaphore to see if we can free the previous frame */
+			if( *(f->frame_begin_timestamp) ) {
+				int prev_frame;
+				struct frame *prev_f;
+
+				
+
+				/* don't reset, need this later *(f->frame_begin_timestamp) = 0; */
+				irq_printk("  BEGIN\n");
+
+				prev_frame = frame - 1;
+				if(prev_frame == -1)
+					prev_frame += video->n_frames;
+				prev_f = video->frames[prev_frame];
+				
+				/* make sure we can actually garbage collect
+				   this frame */
+				if( (prev_f->state == FRAME_READY) &&
+				    prev_f->done && (!f->done) ) 
+				{
+					frame_reset(prev_f);
+					video->n_clear_frames++;
+					wake = 1;
+					video->active_frame = frame;
+
+					irq_printk("  BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame);
+				} else {
+					irq_printk("  BEGIN - can't free yet\n");
+				}
+
+				f->done = 1;
+			}
+
+		     
+			/* see if we need to set the timestamp for the next frame */
+			if( *(f->mid_frame_timestamp) ) {
+				struct frame *next_frame;
+				u32 ts_cyc, ts_off;
+
+				*(f->mid_frame_timestamp) = 0;
+
+				irq_printk("  MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n",
+				       *(f->frame_begin_timestamp) & 0x1FFF, *(f->frame_begin_timestamp) & 0xF,
+				       f->assigned_timestamp >> 12,       f->assigned_timestamp & 0xFFF);
+
+				/* prepare next frame and assign timestamp */
+				next_frame = video->frames[ (frame+1) % video->n_frames ];
+
+				if(next_frame->state == FRAME_READY) {
+					irq_printk("  MIDDLE - next frame is ready, good\n");
+				} else {
+					debug_printk("dv1394: Underflow! At least one frame has been dropped.\n");
+					next_frame = f;
+				}
+
+				/* set the timestamp to the timestamp of the last frame sent,
+				   plus the length of the last frame sent, plus the syt latency */
+				ts_cyc = *(f->frame_begin_timestamp) & 0xF;
+				/* advance one frame, plus syt latency (typically 2-3) */
+				ts_cyc += f->n_packets + video->syt_offset ; 
+
+				ts_off = 0; 
+
+				ts_cyc += ts_off/3072;
+				ts_off %= 3072;
+
+				next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off;
+				if(next_frame->cip_syt1) {
+					next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8;
+					next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF;
+				}
+				if(next_frame->cip_syt2) {
+					next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8;
+					next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF;
+				}
+
+			}
+
+			/* see if the frame looped */
+			if( *(f->frame_end_timestamp) ) {
+
+				*(f->frame_end_timestamp) = 0;
+
+				debug_printk("  END - the frame looped at least once\n");
+
+				video->dropped_frames++;
+			}
+
+
+		
+		} /* for(each frame) */
+
+		spin_unlock(&video->spinlock);
+		
+	} /* end XMIT portion */
+
+	/***** RECEIVE INTERRUPT and DMA ACTIVE *****/
+
+	else if( (video->ohci_ir_ctx != -1) &&
+		 (isoRecvIntEvent & (1 << video->ohci_ir_ctx)) &&
+		 (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) { 
+
+		int sof=0; /* start-of-frame flag */
+		struct frame *f;
+
+		spin_lock(&video->spinlock);
+
+		irq_printk("received packet %02d, timestamp=%04x, length=%04x, sof=%02x%02x\n", video->current_packet,
+			   video->packet_buffer[video->current_packet].timestamp, video->packet_buffer[video->current_packet].data_length, 
+			   video->packet_buffer[video->current_packet].data[0], video->packet_buffer[video->current_packet].data[1]);
+		
+		f = video->frames[video->active_frame];
+
+		/* exclude empty packet */
+		if (video->packet_buffer[video->current_packet].data_length > 8) {
+		
+			/* check for start of frame */
+			sof = (video->packet_buffer[video->current_packet].data[0] == 0x1f &&
+				video->packet_buffer[video->current_packet].data[1] == 0x07);
+
+			if (!video->first_frame) {
+				if (sof) {
+					video->first_frame = 1;
+				}
+
+			} else if (sof) {
+				/* close current frame */
+				frame_reset(f);  /* f->state = STATE_CLEAR */
+				video->n_clear_frames++;
+				if (video->n_clear_frames > video->n_frames) {
+					video->n_clear_frames = video->n_frames;
+					video->dropped_frames++;
+				}
+				if (video->first_clear_frame == -1)
+					video->first_clear_frame = video->active_frame;
+
+				/* get the next frame */
+				video->active_frame = (video->active_frame + 1) % video->n_frames;
+				f = video->frames[video->active_frame];
+			
+				irq_printk("   frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n",
+					   video->active_frame, video->n_clear_frames, video->first_clear_frame);
+ 			}
+			if (video->first_frame) {
+				if (sof) {
+					/* open next frame */
+					f->state = FRAME_READY;
+				}
+				
+				/* copy to buffer */
+				if (f->n_packets > (video->frame_size / 480)) {
+					printk(KERN_ERR "frame buffer overflow during receive\n");
+				}
+
+				/* make sure we are seeing the latest changes to packet_buffer */
+				pci_dma_sync_single(video->ohci->dev,
+						    video->packet_buffer_dma,
+						    video->packet_buffer_size,
+						    PCI_DMA_FROMDEVICE);
+				
+				frame_put_packet( f, &video->packet_buffer[video->current_packet]);
+				
+			} /* first_frame */
+ 
+		} /* not empty packet */
+ 
+		/* advance packet_buffer cursor */
+		video->current_packet = (video->current_packet + 1) % MAX_PACKET_BUFFER;
+ 
+ 		spin_unlock(&video->spinlock);
+
+		wake = 1; /* why the hell not? */
+
+	} /* receive interrupt */
+
+	if(wake) {
+		
+		/* send SIGIO */
+		
+		if(isoRecvIntEvent & (1))
+			kill_fasync(&video->fasync, SIGIO, POLL_IN);
+		
+		if(isoXmitIntEvent & (1))
+			kill_fasync(&video->fasync, SIGIO, POLL_OUT);
+		
+		/* wake readers/writers/ioctl'ers */
+		wake_up_interruptible(&video->waitq);
+	}
+}
+
+static struct file_operations dv1394_fops=
+{
+        OWNER_THIS_MODULE
+	poll:           dv1394_poll,
+	ioctl:		dv1394_ioctl,
+	mmap:		dv1394_mmap,
+	open:		dv1394_open,
+	write:          dv1394_write,
+	read:           dv1394_read,
+	release:	dv1394_release,
+	fasync:         dv1394_fasync,
+};
+
+
+static int dv1394_init(struct ti_ohci *ohci)
+{
+	struct video_card *video;
+	unsigned long flags;
+	char buf[16];
+	int i;
+
+	video = kmalloc(sizeof(struct video_card), GFP_KERNEL);
+	if(!video) {
+		printk(KERN_ERR "dv1394: cannot allocate video_card\n");
+		goto err;
+	}
+	
+	memset(video, 0, sizeof(struct video_card));
+	
+	if (ohci1394_hook_irq(ohci, irq_handler, (void*) video) != 0) {
+		printk(KERN_ERR "dv1394: ohci1394_hook_irq() failed\n");
+		goto err_free;
+	}
+
+	
+	video->ohci = ohci;
+	video->id = ohci->id;
+
+	if ( dv1394_procfs_add_entry(video) < 0 )
+		goto err_free;
+
+	video->ohci_it_ctx = -1;
+	video->ohci_ir_ctx = -1;
+
+	video->ohci_IsoXmitContextControlSet = 0;
+	video->ohci_IsoXmitContextControlClear = 0;
+	video->ohci_IsoXmitCommandPtr = 0;
+	
+	video->ohci_IsoRcvContextControlSet = 0;
+	video->ohci_IsoRcvContextControlClear = 0;
+	video->ohci_IsoRcvCommandPtr = 0;
+	video->ohci_IsoRcvContextMatch = 0;
+		
+	video->n_frames = 0; /* flag that video is not initialized */
+	video->channel = -1;
+	video->active_frame = -1;
+	
+	/* initialize the following for proc_fs */
+	video->pal_or_ntsc = DV1394_NTSC;
+	video->cip_n = 0; /* 0 = use builtin default */
+	video->cip_d = 0;
+	video->syt_offset = 0;
+
+	for(i = 0; i < DV1394_MAX_FRAMES; i++)
+		video->frames[i] = NULL;
+
+	video->user_buf = NULL;
+	video->user_buf_size = 0;
+
+	clear_bit(0, &video->open);
+	spin_lock_init(&video->spinlock);
+	init_MUTEX(&video->sem);
+	init_waitqueue_head(&video->waitq);
+	video->fasync = NULL;
+
+	spin_lock_irqsave(&dv1394_cards_lock, flags);
+	INIT_LIST_HEAD(&video->list);
+	list_add_tail(&video->list, &dv1394_cards);
+	spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+	
+	snprintf(buf, sizeof(buf), "%d", video->id);
+	
+	video->devfs_handle = devfs_register(dv1394_devfs_handle,
+					     buf, DEVFS_FL_NONE,
+					     IEEE1394_MAJOR,
+					     IEEE1394_MINOR_BLOCK_DV1394*16 + video->id,
+					     S_IFCHR | S_IRUGO | S_IWUGO,
+					     &dv1394_fops,
+					     (void*) video);
+
+	debug_printk("dv1394: dv1394_init() OK on ID %d\n", ohci->id);
+	
+	return 0;
+
+ err_free:
+	kfree(video);
+ err:
+	return -1;
+}
+
+static void dv1394_un_init(struct video_card *video)
+{
+	unsigned long flags;
+	
+	/* obviously nobody has the driver open at this point */
+	do_dv1394_shutdown(video, 1);
+	ohci1394_unhook_irq(video->ohci, irq_handler, (void*) video);
+	if(video->devfs_handle)
+		devfs_unregister(video->devfs_handle);
+
+	spin_lock_irqsave(&dv1394_cards_lock, flags);
+	list_del(&video->list);
+	spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+	
+	kfree(video);
+}
+
+	
+static void dv1394_remove_host (struct hpsb_host *host)
+{
+	struct ti_ohci *ohci;
+	struct video_card *video = NULL;
+	unsigned long flags;
+	struct list_head *lh;
+	
+	/* We only work with the OHCI-1394 driver */
+	if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+		return;
+
+	ohci = (struct ti_ohci *)host->hostdata;
+
+
+	/* find the corresponding video_card */
+	spin_lock_irqsave(&dv1394_cards_lock, flags);
+	if(!list_empty(&dv1394_cards)) {
+		struct video_card *p;
+		list_for_each(lh, &dv1394_cards) {
+			p = list_entry(lh, struct video_card, list);
+			if(p->id == ohci->id) {
+				video = p;
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+	if(video) {
+		char buf[16];
+		dv1394_un_init(video);
+		snprintf( buf, sizeof(buf), "%i", video->id);
+#ifdef CONFIG_PROC_FS
+		remove_proc_entry( buf, dv1394_procfs_entry);
+#endif
+	}
+}
+
+static void dv1394_add_host (struct hpsb_host *host)
+{
+	struct ti_ohci *ohci;
+
+	/* We only work with the OHCI-1394 driver */
+	if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+		return;
+
+	ohci = (struct ti_ohci *)host->hostdata;
+
+	dv1394_init(ohci);
+}
+
+static struct hpsb_highlevel_ops hl_ops = {
+	add_host:	dv1394_add_host,
+	remove_host:	dv1394_remove_host,
+};
+
+
+/*** KERNEL MODULE HANDLERS ************************************************/
+
+MODULE_AUTHOR("Dan Maas <dmaas@dcine.com>, Dan Dennedy <dan@dennedy.org>");
+MODULE_DESCRIPTION("driver for DV input/output on OHCI board");
+MODULE_SUPPORTED_DEVICE("dv1394");
+MODULE_LICENSE("GPL");
+
+static void __exit dv1394_exit_module(void)
+{
+	hpsb_unregister_highlevel (hl_handle);
+	ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
+	devfs_unregister(dv1394_devfs_handle);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry( "dv", ieee1394_procfs_entry);
+#endif
+}
+
+static int __init dv1394_init_module(void)
+{
+	if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_DV1394,
+				      THIS_MODULE, &dv1394_fops)) {
+		printk(KERN_ERR "dv1394: unable to register character device\n");
+		return -EIO;
+	}
+
+	dv1394_devfs_handle = devfs_mk_dir(ieee1394_devfs_handle, "dv", NULL);
+
+#ifdef CONFIG_PROC_FS
+	dv1394_procfs_entry = proc_mkdir( "dv", ieee1394_procfs_entry);
+	if (dv1394_procfs_entry == NULL) {
+		printk(KERN_ERR "dv1394: unable to create /proc/ieee1394/dv\n");
+		ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
+		devfs_unregister(dv1394_devfs_handle);
+		return -ENOMEM;
+	}
+	dv1394_procfs_entry->owner = THIS_MODULE;
+#endif
+
+	hl_handle = hpsb_register_highlevel ("dv1394", &hl_ops);
+	if (hl_handle == NULL) {
+		printk(KERN_ERR "dv1394: hpsb_register_highlevel failed\n");
+		ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
+		devfs_unregister(dv1394_devfs_handle);
+#ifdef CONFIG_PROC_FS
+		remove_proc_entry( "dv", ieee1394_procfs_entry);
+#endif
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+module_init(dv1394_init_module);
+module_exit(dv1394_exit_module);
+
diff -urN linux-2.5.3-pre2/drivers/ieee1394/dv1394.h linux/drivers/ieee1394/dv1394.h
--- linux-2.5.3-pre2/drivers/ieee1394/dv1394.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ieee1394/dv1394.h	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,277 @@
+/*
+ * dv1394.h - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *   video1394.h - driver for OHCI 1394 boards
+ *   Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *                          Peter Schlaile <udbz@rz.uni-karlsruhe.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_H
+#define _DV_1394_H
+
+/* This is the public user-space interface. Try not to break it. */
+
+#define DV1394_API_VERSION 0x20011127
+
+/* ********************
+   **                **
+   **   DV1394 API   **
+   **                **
+   ********************
+
+   There are two methods of operating the DV1394 DV output device.
+
+   1)
+
+   The simplest is an interface based on write(): simply write
+   full DV frames of data to the device, and they will be transmitted
+   as quickly as possible. The FD may be set for non-blocking I/O,
+   in which case you can use select() or poll() to wait for output
+   buffer space.
+
+   To set the DV output parameters (e.g. whether you want NTSC or PAL
+   video), use the DV1394_INIT ioctl, passing in the parameters you
+   want in a struct dv1394_init.
+ 
+   Example 1:
+         To play a raw .DV file:   cat foo.DV > /dev/dv1394
+	 (cat will use write() internally)
+
+   Example 2:
+           static struct dv1394_init init = {
+	      0x63,        (broadcast channel)
+              4,           (four-frame ringbuffer)
+	      DV1394_NTSC, (send NTSC video)
+	      0, 0         (default empty packet rate)
+           }
+
+	   ioctl(fd, DV1394_INIT, &init);
+
+	   while(1) {
+	          read( <a raw DV file>, buf, DV1394_NTSC_FRAME_SIZE );
+		  write( <the dv1394 FD>, buf, DV1394_NTSC_FRAME_SIZE );
+           }
+
+   2)
+
+   For more control over buffering, and to avoid unnecessary copies
+   of the DV data, you can use the more sophisticated the mmap() interface. 
+   First, call the DV1394_INIT ioctl to specify your parameters, 
+   including the number of frames in the ringbuffer. Then, calling mmap() 
+   on the dv1394 device will give you direct access to the ringbuffer
+   from which the DV card reads your frame data.
+
+   The ringbuffer is simply one large, contiguous region of memory
+   containing two or more frames of packed DV data. Each frame of DV data
+   is 120000 bytes (NTSC) or 144000 bytes (PAL).
+
+   Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES
+   ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl
+   or select()/poll() to wait until the frames are transmitted. Next, you'll
+   need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer
+   frames are clear (ready to be filled with new DV data). Finally, use
+   DV1394_SUBMIT_FRAMES again to send the new data to the DV output.
+
+
+   Example: here is what a four-frame ringbuffer might look like
+            during DV transmission:
+
+
+         frame 0   frame 1   frame 2   frame 3
+
+	*--------------------------------------*
+        | CLEAR   | DV data | DV data | CLEAR  |
+        *--------------------------------------*
+                   <ACTIVE> 
+
+	transmission goes in this direction --->>>
+
+
+   The DV hardware is currently transmitting the data in frame 1.
+   Once frame 1 is finished, it will automatically transmit frame 2.
+   (if frame 2 finishes before frame 3 is submitted, the device
+   will continue to transmit frame 2, and will increase the dropped_frames
+   counter each time it repeats the transmission).
+
+ 
+   If you called DV1394_GET_STATUS at this instant, you would
+   receive the following values:
+   
+                  n_frames          = 4
+		  active_frame      = 1
+		  first_clear_frame = 3
+		  n_clear_frames    = 2
+
+   At this point, you should write new DV data into frame 3 and optionally
+   frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that
+   it may transmit the new frames.
+
+*/
+
+
+/* maximum number of frames in the ringbuffer */
+#define DV1394_MAX_FRAMES 32
+
+/* number of *full* isochronous packets per DV frame */
+#define DV1394_NTSC_PACKETS_PER_FRAME 250
+#define DV1394_PAL_PACKETS_PER_FRAME  300
+
+/* size of one frame's worth of DV data, in bytes */
+#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME)
+#define DV1394_PAL_FRAME_SIZE  (480 * DV1394_PAL_PACKETS_PER_FRAME)
+
+
+/* ioctl() commands */
+
+enum {
+	/* I don't like using 0 as a valid ioctl() */
+	DV1394_INVALID = 0,
+
+
+	/* get the driver ready to transmit video.
+	   pass a struct dv1394_init* as the parameter (see below),
+	   or NULL to get default parameters */
+	DV1394_INIT,
+
+
+	/* stop transmitting video and free the ringbuffer */
+	DV1394_SHUTDOWN,
+
+
+	/* submit N new frames to be transmitted, where
+	   the index of the first new frame is first_clear_buffer,
+	   and the index of the last new frame is
+	   (first_clear_buffer + N) % n_frames */
+	DV1394_SUBMIT_FRAMES,
+
+
+	/* block until N buffers are clear (pass N as the parameter)
+	   Because we re-transmit the last frame on underrun, there
+	   will at most be n_frames - 1 clear frames at any time */
+	DV1394_WAIT_FRAMES,
+
+	/* capture new frames that have been received, where
+	   the index of the first new frame is first_clear_buffer,
+	   and the index of the last new frame is
+	   (first_clear_buffer + N) % n_frames */
+	DV1394_RECEIVE_FRAMES,
+
+
+	DV1394_START_RECEIVE,
+
+
+	/* pass a struct dv1394_status* as the parameter (see below) */
+	DV1394_GET_STATUS,
+};
+
+
+
+enum pal_or_ntsc {
+	DV1394_NTSC = 0,
+	DV1394_PAL
+};
+
+
+
+
+/* this is the argument to DV1394_INIT */
+struct dv1394_init {
+	/* DV1394_API_VERSION */
+	unsigned int api_version;
+	
+	/* isochronous transmission channel to use */
+	unsigned int channel;
+
+	/* number of frames in the ringbuffer. Must be at least 2
+	   and at most DV1394_MAX_FRAMES. */
+	unsigned int n_frames;
+
+	/* send/receive PAL or NTSC video format */
+	enum pal_or_ntsc format;
+
+	/* the following are used only for transmission */
+ 
+	/* set these to zero unless you want a
+	   non-default empty packet rate (see below) */
+	unsigned long cip_n;
+	unsigned long cip_d;
+
+	/* set this to zero unless you want a
+	   non-default SYT cycle offset (default = 3 cycles) */
+	unsigned int syt_offset;
+};
+
+/* Q: What are cip_n and cip_d? */
+
+/*
+  A: DV video streams do not utilize 100% of the potential bandwidth offered
+  by IEEE 1394 (FireWire). To achieve the correct rate of data transmission,
+  DV devices must periodically insert empty packets into the 1394 data stream.
+  Typically there is one empty packet per 14-16 data-carrying packets.
+
+  Some DV devices will accept a wide range of empty packet rates, while others
+  require a precise rate. If the dv1394 driver produces empty packets at
+  a rate that your device does not accept, you may see ugly patterns on the
+  DV output, or even no output at all.
+
+  The default empty packet insertion rate seems to work for many people; if
+  your DV output is stable, you can simply ignore this discussion. However,
+  we have exposed the empty packet rate as a parameter to support devices that
+  do not work with the default rate. 
+
+  The decision to insert an empty packet is made with a numerator/denominator
+  algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D.
+  You can alter the empty packet rate by passing non-zero values for cip_n
+  and cip_d to the INIT ioctl.
+  
+ */
+
+
+
+struct dv1394_status {
+	/* this embedded init struct returns the current dv1394
+	   parameters in use */
+	struct dv1394_init init;
+
+	/* the ringbuffer frame that is currently being
+	   displayed. (-1 if the device is not transmitting anything) */
+	int active_frame;
+
+	/* index of the first buffer (ahead of active_frame) that
+	   is ready to be filled with data */
+	unsigned int first_clear_frame;
+
+	/* how many buffers, including first_clear_buffer, are
+	   ready to be filled with data */
+	unsigned int n_clear_frames;
+
+	/* how many times the DV output has underflowed
+	   since the last call to DV1394_GET_STATUS */
+	unsigned int dropped_frames;
+
+	/* N.B. The dropped_frames counter is only a lower bound on the actual
+	   number of dropped frames, with the special case that if dropped_frames
+	   is zero, then it is guaranteed that NO frames have been dropped
+	   since the last call to DV1394_GET_STATUS.
+	*/
+};
+
+
+#endif /* _DV_1394_H */
diff -urN linux-2.5.3-pre2/drivers/ieee1394/highlevel.c linux/drivers/ieee1394/highlevel.c
--- linux-2.5.3-pre2/drivers/ieee1394/highlevel.c	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/highlevel.c	Mon Jan 21 17:23:22 2002
@@ -44,10 +44,11 @@
         hl->op = ops;
 
         write_lock_irq(&hl_drivers_lock);
-        hl_all_hosts(hl, 1);
         list_add_tail(&hl->hl_list, &hl_drivers);
         write_unlock_irq(&hl_drivers_lock);
 
+        hl_all_hosts(hl->op->add_host);
+
         return hl;
 }
 
@@ -73,9 +74,11 @@
 
         write_lock_irq(&hl_drivers_lock);
         list_del(&hl->hl_list);
-        hl_all_hosts(hl, 0);
         write_unlock_irq(&hl_drivers_lock);
 
+        if (hl->op->remove_host)
+		hl_all_hosts(hl->op->remove_host);
+
         kfree(hl);
 }
 
@@ -87,7 +90,7 @@
         int retval = 0;
 
         if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
-                HPSB_ERR(__FUNCTION__ " called with invalid addresses");
+                HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
                 return 0;
         }
 
@@ -131,12 +134,12 @@
                          unsigned int channel)
 {
         if (channel > 63) {
-                HPSB_ERR(__FUNCTION__ " called with invalid channel");
+                HPSB_ERR("%s called with invalid channel", __FUNCTION__);
                 return;
         }
 
         if (host->iso_listen_count[channel]++ == 0) {
-                host->template->devctl(host, ISO_LISTEN_CHANNEL, channel);
+                host->ops->devctl(host, ISO_LISTEN_CHANNEL, channel);
         }
 }
 
@@ -144,46 +147,58 @@
                            unsigned int channel)
 {
         if (channel > 63) {
-                HPSB_ERR(__FUNCTION__ " called with invalid channel");
+                HPSB_ERR("%s called with invalid channel", __FUNCTION__);
                 return;
         }
 
         if (--host->iso_listen_count[channel] == 0) {
-                host->template->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
+                host->ops->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
         }
 }
 
 
-#define DEFINE_MULTIPLEXER(Function)			\
-void highlevel_##Function(struct hpsb_host *host)	\
-{							\
-	struct list_head *lh;				\
-	void (*funcptr)(struct hpsb_host*);		\
-	read_lock(&hl_drivers_lock);			\
-	list_for_each(lh, &hl_drivers) {		\
-		funcptr = list_entry(lh, struct hpsb_highlevel, hl_list) \
-				->op->Function;		\
-		if (funcptr) funcptr(host);		\
-	}						\
-	read_unlock(&hl_drivers_lock);			\
-}
-
-DEFINE_MULTIPLEXER(add_host)
-DEFINE_MULTIPLEXER(remove_host)
-DEFINE_MULTIPLEXER(host_reset)
-#undef DEFINE_MULTIPLEXER
-
-/* Add one host to our list */
-void highlevel_add_one_host (struct hpsb_host *host)
-{
-	if (host->template->initialize_host)
-		if (!host->template->initialize_host(host))
-			goto fail;
-	host->initialized = 1;
-	highlevel_add_host (host);
-	hpsb_reset_bus (host, LONG_RESET);
-fail:
-	host->template->number_of_hosts++;
+void highlevel_add_host(struct hpsb_host *host)
+{
+        struct list_head *entry;
+        struct hpsb_highlevel *hl;
+
+        read_lock(&hl_drivers_lock);
+        list_for_each(entry, &hl_drivers) {
+                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+
+		hl->op->add_host(host);
+        }
+        read_unlock(&hl_drivers_lock);
+}
+
+void highlevel_remove_host(struct hpsb_host *host)
+{
+        struct list_head *entry;
+        struct hpsb_highlevel *hl;
+
+        write_lock_irq(&hl_drivers_lock);
+	list_for_each(entry, &hl_drivers) {
+                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+
+		if (hl->op->remove_host)
+			hl->op->remove_host(host);
+        }
+        write_unlock_irq(&hl_drivers_lock);
+}
+
+void highlevel_host_reset(struct hpsb_host *host)
+{
+        struct list_head *entry;
+        struct hpsb_highlevel *hl;
+
+	read_lock(&hl_drivers_lock);
+	list_for_each(entry, &hl_drivers) {
+                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+
+                if (hl->op->host_reset)
+                        hl->op->host_reset(host);
+        }
+	read_unlock(&hl_drivers_lock);
 }
 
 void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
@@ -242,12 +257,11 @@
 
         while (as->start <= addr) {
                 if (as->end > addr) {
-                        partlength = MIN((unsigned int)(as->end - addr),
-                                         length);
+                        partlength = min(as->end - addr, (u64) length);
 
                         if (as->op->read != NULL) {
-                                rcode = as->op->read(host, nodeid, buffer, addr,
-                                                     partlength);
+                                rcode = as->op->read(host, nodeid, buffer,
+						     addr, partlength);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
@@ -288,12 +302,11 @@
 
         while (as->start <= addr) {
                 if (as->end > addr) {
-                        partlength = MIN((unsigned int)(as->end - addr),
-                                         length);
+                        partlength = min(as->end - addr, (u64) length);
 
                         if (as->op->write != NULL) {
-                                rcode = as->op->write(host, nodeid, destid, data,
-						      addr, partlength);
+                                rcode = as->op->write(host, nodeid, destid,
+						      data, addr, partlength);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
diff -urN linux-2.5.3-pre2/drivers/ieee1394/highlevel.h linux/drivers/ieee1394/highlevel.h
--- linux-2.5.3-pre2/drivers/ieee1394/highlevel.h	Thu Jul 19 17:48:15 2001
+++ linux/drivers/ieee1394/highlevel.h	Mon Jan 21 17:23:22 2002
@@ -91,7 +91,6 @@
 void init_hpsb_highlevel(void);
 
 void highlevel_add_host(struct hpsb_host *host);
-void highlevel_add_one_host(struct hpsb_host *host);
 void highlevel_remove_host(struct hpsb_host *host);
 void highlevel_host_reset(struct hpsb_host *host);
 
diff -urN linux-2.5.3-pre2/drivers/ieee1394/hosts.c linux/drivers/ieee1394/hosts.c
--- linux-2.5.3-pre2/drivers/ieee1394/hosts.c	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/hosts.c	Mon Jan 21 17:23:22 2002
@@ -11,101 +11,86 @@
  */
 
 #include <linux/config.h>
-
 #include <linux/types.h>
+#include <linux/list.h>
 #include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
+#include <linux/slab.h>
 
 #include "ieee1394_types.h"
 #include "hosts.h"
 #include "ieee1394_core.h"
 #include "highlevel.h"
 
+static struct list_head hosts = LIST_HEAD_INIT(hosts);
+static struct list_head host_drivers = LIST_HEAD_INIT(host_drivers);
 
-static LIST_HEAD(templates);
-static spinlock_t templates_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t hosts_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t host_drivers_lock = SPIN_LOCK_UNLOCKED;
 
-/*
- * This function calls the add_host/remove_host hooks for every host currently
- * registered.  Init == TRUE means add_host.
- */
-void hl_all_hosts(struct hpsb_highlevel *hl, int init)
+
+static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
 {
-	struct list_head *tlh, *hlh;
-        struct hpsb_host_template *tmpl;
-        struct hpsb_host *host;
+        return 0;
+}
 
-        spin_lock(&templates_lock);
+static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
+{
+        return -1;
+}
 
-	list_for_each(tlh, &templates) {
-                tmpl = list_entry(tlh, struct hpsb_host_template, list);
-		list_for_each(hlh, &tmpl->hosts) {
-			host = list_entry(hlh, struct hpsb_host, list);
-                        if (host->initialized) {
-                                if (init) {
-                                        if (hl->op->add_host) {
-                                                hl->op->add_host(host);
-                                        }
-                                } else {
-                                        if (hl->op->remove_host) {
-                                                hl->op->remove_host(host);
-                                        }
-                                }
-                        }
-                }
-        }
+static struct hpsb_host_operations dummy_ops = {
+        transmit_packet:  dummy_transmit_packet,
+        devctl:           dummy_devctl
+};
 
-        spin_unlock(&templates_lock);
-}
 
-int hpsb_inc_host_usage(struct hpsb_host *host)
+int hpsb_ref_host(struct hpsb_host *host)
 {
-	struct list_head *tlh, *hlh;
-        struct hpsb_host_template *tmpl;
-        int retval = 0;
+        struct list_head *lh;
 	unsigned long flags;
+        int retval = 0;
 
-        spin_lock_irqsave(&templates_lock, flags);
-
-	list_for_each(tlh, &templates) {
-                tmpl = list_entry(tlh, struct hpsb_host_template, list);
-		list_for_each(hlh, &tmpl->hosts) {
-			if (host == list_entry(hlh, struct hpsb_host, list)) {
-                                tmpl->devctl(host, MODIFY_USAGE, 1);
+        spin_lock_irqsave(&hosts_lock, flags);
+        list_for_each(lh, &hosts) {
+                if (host == list_entry(lh, struct hpsb_host, host_list)) {
+                        if (host->ops->devctl(host, MODIFY_USAGE, 1)) {
+				host->refcount++;
                                 retval = 1;
-                                break;
-                        }
                 }
-		if (retval)
 			break;
         }
-
-        spin_unlock_irqrestore(&templates_lock, flags);
+        }
+        spin_unlock_irqrestore(&hosts_lock, flags);
 
         return retval;
 }
 
-void hpsb_dec_host_usage(struct hpsb_host *host)
+void hpsb_unref_host(struct hpsb_host *host)
 {
-        host->template->devctl(host, MODIFY_USAGE, 0);
+        unsigned long flags;
+
+        host->ops->devctl(host, MODIFY_USAGE, 0);
+
+        spin_lock_irqsave(&hosts_lock, flags);
+        host->refcount--;
+
+        if (!host->refcount && !host->is_shutdown)
+                kfree(host);
+        spin_unlock_irqrestore(&hosts_lock, flags);
 }
 
-/*
- * The following function is exported for module usage.  It will be called from
- * the detect function of a adapter driver.
- */
-struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl, 
-                                size_t hd_size)
+struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra)
 {
         struct hpsb_host *h;
 
-        h = vmalloc(sizeof(struct hpsb_host) + hd_size);
+        h = kmalloc(sizeof(struct hpsb_host) + extra, SLAB_KERNEL);
         if (!h) return NULL;
 
-        memset(h, 0, sizeof(struct hpsb_host) + hd_size);
+        memset(h, 0, sizeof(struct hpsb_host) + extra);
 
-        atomic_set(&h->generation, 0);
+        h->driver = drv;
+        h->ops = drv->ops;
+	h->hostdata = h + 1;
 
         INIT_LIST_HEAD(&h->pending_packets);
         spin_lock_init(&h->pending_pkt_lock);
@@ -113,113 +98,92 @@
         sema_init(&h->tlabel_count, 64);
         spin_lock_init(&h->tlabel_lock);
 
+	atomic_set(&h->generation, 0);
+
 	INIT_TQUEUE(&h->timeout_tq, (void (*)(void*))abort_timedouts, h);
 
         h->topology_map = h->csr.topology_map + 3;
         h->speed_map = (u8 *)(h->csr.speed_map + 2);
 
-        h->template = tmpl;
-        if (hd_size)
-                h->hostdata = &h->embedded_hostdata[0];
+	return h;
+}
+
+void hpsb_add_host(struct hpsb_host *host)
+{
+        unsigned long flags;
 
-	list_add_tail(&h->list, &tmpl->hosts);
+        spin_lock_irqsave(&hosts_lock, flags);
+        host->driver->number_of_hosts++;
+        list_add_tail(&host->driver_list, &host->driver->hosts);
+        list_add_tail(&host->host_list, &hosts);
+        spin_unlock_irqrestore(&hosts_lock, flags);
 
-        return h;
+        highlevel_add_host(host);
+        host->ops->devctl(host, RESET_BUS, 0);
 }
 
-static void free_all_hosts(struct hpsb_host_template *tmpl)
+void hpsb_remove_host(struct hpsb_host *host)
 {
-	struct list_head *hlh, *next;
-        struct hpsb_host *host;
+        struct hpsb_host_driver *drv = host->driver;
+        unsigned long flags;
 
-	list_for_each_safe(hlh, next, &tmpl->hosts) {
-		host = list_entry(hlh, struct hpsb_host, list);
-                vfree(host);
-        }
+        host->is_shutdown = 1;
+        host->ops = &dummy_ops;
+        highlevel_remove_host(host);
+
+        spin_lock_irqsave(&hosts_lock, flags);
+        list_del(&host->driver_list);
+        list_del(&host->host_list);
+
+        drv->number_of_hosts--;
+        if (!host->refcount) kfree(host);
+        spin_unlock_irqrestore(&hosts_lock, flags);
 }
 
 
-static void init_hosts(struct hpsb_host_template *tmpl)
+struct hpsb_host_driver *hpsb_register_lowlevel(struct hpsb_host_operations *op,
+                                                const char *name)
 {
-        int count;
-	struct list_head *hlh;
-        struct hpsb_host *host;
+        struct hpsb_host_driver *drv;
 
-        count = tmpl->detect_hosts(tmpl);
+        drv = kmalloc(sizeof(struct hpsb_host_driver), SLAB_KERNEL);
+        if (!drv) return NULL;
 
-	list_for_each(hlh, &tmpl->hosts) {
-		host = list_entry(hlh, struct hpsb_host, list);
-                if (tmpl->initialize_host(host)) {
-                        host->initialized = 1;
+        INIT_LIST_HEAD(&drv->list);
+        INIT_LIST_HEAD(&drv->hosts);
+        drv->number_of_hosts = 0;
+        drv->name = name;
+        drv->ops = op;
 
-                        highlevel_add_host(host);
-                        hpsb_reset_bus(host, LONG_RESET);
-                }
-        }
+        spin_lock(&host_drivers_lock);
+        list_add_tail(&drv->list, &host_drivers);
+        spin_unlock(&host_drivers_lock);
 
-        tmpl->number_of_hosts = count;
-        HPSB_INFO("detected %d %s adapter%s", count, tmpl->name,
-                  (count != 1 ? "s" : ""));
+        return drv;
 }
 
-static void shutdown_hosts(struct hpsb_host_template *tmpl)
+void hpsb_unregister_lowlevel(struct hpsb_host_driver *drv)
 {
-	struct list_head *hlh;
-        struct hpsb_host *host;
+        spin_lock(&host_drivers_lock);
+        list_del(&drv->list);
+        spin_unlock(&host_drivers_lock);
 
-	list_for_each(hlh, &tmpl->hosts) {
-		host = list_entry(hlh, struct hpsb_host, list);
-                if (host->initialized) {
-                        host->initialized = 0;
-                        abort_requests(host);
-
-                        highlevel_remove_host(host);
-
-                        tmpl->release_host(host);
-                        while (test_bit(0, &host->timeout_tq.sync)) {
-                                schedule();
-                        }
-                }
-        }
-        free_all_hosts(tmpl);
-        tmpl->release_host(NULL);
-
-        tmpl->number_of_hosts = 0;
+        kfree(drv);
 }
 
 
 /*
- * The following two functions are exported symbols for module usage.
+ * This function calls the given function for every host currently registered.
  */
-int hpsb_register_lowlevel(struct hpsb_host_template *tmpl)
+void hl_all_hosts(void (*function)(struct hpsb_host*))
 {
-	INIT_LIST_HEAD(&tmpl->hosts);
-	tmpl->number_of_hosts = 0;
-
-        spin_lock(&templates_lock);
-	list_add_tail(&tmpl->list, &templates);
-        spin_unlock(&templates_lock);
-
-	/* PCI cards should be smart and use the PCI detection layer, and
-	 * not this one shot deal. detect_hosts() will be obsoleted soon. */
-	if (tmpl->detect_hosts != NULL) {
-		HPSB_DEBUG("Registered %s driver, initializing now", tmpl->name);
-		init_hosts(tmpl);
-	}
-
-        return 0;
-}
-
-void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl)
-{
-        shutdown_hosts(tmpl);
+        struct list_head *lh;
+        struct hpsb_host *host;
 
-        if (tmpl->number_of_hosts)
-                HPSB_PANIC("attempted to remove busy host template "
-			   "of %s at address 0x%p", tmpl->name, tmpl);
-	else {
-		spin_lock(&templates_lock);
-		list_del(&tmpl->list);
-		spin_unlock(&templates_lock);
+        spin_lock_irq(&hosts_lock);
+        list_for_each (lh, &hosts) {
+                host = list_entry(lh, struct hpsb_host, host_list);
+                function(host);
 	}
+        spin_unlock_irq(&hosts_lock);
 }
diff -urN linux-2.5.3-pre2/drivers/ieee1394/hosts.h linux/drivers/ieee1394/hosts.h
--- linux-2.5.3-pre2/drivers/ieee1394/hosts.h	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/hosts.h	Mon Jan 21 17:23:22 2002
@@ -13,11 +13,15 @@
 struct hpsb_packet;
 
 struct hpsb_host {
-/* private fields (hosts, do not use them) */
-	struct list_head list;
+        struct list_head host_list;
+
+        struct hpsb_host_operations *ops;
+        void *hostdata;
 
         atomic_t generation;
 
+        int refcount;
+
         struct list_head pending_packets;
         spinlock_t pending_pkt_lock;
         struct tq_struct timeout_tq;
@@ -28,16 +32,8 @@
         struct semaphore tlabel_count;
         spinlock_t tlabel_lock;
 
-        int reset_retries;
-        quadlet_t *topology_map;
-        u8 *speed_map;
-        struct csr_control csr;
-
         unsigned char iso_listen_count[64];
 
-/* readonly fields for hosts */
-        struct hpsb_host_template *template;
-
         int node_count; /* number of identified nodes on this bus */
         int selfid_count; /* total number of SelfIDs received */
 
@@ -45,9 +41,9 @@
         nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
         nodeid_t busmgr_id; /* ID of this bus' bus manager */
 
-        unsigned initialized:1; /* initialized and usable */
-        unsigned in_bus_reset:1; /* in bus reset / SelfID stage */
-        unsigned attempt_root:1; /* attempt to become root during next reset */
+        /* this nodes state */
+        unsigned in_bus_reset:1;
+        unsigned is_shutdown:1;
 
         /* this nodes' duties on the bus */
         unsigned is_root:1;
@@ -55,11 +51,15 @@
         unsigned is_irm:1;
         unsigned is_busmgr:1;
 
-/* fields readable and writeable by the hosts */
+        int reset_retries;
+        quadlet_t *topology_map;
+        u8 *speed_map;
+        struct csr_control csr;
+
+        struct hpsb_host_driver *driver;
+        struct list_head driver_list;
 
-        void *hostdata;
 	struct pci_dev *pdev;
-        int embedded_hostdata[0];
 };
 
 
@@ -88,8 +88,10 @@
          * Return void. */
         CANCEL_REQUESTS,
 
-        /* Decrease module usage count if arg == 0, increase otherwise.  Return
-         * void. */
+        /* Decrease host usage count if arg == 0, increase otherwise.  Return
+         * 1 for success, 0 for failure.  Increase usage may fail if the driver
+         * is in the process of shutting itself down.  Decrease usage can not
+         * fail. */
         MODIFY_USAGE,
 
         /* Start or stop receiving isochronous channel in arg.  Return void.
@@ -109,37 +111,7 @@
         SHORT_RESET
 };
 
-struct hpsb_host_template {
-	struct list_head list;
-
-        struct list_head hosts;
-        int number_of_hosts;
-
-        /* fields above will be ignored and overwritten after registering */
-
-        /* This should be the name of the driver (single word) and must not be
-         * NULL. */
-        const char *name;
-
-        /* This function shall detect all available adapters of this type and
-         * call hpsb_get_host for each one.  The initialize_host function will
-         * be called to actually set up these adapters.  The number of detected
-         * adapters or zero if there are none must be returned.
-         */
-        int (*detect_hosts) (struct hpsb_host_template *template);
-
-        /* After detecting and registering hosts, this function will be called
-         * for every registered host.  It shall set up the host to be fully
-         * functional for bus operations and return 0 for failure.
-         */
-        int (*initialize_host) (struct hpsb_host *host);
-
-        /* To unload modules, this function is provided.  It shall free all
-         * resources this host is using (if host is not NULL) or free all
-         * resources globally allocated by the driver (if host is NULL).
-         */
-        void (*release_host) (struct hpsb_host *host); 
-
+struct hpsb_host_operations {
         /* This function must store a pointer to the configuration ROM into the
          * location referenced to by pointer and return the size of the ROM. It
          * may not fail.  If any allocation is required, it must be done
@@ -175,34 +147,43 @@
                                  quadlet_t data, quadlet_t compare);
 };
 
+struct hpsb_host_driver {
+        struct list_head list;
 
+        struct list_head hosts;
 
-/* mid level internal use */
+        int number_of_hosts;
+        const char *name;
+
+        struct hpsb_host_operations *ops;
+};
+
+
+/* core internal use */
 void register_builtin_lowlevels(void);
 
 /* high level internal use */
 struct hpsb_highlevel;
-void hl_all_hosts(struct hpsb_highlevel *hl, int init);
+void hl_all_hosts(void (*function)(struct hpsb_host*));
 
-/* 
- * These functions are for lowlevel (host) driver use.
- */
-int hpsb_register_lowlevel(struct hpsb_host_template *tmpl);
-void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl);
 
 /*
- * Get a initialized host structure with hostdata_size bytes allocated in
- * embedded_hostdata for free usage.  Returns NULL for failure.  
+ * In order to prevent hosts from unloading, use hpsb_ref_host().  This prevents
+ * the host from going away (e.g. makes module unloading of the driver
+ * impossible), but still can not guarantee it (e.g. PC-Card being pulled by the
+ * user).  hpsb_ref_host() returns false if host could not be locked.  If it is
+ * successful, host is valid as a pointer until hpsb_unref_host() (not just
+ * until after remove_host).
  */
-struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl, 
-                                size_t hostdata_size);
+int hpsb_ref_host(struct hpsb_host *host);
+void hpsb_unref_host(struct hpsb_host *host);
 
-/*
- * Increase / decrease host usage counter.  Increase function will return true
- * only if successful (host still existed).  Decrease function expects host to
- * exist.
- */
-int hpsb_inc_host_usage(struct hpsb_host *host);
-void hpsb_dec_host_usage(struct hpsb_host *host);
+struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra);
+void hpsb_add_host(struct hpsb_host *host);
+void hpsb_remove_host(struct hpsb_host *h);
+
+struct hpsb_host_driver *hpsb_register_lowlevel(struct hpsb_host_operations *op,
+                                                const char *name);
+void hpsb_unregister_lowlevel(struct hpsb_host_driver *drv);
 
 #endif /* _IEEE1394_HOSTS_H */
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394.h linux/drivers/ieee1394/ieee1394.h
--- linux-2.5.3-pre2/drivers/ieee1394/ieee1394.h	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/ieee1394.h	Mon Jan 21 17:23:22 2002
@@ -39,10 +39,10 @@
 #define ACK_TYPE_ERROR           0xe 
 
 /* Non-standard "ACK codes" for internal use */
-#define ACKX_NONE                -1
-#define ACKX_SEND_ERROR          -2
-#define ACKX_ABORTED             -3
-#define ACKX_TIMEOUT             -4
+#define ACKX_NONE                (-1)
+#define ACKX_SEND_ERROR          (-2)
+#define ACKX_ABORTED             (-3)
+#define ACKX_TIMEOUT             (-4)
 
 
 #define SPEED_100                0x0
@@ -116,7 +116,7 @@
 
 /*
  * Note: these mean to be bit fields of a big endian SelfID as seen on a little
- * endian machine.
+ * endian machine.  Without swapping.
  */
 
 struct selfid {
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.c linux/drivers/ieee1394/ieee1394_core.c
--- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.c	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/ieee1394_core.c	Mon Jan 21 17:23:22 2002
@@ -18,9 +18,11 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/proc_fs.h>
 #include <asm/bitops.h>
 #include <asm/byteorder.h>
 #include <asm/semaphore.h>
+#include <asm/smplock.h>
 
 #include "ieee1394_types.h"
 #include "ieee1394.h"
@@ -135,12 +137,8 @@
 
 int hpsb_reset_bus(struct hpsb_host *host, int type)
 {
-        if (!host->initialized) {
-                return 1;
-        }
-
         if (!host->in_bus_reset) {
-                host->template->devctl(host, RESET_BUS, type);
+                host->ops->devctl(host, RESET_BUS, type);
                 return 0;
         } else {
                 return 1;
@@ -151,8 +149,8 @@
 int hpsb_bus_reset(struct hpsb_host *host)
 {
         if (host->in_bus_reset) {
-                HPSB_NOTICE(__FUNCTION__ 
-                            " called while bus reset already in progress");
+                HPSB_NOTICE("%s called while bus reset already in progress",
+			    __FUNCTION__);
                 return 1;
         }
 
@@ -171,10 +169,10 @@
  * Verify num_of_selfids SelfIDs and return number of nodes.  Return zero in
  * case verification failed.
  */
-static int check_selfids(struct hpsb_host *host, unsigned int num_of_selfids)
+static int check_selfids(struct hpsb_host *host)
 {
         int nodeid = -1;
-        int rest_of_selfids = num_of_selfids;
+        int rest_of_selfids = host->selfid_count;
         struct selfid *sid = (struct selfid *)host->topology_map;
         struct ext_selfid *esid;
         int esid_seq = 23;
@@ -318,15 +316,18 @@
 
 void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
 {
-        host->node_id = 0xffc0 | phyid;
-        host->in_bus_reset = 0;
+	if (!host->in_bus_reset)
+		HPSB_NOTICE("SelfID completion called outside of bus reset!");
+
+        host->node_id = LOCAL_BUS | phyid;
         host->is_root = isroot;
 
-        host->node_count = check_selfids(host, host->selfid_count);
+        host->node_count = check_selfids(host);
         if (!host->node_count) {
                 if (host->reset_retries++ < 20) {
                         /* selfid stage did not complete without error */
                         HPSB_NOTICE("Error in SelfID stage, resetting");
+			host->in_bus_reset = 0;
                         hpsb_reset_bus(host, LONG_RESET);
                         return;
                 } else {
@@ -346,8 +347,9 @@
         }
 
         host->reset_retries = 0;
-        atomic_inc(&host->generation);
-        if (isroot) host->template->devctl(host, ACT_CYCLE_MASTER, 1);
+        if (isroot) host->ops->devctl(host, ACT_CYCLE_MASTER, 1);
+	atomic_inc(&host->generation);
+	host->in_bus_reset = 0;
         highlevel_host_reset(host);
 }
 
@@ -402,7 +404,7 @@
 {
         struct hpsb_host *host = packet->host;
 
-        if (!host->initialized || host->in_bus_reset 
+        if (host->is_shutdown || host->in_bus_reset
             || (packet->generation != get_hpsb_generation(host))) {
                 return 0;
         }
@@ -431,7 +433,7 @@
         }
 #endif
 
-        return host->template->transmit_packet(host, packet);
+        return host->ops->transmit_packet(host, packet);
 }
 
 static void send_packet_nocare(struct hpsb_packet *packet)
@@ -727,7 +729,7 @@
         struct list_head *lh;
         LIST_HEAD(llist);
 
-        host->template->devctl(host, CANCEL_REQUESTS, 0);
+        host->ops->devctl(host, CANCEL_REQUESTS, 0);
 
         spin_lock_irqsave(&host->pending_pkt_lock, flags);
         list_splice(&host->pending_packets, &llist);
@@ -786,10 +788,228 @@
 }
 
 
+/*
+ * character device dispatching (see ieee1394_core.h)
+ * Dan Maas <dmaas@dcine.com>
+ */
+
+static struct {
+	struct file_operations *file_ops;
+	struct module *module;
+} ieee1394_chardevs[16];
+
+static rwlock_t ieee1394_chardevs_lock = RW_LOCK_UNLOCKED;
+
+static int ieee1394_dispatch_open(struct inode *inode, struct file *file);
+
+static struct file_operations ieee1394_chardev_ops = {
+	OWNER_THIS_MODULE
+	open: ieee1394_dispatch_open,
+};
+
+devfs_handle_t ieee1394_devfs_handle;
+
+
+/* claim a block of minor numbers */
+int ieee1394_register_chardev(int blocknum,
+			      struct module *module,
+			      struct file_operations *file_ops)
+{
+	int retval;
+	
+	if( (blocknum < 0) || (blocknum > 15) )
+		return -EINVAL;
+
+	write_lock(&ieee1394_chardevs_lock);
+
+	if(ieee1394_chardevs[blocknum].file_ops == NULL) {
+		/* grab the minor block */
+		ieee1394_chardevs[blocknum].file_ops = file_ops;
+		ieee1394_chardevs[blocknum].module = module;
+		
+		retval = 0;
+
+		V22_COMPAT_MOD_INC_USE_COUNT;
+	} else {
+		/* block already taken */
+		retval = -EBUSY;
+	}
+	
+	write_unlock(&ieee1394_chardevs_lock);
+
+	return retval;
+}
+
+/* release a block of minor numbers */
+void ieee1394_unregister_chardev(int blocknum)
+{
+	if( (blocknum < 0) || (blocknum > 15) )
+		return;
+	
+	write_lock(&ieee1394_chardevs_lock);
+	
+	if(ieee1394_chardevs[blocknum].file_ops) {
+		ieee1394_chardevs[blocknum].file_ops = NULL;
+		ieee1394_chardevs[blocknum].module = NULL;
+		V22_COMPAT_MOD_DEC_USE_COUNT;
+	}
+	
+	write_unlock(&ieee1394_chardevs_lock);
+}
+
+/* the point of entry for open() on any ieee1394 character device */
+static int ieee1394_dispatch_open(struct inode *inode, struct file *file)
+{
+	struct file_operations *file_ops;
+	struct module *module;
+	int blocknum;
+	int retval = -ENODEV;
+
+	/*
+	  Maintaining correct module reference counts is tricky here!
+
+	  For Linux v2.2:
+
+	  The task-specific driver is expected to maintain its own
+	  reference count via V22_COMPAT_MOD_[INC,DEC]_USE_COUNT.
+	  We don't need to do anything special.
+	  
+	  For Linux v2.4 and later:
+	  
+	  The key thing to remember is that the VFS increments the
+	  reference count of ieee1394 before it calls
+	  ieee1394_dispatch_open().
+
+	  If the open() succeeds, then we need to transfer this extra
+	  reference to the task-specific driver module (e.g. raw1394).
+	  The VFS will deref the driver module automatically when the
+	  file is later released.
+
+	  If the open() fails, then the VFS will drop the
+	  reference count of whatever module file->f_op->owner points
+	  to, immediately after this function returns.
+
+	  The comments below refer to the 2.4 case, since the 2.2
+	  case is trivial.
+	  
+	*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+	/* 2.2 */
+#define INCREF(mod) do {} while (0)
+#define DECREF(mod) do {} while (0)
+#else
+	/* 2.4 */
+#define INCREF(mod_) do { struct module *mod = (struct module*) mod_; \
+                          if(mod != NULL) __MOD_INC_USE_COUNT(mod); } while(0)
+#define DECREF(mod_) do { struct module *mod = (struct module*) mod_; \
+                          if(mod != NULL) __MOD_DEC_USE_COUNT(mod); } while(0)
+#endif
+	
+        /* shift away lower four bits of the minor
+	   to get the index of the ieee1394_driver
+	   we want */
+	
+	blocknum = (minor(inode->i_rdev) >> 4) & 0xF;
+
+	/* printk("ieee1394_dispatch_open(%d)", blocknum); */
+
+	/* lock the whole kernel here, to prevent a driver from
+	   being unloaded between the file_ops lookup and the open */
+
+	lock_kernel();
+
+	read_lock(&ieee1394_chardevs_lock);
+	file_ops = ieee1394_chardevs[blocknum].file_ops;
+	module = ieee1394_chardevs[blocknum].module;
+	read_unlock(&ieee1394_chardevs_lock);
+
+	if(file_ops == NULL) {
+		goto out_fail;
+	}
+
+	/* redirect all subsequent requests to the driver's
+	   own file_operations */
+	file->f_op = file_ops;
+
+	/* bump the reference count of the driver that
+	   will receive the open() */
+	INCREF(module);
+	
+	/* at this point BOTH ieee1394 and the task-specific driver have
+	   an extra reference */
+
+	/* follow through with the open() */
+	retval = file_ops->open(inode, file);
+
+	if(retval) {
+		
+		/* if the open() failed, then we need to drop the
+                   extra reference we gave to the task-specific
+                   driver */
+
+		DECREF(module);
+		goto out_fail;
+		
+	} else {
+
+		/* if the open() succeeded, then ieee1394 will be left
+		   with an extra module reference, so we discard it here.*/
+
+		DECREF(THIS_MODULE);
+
+		/* the task-specific driver still has the extra
+		   reference we gave it. This extra reference prevents
+		   the module from unloading while the file is open,
+		   and will be dropped by the VFS when the file is
+		   released. */
+		
+		unlock_kernel();
+		return 0;
+	}
+	       
+out_fail:
+	/* point the file's f_ops back to ieee1394. The VFS will then
+	   decrement ieee1394's reference count immediately after this
+	   function returns. */
+	
+	file->f_op = &ieee1394_chardev_ops;
+	unlock_kernel();
+	return retval;
+
+#undef INCREF
+#undef DECREF
+	     
+}
+
+struct proc_dir_entry *ieee1394_procfs_entry;
+
 static int __init ieee1394_init(void)
 {
 	hpsb_packet_cache = kmem_cache_create("hpsb_packet", sizeof(struct hpsb_packet),
 					      0, 0, NULL, NULL);
+
+	ieee1394_devfs_handle = devfs_mk_dir(NULL, "ieee1394", NULL);
+
+	if (register_chrdev(IEEE1394_MAJOR, "ieee1394", &ieee1394_chardev_ops)) {
+		HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR);
+		devfs_unregister(ieee1394_devfs_handle);
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_PROC_FS
+	/* Must be done before we start everything else, since the drivers
+	 * may use it.  */
+	ieee1394_procfs_entry = proc_mkdir( "ieee1394", proc_bus);
+	if (ieee1394_procfs_entry == NULL) {
+		HPSB_ERR("unable to create /proc/bus/ieee1394\n");
+		unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
+		devfs_unregister(ieee1394_devfs_handle);
+		return -ENOMEM;
+	}
+	ieee1394_procfs_entry->owner = THIS_MODULE;
+#endif
+
 	init_hpsb_highlevel();
 	init_csr();
 	if (!disable_nodemgr)
@@ -807,6 +1027,13 @@
 
 	cleanup_csr();
 	kmem_cache_destroy(hpsb_packet_cache);
+
+	unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
+	
+	/* it's ok to pass a NULL devfs_handle to devfs_unregister */
+	devfs_unregister(ieee1394_devfs_handle);
+	
+	remove_proc_entry("ieee1394", proc_bus);
 }
 
 module_init(ieee1394_init);
@@ -815,9 +1042,11 @@
 /* Exported symbols */
 EXPORT_SYMBOL(hpsb_register_lowlevel);
 EXPORT_SYMBOL(hpsb_unregister_lowlevel);
-EXPORT_SYMBOL(hpsb_get_host);
-EXPORT_SYMBOL(hpsb_inc_host_usage);
-EXPORT_SYMBOL(hpsb_dec_host_usage);
+EXPORT_SYMBOL(hpsb_alloc_host);
+EXPORT_SYMBOL(hpsb_add_host);
+EXPORT_SYMBOL(hpsb_remove_host);
+EXPORT_SYMBOL(hpsb_ref_host);
+EXPORT_SYMBOL(hpsb_unref_host);
 EXPORT_SYMBOL(hpsb_speedto_str);
 
 EXPORT_SYMBOL(alloc_hpsb_packet);
@@ -867,7 +1096,6 @@
 EXPORT_SYMBOL(highlevel_add_host);
 EXPORT_SYMBOL(highlevel_remove_host);
 EXPORT_SYMBOL(highlevel_host_reset);
-EXPORT_SYMBOL(highlevel_add_one_host);
 
 EXPORT_SYMBOL(hpsb_guid_get_entry);
 EXPORT_SYMBOL(hpsb_nodeid_get_entry);
@@ -876,3 +1104,9 @@
 EXPORT_SYMBOL(hpsb_register_protocol);
 EXPORT_SYMBOL(hpsb_unregister_protocol);
 EXPORT_SYMBOL(hpsb_release_unit_directory);
+
+EXPORT_SYMBOL(ieee1394_register_chardev);
+EXPORT_SYMBOL(ieee1394_unregister_chardev);
+EXPORT_SYMBOL(ieee1394_devfs_handle);
+
+EXPORT_SYMBOL(ieee1394_procfs_entry);
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.h linux/drivers/ieee1394/ieee1394_core.h
--- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.h	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/ieee1394_core.h	Mon Jan 21 17:23:22 2002
@@ -4,6 +4,8 @@
 
 #include <linux/tqueue.h>
 #include <linux/slab.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/proc_fs.h>
 #include <asm/semaphore.h>
 #include "hosts.h"
 
@@ -96,7 +98,6 @@
         return atomic_read(&host->generation);
 }
 
-
 /*
  * Queue packet for transmitting, return 0 for failure.
  */
@@ -151,5 +152,66 @@
  */
 void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
                           int write_acked);
+
+
+/*
+ * CHARACTER DEVICE DISPATCHING
+ *
+ * All ieee1394 character device drivers share the same major number
+ * (major 171).  The 256 minor numbers are allocated to the various
+ * task-specific interfaces (raw1394, video1394, dv1394, etc) in
+ * blocks of 16.
+ *
+ * The core ieee1394.o modules handles the initial open() for all
+ * character devices on major 171; it then dispatches to the
+ * appropriate task-specific driver.
+ *
+ * Minor device number block allocations:
+ *
+ * Block 0  (  0- 15)  raw1394
+ * Block 1  ( 16- 31)  video1394
+ * Block 2  ( 32- 47)  dv1394
+ *
+ * Blocks 3-14 free for future allocation
+ *
+ * Block 15 (240-255)  reserved for drivers under development, etc.
+ */
+
+#define IEEE1394_MAJOR               171
+
+#define IEEE1394_MINOR_BLOCK_RAW1394       0
+#define IEEE1394_MINOR_BLOCK_VIDEO1394     1
+#define IEEE1394_MINOR_BLOCK_DV1394        2
+#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15
+
+/* return the index (within a minor number block) of a file */
+static inline unsigned char ieee1394_file_to_instance(struct file *file)
+{
+	unsigned char minor = minor(file->f_dentry->d_inode->i_rdev);
+	
+	/* return lower 4 bits */
+	return minor & 0xF;
+}
+
+/* 
+ * Task-specific drivers should call ieee1394_register_chardev() to
+ * request a block of 16 minor numbers.
+ *
+ * Returns 0 if the request was successful, -EBUSY if the block was
+ * already taken.
+ */
+
+int  ieee1394_register_chardev(int blocknum,           /* 0-15 */
+			       struct module *module,  /* THIS_MODULE */
+			       struct file_operations *file_ops);
+
+/* release a block of minor numbers */
+void ieee1394_unregister_chardev(int blocknum);
+
+/* the devfs handle for /dev/ieee1394; NULL if devfs is not in use */
+extern devfs_handle_t ieee1394_devfs_handle;
+
+/* the proc_fs entry for /proc/ieee1394 */
+extern struct proc_dir_entry *ieee1394_procfs_entry;
 
 #endif /* _IEEE1394_CORE_H */
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_syms.c linux/drivers/ieee1394/ieee1394_syms.c
--- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_syms.c	Sun Nov 11 10:09:32 2001
+++ linux/drivers/ieee1394/ieee1394_syms.c	Wed Dec 31 16:00:00 1969
@@ -1,88 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * Exported symbols for module usage.
- *
- * Copyright (C) 1999 Andreas E. Bombe
- *
- * This code is licensed under the GPL.  See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "ieee1394_transactions.h"
-#include "ieee1394_hotplug.h"
-#include "highlevel.h"
-#include "nodemgr.h"
-
-EXPORT_SYMBOL(hpsb_register_lowlevel);
-EXPORT_SYMBOL(hpsb_unregister_lowlevel);
-EXPORT_SYMBOL(hpsb_get_host);
-EXPORT_SYMBOL(hpsb_inc_host_usage);
-EXPORT_SYMBOL(hpsb_dec_host_usage);
-
-EXPORT_SYMBOL(alloc_hpsb_packet);
-EXPORT_SYMBOL(free_hpsb_packet);
-EXPORT_SYMBOL(hpsb_send_packet);
-EXPORT_SYMBOL(hpsb_reset_bus);
-EXPORT_SYMBOL(hpsb_bus_reset);
-EXPORT_SYMBOL(hpsb_selfid_received);
-EXPORT_SYMBOL(hpsb_selfid_complete);
-EXPORT_SYMBOL(hpsb_packet_sent);
-EXPORT_SYMBOL(hpsb_packet_received);
-
-EXPORT_SYMBOL(get_tlabel);
-EXPORT_SYMBOL(free_tlabel);
-EXPORT_SYMBOL(fill_async_readquad);
-EXPORT_SYMBOL(fill_async_readquad_resp);
-EXPORT_SYMBOL(fill_async_readblock);
-EXPORT_SYMBOL(fill_async_readblock_resp);
-EXPORT_SYMBOL(fill_async_writequad);
-EXPORT_SYMBOL(fill_async_writeblock);
-EXPORT_SYMBOL(fill_async_write_resp);
-EXPORT_SYMBOL(fill_async_lock);
-EXPORT_SYMBOL(fill_async_lock_resp);
-EXPORT_SYMBOL(fill_iso_packet);
-EXPORT_SYMBOL(fill_phy_packet);
-EXPORT_SYMBOL(hpsb_make_readqpacket);
-EXPORT_SYMBOL(hpsb_make_readbpacket);
-EXPORT_SYMBOL(hpsb_make_writeqpacket);
-EXPORT_SYMBOL(hpsb_make_writebpacket);
-EXPORT_SYMBOL(hpsb_make_lockpacket);
-EXPORT_SYMBOL(hpsb_make_phypacket);
-EXPORT_SYMBOL(hpsb_packet_success);
-EXPORT_SYMBOL(hpsb_make_packet);
-EXPORT_SYMBOL(hpsb_read);
-EXPORT_SYMBOL(hpsb_write);
-EXPORT_SYMBOL(hpsb_lock);
-
-EXPORT_SYMBOL(hpsb_register_highlevel);
-EXPORT_SYMBOL(hpsb_unregister_highlevel);
-EXPORT_SYMBOL(hpsb_register_addrspace);
-EXPORT_SYMBOL(hpsb_listen_channel);
-EXPORT_SYMBOL(hpsb_unlisten_channel);
-EXPORT_SYMBOL(highlevel_read);
-EXPORT_SYMBOL(highlevel_write);
-EXPORT_SYMBOL(highlevel_lock);
-EXPORT_SYMBOL(highlevel_lock64);
-EXPORT_SYMBOL(highlevel_add_host);
-EXPORT_SYMBOL(highlevel_remove_host);
-EXPORT_SYMBOL(highlevel_host_reset);
-EXPORT_SYMBOL(highlevel_add_one_host);
-
-EXPORT_SYMBOL(hpsb_guid_get_entry);
-EXPORT_SYMBOL(hpsb_nodeid_get_entry);
-EXPORT_SYMBOL(hpsb_get_host_by_ne);
-EXPORT_SYMBOL(hpsb_guid_fill_packet);
-EXPORT_SYMBOL(hpsb_register_protocol);
-EXPORT_SYMBOL(hpsb_unregister_protocol);
-EXPORT_SYMBOL(hpsb_release_unit_directory);
-
-MODULE_LICENSE("GPL");
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_transactions.c linux/drivers/ieee1394/ieee1394_transactions.c
--- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_transactions.c	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/ieee1394_transactions.c	Mon Jan 21 17:23:22 2002
@@ -246,7 +246,7 @@
                                  packet->node_id);
                         return -EAGAIN;
                 }
-                HPSB_PANIC("reached unreachable code 1 in " __FUNCTION__);
+                HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__);
 
         case ACK_BUSY_X:
         case ACK_BUSY_A:
@@ -290,7 +290,7 @@
                 return -EAGAIN;
         }
 
-        HPSB_PANIC("reached unreachable code 2 in " __FUNCTION__);
+        HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__);
 }
 
 
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_types.h linux/drivers/ieee1394/ieee1394_types.h
--- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_types.h	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/ieee1394_types.h	Mon Jan 21 17:23:22 2002
@@ -6,6 +6,7 @@
 #include <linux/types.h>
 #include <linux/version.h>
 #include <linux/list.h>
+#include <linux/init.h>
 #include <asm/byteorder.h>
 
 
@@ -18,6 +19,16 @@
 
 #define INIT_TQ_LINK(tq) INIT_LIST_HEAD(&(tq).list)
 #define INIT_TQ_HEAD(tq) INIT_LIST_HEAD(&(tq))
+#endif
+
+/* The great kdev_t changeover in 2.5.x */
+#include <linux/kdev_t.h>
+#ifndef minor
+#define minor(dev) MINOR(dev)
+#endif
+
+#ifndef __devexit_p
+#define __devexit_p(x) x
 #endif
 
 /* This showed up around this time */
diff -urN linux-2.5.3-pre2/drivers/ieee1394/nodemgr.c linux/drivers/ieee1394/nodemgr.c
--- linux-2.5.3-pre2/drivers/ieee1394/nodemgr.c	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/nodemgr.c	Mon Jan 21 17:23:22 2002
@@ -16,6 +16,10 @@
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
+#include <linux/completion.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
 
 #include "ieee1394_types.h"
 #include "ieee1394.h"
@@ -59,13 +63,200 @@
 
 struct host_info {
 	struct hpsb_host *host;
-	struct tq_struct task;
 	struct list_head list;
+	struct completion started;
+	struct completion exited;
+	wait_queue_head_t wait;
+	int pid;
 };
 
+#ifdef CONFIG_PROC_FS
+
+static int raw1394_read_proc(char *buffer, char **start, off_t offset,
+			     int size, int *eof, void *data )
+{
+	struct list_head *lh;
+	struct node_entry *ne;
+	int disp_size = 0;
+	char display_str[1024];
+
+#define PUTF(fmt, args...) disp_size += sprintf(display_str, fmt, ## args); strcat(buffer,display_str)
+	buffer[0] = '\0';
+	list_for_each(lh, &node_list) {
+		ne = list_entry(lh, struct node_entry, list);
+		if (!ne)
+			continue;
+		PUTF("Node[" NODE_BUS_FMT "]  GUID[%016Lx]:\n",
+		     NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+		if (ne->host != NULL && ne->host->node_id == ne->nodeid) {
+			PUTF("\tNodes connected : %d\n", ne->host->node_count);
+			PUTF("\tSelfIDs received: %d\n", ne->host->selfid_count);
+			PUTF("\tOwn ID          : 0x%08x\n",ne->host->node_id);
+			PUTF("\tIrm ID          : 0x%08x\n",ne->host->irm_id);
+			PUTF("\tBusMgr ID       : 0x%08x\n",ne->host->busmgr_id);
+			PUTF("\tBR IR IC II IB\n");
+			PUTF("\t%02d %02d %02d %02d %02d\n",
+			     ne->host->in_bus_reset, ne->host->is_root,
+			     ne->host->is_cycmst, ne->host->is_irm,
+			     ne->host->is_busmgr);
+		}
+		PUTF("\tVendor ID: %s [0x%06x]\n",
+		     ne->vendor_name ?: "Unknown", ne->vendor_id);
+		PUTF("\tCapabilities: 0x%06x\n", ne->capabilities);
+		PUTF("\tirmc=%d cmc=%d isc=%d bmc=%d pmc=%d cyc_clk_acc=%d max_rec=%d gen=%d lspd=%d\n",
+		     ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
+		     ne->busopt.pmc, ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
+		     ne->busopt.generation, ne->busopt.lnkspd);
+	}
+#undef PUTF
+    return disp_size;
+}
+#endif /* CONFIG_PROC_FS */
+
 static void nodemgr_process_config_rom(struct node_entry *ne, 
 				       quadlet_t busoptions);
 
+static int nodemgr_read_quadlet(struct hpsb_host *host,
+				nodeid_t nodeid, octlet_t address,
+				quadlet_t *quad)
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 3; i++) {
+		ret = hpsb_read(host, nodeid, address, quad, 4);
+		if (ret != -EAGAIN)
+			break;
+	}
+	*quad = be32_to_cpu(*quad);
+
+	return ret;
+}
+
+static int nodemgr_size_text_leaf(struct hpsb_host *host,
+				  nodeid_t nodeid,
+				  octlet_t address)
+{
+	quadlet_t quad;
+	int size = 0;
+	if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+		return -1;
+	if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) {
+		/* This is the offset.  */
+		address += 4 * CONFIG_ROM_VALUE(quad); 
+		if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+			return -1;
+		/* Now we got the size of the text descriptor leaf. */
+		size = CONFIG_ROM_LEAF_LENGTH(quad);
+	}
+	return size;
+}
+
+static int nodemgr_read_text_leaf(struct hpsb_host *host,
+				  nodeid_t nodeid,
+				  octlet_t address,
+				  quadlet_t *quadp)
+{
+	quadlet_t quad;
+	int i, size, ret;
+
+	if (nodemgr_read_quadlet(host, nodeid, address, &quad)
+	    && CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF)
+		return -1;
+
+	/* This is the offset.  */
+	address += 4 * CONFIG_ROM_VALUE(quad);
+	if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+		return -1;
+
+	/* Now we got the size of the text descriptor leaf. */
+	size = CONFIG_ROM_LEAF_LENGTH(quad) - 2;
+	if (size <= 0)
+		return -1;
+
+	address += 4;
+	for (i = 0; i < 2; i++, address += 4, quadp++) {
+		if (nodemgr_read_quadlet(host, nodeid, address, quadp))
+			return -1;
+	}
+
+	/* Now read the text string.  */
+	ret = -ENXIO;
+	for (; size > 0; size--, address += 4, quadp++) {
+		for (i = 0; i < 3; i++) {
+			ret = hpsb_read(host, nodeid, address, quadp, 4);
+			if (ret != -EAGAIN)
+				break;
+		}
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static struct node_entry *nodemgr_scan_root_directory
+	(struct hpsb_host *host, nodeid_t nodeid)
+{
+	octlet_t address;
+	quadlet_t quad;
+	int length;
+	int code, size, total_size;
+	struct node_entry *ne;
+
+	address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
+	
+	if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+		return NULL;
+	address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
+
+	if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+		return NULL;
+	length = CONFIG_ROM_ROOT_LENGTH(quad);
+	address += 4;
+
+	size = 0;
+	total_size = sizeof(struct node_entry);
+	for (; length > 0; length--, address += 4) {
+		if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+			return NULL;
+		code = CONFIG_ROM_KEY(quad);
+
+		if (code == CONFIG_ROM_VENDOR_ID) {
+			/* Check if there is a text descriptor leaf
+			   immediately after this.  */
+			length--;
+			if (length <= 0)
+				break;	
+			address += 4;
+			size = nodemgr_size_text_leaf(host, nodeid,
+						      address);
+			switch (size) {
+			case -1:
+				return NULL;
+				break;
+			case 0:
+				break;
+			default:
+				total_size += (size + 1) * sizeof (quadlet_t);
+				break;
+			}
+			break;
+		}
+	}
+	ne = kmalloc(total_size, SLAB_ATOMIC);
+	if (ne != NULL) {
+		if (size != 0) {
+			ne->vendor_name
+				= (const char *) &(ne->quadlets[2]);
+			ne->quadlets[size] = 0;
+		}
+		else {
+			ne->vendor_name = NULL;
+		}
+	}
+	return ne; 
+}
 
 static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
 					      struct hpsb_host *host, nodeid_t nodeid)
@@ -73,7 +264,7 @@
         struct node_entry *ne;
         unsigned long flags;
 
-        ne = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
+	ne = nodemgr_scan_root_directory (host, nodeid);
         if (!ne) return NULL;
 
         INIT_LIST_HEAD(&ne->list);
@@ -89,9 +280,10 @@
 
 	nodemgr_process_config_rom (ne, busoptions);
 
-	HPSB_DEBUG("%s added: node " NODE_BUS_FMT ", GUID %016Lx",
+	HPSB_DEBUG("%s added: Node[" NODE_BUS_FMT "]  GUID[%016Lx]  [%s]",
 		   (host->node_id == nodeid) ? "Local host" : "Device",
-		   NODE_BUS_ARGS(nodeid), (unsigned long long)guid);
+		   NODE_BUS_ARGS(nodeid), (unsigned long long)guid,
+		   ne->vendor_name ?: "Unknown");
 
         return ne;
 }
@@ -122,30 +314,107 @@
 	return NULL;
 }
 
-int nodemgr_read_quadlet(struct node_entry *ne,
-			 octlet_t address, quadlet_t *quad)
+static struct unit_directory *nodemgr_scan_unit_directory
+	(struct node_entry *ne, octlet_t address)
 {
-	int i;
-	int ret = 0;
+	struct unit_directory *ud;
+	quadlet_t quad;
+	u8 flags, todo;
+	int length, size, total_size, count;
+	int vendor_name_size, model_name_size;
 
-	for (i = 0; i < 3; i++) {
-		ret = hpsb_read(ne->host, ne->nodeid, address, quad, 4);
-		if (ret != -EAGAIN)
+	if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
+		return NULL;
+	length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
+	address += 4;
+
+	size = 0;
+	total_size = sizeof (struct unit_directory);
+	flags = 0;
+	count = 0;
+	vendor_name_size = 0;
+	model_name_size = 0;
+	for (; length > 0; length--, address += 4) {
+		int code;
+		quadlet_t value;
+
+retry:
+		if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
+			return NULL;
+		code = CONFIG_ROM_KEY(quad);
+		value = CONFIG_ROM_VALUE(quad);
+
+		todo = 0;
+		switch (code) {
+		case CONFIG_ROM_VENDOR_ID:
+			todo = UNIT_DIRECTORY_VENDOR_TEXT;
 			break;
-	}
-	*quad = be32_to_cpu(*quad);
 
-	return ret;
-}
+		case CONFIG_ROM_MODEL_ID:
+			todo = UNIT_DIRECTORY_MODEL_TEXT;
+			break;
+
+		case CONFIG_ROM_SPECIFIER_ID:
+		case CONFIG_ROM_UNIT_SW_VERSION:
+			break;
+
+		case CONFIG_ROM_DESCRIPTOR_LEAF:
+		case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
+			/* TODO: read strings... icons? */
+			break;
+
+		default:
+			/* Which types of quadlets do we want to
+			   store?  Only count immediate values and
+			   CSR offsets for now.  */
+			code &= CONFIG_ROM_KEY_TYPE_MASK;
+			if ((code & 0x80) == 0)
+				count++;
+			break;
+		}
 
-#define CONFIG_ROM_VENDOR_ID		0x03
-#define CONFIG_ROM_MODEL_ID		0x17
-#define CONFIG_ROM_NODE_CAPABILITES	0x0C
-#define CONFIG_ROM_UNIT_DIRECTORY	0xd1
-#define CONFIG_ROM_SPECIFIER_ID		0x12 
-#define CONFIG_ROM_VERSION		0x13
-#define CONFIG_ROM_DESCRIPTOR_LEAF	0x81
-#define CONFIG_ROM_DESCRIPTOR_DIRECTORY	0xc1
+		if (todo) {
+			/* Check if there is a text descriptor leaf
+			   immediately after this.  */
+			length--;
+			if (length <= 0)
+				break;
+			address += 4;
+			size = nodemgr_size_text_leaf(ne->host,
+						      ne->nodeid,
+						      address);
+			if (todo | UNIT_DIRECTORY_VENDOR_TEXT)
+				vendor_name_size = size;
+			else
+				model_name_size = size;
+			switch (size) {
+			case -1:
+				return NULL;
+				break;
+			case 0:
+				goto retry;
+				break;
+			default:
+				flags |= todo;
+				total_size += (size + 1) * sizeof (quadlet_t);
+				break;
+			}
+		}
+	}
+	total_size += count * sizeof (quadlet_t);
+	ud = kmalloc (total_size, GFP_KERNEL);
+	if (ud != NULL) {
+		memset (ud, 0, sizeof *ud);
+		ud->flags = flags;
+		ud->count = count;
+		ud->vendor_name_size = vendor_name_size;
+		ud->model_name_size = model_name_size;
+		/* If there is no vendor name in the unit directory,
+		   use the one in the root directory.  */
+		ud->vendor_name = ne->vendor_name;
+	}
+	return ud;
+}
 
 /* This implementation currently only scans the config rom and its
  * immediate unit directories looking for software_id and
@@ -156,41 +425,75 @@
 					   octlet_t address)
 {
 	struct unit_directory *ud;
-	octlet_t a;
 	quadlet_t quad;
-	int length, i;
+	quadlet_t *infop;
+	int length;
 
-	if (!(ud = kmalloc (sizeof *ud, GFP_KERNEL)))
+	if (!(ud = nodemgr_scan_unit_directory(ne, address)))
 		goto unit_directory_error;
 
-	memset (ud, 0, sizeof *ud);
 	ud->ne = ne;
 	ud->address = address;
-	ud->arb_count = 0;
 
-	if (nodemgr_read_quadlet(ne, address, &quad))
+	if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
 		goto unit_directory_error;
-	length = quad >> 16;
-	a = address + 4;
+	length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
+	address += 4;
 
-	for (i = 0; i < length; i++, a += 4) {
+	infop = (quadlet_t *) ud->quadlets;
+	for (; length > 0; length--, address += 4, infop++) {
 		int code;
 		quadlet_t value;
+		quadlet_t *quadp;
 
-		if (nodemgr_read_quadlet(ne, a, &quad))
+		if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
 			goto unit_directory_error;
-		code = quad >> 24;
-		value = quad & 0xffffff;
+		code = CONFIG_ROM_KEY(quad) ;
+		value = CONFIG_ROM_VALUE(quad);
 
 		switch (code) {
 		case CONFIG_ROM_VENDOR_ID:
 			ud->vendor_id = value;
 			ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
+			if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) {
+				length--;
+				address += 4;
+				quadp = &(ud->quadlets[ud->count]);
+				if (nodemgr_read_text_leaf(ne->host,
+							   ne->nodeid,
+							   address,
+							   quadp) == 0
+				    && quadp[0] == 0
+				    && quadp[1] == 0) {
+				    	/* We only support minimal
+					   ASCII and English. */
+					quadp[ud->vendor_name_size] = 0;
+					ud->vendor_name
+						= (const char *) &(quadp[2]);
+				}
+			}
 			break;
 
 		case CONFIG_ROM_MODEL_ID:
 			ud->model_id = value;
 			ud->flags |= UNIT_DIRECTORY_MODEL_ID;
+			if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) {
+				length--;
+				address += 4;
+				quadp = &(ud->quadlets[ud->count + ud->vendor_name_size + 1]);
+				if (nodemgr_read_text_leaf(ne->host,
+							   ne->nodeid,
+							   address,
+							   quadp) == 0
+				    && quadp[0] == 0
+				    && quadp[1] == 0) {
+				    	/* We only support minimal
+					   ASCII and English. */
+					quadp[ud->model_name_size] = 0;
+					ud->model_name
+						= (const char *) &(quadp[2]);
+				}
+			}
 			break;
 
 		case CONFIG_ROM_SPECIFIER_ID:
@@ -198,7 +501,7 @@
 			ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
 			break;
 
-		case CONFIG_ROM_VERSION:
+		case CONFIG_ROM_UNIT_SW_VERSION:
 			ud->version = value;
 			ud->flags |= UNIT_DIRECTORY_VERSION;
 			break;
@@ -209,12 +512,13 @@
 			break;
 
 		default:
-			if (ud->arb_count < 16) {
-				/* Place them in the arbitrary pairs */
-				ud->arb_keys[ud->arb_count] = code;
-				ud->arb_values[ud->arb_count] = value;
-				ud->arb_count++;
-			}
+			/* Which types of quadlets do we want to
+			   store?  Only count immediate values and
+			   CSR offsets for now.  */
+			code &= CONFIG_ROM_KEY_TYPE_MASK;
+			if ((code & 0x80) == 0)
+				*infop = quad;
+			break;
 		}
 	}
 
@@ -233,53 +537,73 @@
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
 	struct list_head *l;
 
-	HPSB_DEBUG("vendor_id=0x%06x, capabilities=0x%06x",
-		   ne->vendor_id, ne->capabilities);
+	HPSB_DEBUG("vendor_id=0x%06x [%s], capabilities=0x%06x",
+		   ne->vendor_id, ne->vendor_name ?: "Unknown",
+		   ne->capabilities);
 	list_for_each (l, &ne->unit_directories) {
 		struct unit_directory *ud = list_entry (l, struct unit_directory, node_list);
 		HPSB_DEBUG("unit directory:");
 		if (ud->flags & UNIT_DIRECTORY_VENDOR_ID)
-			HPSB_DEBUG("  vendor_id=0x%06x ", ud->vendor_id);
+			HPSB_DEBUG("  vendor_id=0x%06x [%s]",
+				   ud->vendor_id,
+				   ud->vendor_name ?: "Unknown");
 		if (ud->flags & UNIT_DIRECTORY_MODEL_ID)
-			HPSB_DEBUG("  model_id=0x%06x ", ud->model_id);
+			HPSB_DEBUG("  model_id=0x%06x [%s]",
+				   ud->model_id,
+				   ud->model_name ?: "Unknown");
 		if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
 			HPSB_DEBUG("  sw_specifier_id=0x%06x ", ud->specifier_id);
 		if (ud->flags & UNIT_DIRECTORY_VERSION)
 			HPSB_DEBUG("  sw_version=0x%06x ", ud->version);
 	}
-#else
-	return;
 #endif
+	return;
 }
 
 static void nodemgr_process_root_directory(struct node_entry *ne)
 {
 	octlet_t address;
 	quadlet_t quad;
-	int length, i;
+	int length;
 
 	address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
 	
-	if (nodemgr_read_quadlet(ne, address, &quad))
+	if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
 		return;
-	address += 4 + (quad >> 24) * 4;
+	address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
 
-	if (nodemgr_read_quadlet(ne, address, &quad))
+	if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
 		return;
-	length = quad >> 16;
+	length = CONFIG_ROM_ROOT_LENGTH(quad);
 	address += 4;
 
-	for (i = 0; i < length; i++, address += 4) {
+	for (; length > 0; length--, address += 4) {
 		int code, value;
 
-		if (nodemgr_read_quadlet(ne, address, &quad))
+		if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
 			return;
-		code = quad >> 24;
-		value = quad & 0xffffff;
+		code = CONFIG_ROM_KEY(quad);
+		value = CONFIG_ROM_VALUE(quad);
 
 		switch (code) {
 		case CONFIG_ROM_VENDOR_ID:
 			ne->vendor_id = value;
+			/* Now check if there is a vendor name text
+			   string.  */
+			if (ne->vendor_name != NULL) {
+				length--;
+				address += 4;
+				if (nodemgr_read_text_leaf(ne->host,
+							   ne->nodeid,
+							   address,
+							   ne->quadlets)
+				    != 0
+				    || ne->quadlets [0] != 0
+				    || ne->quadlets [1] != 0)
+				    	/* We only support minimal
+					   ASCII and English. */
+					ne->vendor_name = NULL;
+			}
 			break;
 
 		case CONFIG_ROM_NODE_CAPABILITES:
@@ -359,7 +683,7 @@
 	kfree(buf);
 	kfree(envp);
 	if (value != 0)
-		HPSB_DEBUG("NodeMgr: hotplug policy returned 0x%x", value);
+		HPSB_DEBUG("NodeMgr: hotplug policy returned %d", value);
 }
 
 #else
@@ -369,9 +693,8 @@
 {
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
 	HPSB_DEBUG("NodeMgr: nodemgr_call_policy(): hotplug not enabled");
-#else
-	return;
 #endif
+	return;
 } 
 
 #endif /* CONFIG_HOTPLUG */
@@ -582,13 +905,13 @@
                                struct hpsb_host *host, nodeid_t nodeid)
 {
 	struct list_head *lh;
+	struct unit_directory *ud;
 
-	if (ne->nodeid != nodeid)
+	if (ne->nodeid != nodeid) {
 		HPSB_DEBUG("Node " NODE_BUS_FMT " changed to " NODE_BUS_FMT,
 			   NODE_BUS_ARGS(ne->nodeid), NODE_BUS_ARGS(nodeid));
-
-	ne->host = host;
-	ne->nodeid = nodeid;
+		ne->nodeid = nodeid;
+	}
 
 	if (ne->busopt.generation != ((busoptions >> 4) & 0xf))
 		nodemgr_process_config_rom (ne, busoptions);
@@ -597,8 +920,6 @@
 	atomic_set(&ne->generation, get_hpsb_generation(ne->host));
 
 	list_for_each (lh, &ne->unit_directories) {
-		struct unit_directory *ud;
-
 		ud = list_entry (lh, struct unit_directory, node_list);
 		if (ud->driver != NULL && ud->driver->update != NULL)
 			ud->driver->update(ud);
@@ -613,6 +934,7 @@
 	int header_count;
 	unsigned header_size;
 	quadlet_t quad;
+	int ret;
 
 retry_configrom:
 
@@ -636,9 +958,10 @@
 	 * to work though, and we are forced to doing quadlet
 	 * sized reads.  */
 
-	if (hpsb_read(host, nodeid, base, &quad, 4)) {
-		HPSB_ERR("ConfigROM quadlet transaction error for node " NODE_BUS_FMT,
-			 NODE_BUS_ARGS(nodeid));
+	ret = hpsb_read(host, nodeid, base, &quad, 4);
+	if (ret) {
+		HPSB_ERR("ConfigROM quadlet transaction error (%d) for node " NODE_BUS_FMT,
+			 ret, NODE_BUS_ARGS(nodeid));
 		goto retry_configrom;
 	}
 	buffer[header_count++] = be32_to_cpu(quad);
@@ -652,9 +975,10 @@
 	}
 
 	while (header_count <= header_size && header_count < buffer_length) {
-		if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) {
-			HPSB_ERR("ConfigROM quadlet transaction error for " NODE_BUS_FMT,
-				 NODE_BUS_ARGS(nodeid));
+		ret = hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4);
+		if (ret) {
+			HPSB_ERR("ConfigROM quadlet transaction error (%d) for " NODE_BUS_FMT,
+				 ret, NODE_BUS_ARGS(nodeid));
 			goto retry_configrom;
 		}
 		buffer[header_count++] = be32_to_cpu(quad);
@@ -667,8 +991,10 @@
 {
 	unsigned long flags;
 
-	HPSB_DEBUG("Device removed: node " NODE_BUS_FMT ", GUID %016Lx",
-		   NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+	HPSB_DEBUG("%s removed: Node[" NODE_BUS_FMT "]  GUID[%016Lx]  [%s]",
+		   (ne->host->node_id == ne->nodeid) ? "Local host" : "Device",
+		   NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid,
+		   ne->vendor_name ?: "Unknown");
 
 	write_lock_irqsave(&unit_directory_lock, flags);
 	nodemgr_free_unit_directories(ne);
@@ -681,52 +1007,47 @@
 
 /* This is where we probe the nodes for their information and provided
  * features.  */
-static void nodemgr_node_probe(void *data)
+static void nodemgr_node_probe_one(struct hpsb_host *host, nodeid_t nodeid)
 {
-	struct hpsb_host *host = (struct hpsb_host *)data;
-        struct selfid *sid = (struct selfid *)host->topology_map;
-	struct list_head *lh, *next;
 	struct node_entry *ne;
-        int nodecount = host->node_count;
-        nodeid_t nodeid = LOCAL_BUS;
 	quadlet_t buffer[5];
 	octlet_t guid;
-	unsigned long flags;
 
 	/* We need to detect when the ConfigROM's generation has changed,
 	 * so we only update the node's info when it needs to be.  */
-        for (; nodecount; nodecount--, nodeid++, sid++) {
-		/* Skip extended, and non-active node's */
-                while (sid->extended)
-			sid++;
-                if (!sid->link_active)
-			continue;
 
-		if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2))
-			continue;
+	if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2))
+		return;
 
-		if (buffer[1] != IEEE1394_BUSID_MAGIC) {
-			/* This isn't a 1394 device */
-			HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device",
-				 NODE_BUS_ARGS(nodeid));
-			continue;
-		}
+	if (buffer[1] != IEEE1394_BUSID_MAGIC) {
+		/* This isn't a 1394 device */
+		HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device",
+			 NODE_BUS_ARGS(nodeid));
+		return;
+	}
 
-		guid = ((u64)buffer[3] << 32) | buffer[4];
-		ne = hpsb_guid_get_entry(guid);
+	guid = ((u64)buffer[3] << 32) | buffer[4];
+	ne = hpsb_guid_get_entry(guid);
 
-		if (!ne)
-			nodemgr_create_node(guid, buffer[2], host, nodeid);
-		else
-			nodemgr_update_node(ne, buffer[2], host, nodeid);
-        }
+	if (!ne)
+		nodemgr_create_node(guid, buffer[2], host, nodeid);
+	else
+		nodemgr_update_node(ne, buffer[2], host, nodeid);
+
+	return;
+}
+
+static void nodemgr_node_probe_cleanup(struct hpsb_host *host)
+{
+	unsigned long flags;
+	struct list_head *lh, *next;
+	struct node_entry *ne;
 
 	/* Now check to see if we have any nodes that aren't referenced
 	 * any longer.  */
-        write_lock_irqsave(&node_lock, flags);
-	for (lh = node_list.next; lh != &node_list; lh = next) {
+	write_lock_irqsave(&node_lock, flags);
+	list_for_each_safe(lh, next, &node_list) {
 		ne = list_entry(lh, struct node_entry, list);
-		next = lh->next;
 
 		/* Only checking this host */
 		if (ne->host != host)
@@ -744,6 +1065,56 @@
 	return;
 }
 
+static void nodemgr_node_probe(struct hpsb_host *host)
+{
+	int nodecount = host->node_count;
+	struct selfid *sid = (struct selfid *)host->topology_map;
+	nodeid_t nodeid = LOCAL_BUS;
+
+	/* Scan each node on the bus */
+	for (; nodecount; nodecount--, nodeid++, sid++) {
+		while (sid->extended)
+			sid++;
+		if (!sid->link_active)
+			continue;
+
+		nodemgr_node_probe_one(host, nodeid);
+	}
+
+	/* Cleanup if needed */
+	nodemgr_node_probe_cleanup(host);
+
+	return;
+}
+
+static int nodemgr_host_thread(void *__hi)
+{
+	struct host_info *hi = (struct host_info *)__hi;
+	lock_kernel();
+
+	/* No userlevel access needed */
+	daemonize();
+
+	strcpy(current->comm, "NodeMgr");
+
+	complete(&hi->started);
+
+	/* Sit and wait for a signal to probe the nodes on the bus. This
+	 * happens when we get a bus reset.  */
+	do {
+		interruptible_sleep_on(&hi->wait);
+		if (!signal_pending(current))
+			nodemgr_node_probe(hi->host);
+	} while (!signal_pending(current));
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name);
+#endif
+
+	unlock_kernel();
+
+	complete_and_exit(&hi->exited, 0);
+}
 
 struct node_entry *hpsb_guid_get_entry(u64 guid)
 {
@@ -802,7 +1173,18 @@
 	 * until the first bus reset.  */
 	hi->host = host;
 	INIT_LIST_HEAD(&hi->list);
-	INIT_TQUEUE(&hi->task, nodemgr_node_probe, host);
+	init_completion(&hi->started);
+	init_completion(&hi->exited);
+	init_waitqueue_head(&hi->wait);
+
+	hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+	if (hi->pid < 0) {
+		HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s", host->driver->name);
+		return;
+	}
+
+	wait_for_completion(&hi->started);
 
 	spin_lock_irqsave (&host_info_lock, flags);
 	list_add_tail (&hi->list, &host_info_list);
@@ -831,7 +1213,11 @@
 		goto done_reset_host;
 	}
 
-	schedule_task(&hi->task);
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	HPSB_DEBUG ("NodeMgr: Processing host reset for %s", host->driver->name);
+#endif
+
+	wake_up(&hi->wait);
 
 done_reset_host:
 	spin_unlock_irqrestore (&host_info_lock, flags);
@@ -844,40 +1230,42 @@
 	struct list_head *lh, *next;
 	struct node_entry *ne;
 	unsigned long flags;
+	struct host_info *hi = NULL;
 
-	/* Make sure we have no active scans */
-	flush_scheduled_tasks();
+	spin_lock_irqsave (&host_info_lock, flags);
+	list_for_each_safe(lh, next, &host_info_list) {
+		struct host_info *myhi = list_entry(lh, struct host_info, list);
+		if (myhi->host == host) {
+			list_del(&myhi->list);
+			hi = myhi;
+			break;
+		}
+	}
 
-	/* First remove all node entries for this host */
-	write_lock_irqsave(&node_lock, flags);
+	if (!hi)
+		HPSB_ERR ("NodeMgr: host %s does not exist, cannot remove",
+			  host->driver->name);
+	spin_unlock_irqrestore (&host_info_lock, flags);
 
-	for (lh = node_list.next; lh != &node_list; lh = next) {
+	/* Even if we fail the host_info part, remove all the node
+	 * entries.  */
+	write_lock_irqsave(&node_lock, flags);
+	list_for_each_safe(lh, next, &node_list) {
 		ne = list_entry(lh, struct node_entry, list);
-		next = lh->next;
-
-		/* Only checking this host */
-		if (ne->host != host)
-			continue;
 
+		if (ne->host == host)
 		nodemgr_remove_node(ne);
 	}
 	write_unlock_irqrestore(&node_lock, flags);
 
-	spin_lock_irqsave (&host_info_lock, flags);
-	list_for_each(lh, &host_info_list) {
-		struct host_info *hi = list_entry(lh, struct host_info, list);
-		if (hi->host == host) {
-			list_del(&hi->list);
-			kfree (hi);
-			break;
+	if (hi) {
+		if (hi->pid >= 0) {
+			kill_proc(hi->pid, SIGTERM, 1);
+			wait_for_completion(&hi->exited);
 		}
+		kfree(hi);
 	}
 
-	if (lh == host_info_list.next)
-		HPSB_ERR ("NodeMgr: could not remove non-existent host");
-
-	spin_unlock_irqrestore (&host_info_lock, flags);
-
 	return;
 }
 
@@ -889,8 +1277,14 @@
 
 static struct hpsb_highlevel *hl;
 
+#define PROC_ENTRY "devices"
+
 void init_ieee1394_nodemgr(void)
 {
+#ifdef CONFIG_PROC_FS
+	if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL))
+		HPSB_ERR("Can't create devices procfs entry");
+#endif
         hl = hpsb_register_highlevel("Node manager", &nodemgr_ops);
         if (!hl) {
 		HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization");
@@ -900,4 +1294,7 @@
 void cleanup_ieee1394_nodemgr(void)
 {
         hpsb_unregister_highlevel(hl);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry);
+#endif
 }
diff -urN linux-2.5.3-pre2/drivers/ieee1394/nodemgr.h linux/drivers/ieee1394/nodemgr.h
--- linux-2.5.3-pre2/drivers/ieee1394/nodemgr.h	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/nodemgr.h	Mon Jan 21 17:23:22 2002
@@ -20,6 +20,44 @@
 #ifndef _IEEE1394_NODEMGR_H
 #define _IEEE1394_NODEMGR_H
 
+#define CONFIG_ROM_BUS_INFO_LENGTH(q)		((q) >> 24)
+#define CONFIG_ROM_BUS_CRC_LENGTH(q)		(((q) >> 16) & 0xff)
+#define CONFIG_ROM_BUS_CRC(q)			((q) & 0xffff)
+
+#define CONFIG_ROM_ROOT_LENGTH(q)		((q) >> 16)
+#define CONFIG_ROM_ROOT_CRC(q)			((q) & 0xffff)
+
+#define CONFIG_ROM_DIRECTORY_LENGTH(q)		((q) >> 16)
+#define CONFIG_ROM_DIRECTORY_CRC(q)		((q) & 0xffff)
+
+#define CONFIG_ROM_LEAF_LENGTH(q)		((q) >> 16)
+#define CONFIG_ROM_LEAF_CRC(q)			((q) & 0xffff)
+
+#define CONFIG_ROM_DESCRIPTOR_TYPE(q)		((q) >> 24)
+#define CONFIG_ROM_DESCRIPTOR_SPECIFIER_ID(q)	((q) & 0xffffff)
+#define CONFIG_ROM_DESCRIPTOR_WIDTH(q)		((q) >> 28)
+#define CONFIG_ROM_DESCRIPTOR_CHAR_SET(q)	(((q) >> 16) & 0xfff)
+#define CONFIG_ROM_DESCRIPTOR_LANG(q)		((q) & 0xffff)
+
+#define CONFIG_ROM_KEY_ID_MASK			0x3f
+#define CONFIG_ROM_KEY_TYPE_MASK		0xc0
+#define CONFIG_ROM_KEY_TYPE_IMMEDIATE		0x00
+#define CONFIG_ROM_KEY_TYPE_OFFSET		0x40
+#define CONFIG_ROM_KEY_TYPE_LEAF		0x80
+#define CONFIG_ROM_KEY_TYPE_DIRECTORY		0xc0
+
+#define CONFIG_ROM_KEY(q)			((q) >> 24)
+#define CONFIG_ROM_VALUE(q)			((q) & 0xffffff)
+
+#define CONFIG_ROM_VENDOR_ID			0x03
+#define CONFIG_ROM_MODEL_ID			0x17
+#define CONFIG_ROM_NODE_CAPABILITES		0x0C
+#define CONFIG_ROM_UNIT_DIRECTORY		0xd1
+#define CONFIG_ROM_SPECIFIER_ID			0x12 
+#define CONFIG_ROM_UNIT_SW_VERSION		0x13
+#define CONFIG_ROM_DESCRIPTOR_LEAF		0x81
+#define CONFIG_ROM_DESCRIPTOR_DIRECTORY		0xc1
+
 /* '1' '3' '9' '4' in ASCII */
 #define IEEE1394_BUSID_MAGIC	0x31333934
 
@@ -42,6 +80,8 @@
 #define UNIT_DIRECTORY_MODEL_ID		0x02
 #define UNIT_DIRECTORY_SPECIFIER_ID	0x04
 #define UNIT_DIRECTORY_VERSION		0x08
+#define UNIT_DIRECTORY_VENDOR_TEXT	0x10
+#define UNIT_DIRECTORY_MODEL_TEXT	0x20
 
 /*
  * A unit directory corresponds to a protocol supported by the
@@ -58,17 +98,14 @@
 	octlet_t address;	/* Address of the unit directory on the node */
 	u8 flags;		/* Indicates which entries were read */
 	quadlet_t vendor_id;
-	char *vendor_name;
+	const char *vendor_name;
+	int vendor_name_size;
 	quadlet_t model_id;
-	char *model_name;
+	const char *model_name;
+	int model_name_size;
 	quadlet_t specifier_id;
 	quadlet_t version;
 
-	/* Groupings for arbitrary key/value pairs */
-	int arb_count;			/* Number of arbitrary key/values */
-	char arb_keys[16];		/* Up to 16 keys */
-	quadlet_t arb_values[16];	/* Same for values */
-
 	struct hpsb_protocol_driver *driver;
 	void *driver_data;
 
@@ -77,6 +114,9 @@
 
 	/* For linking directories belonging to a node */
 	struct list_head node_list;
+
+	int count;		/* Number of quadlets */
+	quadlet_t quadlets[0];
 };
 
 struct node_entry {
@@ -91,6 +131,9 @@
 	u32 vendor_id;
 	u32 capabilities;	
 	struct list_head unit_directories;
+
+	const char *vendor_name;
+	quadlet_t quadlets[0];
 };
 
 static inline int hpsb_node_entry_valid(struct node_entry *ne)
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ohci1394.c linux/drivers/ieee1394/ohci1394.c
--- linux-2.5.3-pre2/drivers/ieee1394/ohci1394.c	Sun Dec 30 10:31:51 2001
+++ linux/drivers/ieee1394/ohci1394.c	Mon Jan 21 17:23:22 2002
@@ -77,6 +77,12 @@
  *  . Config ROM generation
  */
 
+/* Issues:
+ *
+ * - devctl BUS_RESET should treat arg as reset type
+ *
+ */
+
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -105,16 +111,12 @@
 #include <linux/init.h>
 
 #ifdef CONFIG_ALL_PPC
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 #endif
 
-/* Revert to old bus reset algorithm that works on my Pismo until
- * the new one is fixed
- */
-#undef BUSRESET_WORKAROUND
-
 #include "ieee1394.h"
 #include "ieee1394_types.h"
 #include "hosts.h"
@@ -139,10 +141,10 @@
 
 #ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG
 #define OHCI_DMA_ALLOC(fmt, args...) \
-	HPSB_ERR("%s("__FUNCTION__")alloc(%d): "fmt, OHCI1394_DRIVER_NAME, \
+	HPSB_ERR("%s(%s)alloc(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
 		++global_outstanding_dmas, ## args)
 #define OHCI_DMA_FREE(fmt, args...) \
-	HPSB_ERR("%s("__FUNCTION__")free(%d): "fmt, OHCI1394_DRIVER_NAME, \
+	HPSB_ERR("%s(%s)free(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
 		--global_outstanding_dmas, ## args)
 u32 global_outstanding_dmas = 0;
 #else
@@ -158,35 +160,23 @@
 #define PRINT(level, card, fmt, args...) \
 printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
 
-#define PCI_CLASS_FIREWIRE_OHCI     ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
-
-static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
-	{
-		class: 		PCI_CLASS_FIREWIRE_OHCI,
-		class_mask: 	0x00ffffff,
-		vendor:		PCI_ANY_ID,
-		device:		PCI_ANY_ID,
-		subvendor:	PCI_ANY_ID,
-		subdevice:	PCI_ANY_ID,
-	},
-	{ 0, },
-};
-MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
-
 static char version[] __devinitdata =
-	"v0.51 08/08/01 Ben Collins <bcollins@debian.org>";
+	"$Revision: 1.91 $ Ben Collins <bcollins@debian.org>";
 
 /* Module Parameters */
 MODULE_PARM(attempt_root,"i");
 MODULE_PARM_DESC(attempt_root, "Attempt to make the host root.");
 static int attempt_root = 0;
 
-static unsigned int card_id_counter = 0;
-
 static void dma_trm_tasklet(unsigned long data);
-static void remove_card(struct ti_ohci *ohci);
 static void dma_trm_reset(struct dma_trm_ctx *d);
 
+static void __devexit ohci1394_pci_remove(struct pci_dev *pdev);
+
+static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
+					  quadlet_t isoRecvEvent, 
+					  quadlet_t isoXmitEvent);
+
 #ifndef __LITTLE_ENDIAN
 /* Swap a series of quads inplace. */
 static __inline__ void block_swab32(quadlet_t *data, size_t size) {
@@ -314,8 +304,8 @@
 	if ((self_id_count & 0x80000000) || 
 	    ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) {
 		PRINT(KERN_ERR, ohci->id, 
-		      "Error in reception of SelfID packets [0x%08x/0x%08x]",
-		      self_id_count, q0);
+		      "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)",
+		      self_id_count, q0, ohci->self_id_errors);
 
 		/* Tip by James Goodwin <jamesg@Filanet.com>:
 		 * We had an error, generate another bus reset in response.  */
@@ -328,6 +318,9 @@
 		}
 		return;
 	}
+
+	/* SelfID Ok, reset error counter. */
+	ohci->self_id_errors = 0;
 	
 	size = ((self_id_count & 0x00001FFC) >> 2) - 1;
 	q++;
@@ -361,7 +354,7 @@
 	return;
 }
 
-static int ohci_soft_reset(struct ti_ohci *ohci) {
+static void ohci_soft_reset(struct ti_ohci *ohci) {
 	int i;
 
 	reg_write(ohci, OHCI1394_HCControlSet, 0x00010000);
@@ -372,9 +365,13 @@
 		mdelay(1);
 	}
 
-	DBGMSG (ohci->id, "Soft reset finished");
+	/* Now reenable LPS, since that's usually what we want after a
+	 * softreset anyway. Wait 50msec to make sure we have full link
+	 * enabled.  */
+	reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
+	mdelay(50);
 
-	return 0;
+	DBGMSG (ohci->id, "Soft reset finished");
 }
 
 static int run_context(struct ti_ohci *ohci, int reg, char *msg)
@@ -415,8 +412,7 @@
 	for (i=0; i<d->num_desc; i++) {
 		u32 c;
 		
-		c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
-			DMA_CTL_BRANCH;
+		c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
 		if (generate_irq)
 			c |= DMA_CTL_IRQ;
 				
@@ -489,19 +485,14 @@
 static void ohci_init_config_rom(struct ti_ohci *ohci);
 
 /* Global initialization */
-static int ohci_initialize(struct hpsb_host *host)
+static void ohci_initialize(struct ti_ohci *ohci)
 {
-	struct ti_ohci *ohci=host->hostdata;
-	int retval, i;
+	int i;
 	quadlet_t buf;
 
 	spin_lock_init(&ohci->phy_reg_lock);
 	spin_lock_init(&ohci->event_lock);
   
-	/* Soft reset */
-	if ((retval = ohci_soft_reset(ohci)) < 0)
-		return retval;
-
 	/* Put some defaults to these undefined bus options */
 	buf = reg_read(ohci, OHCI1394_BusOptions);
 	buf |=  0x60000000; /* Enable CMC and ISC */
@@ -509,19 +500,6 @@
 	buf &= ~0x98000000; /* Disable PMC, IRMC and BMC */
 	reg_write(ohci, OHCI1394_BusOptions, buf);
 
-	/* Set Link Power Status (LPS) */
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
-
-	/* After enabling LPS, we need to wait for the connection
-	 * between phy and link to be established.  This should be
-	 * signaled by the LPS bit becoming 1, but this happens
-	 * immediately.  Instead we wait for reads from LinkControl to
-	 * give a valid result, i.e. not 0xffffffff. */
-	while (reg_read(ohci, OHCI1394_LinkControlSet) == 0xffffffff) {
-		DBGMSG(ohci->id, "waiting for phy-link connection");
-		mdelay(2);
-	}
-
 	/* Set the bus number */
 	reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
 
@@ -558,10 +536,6 @@
 	reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
 
 	/* Initialize IR dma */
-	ohci->nb_iso_rcv_ctx = 
-		get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
-	DBGMSG(ohci->id, "%d iso receive contexts available",
-	       ohci->nb_iso_rcv_ctx);
 	for (i=0;i<ohci->nb_iso_rcv_ctx;i++) {
 		reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i,
 			  0xffffffff);
@@ -580,10 +554,6 @@
 	reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
 
 	/* Initialize IT dma */
-	ohci->nb_iso_xmit_ctx = 
-		get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
-	DBGMSG(ohci->id, "%d iso transmit contexts available",
-	       ohci->nb_iso_xmit_ctx);
 	for (i=0;i<ohci->nb_iso_xmit_ctx;i++) {
 		reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i,
 			  0xffffffff);
@@ -645,32 +615,19 @@
 		  OHCI1394_respTxComplete |
 		  OHCI1394_reqTxComplete |
 		  OHCI1394_isochRx |
-		  OHCI1394_isochTx |
-		  OHCI1394_unrecoverableError
-		);
+		  OHCI1394_isochTx);
 
 	/* Enable link */
 	reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
 
 	buf = reg_read(ohci, OHCI1394_Version);
-	PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d]  MMIO=[%lx-%lx]"
-	      "  Max Packet=[%d]", ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
+	PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d]  "
+	      "MMIO=[%lx-%lx]  Max Packet=[%d]",
+	      ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
 	      ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
 	      pci_resource_start(ohci->dev, 0),
 	      pci_resource_start(ohci->dev, 0) + pci_resource_len(ohci->dev, 0),
 	      ohci->max_packet_size);
-
-	return 1;
-}
-
-static void ohci_remove(struct hpsb_host *host)
-{
-	struct ti_ohci *ohci;
-
-	if (host != NULL) {
-		ohci = host->hostdata;
-		remove_card(ohci);
-	}
 }
 
 /* 
@@ -692,7 +649,7 @@
 	d->prg_cpu[idx]->begin.address = 0;
 	d->prg_cpu[idx]->begin.branchAddress = 0;
 
-	if (d->ctx==1) {
+	if (d->type == DMA_CTX_ASYNC_RESP) {
 		/* 
 		 * For response packets, we need to put a timeout value in
 		 * the 16 lower bits of the status... let's try 1 sec timeout 
@@ -741,9 +698,9 @@
                         if (cross_bound((unsigned long)packet->data, 
                                         packet->data_size)>0) {
                                 /* FIXME: do something about it */
-                                PRINT(KERN_ERR, ohci->id, __FUNCTION__
-                                      ": packet data addr: %p size %Zd bytes "
-                                      "cross page boundary", 
+                                PRINT(KERN_ERR, ohci->id,
+                                      "%s: packet data addr: %p size %Zd bytes "
+                                      "cross page boundary", __FUNCTION__,
                                       packet->data, packet->data_size);
                         }
 
@@ -933,10 +890,8 @@
 	switch (cmd) {
 	case RESET_BUS:
 		DBGMSG(ohci->id, "devctl: Bus reset requested%s",
-		       ((host->attempt_root || attempt_root) ? 
-		       " and attempting to become root" : ""));
-		set_phy_reg_mask (ohci, 1, 0x40 | ((host->attempt_root || attempt_root) ?
-				  0x80 : 0));
+		       attempt_root ? " and attempting to become root" : "");
+		set_phy_reg_mask (ohci, 1, 0x40 | (attempt_root ? 0x80 : 0));
 		break;
 
 	case GET_CYCLE_COUNTER:
@@ -988,9 +943,9 @@
 		u64 mask;
 
 		if (arg<0 || arg>63) {
-			PRINT(KERN_ERR, ohci->id, __FUNCTION__
-			      "IS0 listen channel %d is out of range", 
-			      arg);
+			PRINT(KERN_ERR, ohci->id,
+			      "%s: IS0 listen channel %d is out of range", 
+			      __FUNCTION__, arg);
 			return -EFAULT;
 		}
 
@@ -999,9 +954,9 @@
                 spin_lock_irqsave(&ohci->IR_channel_lock, flags);
 
 		if (ohci->ISO_channel_usage & mask) {
-			PRINT(KERN_ERR, ohci->id, __FUNCTION__
-			      "IS0 listen channel %d is already used", 
-			      arg);
+			PRINT(KERN_ERR, ohci->id,
+			      "%s: IS0 listen channel %d is already used", 
+			      __FUNCTION__, arg);
 			spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
 			return -EFAULT;
 		}
@@ -1024,9 +979,9 @@
 		u64 mask;
 
 		if (arg<0 || arg>63) {
-			PRINT(KERN_ERR, ohci->id, __FUNCTION__
-			      "IS0 unlisten channel %d is out of range", 
-			      arg);
+			PRINT(KERN_ERR, ohci->id,
+			      "%s: IS0 unlisten channel %d is out of range", 
+			      __FUNCTION__, arg);
 			return -EFAULT;
 		}
 
@@ -1035,9 +990,9 @@
                 spin_lock_irqsave(&ohci->IR_channel_lock, flags);
 
 		if (!(ohci->ISO_channel_usage & mask)) {
-			PRINT(KERN_ERR, ohci->id, __FUNCTION__
-			      "IS0 unlisten channel %d is not used", 
-			      arg);
+			PRINT(KERN_ERR, ohci->id,
+			      "%s: IS0 unlisten channel %d is not used", 
+			      __FUNCTION__, arg);
 			spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
 			return -EFAULT;
 		}
@@ -1130,11 +1085,7 @@
 	 * selfIDComplete interrupt. */
 	spin_lock_irqsave(&ohci->event_lock, flags);
 	event = reg_read(ohci, OHCI1394_IntEventClear);
-#ifdef BUSRESET_WORKAROUND
-	reg_write(ohci, OHCI1394_IntEventClear, event);
-#else
 	reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset);
-#endif
 	spin_unlock_irqrestore(&ohci->event_lock, flags);
 
 	if (!event) return;
@@ -1144,7 +1095,6 @@
 	/* Die right here an now */
 	if (event & OHCI1394_unrecoverableError) {
 		PRINT(KERN_ERR, ohci->id, "Unrecoverable error, shutting down card!");
-		remove_card(ohci);
 		return;
 	}
 
@@ -1153,16 +1103,22 @@
 		 * selfID phase, so we disable busReset interrupts, to
 		 * avoid burying the cpu in interrupt requests. */
 		spin_lock_irqsave(&ohci->event_lock, flags);
-#ifdef BUSRESET_WORKAROUND
-		reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
-#else
   		reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
-#endif		
+		if (ohci->dev->vendor == PCI_VENDOR_ID_APPLE && 
+		    ohci->dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
+  			udelay(10);
+  			while(reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+  				reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+				spin_unlock_irqrestore(&ohci->event_lock, flags);
+	  			udelay(10);
+				spin_lock_irqsave(&ohci->event_lock, flags);
+  			}
+  		}
 		spin_unlock_irqrestore(&ohci->event_lock, flags);
 		if (!host->in_bus_reset) {
 			DBGMSG(ohci->id, "irq_handler: Bus reset requested%s",
-			      ((host->attempt_root || attempt_root) ?
-			      " and attempting to become root" : ""));
+			      (attempt_root) ? " and attempting to become root"
+			       : "");
 
 			/* Subsystem call */
 			hpsb_bus_reset(ohci->host);
@@ -1233,9 +1189,9 @@
 			else
 				tasklet_schedule(&d->task);
 		}
-		if (ohci->video_tmpl) 
-			ohci->video_tmpl->irq_handler(ohci->id, isoRecvIntEvent,
-						      0);
+
+		ohci1394_run_irq_hooks(ohci, isoRecvIntEvent, 0);
+
 		event &= ~OHCI1394_isochRx;
 	}
 	if (event & OHCI1394_isochTx) {
@@ -1248,9 +1204,9 @@
                        DBGMSG(ohci->id, "Got isochTx interrupt "
                                "status=0x%08x isoXmitIntEvent=%08x",
                               reg_read(ohci, d->ctrlSet), isoXmitIntEvent);
-		if (ohci->video_tmpl) 
-			ohci->video_tmpl->irq_handler(ohci->id, 0,
-						      isoXmitIntEvent);
+
+		ohci1394_run_irq_hooks(ohci, 0, isoXmitIntEvent);
+		
 		if (isoXmitIntEvent & 0x1) {
 			if (reg_read(ohci, d->ctrlSet) & 0x800)
 				ohci1394_stop_context(ohci, d->ctrlClear, "isochTx");
@@ -1308,12 +1264,10 @@
 
 		/* Finally, we clear the busReset event and reenable
 		 * the busReset interrupt. */
-#ifndef BUSRESET_WORKAROUND
 		spin_lock_irqsave(&ohci->event_lock, flags);
 		reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); 
 		reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
 		spin_unlock_irqrestore(&ohci->event_lock, flags);
-#endif
 		event &= ~OHCI1394_selfIDComplete;	
 	}
 
@@ -1359,7 +1313,7 @@
 {
 	int length = -1;
 
-	if (d->ctx < 2) { /* Async Receive Response/Request */
+	if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) {
 		length = TCODE_SIZE[tcode];
 		if (length == 0) {
 			if (offset + 12 >= d->buf_size) {
@@ -1370,7 +1324,7 @@
 			}
 			length += 20;
 		}
-	} else if (d->ctx == 2) { /* Iso receive */
+	} else if (d->type == DMA_CTX_ISO) {
 		/* Assumption: buffer fill mode with header/trailer */
 		length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8;
 	}
@@ -1400,11 +1354,8 @@
 	offset = d->buf_offset;
 	buf_ptr = d->buf_cpu[idx] + offset/4;
 
-	dma_cache_wback_inv(&(d->prg_cpu[idx]->status), sizeof(d->prg_cpu[idx]->status));
 	rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
-
 	bytes_left = d->buf_size - rescount - offset;
-	dma_cache_wback_inv(buf_ptr, bytes_left);
 
 	while (bytes_left > 0) {
 		tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf;
@@ -1456,7 +1407,6 @@
 			insert_dma_buffer(d, idx);
 			idx = (idx+1) % d->num_desc;
 			buf_ptr = d->buf_cpu[idx];
-			dma_cache_wback_inv(buf_ptr, d->buf_size);
 			offset=0;
 
 			while (split_left >= d->buf_size) {
@@ -1466,7 +1416,6 @@
 				insert_dma_buffer(d, idx);
 				idx = (idx+1) % d->num_desc;
 				buf_ptr = d->buf_cpu[idx];
-                                dma_cache_wback_inv(buf_ptr, d->buf_size);
 			}
 
 			if (split_left > 0) {
@@ -1513,8 +1462,6 @@
 			       d->ctx);
 #endif
 
-                dma_cache_wback_inv(&(d->prg_cpu[idx]->status),
-                        sizeof(d->prg_cpu[idx]->status));
 	       	rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
 
 		bytes_left = d->buf_size - rescount - offset;
@@ -1653,6 +1600,11 @@
 	}
 	if ((*d)->spb) kfree((*d)->spb);
 
+	/* clear ISO context usage bit */
+	if ((*d)->type == DMA_CTX_ISO) {
+		clear_bit((*d)->ctx, &ohci->ir_ctx_usage);
+	}
+	       
 	kfree(*d);
 	*d = NULL;
 
@@ -1660,15 +1612,21 @@
 }
 
 static struct dma_rcv_ctx *
-alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
-		  int buf_size, int split_buf_size, 
-		  int ctrlSet, int ctrlClear, int cmdPtr)
+alloc_dma_rcv_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
+		  int buf_size, int split_buf_size, int context_base)
 {
-	struct dma_rcv_ctx *d=NULL;
+	struct dma_rcv_ctx *d;
 	int i;
 
-	d = (struct dma_rcv_ctx *)kmalloc(sizeof(struct dma_rcv_ctx), 
-					  GFP_KERNEL);
+	if (type == DMA_CTX_ISO) {
+		/* try to claim the ISO context usage bit */
+		if (test_and_set_bit(ctx, &ohci->ir_ctx_usage)) {
+			PRINT(KERN_ERR, ohci->id, "IR DMA context %d is not available", ctx);
+			return NULL;
+		}
+	}
+	
+	d = kmalloc(sizeof(struct dma_rcv_ctx), GFP_KERNEL);
 
 	if (d == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_rcv_ctx");
@@ -1677,21 +1635,17 @@
 
 	memset (d, 0, sizeof (struct dma_rcv_ctx));
 
-	d->ohci = (void *)ohci;
+	d->ohci = ohci;
+	d->type = type;
 	d->ctx = ctx;
 
 	d->num_desc = num_desc;
 	d->buf_size = buf_size;
 	d->split_buf_size = split_buf_size;
-	d->ctrlSet = ctrlSet;
-	d->ctrlClear = ctrlClear;
-	d->cmdPtr = cmdPtr;
-
-	d->buf_cpu = NULL;
-	d->buf_bus = NULL;
-	d->prg_cpu = NULL;
-	d->prg_bus = NULL;
-	d->spb = NULL;
+
+	d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+	d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+	d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
 
 	d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL);
 	d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL);
@@ -1790,36 +1744,47 @@
 		kfree((*d)->prg_bus);
 	}
 
+	/* clear the ISO context usage bit */
+	if ((*d)->type == DMA_CTX_ISO) {
+		clear_bit((*d)->ctx, &ohci->it_ctx_usage);
+	}
+
 	kfree(*d);
 	*d = NULL;
 	return 0;
 }
 
 static struct dma_trm_ctx *
-alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
-		  int ctrlSet, int ctrlClear, int cmdPtr)
+alloc_dma_trm_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
+		  int context_base)
 {
-	struct dma_trm_ctx *d=NULL;
+	struct dma_trm_ctx *d;
 	int i;
 
-	d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx), 
-					  GFP_KERNEL);
+	if (type == DMA_CTX_ISO) {
+		/* try to claim the ISO context usage bit */
+		if (test_and_set_bit(ctx, &ohci->it_ctx_usage)) {
+			PRINT(KERN_ERR, ohci->id, "IT DMA context %d is not available", ctx);
+			return NULL;
+		}
+	}
+	
+	d = kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL);
 
-	if (d==NULL) {
+	if (d == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_trm_ctx");
 		return NULL;
 	}
 
 	memset (d, 0, sizeof (struct dma_trm_ctx));
 
-	d->ohci = (void *)ohci;
+	d->ohci = ohci;
+	d->type = type;
 	d->ctx = ctx;
 	d->num_desc = num_desc;
-	d->ctrlSet = ctrlSet;
-	d->ctrlClear = ctrlClear;
-	d->cmdPtr = cmdPtr;
-	d->prg_cpu = NULL;
-	d->prg_bus = NULL;
+	d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+	d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+	d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
 
 	d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), 
 			     GFP_KERNEL);
@@ -1833,7 +1798,7 @@
 	memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*));
 	memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t));
 
-	for (i=0; i<d->num_desc; i++) {
+	for (i = 0; i < d->num_desc; i++) {
                 d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, 
 						     sizeof(struct at_dma_prg),
 						     d->prg_bus+i);
@@ -1896,6 +1861,21 @@
 
 #define cf_put_keyval(cr, key, val) (((cr)->data++)[0] = cpu_to_be32(((key) << 24) | (val)))
 
+static inline void cf_put_str(struct config_rom_ptr *cr, const char *str)
+{
+	int t;
+	char fourb[4];
+
+	while (str[0]) {
+		memset(fourb, 0, 4);
+		for (t = 0; t < 4 && str[t]; t++)
+			fourb[t] = str[t];
+		cf_put_4bytes(cr, fourb[0], fourb[1], fourb[2], fourb[3]);
+		str += strlen(str) < 4 ? strlen(str) : 4;
+	}
+	return;
+}
+
 static inline void cf_put_crc16(struct config_rom_ptr *cr, int unit)
 {
 	*cr->unitdir[unit].start =
@@ -1965,8 +1945,6 @@
 	cf_put_keyval(&cr, 0x03, 0x00005e);	/* Vendor ID */
 	cf_put_refer(&cr, 0x81, 2);		/* Textual description unit */
 	cf_put_keyval(&cr, 0x0c, 0x0083c0);	/* Node capabilities */
-	cf_put_refer(&cr, 0xd1, 3);		/* IPv4 unit directory */
-	cf_put_refer(&cr, 0xd1, 4);		/* IPv6 unit directory */
 	/* NOTE: Add other unit referers here, and append at bottom */
 	cf_unit_end(&cr);
 
@@ -1974,49 +1952,7 @@
 	cf_unit_begin(&cr, 2);
 	cf_put_keyval(&cr, 0, 0);
 	cf_put_1quad(&cr, 0);
-	cf_put_4bytes(&cr, 'L', 'i', 'n', 'u');
-	cf_put_4bytes(&cr, 'x', ' ', '1', '3');
-	cf_put_4bytes(&cr, '9', '4', 0x0, 0x0);
-	cf_unit_end(&cr);
-
-	/* IPv4 unit directory, RFC 2734 */
-	cf_unit_begin(&cr, 3);
-	cf_put_keyval(&cr, 0x12, 0x00005e);	/* Unit spec ID */
-	cf_put_refer(&cr, 0x81, 6);		/* Textual description unit */
-	cf_put_keyval(&cr, 0x13, 0x000001);	/* Unit software version */
-	cf_put_refer(&cr, 0x81, 7);		/* Textual description unit */
-	cf_unit_end(&cr);
-
-	cf_unit_begin(&cr, 6);
-	cf_put_keyval(&cr, 0, 0);
-	cf_put_1quad(&cr, 0);
-	cf_put_4bytes(&cr, 'I', 'A', 'N', 'A');
-	cf_unit_end(&cr);
-
-	cf_unit_begin(&cr, 7);
-	cf_put_keyval(&cr, 0, 0);
-	cf_put_1quad(&cr, 0);
-	cf_put_4bytes(&cr, 'I', 'P', 'v', '4');
-	cf_unit_end(&cr);
-
-	/* IPv6 unit directory, draft-ietf-ipngwg-1394-01.txt */
-	cf_unit_begin(&cr, 4);
-	cf_put_keyval(&cr, 0x12, 0x00005e);	/* Unit spec ID */
-	cf_put_refer(&cr, 0x81, 8);		/* Textual description unit */
-	cf_put_keyval(&cr, 0x13, 0x000002);	/* (Proposed) Unit software version */
-	cf_put_refer(&cr, 0x81, 9);		/* Textual description unit */
-	cf_unit_end(&cr);
-
-	cf_unit_begin(&cr, 8);
-	cf_put_keyval(&cr, 0, 0);
-	cf_put_1quad(&cr, 0);
-	cf_put_4bytes(&cr, 'I', 'A', 'N', 'A');
-	cf_unit_end(&cr);
-
-	cf_unit_begin(&cr, 9);
-	cf_put_keyval(&cr, 0, 0);
-	cf_put_1quad(&cr, 0);
-	cf_put_4bytes(&cr, 'I', 'P', 'v', '6');
+	cf_put_str(&cr, "Linux OHCI-1394");
 	cf_unit_end(&cr);
 
 	ohci->csr_config_rom_length = cr.data - ohci->csr_config_rom_cpu;
@@ -2034,13 +1970,15 @@
 	return ohci->csr_config_rom_length * 4;
 }
 
-int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data,
-                      quadlet_t compare, int sel)
+static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
+                                 quadlet_t data, quadlet_t compare)
 {
+	struct ti_ohci *ohci = host->hostdata;
 	int i;
-	reg_write(ohci, OHCI1394_CSRData, *data);
+
+	reg_write(ohci, OHCI1394_CSRData, data);
 	reg_write(ohci, OHCI1394_CSRCompareData, compare);
-	reg_write(ohci, OHCI1394_CSRControl, sel & 0x3);
+	reg_write(ohci, OHCI1394_CSRControl, reg & 0x3);
 
 	for (i = 0; i < OHCI_LOOP_COUNT; i++) {
 		if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
@@ -2049,68 +1987,58 @@
 		mdelay(1);
 	}
 
-	*data = reg_read(ohci, OHCI1394_CSRData);
-	return 0;
+	return reg_read(ohci, OHCI1394_CSRData);
 }
 
-static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
-                                 quadlet_t data, quadlet_t compare)
-{
-	struct ti_ohci *ohci=host->hostdata;
-
-	ohci_compare_swap (ohci, &data, compare, reg);
-
-	return data;
-}
-
-static struct hpsb_host_template ohci_template = {
-	name:			OHCI1394_DRIVER_NAME,
-	initialize_host:	ohci_initialize,
-	release_host:		ohci_remove,
+static struct hpsb_host_operations ohci1394_ops = {
 	get_rom:		ohci_get_rom,
 	transmit_packet:	ohci_transmit,
 	devctl:			ohci_devctl,
 	hw_csr_reg:		ohci_hw_csr_reg,
 };
 
+static struct hpsb_host_driver *ohci1394_driver;
+
+
+
+/***********************************
+ * PCI Driver Interface functions  *
+ ***********************************/
 
-#define FAIL(fmt, args...)			\
+#define FAIL(err, fmt, args...)			\
 do {						\
 	PRINT_G(KERN_ERR, fmt , ## args);	\
-	remove_card(ohci);			\
-	return 1;				\
+        ohci1394_pci_remove(dev);               \
+	return err;				\
 } while(0)
 
-static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_device_id *ent)
+static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
+					const struct pci_device_id *ent)
 {
-	struct ti_ohci *ohci;	/* shortcut to currently handled device */
-	struct hpsb_host *host;
-	unsigned long ohci_base, ohci_len;
+	static unsigned int card_id_counter = 0;
 	static int version_printed = 0;
 
+	struct hpsb_host *host;
+	struct ti_ohci *ohci;	/* shortcut to currently handled device */
+	unsigned long ohci_base, ohci_len;
+	int i;
+	
 	if (version_printed++ == 0)
 		PRINT_G(KERN_INFO, "%s", version);
 
-        if (pci_enable_device(dev)) {
-		/* Skip ID's that fail */
-		PRINT_G(KERN_NOTICE, "Failed to enable OHCI hardware %d",
-			card_id_counter++);
-		return -ENXIO;
-        }
+        if (pci_enable_device(dev))
+		FAIL(-ENXIO, "Failed to enable OHCI hardware %d",
+		        card_id_counter++);
         pci_set_master(dev);
 
-	host = hpsb_get_host(&ohci_template, sizeof (struct ti_ohci));
-	if (!host) {
-		PRINT_G(KERN_ERR, "Out of memory trying to allocate host structure");
-		return -ENOMEM;
-	}
+	host = hpsb_alloc_host(ohci1394_driver, sizeof(struct ti_ohci));
+	if (!host) FAIL(-ENOMEM, "Failed to allocate host structure");
+
 	ohci = host->hostdata;
-	ohci->host = host;
-	INIT_LIST_HEAD(&ohci->list);
 	ohci->id = card_id_counter++;
 	ohci->dev = dev;
-	host->pdev = dev;
 	ohci->host = host;
+	host->pdev = dev;
 	pci_set_drvdata(dev, ohci);
 
 	/* We don't want hardware swapping */
@@ -2138,8 +2066,54 @@
 				     &ohci->csr_config_rom_bus);
 	OHCI_DMA_ALLOC("consistent csr_config_rom");
 	if (ohci->csr_config_rom_cpu == NULL)
-		FAIL("Failed to allocate buffer config rom");
+		FAIL(-ENOMEM, "Failed to allocate buffer config rom");
+
+
+	ohci_base = pci_resource_start(dev, 0);
+	ohci_len = pci_resource_len(dev, 0);
+
+	if (!request_mem_region (ohci_base, ohci_len, OHCI1394_DRIVER_NAME))
+		FAIL(-ENOMEM, "MMIO resource (0x%lx@0x%lx) unavailable, aborting.",
+		     ohci_base, ohci_len);
+
+	ohci->registers = ioremap(ohci_base, ohci_len);
+
+	if (ohci->registers == NULL)
+		FAIL(-ENXIO, "Failed to remap registers - card not accessible");
+
+	DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p", ohci->registers);
+
+
+	/* Start off with a softreset, to clear everything to a sane
+	 * state.  This will also set Link Power State (LPS), which we
+	 * need in order to start accessing most of the registers.  */
+	ohci_soft_reset(ohci);
+
+	/* determinte the number of available IR and IT contexts right away,
+	   because they need to be known for alloc_dma_*_ctx() */
+	ohci->nb_iso_rcv_ctx = 
+		get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
+	DBGMSG(ohci->id, "%d iso receive contexts available",
+	       ohci->nb_iso_rcv_ctx);
+
+	ohci->ir_ctx_usage = 0;
+	
+	/* set the usage bits for non-existent contexts so they can't be allocated */
+	for(i = ohci->nb_iso_rcv_ctx; i < sizeof(ohci->ir_ctx_usage)*8; i++)
+		__set_bit(i, &ohci->ir_ctx_usage);
+	
+	ohci->nb_iso_xmit_ctx = 
+		get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
+	DBGMSG(ohci->id, "%d iso transmit contexts available",
+	       ohci->nb_iso_xmit_ctx);
+
+	ohci->it_ctx_usage = 0;
+	
+	/* set the usage bits for non-existent contexts so they can't be allocated */
+	for(i = ohci->nb_iso_xmit_ctx; i < sizeof(ohci->it_ctx_usage)*8; i++)
+		__set_bit(i, &ohci->it_ctx_usage);
 
+	
 	/* 
 	 * self-id dma buffer allocation
 	 */
@@ -2148,103 +2122,101 @@
                       &ohci->selfid_buf_bus);
 	OHCI_DMA_ALLOC("consistent selfid_buf");
 	if (ohci->selfid_buf_cpu == NULL)
-		FAIL("Failed to allocate DMA buffer for self-id packets");
+		FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets");
 
 	if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff)
 		PRINT(KERN_INFO, ohci->id, "SelfID buffer %p is not aligned on "
 		      "8Kb boundary... may cause problems on some CXD3222 chip", 
 		      ohci->selfid_buf_cpu);  
 
-	ohci->it_context =
-		alloc_dma_trm_ctx(ohci, 2, IT_NUM_DESC,
-				  OHCI1394_IsoXmitContextControlSet,
-				  OHCI1394_IsoXmitContextControlClear,
-				  OHCI1394_IsoXmitCommandPtr);
-
-	if (ohci->it_context == NULL)
-		FAIL("Failed to allocate IT context");
-
-	ohci_base = pci_resource_start(dev, 0);
-	ohci_len = pci_resource_len(dev, 0);
-
-	if (!request_mem_region (ohci_base, ohci_len, host->template->name))
-		FAIL("MMIO resource (0x%lx@0x%lx) unavailable, aborting.",
-		     ohci_base, ohci_len);
-
-	ohci->registers = ioremap(ohci_base, ohci_len);
-
-	if (ohci->registers == NULL)
-		FAIL("Failed to remap registers - card not accessible");
-
-	DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p",
-	      ohci->registers);
+	/* No self-id errors at startup */
+	ohci->self_id_errors = 0;
 
+	/* AR DMA request context allocation */
 	ohci->ar_req_context = 
-		alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC,
+		alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
 				  AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
-				  OHCI1394_AsReqRcvContextControlSet,
-				  OHCI1394_AsReqRcvContextControlClear,
-				  OHCI1394_AsReqRcvCommandPtr);
+				  OHCI1394_AsReqRcvContextBase);
 
 	if (ohci->ar_req_context == NULL)
-		FAIL("Failed to allocate AR Req context");
+		FAIL(-ENOMEM, "Failed to allocate AR Req context");
 
+	/* AR DMA response context allocation */
 	ohci->ar_resp_context = 
-		alloc_dma_rcv_ctx(ohci, 1, AR_RESP_NUM_DESC,
+		alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
 				  AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
-				  OHCI1394_AsRspRcvContextControlSet,
-				  OHCI1394_AsRspRcvContextControlClear,
-				  OHCI1394_AsRspRcvCommandPtr);
+				  OHCI1394_AsRspRcvContextBase);
 	
 	if (ohci->ar_resp_context == NULL)
-		FAIL("Failed to allocate AR Resp context");
+		FAIL(-ENOMEM, "Failed to allocate AR Resp context");
 
+	/* AT DMA request context */
 	ohci->at_req_context = 
-		alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC,
-				  OHCI1394_AsReqTrContextControlSet,
-				  OHCI1394_AsReqTrContextControlClear,
-				  OHCI1394_AsReqTrCommandPtr);
+		alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
+				  OHCI1394_AsReqTrContextBase);
 	
 	if (ohci->at_req_context == NULL)
-		FAIL("Failed to allocate AT Req context");
+		FAIL(-ENOMEM, "Failed to allocate AT Req context");
 
+	/* AT DMA response context */
 	ohci->at_resp_context = 
-		alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC,
-				  OHCI1394_AsRspTrContextControlSet,
-				  OHCI1394_AsRspTrContextControlClear,
-				  OHCI1394_AsRspTrCommandPtr);
+		alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
+				  OHCI1394_AsRspTrContextBase);
 	
 	if (ohci->at_resp_context == NULL)
-		FAIL("Failed to allocate AT Resp context");
+		FAIL(-ENOMEM, "Failed to allocate AT Resp context");
 
+	/* IR DMA context */
 	ohci->ir_context =
-		alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC,
+		alloc_dma_rcv_ctx(ohci, DMA_CTX_ISO, 0, IR_NUM_DESC,
 				  IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
-				  OHCI1394_IsoRcvContextControlSet,
-				  OHCI1394_IsoRcvContextControlClear,
-				  OHCI1394_IsoRcvCommandPtr);
+				  OHCI1394_IsoRcvContextBase);
 
 	if (ohci->ir_context == NULL)
-		FAIL("Failed to allocate IR context");
+		FAIL(-ENOMEM, "Failed to allocate IR context");
+
+	
+	/* IT DMA context allocation */
+	ohci->it_context =
+		alloc_dma_trm_ctx(ohci, DMA_CTX_ISO, 0, IT_NUM_DESC,
+				  OHCI1394_IsoXmitContextBase);
+
+	if (ohci->it_context == NULL)
+		FAIL(-ENOMEM, "Failed to allocate IT context");
 
 	ohci->ISO_channel_usage = 0;
         spin_lock_init(&ohci->IR_channel_lock);
 
+	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+		ohci->irq_hooks[i].irq_handler = NULL;
+		ohci->irq_hooks[i].data = NULL;
+	}
+	
 	if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
 			 OHCI1394_DRIVER_NAME, ohci))
-		FAIL("Failed to allocate shared interrupt %d", dev->irq);
+		FAIL(-ENOMEM, "Failed to allocate shared interrupt %d", dev->irq);
+
+	ohci_initialize(ohci);
 
 	/* Tell the highlevel this host is ready */
-	highlevel_add_one_host (host);
+	hpsb_add_host(host);
 
 	return 0;
 #undef FAIL
 }
 
-static void remove_card(struct ti_ohci *ohci)
+static void __devexit ohci1394_pci_remove(struct pci_dev *pdev)
 {
+	struct ti_ohci *ohci;
 	quadlet_t buf;
 
+	ohci = pci_get_drvdata(pdev);
+	if (!ohci)
+		return;
+
+	if (ohci->host)
+		hpsb_remove_host(ohci->host);
+
 	/* Soft reset before we start */
 	ohci_soft_reset(ohci);
 
@@ -2308,15 +2280,48 @@
 
 		of_node = pci_device_to_OF_node(ohci->dev);
 		if (of_node) {
-			feature_set_firewire_power(of_node, 0);
-			feature_set_firewire_cable_power(of_node, 0);
+			pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0);
+			pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, of_node, 0, 0);
 		}
 	}
 #endif /* CONFIG_ALL_PPC */
 
 	pci_set_drvdata(ohci->dev, NULL);
+	kfree(ohci);
 }
 
+#define PCI_CLASS_FIREWIRE_OHCI     ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
+
+static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
+	{
+		class: 		PCI_CLASS_FIREWIRE_OHCI,
+		class_mask: 	0x00ffffff,
+		vendor:		PCI_ANY_ID,
+		device:		PCI_ANY_ID,
+		subvendor:	PCI_ANY_ID,
+		subdevice:	PCI_ANY_ID,
+	},
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
+
+static struct pci_driver ohci1394_pci_driver = {
+	name:		OHCI1394_DRIVER_NAME,
+	id_table:	ohci1394_pci_tbl,
+	probe:		ohci1394_pci_probe,
+	remove:		__devexit_p(ohci1394_pci_remove),
+};
+
+
+
+/***********************************
+ * OHCI1394 Video Interface        *
+ ***********************************/
+
+/* essentially the only purpose of this code is to allow another
+   module to hook into ohci's interrupt handler */
+
 void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
 {
 	int i=0;
@@ -2336,111 +2341,96 @@
 	if (msg) PRINT(KERN_ERR, ohci->id, "%s: dma prg stopped", msg);
 }
 
-int ohci1394_register_video(struct ti_ohci *ohci,
-			    struct video_template *tmpl)
+static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
+					  quadlet_t isoRecvEvent, 
+					  quadlet_t isoXmitEvent)
 {
-	if (ohci->video_tmpl)
-		return -ENFILE;
-	ohci->video_tmpl = tmpl;
-	MOD_INC_USE_COUNT;
-	return 0;
+	int i;
+	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+		if(ohci->irq_hooks[i].irq_handler != NULL) {
+			ohci->irq_hooks[i].irq_handler(ohci->id, isoRecvEvent, isoXmitEvent,
+						       ohci->irq_hooks[i].data);
+		}
+	}
 }
-	
-void ohci1394_unregister_video(struct ti_ohci *ohci,
-			       struct video_template *tmpl)
+
+int ohci1394_hook_irq(struct ti_ohci *ohci,
+		      void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+		      void *data)
 {
-	if (ohci->video_tmpl != tmpl) {
-		PRINT(KERN_ERR, ohci->id, 
-		      "Trying to unregister wrong video device");
-	} else {
-		ohci->video_tmpl = NULL;
-		MOD_DEC_USE_COUNT;
+	int i;
+	
+	/* find a free slot */
+	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+		if(ohci->irq_hooks[i].irq_handler == NULL)
+			break;
 	}
+
+	if(i >= OHCI1394_MAX_IRQ_HOOKS)
+		return -EBUSY;
+
+	ohci->irq_hooks[i].irq_handler = irq_handler;
+	ohci->irq_hooks[i].data = data;
+
+	/* ohci1394 will never be unloaded while an IRQ hook is
+	   in use, because the user must reference this symbol */
+	
+	return 0;
 }
 
-#if 0
-int ohci1394_request_channel(struct ti_ohci *ohci, int channel)
+void ohci1394_unhook_irq(struct ti_ohci *ohci,
+			 void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+			 void *data)
 {
-	int csrSel;
-	quadlet_t chan, data1=0, data2=0;
-	int timeout = 32;
-
-	if (channel<32) {
-		chan = 1<<channel;
-		csrSel = 2;
-	}
-	else {
-		chan = 1<<(channel-32);
-		csrSel = 3;
-	}
-	if (ohci_compare_swap(ohci, &data1, 0, csrSel)<0) {
-		PRINT(KERN_INFO, ohci->id, "request_channel timeout");
-		return -1;
+	int i;
+	
+	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+		if( (ohci->irq_hooks[i].irq_handler == irq_handler) &&
+		    (ohci->irq_hooks[i].data == data) )
+			break;
 	}
-	while (timeout--) {
-		if (data1 & chan) {
-			PRINT(KERN_INFO, ohci->id, 
-			      "request channel %d failed", channel);
-			return -1;
-		}
-		data2 = data1;
-		data1 |= chan;
-		if (ohci_compare_swap(ohci, &data1, data2, csrSel)<0) {
-			PRINT(KERN_INFO, ohci->id, "request_channel timeout");
-			return -1;
-		}
-		if (data1==data2) {
-			PRINT(KERN_INFO, ohci->id, 
-			      "request channel %d succeded", channel);
-			return 0;
-		}
+	
+	if(i < OHCI1394_MAX_IRQ_HOOKS) {
+		ohci->irq_hooks[i].irq_handler = NULL;
+		ohci->irq_hooks[i].data = NULL;
 	}
-	PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel);
-	return -1;
 }
-#endif
 
 EXPORT_SYMBOL(ohci1394_stop_context);
-EXPORT_SYMBOL(ohci1394_register_video);
-EXPORT_SYMBOL(ohci1394_unregister_video);
+EXPORT_SYMBOL(ohci1394_hook_irq);
+EXPORT_SYMBOL(ohci1394_unhook_irq);
 
-MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
-MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
-MODULE_LICENSE("GPL");
 
-static void __devexit ohci1394_remove_one(struct pci_dev *pdev)
-{
-	struct ti_ohci *ohci = pci_get_drvdata(pdev);
 
-	if (ohci) {
-		remove_card (ohci);
-		pci_set_drvdata(pdev, NULL);
-	}
-}
+/***********************************
+ * General module initialization   *
+ ***********************************/
 
-static struct pci_driver ohci1394_driver = {
-	name:		OHCI1394_DRIVER_NAME,
-	id_table:	ohci1394_pci_tbl,
-	probe:		ohci1394_add_one,
-	remove:		ohci1394_remove_one,
-};
+MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
+MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
+MODULE_LICENSE("GPL");
 
 static void __exit ohci1394_cleanup (void)
 {
-	hpsb_unregister_lowlevel(&ohci_template);
-	pci_unregister_driver(&ohci1394_driver);
+	pci_unregister_driver(&ohci1394_pci_driver);
+	hpsb_unregister_lowlevel(ohci1394_driver);
 }
 
 static int __init ohci1394_init(void)
 {
 	int ret;
-	if (hpsb_register_lowlevel(&ohci_template)) {
-		PRINT_G(KERN_ERR, "Registering failed");
-		return -ENXIO;
-	}
-	if ((ret = pci_module_init(&ohci1394_driver))) {
-		PRINT_G(KERN_ERR, "PCI module init failed");
-		hpsb_unregister_lowlevel(&ohci_template);
+
+	ohci1394_driver = hpsb_register_lowlevel(&ohci1394_ops,
+						 OHCI1394_DRIVER_NAME);
+	if (!ohci1394_driver) {
+		PRINT_G(KERN_ERR, "hpsb_register_lowlevel failed");
+		return -ENOMEM;
+	}
+
+	ret = pci_module_init(&ohci1394_pci_driver);
+	if (ret < 0) {
+		PRINT_G(KERN_ERR, "pci_module_init failed");
+		hpsb_unregister_lowlevel(ohci1394_driver);
 		return ret;
 	}
 	return ret;
diff -urN linux-2.5.3-pre2/drivers/ieee1394/ohci1394.h linux/drivers/ieee1394/ohci1394.h
--- linux-2.5.3-pre2/drivers/ieee1394/ohci1394.h	Mon Oct  1 21:24:24 2001
+++ linux/drivers/ieee1394/ohci1394.h	Mon Jan 21 17:23:22 2002
@@ -76,9 +76,13 @@
 	quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */
 };
 
+/* identify whether a DMA context is asynchronous or isochronous */
+enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO };
+
 /* DMA receive context */
 struct dma_rcv_ctx {
-	void *ohci;
+	struct ti_ohci *ohci;
+	enum context_type type;
 	int ctx;
 	unsigned int num_desc;
 
@@ -105,7 +109,8 @@
 
 /* DMA transmit context */	
 struct dma_trm_ctx {
-	void *ohci;
+	struct ti_ohci *ohci;
+	enum context_type type;
 	int ctx;
 	unsigned int num_desc;
 
@@ -133,18 +138,9 @@
 	int cmdPtr;
 };
 
-/* video device template */
-struct video_template {
-	void (*irq_handler) (int card, quadlet_t isoRecvEvent, 
-			     quadlet_t isoXmitEvent);
-};
-
-
 struct ti_ohci {
         int id; /* sequential card number */
 
-	struct list_head list;
-
         struct pci_dev *dev;
 
         u32 state;
@@ -175,11 +171,13 @@
 	struct dma_rcv_ctx *ir_context;
         spinlock_t IR_channel_lock;
 	int nb_iso_rcv_ctx;
-
+	unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */
+	
         /* iso transmit */
 	struct dma_trm_ctx *it_context;
 	int nb_iso_xmit_ctx;
-
+	unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */
+	
         u64 ISO_channel_usage;
 
         /* IEEE-1394 part follows */
@@ -192,8 +190,15 @@
 
 	int self_id_errors;
 
-	/* video device */
-	struct video_template *video_tmpl;
+	/* IRQ hooks, for video1394 and dv1394 */
+	
+#define OHCI1394_MAX_IRQ_HOOKS 4
+	
+	struct ohci1394_irq_hook {
+		void (*irq_handler) (int card, quadlet_t isoRecvEvent, 
+				     quadlet_t isoXmitEvent, void *data);
+		void *data;
+	} irq_hooks[OHCI1394_MAX_IRQ_HOOKS];
 
 	/* Swap the selfid buffer? */
 	unsigned int selfid_swap:1;
@@ -230,6 +235,12 @@
 /* 2 KiloBytes of register space */
 #define OHCI1394_REGISTER_SIZE                0x800       
 
+/* Offsets relative to context bases defined below */
+
+#define OHCI1394_ContextControlSet            0x000
+#define OHCI1394_ContextControlClear          0x004
+#define OHCI1394_ContextCommandPtr            0x00C
+
 /* register map */
 #define OHCI1394_Version                      0x000
 #define OHCI1394_GUID_ROM                     0x004
@@ -281,27 +292,37 @@
 #define OHCI1394_PhyReqFilterLoSet            0x118
 #define OHCI1394_PhyReqFilterLoClear          0x11C
 #define OHCI1394_PhyUpperBound                0x120
+
+#define OHCI1394_AsReqTrContextBase           0x180
 #define OHCI1394_AsReqTrContextControlSet     0x180
 #define OHCI1394_AsReqTrContextControlClear   0x184
 #define OHCI1394_AsReqTrCommandPtr            0x18C
+
+#define OHCI1394_AsRspTrContextBase           0x1A0
 #define OHCI1394_AsRspTrContextControlSet     0x1A0
 #define OHCI1394_AsRspTrContextControlClear   0x1A4
 #define OHCI1394_AsRspTrCommandPtr            0x1AC
+
+#define OHCI1394_AsReqRcvContextBase          0x1C0
 #define OHCI1394_AsReqRcvContextControlSet    0x1C0
 #define OHCI1394_AsReqRcvContextControlClear  0x1C4
 #define OHCI1394_AsReqRcvCommandPtr           0x1CC
+
+#define OHCI1394_AsRspRcvContextBase          0x1E0
 #define OHCI1394_AsRspRcvContextControlSet    0x1E0
 #define OHCI1394_AsRspRcvContextControlClear  0x1E4
 #define OHCI1394_AsRspRcvCommandPtr           0x1EC
 
 /* Isochronous transmit registers */
 /* Add (32 * n) for context n */
+#define OHCI1394_IsoXmitContextBase           0x200
 #define OHCI1394_IsoXmitContextControlSet     0x200
 #define OHCI1394_IsoXmitContextControlClear   0x204
 #define OHCI1394_IsoXmitCommandPtr            0x20C
 
 /* Isochronous receive registers */
 /* Add (32 * n) for context n */
+#define OHCI1394_IsoRcvContextBase            0x400
 #define OHCI1394_IsoRcvContextControlSet      0x400
 #define OHCI1394_IsoRcvContextControlClear    0x404
 #define OHCI1394_IsoRcvCommandPtr             0x40C
@@ -346,10 +367,13 @@
 
 void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg);
 struct ti_ohci *ohci1394_get_struct(int card_num);
-int ohci1394_register_video(struct ti_ohci *ohci,
-			    struct video_template *tmpl);
-void ohci1394_unregister_video(struct ti_ohci *ohci,
-			       struct video_template *tmpl);
 
+int ohci1394_hook_irq(struct ti_ohci *ohci,
+		      void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+		      void *data);
+
+void ohci1394_unhook_irq(struct ti_ohci *ohci,
+			 void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+			 void *data);
 #endif
 
diff -urN linux-2.5.3-pre2/drivers/ieee1394/pcilynx.c linux/drivers/ieee1394/pcilynx.c
--- linux-2.5.3-pre2/drivers/ieee1394/pcilynx.c	Sun Nov 11 10:20:21 2001
+++ linux/drivers/ieee1394/pcilynx.c	Mon Jan 21 17:23:22 2002
@@ -42,10 +42,6 @@
 #include "pcilynx.h"
 
 
-#if MAX_PCILYNX_CARDS > PCILYNX_MINOR_ROM_START
-#error Max number of cards is bigger than PCILYNX_MINOR_ROM_START - this does not work.
-#endif
-
 /* print general (card independent) information */
 #define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
 /* print card specific information */
@@ -60,9 +56,8 @@
 #endif
 
 
-static struct ti_lynx cards[MAX_PCILYNX_CARDS];
-static int num_of_cards = 0;
-static struct hpsb_host_template lynx_template;
+static struct hpsb_host_driver *lynx_driver;
+static unsigned int card_id;
 
 /*
  * PCL handling functions.
@@ -143,10 +138,6 @@
 #endif
 
 
-static int add_card(struct pci_dev *dev, const struct pci_device_id *devid);
-static void remove_card(struct pci_dev *dev);
-
-
 
 /***********************************
  * IEEE-1394 functionality section *
@@ -161,8 +152,9 @@
         unsigned long flags;
 
         if (addr > 15) {
-                PRINT(KERN_ERR, lynx->id, __FUNCTION__
-                      ": PHY register address %d out of range", addr);
+                PRINT(KERN_ERR, lynx->id,
+                      "%s: PHY register address %d out of range",
+		      __FUNCTION__, addr);
                 return -1;
         }
 
@@ -173,8 +165,8 @@
                 retval = reg_read(lynx, LINK_PHY);
 
                 if (i > 10000) {
-                        PRINT(KERN_ERR, lynx->id, __FUNCTION__ 
-                              ": runaway loop, aborting");
+                        PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting",
+			      __FUNCTION__);
                         retval = -1;
                         break;
                 }
@@ -196,14 +188,14 @@
         unsigned long flags;
 
         if (addr > 15) {
-                PRINT(KERN_ERR, lynx->id, __FUNCTION__
-                      ": PHY register address %d out of range", addr);
+                PRINT(KERN_ERR, lynx->id,
+                      "%s: PHY register address %d out of range", __FUNCTION__, addr);
                 return -1;
         }
 
         if (val > 0xff) {
-                PRINT(KERN_ERR, lynx->id, __FUNCTION__
-                      ": PHY register value %d out of range", val);
+                PRINT(KERN_ERR, lynx->id,
+                      "%s: PHY register value %d out of range", __FUNCTION__, val);
                 return -1;
         }
 
@@ -222,8 +214,8 @@
         int reg;
 
         if (page > 7) {
-                PRINT(KERN_ERR, lynx->id, __FUNCTION__
-                      ": PHY page %d out of range", page);
+                PRINT(KERN_ERR, lynx->id,
+                      "%s: PHY page %d out of range", __FUNCTION__, page);
                 return -1;
         }
 
@@ -244,8 +236,8 @@
         int reg;
 
         if (port > 15) {
-                PRINT(KERN_ERR, lynx->id, __FUNCTION__
-                      ": PHY port %d out of range", port);
+                PRINT(KERN_ERR, lynx->id,
+                      "%s: PHY port %d out of range", __FUNCTION__, port);
                 return -1;
         }
 
@@ -377,7 +369,7 @@
 
         hpsb_selfid_complete(host, phyid, isroot);
 
-        if (host->in_bus_reset) return;
+        if (host->in_bus_reset) return; /* in bus reset again */
 
         if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER);
         reg_set_bits(lynx, LINK_CONTROL,
@@ -440,160 +432,7 @@
 }
 
 
-#if 0
-static int lynx_detect(struct hpsb_host_template *tmpl)
-{
-        struct hpsb_host *host;
-        int i;
-
-        init_driver();
-
-        for (i = 0; i < num_of_cards; i++) {
-                host = hpsb_get_host(tmpl, 0);
-                if (host == NULL) {
-                        /* simply don't init more after out of mem */
-                        return i;
-                }
-                host->hostdata = &cards[i];
-                cards[i].host = host;
-        }
-
-        return num_of_cards;
-}
-#endif
-
-static int lynx_initialize(struct hpsb_host *host)
-{
-        struct ti_lynx *lynx = host->hostdata;
-        struct ti_pcl pcl;
-        int i;
-        u32 *pcli;
-
-        lynx->selfid_size = -1;
-        lynx->phy_reg0 = -1;
-
-        lynx->async.queue = NULL;
-        
-        pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
-        put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
-        
-        pcl.next = PCL_NEXT_INVALID;
-        pcl.async_error_next = PCL_NEXT_INVALID;
-#ifdef __BIG_ENDIAN
-        pcl.buffer[0].control = PCL_CMD_RCV | 16;
-        pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
-#else
-        pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16;
-        pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
-#endif
-        pcl.buffer[0].pointer = lynx->rcv_page_dma;
-        pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
-        put_pcl(lynx, lynx->rcv_pcl, &pcl);
-        
-        pcl.next = pcl_bus(lynx, lynx->async.pcl);
-        pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
-        put_pcl(lynx, lynx->async.pcl_start, &pcl);
-
-        pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
-        pcl.async_error_next = PCL_NEXT_INVALID;
-        put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
-
-        pcl.next = PCL_NEXT_INVALID;
-        pcl.async_error_next = PCL_NEXT_INVALID;
-        pcl.buffer[0].control = PCL_CMD_RCV | 4;
-#ifndef __BIG_ENDIAN
-        pcl.buffer[0].control |= PCL_BIGENDIAN;
-#endif
-        pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
-
-        for (i = 0; i < NUM_ISORCV_PCL; i++) {
-                int page = i / ISORCV_PER_PAGE;
-                int sec = i % ISORCV_PER_PAGE;
-
-                pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] 
-                        + sec * MAX_ISORCV_SIZE;
-                pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
-                put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
-        }
-
-        pcli = (u32 *)&pcl;
-        for (i = 0; i < NUM_ISORCV_PCL; i++) {
-                pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
-        }
-        put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
-
-        /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
-        reg_write(lynx, FIFO_SIZES, 0x003030a0);
-        /* 20 byte threshold before triggering PCI transfer */
-        reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
-        /* threshold on both send FIFOs before transmitting:
-           FIFO size - cache line size - 1 */
-        i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
-        i = 0x30 - i - 1;
-        reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
-        
-        reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
-
-        reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
-                  | LINK_INT_PHY_REG_RCVD  | LINK_INT_PHY_BUSRESET
-                  | LINK_INT_ISO_STUCK     | LINK_INT_ASYNC_STUCK 
-                  | LINK_INT_SENT_REJECT   | LINK_INT_TX_INVALID_TC
-                  | LINK_INT_GRF_OVERFLOW  | LINK_INT_ITF_UNDERFLOW
-                  | LINK_INT_ATF_UNDERFLOW);
-        
-        reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
-        reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
-        reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
-        reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
-                  DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
-                  | DMA_WORD1_CMP_MATCH_EXACT    | DMA_WORD1_CMP_MATCH_BUS_BCAST
-                  | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
-
-        run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
-
-        reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
-        reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
-        reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
-        reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
-
-        run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
-
-        reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
-                  | LINK_CONTROL_TX_ISO_EN   | LINK_CONTROL_RX_ISO_EN
-                  | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
-                  | LINK_CONTROL_RESET_TX    | LINK_CONTROL_RESET_RX);
-
-        if (!lynx->phyic.reg_1394a) {
-                /* attempt to enable contender bit -FIXME- would this work
-                 * elsewhere? */
-                reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
-                reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); 
-        } else {
-                /* set the contender bit in the extended PHY register
-                 * set. (Should check that bis 0,1,2 (=0xE0) is set
-                 * in register 2?)
-                 */
-                i = get_phy_reg(lynx, 4);
-                if (i != -1) set_phy_reg(lynx, 4, i | 0x40);
-        }
-
-        return 1;
-}
-
-static void lynx_release(struct hpsb_host *host)
-{
-        struct ti_lynx *lynx;
-        
-        if (host != NULL) {
-                lynx = host->hostdata;
-                remove_card(lynx->dev);
-        } else {
-#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
-                unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
-#endif
-        }
-}
-
+/* called from subsystem core */
 static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
 {
         struct ti_lynx *lynx = host->hostdata;
@@ -642,6 +481,8 @@
         return 1;
 }
 
+
+/* called from subsystem core */
 static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
 {
         struct ti_lynx *lynx = host->hostdata;
@@ -666,9 +507,7 @@
                 arg |= (retval == -1 ? 63 : retval);
                 retval = 0;
 
-                PRINT(KERN_INFO, lynx->id, "resetting bus on request%s",
-                      (host->attempt_root ? " and attempting to become root"
-                       : ""));
+                PRINT(KERN_INFO, lynx->id, "resetting bus on request");
 
                 lynx->selfid_size = -1;
                 lynx->phy_reg0 = -1;
@@ -721,6 +560,8 @@
                 } else {
                         MOD_DEC_USE_COUNT;
                 }
+
+                retval = 1;
                 break;
 
         case ISO_LISTEN_CHANNEL:
@@ -1010,8 +851,8 @@
                 membase = md->lynx->aux_port;
                 break;
         default:
-                panic("pcilynx%d: unsupported md->type %d in " __FUNCTION__,
-                      md->lynx->id, md->type);
+                panic("pcilynx%d: unsupported md->type %d in %s",
+                      md->lynx->id, md->type, __FUNCTION__);
         }
 
         down(&md->lynx->mem_dma_mutex);
@@ -1291,6 +1132,7 @@
         }
 }
 
+
 static void iso_rcv_bh(struct ti_lynx *lynx)
 {
         unsigned int idx;
@@ -1335,44 +1177,97 @@
 }
 
 
+static void remove_card(struct pci_dev *dev)
+{
+        struct ti_lynx *lynx;
+        int i;
+
+        lynx = pci_get_drvdata(dev);
+        if (!lynx) return;
+        pci_set_drvdata(dev, NULL);
+
+        switch (lynx->state) {
+        case is_host:
+                reg_write(lynx, PCI_INT_ENABLE, 0);
+                hpsb_remove_host(lynx->host);
+        case have_intr:
+                reg_write(lynx, PCI_INT_ENABLE, 0);
+                free_irq(lynx->dev->irq, lynx);
+        case have_iomappings:
+                reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+                /* Fix buggy cards with autoboot pin not tied low: */
+                reg_write(lynx, DMA0_CHAN_CTRL, 0);
+                iounmap(lynx->registers);
+                iounmap(lynx->local_rom);
+                iounmap(lynx->local_ram);
+                iounmap(lynx->aux_port);
+        case have_1394_buffers:
+                for (i = 0; i < ISORCV_PAGES; i++) {
+                        if (lynx->iso_rcv.page[i]) {
+                                pci_free_consistent(lynx->dev, PAGE_SIZE,
+                                                    lynx->iso_rcv.page[i],
+                                                    lynx->iso_rcv.page_dma[i]);
+                        }
+                }
+                pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
+                                    lynx->rcv_page_dma);
+        case have_aux_buf:
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+                pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer,
+                                    lynx->mem_dma_buffer_dma);
+#endif
+        case have_pcl_mem:
+#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
+                pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
+                                    lynx->pcl_mem_dma);
+#endif
+        case clear:
+                /* do nothing - already freed */
+                ;
+        }
+
+	tasklet_kill(&lynx->iso_rcv.tq);
+        kfree(lynx);
+}
+
+
 static int __devinit add_card(struct pci_dev *dev,
-                              const struct pci_device_id *devid)
+                              const struct pci_device_id *devid_is_unused)
 {
 #define FAIL(fmt, args...) do { \
         PRINT_G(KERN_ERR, fmt , ## args); \
-        num_of_cards--; \
         remove_card(dev); \
-        return -1; \
+        return error; \
         } while (0)
 
+	struct hpsb_host *host;
         struct ti_lynx *lynx; /* shortcut to currently handled device */
-        unsigned int i;
+        struct ti_pcl pcl;
+        u32 *pcli;
+        int i;
+        int error;
 
-        if (num_of_cards == MAX_PCILYNX_CARDS) {
-                PRINT_G(KERN_WARNING, "cannot handle more than %d cards.  "
-                        "Adjust MAX_PCILYNX_CARDS in pcilynx.h.",
-                        MAX_PCILYNX_CARDS);
-                return -1;
-        }
 
-        lynx = &cards[num_of_cards++];
+        error = -ENXIO;
 
         if (pci_set_dma_mask(dev, 0xffffffff))
-                FAIL("DMA address limits not supported for PCILynx hardware %d",
-                     lynx->id);
+                FAIL("DMA address limits not supported for PCILynx hardware");
         if (pci_enable_device(dev))
-                FAIL("failed to enable PCILynx hardware %d", lynx->id);
+                FAIL("failed to enable PCILynx hardware");
         pci_set_master(dev);
 
-        lynx->host = hpsb_get_host(&lynx_template, 0);
-        if (!lynx->host)
-                FAIL("failed to allocate host structure");
-
-        lynx->state = have_host_struct;
-	lynx->host->hostdata = lynx;
-        lynx->id = num_of_cards-1;
+        error = -ENOMEM;
+
+	host = hpsb_alloc_host(lynx_driver, sizeof(struct ti_lynx));
+        if (!host) FAIL("failed to allocate control structure memory");
+
+        lynx = host->hostdata;
+	lynx->id = card_id++;
         lynx->dev = dev;
-	lynx->host->pdev = dev;
+        lynx->state = clear;
+	lynx->host = host;
+        host->pdev = dev;
+        pci_set_drvdata(dev, lynx);
 
         lynx->lock = SPIN_LOCK_UNLOCKED;
         lynx->phy_reg_lock = SPIN_LOCK_UNLOCKED;
@@ -1435,7 +1330,9 @@
         }
 #endif
 
-        reg_write(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+        reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+        /* Fix buggy cards with autoboot pin not tied low: */
+        reg_write(lynx, DMA0_CHAN_CTRL, 0);
 
         if (!request_irq(dev->irq, lynx_irq_handler, SA_SHIRQ,
                          PCILYNX_DRIVER_NAME, lynx)) {
@@ -1502,99 +1399,121 @@
                 PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
         }
 
-	/* Tell the highlevel this host is ready */
-	highlevel_add_one_host (lynx->host);
-
-        return 0;
-#undef FAIL
-}
+        lynx->selfid_size = -1;
+        lynx->phy_reg0 = -1;
 
-static void remove_card(struct pci_dev *dev)
-{
-        struct ti_lynx *lynx;
-        int i;
+        lynx->async.queue = NULL;
 
-        lynx = cards;
-        while (lynx->dev != dev) lynx++;
+        pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
+        put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
 
-        switch (lynx->state) {
-        case have_intr:
-                reg_write(lynx, PCI_INT_ENABLE, 0);
-                free_irq(lynx->dev->irq, lynx);
-        case have_iomappings:
-                reg_write(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
-                iounmap(lynx->registers);
-                iounmap(lynx->local_rom);
-                iounmap(lynx->local_ram);
-                iounmap(lynx->aux_port);
-        case have_1394_buffers:
-                for (i = 0; i < ISORCV_PAGES; i++) {
-                        if (lynx->iso_rcv.page[i]) {
-                                pci_free_consistent(lynx->dev, PAGE_SIZE,
-                                                    lynx->iso_rcv.page[i],
-                                                    lynx->iso_rcv.page_dma[i]);
-                        }
-                }
-                pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
-                                    lynx->rcv_page_dma);
-        case have_aux_buf:
-#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
-                pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer,
-                                    lynx->mem_dma_buffer_dma);
+        pcl.next = PCL_NEXT_INVALID;
+        pcl.async_error_next = PCL_NEXT_INVALID;
+#ifdef __BIG_ENDIAN
+        pcl.buffer[0].control = PCL_CMD_RCV | 16;
+        pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
+#else
+        pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16;
+        pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
 #endif
-        case have_pcl_mem:
-#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
-                pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
-                                    lynx->pcl_mem_dma);
+        pcl.buffer[0].pointer = lynx->rcv_page_dma;
+        pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
+        put_pcl(lynx, lynx->rcv_pcl, &pcl);
+        
+        pcl.next = pcl_bus(lynx, lynx->async.pcl);
+        pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
+        put_pcl(lynx, lynx->async.pcl_start, &pcl);
+
+        pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
+        pcl.async_error_next = PCL_NEXT_INVALID;
+        put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
+
+        pcl.next = PCL_NEXT_INVALID;
+        pcl.async_error_next = PCL_NEXT_INVALID;
+        pcl.buffer[0].control = PCL_CMD_RCV | 4;
+#ifndef __BIG_ENDIAN
+        pcl.buffer[0].control |= PCL_BIGENDIAN;
 #endif
-        case have_host_struct:
-                /* FIXME - verify host freeing */
-        case clear:;
-                /* do nothing - already freed */
+        pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
+
+        for (i = 0; i < NUM_ISORCV_PCL; i++) {
+                int page = i / ISORCV_PER_PAGE;
+                int sec = i % ISORCV_PER_PAGE;
+
+                pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] 
+                        + sec * MAX_ISORCV_SIZE;
+                pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
+                put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
         }
 
-	tasklet_kill(&lynx->iso_rcv.tq);
+        pcli = (u32 *)&pcl;
+        for (i = 0; i < NUM_ISORCV_PCL; i++) {
+                pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
+        }
+        put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
 
-        lynx->state = clear;
-}
+        /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
+        reg_write(lynx, FIFO_SIZES, 0x003030a0);
+        /* 20 byte threshold before triggering PCI transfer */
+        reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
+        /* threshold on both send FIFOs before transmitting:
+           FIFO size - cache line size - 1 */
+        i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
+        i = 0x30 - i - 1;
+        reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
 
-#if 0
-static int init_driver()
-{
-        struct pci_dev *dev = NULL;
-        int success = 0;
+        reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
 
-        if (num_of_cards) {
-                PRINT_G(KERN_DEBUG, __PRETTY_FUNCTION__ " called again");
-                return 0;
-        }
+        reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
+                  | LINK_INT_PHY_REG_RCVD  | LINK_INT_PHY_BUSRESET
+                  | LINK_INT_ISO_STUCK     | LINK_INT_ASYNC_STUCK 
+                  | LINK_INT_SENT_REJECT   | LINK_INT_TX_INVALID_TC
+                  | LINK_INT_GRF_OVERFLOW  | LINK_INT_ITF_UNDERFLOW
+                  | LINK_INT_ATF_UNDERFLOW);
+        
+        reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
+        reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
+        reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
+        reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
+                  DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
+                  | DMA_WORD1_CMP_MATCH_EXACT    | DMA_WORD1_CMP_MATCH_BUS_BCAST
+                  | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
 
-        PRINT_G(KERN_INFO, "looking for PCILynx cards");
+        run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
 
-        while ((dev = pci_find_device(PCI_VENDOR_ID_TI,
-                                      PCI_DEVICE_ID_TI_PCILYNX, dev)) 
-               != NULL) {
-                if (add_card(dev) == 0) {
-                        success = 1;
-                }
-        }
+        reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
+        reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
+        reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
+        reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
 
-        if (success == 0) {
-                PRINT_G(KERN_WARNING, "no operable PCILynx cards found");
-                return -ENXIO;
-        }
+        run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
 
-#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
-        if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) {
-                PRINT_G(KERN_ERR, "allocation of char major number %d failed",
-                        PCILYNX_MAJOR);
-                return -EBUSY;
+        reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
+                  | LINK_CONTROL_TX_ISO_EN   | LINK_CONTROL_RX_ISO_EN
+                  | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
+                  | LINK_CONTROL_RESET_TX    | LINK_CONTROL_RESET_RX);
+
+        if (!lynx->phyic.reg_1394a) {
+                /* attempt to enable contender bit -FIXME- would this work
+                 * elsewhere? */
+                reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
+                reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); 
+        } else {
+                /* set the contender bit in the extended PHY register
+                 * set. (Should check that bis 0,1,2 (=0xE0) is set
+                 * in register 2?)
+                 */
+                i = get_phy_reg(lynx, 4);
+                if (i != -1) set_phy_reg(lynx, 4, i | 0x40);
         }
-#endif
+
+        hpsb_add_host(host);
+        lynx->state = is_host;
 
         return 0;
+#undef FAIL
 }
-#endif
+
 
 
 static size_t get_lynx_rom(struct hpsb_host *host, const quadlet_t **ptr)
@@ -1603,15 +1522,6 @@
         return sizeof(lynx_csr_rom);
 }
 
-static struct hpsb_host_template lynx_template = {
-	name:             PCILYNX_DRIVER_NAME,
-	initialize_host:  lynx_initialize,
-	release_host:     lynx_release,
-	get_rom:          get_lynx_rom,
-	transmit_packet:  lynx_transmit,
-	devctl:           lynx_devctl
-};
-
 static struct pci_device_id pci_table[] __devinitdata = {
 	{
                 vendor:     PCI_VENDOR_ID_TI,
@@ -1622,11 +1532,17 @@
 	{ }			/* Terminating entry */
 };
 
-static struct pci_driver lynx_pcidriver = {
+static struct pci_driver lynx_pci_driver = {
         name:      PCILYNX_DRIVER_NAME,
         id_table:  pci_table,
         probe:     add_card,
-        remove:    remove_card,
+        remove:    __devexit_p(remove_card),
+};
+
+static struct hpsb_host_operations lynx_ops = {
+        get_rom:          get_lynx_rom,
+        transmit_packet:  lynx_transmit,
+        devctl:           lynx_devctl,
 };
 
 MODULE_AUTHOR("Andreas E. Bombe <andreas.bombe@munich.netsurf.de>");
@@ -1635,30 +1551,52 @@
 MODULE_SUPPORTED_DEVICE("pcilynx");
 MODULE_DEVICE_TABLE(pci, pci_table);
 
-static void __exit pcilynx_cleanup(void)
-{
-        pci_unregister_driver(&lynx_pcidriver);
-        hpsb_unregister_lowlevel(&lynx_template);
-        PRINT_G(KERN_INFO, "removed " PCILYNX_DRIVER_NAME " module");
-}
-
 static int __init pcilynx_init(void)
 {
         int ret;
 
-        if (hpsb_register_lowlevel(&lynx_template)) {
-                PRINT_G(KERN_ERR, "registering failed");
-                return -ENXIO;
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+        if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) {
+                PRINT_G(KERN_ERR, "allocation of char major number %d failed",
+                        PCILYNX_MAJOR);
+                return -EBUSY;
+        }
+#endif
+
+        lynx_driver = hpsb_register_lowlevel(&lynx_ops, PCILYNX_DRIVER_NAME);
+        if (!lynx_driver) {
+                ret = -ENOMEM;
+                goto free_char_dev;
         }
 
-        ret = pci_module_init(&lynx_pcidriver);
+        ret = pci_module_init(&lynx_pci_driver);
         if (ret < 0) {
                 PRINT_G(KERN_ERR, "PCI module init failed");
-                hpsb_unregister_lowlevel(&lynx_template);
+                goto unregister_lowlevel;
         }
 
+        return 0;
+
+ unregister_lowlevel:
+        hpsb_unregister_lowlevel(lynx_driver);
+ free_char_dev:
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+        unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
+#endif
+
         return ret;
 }
+
+static void __exit pcilynx_cleanup(void)
+{
+        pci_unregister_driver(&lynx_pci_driver);
+        hpsb_unregister_lowlevel(lynx_driver);
+
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+        unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
+#endif
+}
+
 
 module_init(pcilynx_init);
 module_exit(pcilynx_cleanup);
diff -urN linux-2.5.3-pre2/drivers/ieee1394/pcilynx.h linux/drivers/ieee1394/pcilynx.h
--- linux-2.5.3-pre2/drivers/ieee1394/pcilynx.h	Sun Aug 12 12:39:02 2001
+++ linux/drivers/ieee1394/pcilynx.h	Mon Jan 21 17:23:22 2002
@@ -40,8 +40,8 @@
                 u32 product;
         } phyic;
 
-        enum { clear, have_host_struct,  have_intr, have_aux_buf, have_pcl_mem,
-               have_1394_buffers, have_iomappings } state;
+        enum { clear, have_intr, have_aux_buf, have_pcl_mem,
+               have_1394_buffers, have_iomappings, is_host } state;
         
         /* remapped memory spaces */
         void *registers;
diff -urN linux-2.5.3-pre2/drivers/ieee1394/raw1394.c linux/drivers/ieee1394/raw1394.c
--- linux-2.5.3-pre2/drivers/ieee1394/raw1394.c	Mon Jan 21 17:22:32 2002
+++ linux/drivers/ieee1394/raw1394.c	Mon Jan 21 17:23:22 2002
@@ -467,7 +467,7 @@
                                 hi = list_entry(lh, struct host_info, list);
 
                                 khl->nodes = hi->host->node_count;
-                                strcpy(khl->name, hi->host->template->name);
+                                strcpy(khl->name, hi->host->driver->name);
 
                                 khl++;
                         }
@@ -495,7 +495,7 @@
                                 lh = lh->next;
                         }
                         hi = list_entry(lh, struct host_info, list);
-                        hpsb_inc_host_usage(hi->host);
+                        hpsb_ref_host(hi->host);
                         list_add_tail(&fi->list, &hi->file_info_list);
                         fi->host = hi->host;
                         fi->state = connected;
@@ -915,7 +915,7 @@
 {
         struct file_info *fi;
 
-        if (minor(inode->i_rdev)) {
+        if (ieee1394_file_to_instance(file) > 0) {
                 return -ENXIO;
         }
 
@@ -983,7 +983,7 @@
                 list_del(&fi->list);
                 spin_unlock_irq(&host_info_lock);
 
-                hpsb_dec_host_usage(fi->host);
+                hpsb_unref_host(fi->host);
         }
 
         kfree(fi);
@@ -1017,14 +1017,18 @@
                 return -ENOMEM;
         }
 
-	devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE,
-                                      RAW1394_DEVICE_MAJOR, 0,
+	devfs_handle = devfs_register(NULL,
+				      RAW1394_DEVICE_NAME, DEVFS_FL_NONE,
+                                      IEEE1394_MAJOR,
+				      IEEE1394_MINOR_BLOCK_RAW1394 * 16,
                                       S_IFCHR | S_IRUSR | S_IWUSR, &file_ops,
                                       NULL);
 
-        if (devfs_register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME, 
-                                  &file_ops)) {
-                HPSB_ERR("raw1394 failed to register /dev/raw1394 device");
+        if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_RAW1394,
+				      THIS_MODULE, &file_ops)) {
+                HPSB_ERR("raw1394 failed to register minor device block");
+		devfs_unregister(devfs_handle);
+		hpsb_unregister_highlevel(hl_handle);
                 return -EBUSY;
         }
 	printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME);
@@ -1033,7 +1037,7 @@
 
 static void __exit cleanup_raw1394(void)
 {
-        devfs_unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME);
+        ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_RAW1394);
 	devfs_unregister(devfs_handle);
         hpsb_unregister_highlevel(hl_handle);
 }
diff -urN linux-2.5.3-pre2/drivers/ieee1394/sbp2.c linux/drivers/ieee1394/sbp2.c
--- linux-2.5.3-pre2/drivers/ieee1394/sbp2.c	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/sbp2.c	Mon Jan 21 17:23:22 2002
@@ -2,7 +2,7 @@
  * sbp2.c - SBP-2 protocol driver for IEEE-1394
  *
  * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com)
- * jamesg@filanet.com
+ * jamesg@filanet.com (JSG)
  *
  * 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
@@ -80,7 +80,7 @@
  *
  * The SBP-2 driver is still in an early state, but supports a variety of devices.
  * I have read/written many gigabytes of data from/to SBP-2 drives, and have seen 
- * performance of more than 16 MBytes/s on individual drives (limit of the media 
+ * performance of more than 25 MBytes/s on individual drives (limit of the media 
  * transfer rate).
  *
  * Following are the devices that have been tested successfully:
@@ -91,7 +91,7 @@
  *	- LaCie IEEE-1394 hard drives (several flavors)
  *	- QPS IEEE-1394 CD-RW/DVD drives and hard drives
  *	- BusLink IEEE-1394 hard drives
- *	- Iomega IEEE-1394 Zip/Jazz drives
+ *	- Iomega IEEE-1394 Zip/Jazz/Peerless drives
  *	- ClubMac IEEE-1394 hard drives
  *	- FirePower IEEE-1394 hard drives
  *	- EzQuest IEEE-1394 hard drives and CD-RW drives
@@ -104,7 +104,6 @@
  *	- Sony IEEE-1394 CD-RW drives
  *	- Epson IEEE-1394 scanner
  *	- ADS IEEE-1394 memory stick and compact flash readers 
- *	  (e.g. "insmod sbp2 mode_sense_hack=1" for mem stick and flash readers))
  *	- SBP-2 bridge-based devices (LSI, Oxford Semiconductor, Indigita bridges)
  *	- Various other standard IEEE-1394 hard drives and enclosures
  *
@@ -130,10 +129,6 @@
  *	  add some init code to the kernel to support this... and modules are much
  *	  more flexible anyway.   ;-)
  *
- *     - The scsi stack in recent kernels pass down the data transfer
- *       direction as scsicmd->sc_data_direction, which we should use
- *       instead of the sbp2scsi_direction_table.
- *
  *
  * History:
  *
@@ -222,9 +217,37 @@
  *                 when we register our driver.  This change 
  *                 automtically adds hotplug support to the driver.
  *                                 Kristian Hogsberg <hogsberg@users.sf.net>
+ *
+ *      11/17/01 - Various bugfixes/cleanups:
+ *                 * Remember to logout of device in sbp2_disconnect.
+ *                 * If we fail to reconnect to a device after bus reset
+ *                   remember to release unit directory, so the ieee1394
+ *                   knows we no longer manage it.
+ *                 * Unregister scsi hosts in sbp2_remove_host when a
+ *                   hpsb_host goes away.
+ *                 * Remove stupid hack in sbp2_remove_host.
+ *                 * Switched to "manual" module initialization
+ *                   (i.e. not scsi_module.c) and moved sbp2_cleanup
+ *                   moved sbp2scsi_release to sbp2_module_ext.  The
+ *                   release function is called once pr. registered
+ *                   scsi host, but sbp2_cleanup should only be called
+ *                   upon module unload.  Moved much initialization
+ *                   from sbp2scsi_detect to sbp2_module_init.
+ *                                 Kristian Hogsberg <hogsberg@users.sf.net>
+ *	01/06/02 - Misc bug fixes/enhancements:	(JSG)
+ *		   * Enable use_new_eh_code for scsi stuff.
+ *		   * Do not write all ones for NULL ORB high/low fields, but
+ *		     rather leave reserved areas zeroed (per SBP2 spec).
+ *		   * Use newer scsi transfer direction passed down instead of our
+ *		     direction table.
+ *		   * Bumped login time-out to 20 seconds, as some devices are slow.
+ *		   * Fixed a couple scsi unregister bugs on module unload
+ *	01/13/02 - Fixed compatibility with certain SBP2 devices, such as Iomega
+ *		   1394 devices (Peerless, Jazz). Also a bit of clean-up of the 
+ *		   driver, thanks to H.J.Lu (hjl@lucon.org). Removed mode_sense_hack
+ *		   module load option, as it's been fixed in the 2.4 scsi stack.
  */
-    
-
+
 
 /*
  * Includes
@@ -244,6 +267,7 @@
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
 #include <linux/smp_lock.h>
+#include <linux/init.h>
 #include <asm/current.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -252,6 +276,16 @@
 #include <asm/io.h>
 #include <asm/scatterlist.h>
 
+#ifdef CONFIG_KBUILD_2_5
+#include <scsi.h>
+#include <hosts.h>
+#include <sd.h>
+#else
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+#endif
+
 #include "ieee1394.h"
 #include "ieee1394_types.h"
 #include "ieee1394_core.h"
@@ -260,9 +294,6 @@
 #include "highlevel.h"
 #include "ieee1394_transactions.h"
 #include "ieee1394_hotplug.h"
-#include "../scsi/scsi.h"
-#include "../scsi/hosts.h"
-#include "../scsi/sd.h"
 #include "sbp2.h"
 
 /*
@@ -270,16 +301,6 @@
  */
 
 /*
- * Set mode_sense_hack to 1 if you have some sort of unusual sbp2 device,
- * like a 1394 memory stick reader, compact flash reader, or MO drive that
- * does not support mode sense. Allows you to mount the media rw instead
- * of ro.
- */
-MODULE_PARM(mode_sense_hack,"i");
-MODULE_PARM_DESC(mode_sense_hack, "Emulate mode sense for devices like 1394 memory stick readers");
-static int mode_sense_hack = 0;
-
-/*
  * Change max_speed on module load if you have a bad IEEE-1394 controller
  * that has trouble running 2KB packets at 400mb.
  *
@@ -334,7 +355,7 @@
  */
 
 #ifdef CONFIG_IEEE1394_SBP2_DEBUG_ORBS
-#define SBP2_ORB_DEBUG(fmt, args...)	HPSB_ERR("sbp2("__FUNCTION__"): "fmt, ## args)
+#define SBP2_ORB_DEBUG(fmt, args...)	HPSB_ERR("sbp2(%s): "fmt, __FUNCTION__, ## args)
 static u32 global_outstanding_command_orbs = 0;
 #define outstanding_orb_incr global_outstanding_command_orbs++
 #define outstanding_orb_decr global_outstanding_command_orbs--
@@ -346,10 +367,10 @@
 
 #ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA
 #define SBP2_DMA_ALLOC(fmt, args...) \
-	HPSB_ERR("sbp2("__FUNCTION__")alloc(%d): "fmt, \
+	HPSB_ERR("sbp2(%s)alloc(%d): "fmt, __FUNCTION__, \
 		 ++global_outstanding_dmas, ## args)
 #define SBP2_DMA_FREE(fmt, args...) \
-	HPSB_ERR("sbp2("__FUNCTION__")free(%d): "fmt, \
+	HPSB_ERR("sbp2(%s)free(%d): "fmt, __FUNCTION__, \
 		 --global_outstanding_dmas, ## args)
 static u32 global_outstanding_dmas = 0;
 #else
@@ -357,7 +378,6 @@
 #define SBP2_DMA_FREE(fmt, args...)
 #endif
 
-
 #if CONFIG_IEEE1394_SBP2_DEBUG >= 2
 #define SBP2_DEBUG(fmt, args...)	HPSB_ERR("sbp2: "fmt, ## args)
 #define SBP2_INFO(fmt, args...)		HPSB_ERR("sbp2: "fmt, ## args)
@@ -395,12 +415,11 @@
  * Globals
  */
 
-Scsi_Host_Template *global_scsi_tpnt = NULL;
+static Scsi_Host_Template scsi_driver_template;
 
 static u8 sbp2_speedto_maxrec[] = { 0x7, 0x8, 0x9 };
 
 static LIST_HEAD(sbp2_host_info_list);
-static int sbp2_host_count = 0;
 
 static struct hpsb_highlevel *sbp2_hl_handle = NULL;
 
@@ -671,13 +690,13 @@
 static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id,
 					     struct sbp2scsi_host_info *hi)
 {
-	struct list_head *lh;
+	struct list_head *lh, *next;
 	struct sbp2_command_info *command;
 	unsigned long flags;
         
 	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
 	if (!list_empty(&scsi_id->sbp2_command_orb_completed)) {
-		list_for_each(lh, &scsi_id->sbp2_command_orb_completed) {
+		list_for_each_safe(lh, next, &scsi_id->sbp2_command_orb_completed) {
 			command = list_entry(lh, struct sbp2_command_info, list);
 
 			/* Release our generic DMA's */
@@ -786,14 +805,26 @@
 	hi = (struct sbp2scsi_host_info *) command->Current_SCpnt->host->hostdata[0];
 
 	if (hi == NULL) {
-		printk(KERN_ERR __FUNCTION__": hi == NULL\n");
+		printk(KERN_ERR "%s: hi == NULL\n", __FUNCTION__);
 		return;
 	}
 
 	if (command->cmd_dma) {
-		pci_unmap_single(hi->host->pdev, command->cmd_dma,
-				 command->dma_size, command->dma_dir);
-		SBP2_DMA_FREE("single bulk");
+		if (command->dma_type == CMD_DMA_SINGLE) {
+			pci_unmap_single(hi->host->pdev, command->cmd_dma,
+					 command->dma_size, command->dma_dir);
+			SBP2_DMA_FREE("single bulk");
+		} else if (command->dma_type == CMD_DMA_PAGE) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13)
+			pci_unmap_single(hi->host->pdev, command->cmd_dma,
+					 command->dma_size, command->dma_dir);
+#else
+			pci_unmap_page(hi->host->pdev, command->cmd_dma,
+				       command->dma_size, command->dma_dir);
+#endif /* Linux version < 2.4.13 */
+			SBP2_DMA_FREE("single page");
+		} /* XXX: Check for CMD_DMA_NONE bug */
+		command->dma_type = CMD_DMA_NONE;
 		command->cmd_dma = 0;
 	}
 
@@ -868,7 +899,6 @@
 		hpsb_unregister_highlevel(sbp2_hl_handle);
 		sbp2_hl_handle = NULL;
 	}
-	return;
 }
 
 static int sbp2_probe(struct unit_directory *ud)
@@ -889,8 +919,10 @@
 	SBP2_DEBUG("sbp2_disconnect");
 	hi = sbp2_find_host_info(ud->ne->host);
 
-	if (hi != NULL)
-		sbp2_remove_device(hi, scsi_id);
+	if (hi != NULL) {
+		sbp2_logout_device(hi, scsi_id);
+ 		sbp2_remove_device(hi, scsi_id);
+	}
 }
 
 static void sbp2_update(struct unit_directory *ud)
@@ -909,12 +941,10 @@
 		 */
 		if (sbp2_login_device(hi, scsi_id)) {
 
-			/* Login failed too... so, just mark him as
-			 * unvalidated, so that he gets cleaned up
-			 * later.
-			 */
+			/* Login failed too, just remove the device. */
 			SBP2_ERR("sbp2_reconnect_device failed!");
 			sbp2_remove_device(hi, scsi_id);
+			hpsb_release_unit_directory(ud);
 			return;
 		}
 	}
@@ -974,11 +1004,13 @@
 
 	sbp2_spin_lock(&sbp2_host_info_lock, flags);
 	list_add_tail(&hi->list, &sbp2_host_info_list);
-	sbp2_host_count++;
 	sbp2_spin_unlock(&sbp2_host_info_lock, flags);
 
 	/* Register our host with the SCSI stack. */
-	sbp2scsi_register_scsi_host(hi);
+	hi->scsi_host = scsi_register (&scsi_driver_template, sizeof(void *));
+	if (hi->scsi_host)
+		hi->scsi_host->hostdata[0] = (unsigned long)hi;
+	scsi_driver_template.present++;
 
 	return;
 }
@@ -994,22 +1026,38 @@
 
 	list_for_each (lh, &sbp2_host_info_list) {
 		hi = list_entry(lh, struct sbp2scsi_host_info, list);
-		if (hi->host == host) {
+		if (hi->host == host)
 			return hi;
-		}
 	}
 
-	return(NULL);
+	return NULL;
 }
 
 /*
- * This function is called when the host is removed
+ * This function returns a host info structure for a given Scsi_Host
+ * struct.
+ */
+static struct sbp2scsi_host_info *sbp2_find_host_info_scsi(struct Scsi_Host *host)
+{
+	struct list_head *lh;
+	struct sbp2scsi_host_info *hi;
+
+	list_for_each (lh, &sbp2_host_info_list) {
+		hi = list_entry(lh, struct sbp2scsi_host_info, list);
+		if (hi->scsi_host == host)
+			return hi;
+	}
+
+	return NULL;
+}
+
+/*
+ * This function is called when a host is removed.
  */
 static void sbp2_remove_host(struct hpsb_host *host)
 {
 	struct sbp2scsi_host_info *hi;
 	unsigned long flags;
-	int i;
 
 	SBP2_DEBUG("sbp2_remove_host");
 
@@ -1017,21 +1065,7 @@
 
 	hi = sbp2_find_host_info(host);
 	if (hi != NULL) {
-		/* Here's an annoying hack: we get a disconnect
-		 * callback for each device, so this loop shouldn't be
-		 * necessary.  However, the sbp2 driver receives the
-		 * remove_host callback before the nodemgr, so when we
-		 * get the disconnect callback, we've already freed
-		 * the host.  Thus, we free the devices here...
-		 */
-		for (i = 0; i < SBP2SCSI_MAX_SCSI_IDS; i++) {
-			if (hi->scsi_id[i] != NULL) {
-				sbp2_logout_device(hi, hi->scsi_id[i]);
-				sbp2_remove_device(hi, hi->scsi_id[i]);
-			}
-		}
 		sbp2util_remove_request_packet_pool(hi);
-		sbp2_host_count--;
 		list_del(&hi->list);
 		kfree(hi);
 	}
@@ -1203,10 +1237,7 @@
 	 */
 	if (sbp2_login_device(hi, scsi_id)) {
 
-		/*
-		 * Login failed... so, just mark him as unvalidated, so
-		 * that he gets cleaned up later.
-		 */
+		/* Login failed, just remove the device. */
 		SBP2_ERR("sbp2_login_device failed");
 		sbp2_remove_device(hi, scsi_id);
 		return -EBUSY;
@@ -1231,11 +1262,13 @@
 }
 
 /*
- * This function removes (cleans-up after) any unvalidated sbp2 devices
+ * This function removes an sbp2 device from the sbp2scsi_host_info struct.
  */
 static void sbp2_remove_device(struct sbp2scsi_host_info *hi, 
 			       struct scsi_id_instance_data *scsi_id)
 {
+	SBP2_DEBUG("sbp2_remove_device");
+
 	/* Complete any pending commands with selection timeout */
 	sbp2scsi_complete_all_commands(hi, scsi_id, DID_NO_CONNECT);
        			
@@ -1276,8 +1309,7 @@
 		SBP2_DMA_FREE("single logout orb");
 	}
 
-	SBP2_DEBUG("Unvalidated SBP-2 device removed, SCSI ID = %d", 
-		   scsi_id->id);
+	SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id);
 	hi->scsi_id[scsi_id->id] = NULL;
 	kfree(scsi_id);
 }
@@ -1361,9 +1393,9 @@
 
 	save_flags(flags);
 	cli();
-	/* 10 second timeout */
+	/* 20 second timeout */
 	if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma)
-		sleep_on_timeout(&scsi_id->sbp2_login_wait, 10*HZ);
+		sleep_on_timeout(&scsi_id->sbp2_login_wait, 20*HZ);
 	restore_flags(flags);
 
 	SBP2_DEBUG("sbp2_login_device: initial check");
@@ -1381,7 +1413,7 @@
 
 	/*
 	 * Check status
-	 */				       
+	 */
 	if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
 	    STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
 	    STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
@@ -1591,13 +1623,13 @@
 	ud = scsi_id->ud;
 
 	/* Handle different fields in the unit directory, based on keys */
-	for (i = 0; i < ud->arb_count; i++) {
-		switch (ud->arb_keys[i]) {
+	for (i = 0; i < ud->count; i++) {
+		switch (CONFIG_ROM_KEY(ud->quadlets[i])) {
 		case SBP2_CSR_OFFSET_KEY:
 			/* Save off the management agent address */
 			scsi_id->sbp2_management_agent_addr =
-				CONFIG_ROM_INITIAL_MEMORY_SPACE + 
-				(ud->arb_values[i] << 2);
+				CSR_REGISTER_BASE + 
+				(CONFIG_ROM_VALUE(ud->quadlets[i]) << 2);
 
 			SBP2_DEBUG("sbp2_management_agent_addr = %x",
 				   (unsigned int) scsi_id->sbp2_management_agent_addr);
@@ -1605,14 +1637,16 @@
 
 		case SBP2_COMMAND_SET_SPEC_ID_KEY:
 			/* Command spec organization */
-			scsi_id->sbp2_command_set_spec_id = ud->arb_values[i];
+			scsi_id->sbp2_command_set_spec_id
+				= CONFIG_ROM_VALUE(ud->quadlets[i]);
 			SBP2_DEBUG("sbp2_command_set_spec_id = %x",
 				   (unsigned int) scsi_id->sbp2_command_set_spec_id);
 			break;
 
 		case SBP2_COMMAND_SET_KEY:
 			/* Command set used by sbp2 device */
-			scsi_id->sbp2_command_set = ud->arb_values[i];
+			scsi_id->sbp2_command_set
+				= CONFIG_ROM_VALUE(ud->quadlets[i]);
 			SBP2_DEBUG("sbp2_command_set = %x",
 				   (unsigned int) scsi_id->sbp2_command_set);
 			break;
@@ -1622,7 +1656,8 @@
 			 * Unit characterisitcs (orb related stuff
 			 * that I'm not yet paying attention to)
 			 */
-			scsi_id->sbp2_unit_characteristics = ud->arb_values[i];
+			scsi_id->sbp2_unit_characteristics
+				= CONFIG_ROM_VALUE(ud->quadlets[i]);
 			SBP2_DEBUG("sbp2_unit_characteristics = %x",
 				   (unsigned int) scsi_id->sbp2_unit_characteristics);
 			break;
@@ -1632,7 +1667,8 @@
 			 * Device type and lun (used for
 			 * detemining type of sbp2 device)
 			 */
-			scsi_id->sbp2_device_type_and_lun = ud->arb_values[i];
+			scsi_id->sbp2_device_type_and_lun
+				= CONFIG_ROM_VALUE(ud->quadlets[i]);
 			SBP2_DEBUG("sbp2_device_type_and_lun = %x",
 				   (unsigned int) scsi_id->sbp2_device_type_and_lun);
 			break;
@@ -1645,7 +1681,8 @@
 			 * bridge with 128KB max transfer size
 			 * limitation.
 			 */
-			scsi_id->sbp2_firmware_revision = ud->arb_values[i];
+			scsi_id->sbp2_firmware_revision
+				= CONFIG_ROM_VALUE(ud->quadlets[i]);
 			if (scsi_id->sbp2_firmware_revision ==
 			    SBP2_128KB_BROKEN_FIRMWARE) {
 				SBP2_WARN("warning: Bridge chipset supports 128KB max transfer size");
@@ -1687,9 +1724,9 @@
 	scsi_id->max_payload_size = min(sbp2_speedto_maxrec[scsi_id->speed_code],
 					(u8)(((be32_to_cpu(hi->host->csr.rom[2]) >> 12) & 0xf) - 1));
 
-	SBP2_ERR("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [0x%02x/%u]",
+	SBP2_ERR("Node[" NODE_BUS_FMT "]: Max speed [%s] - Max payload [%u]",
 		 NODE_BUS_ARGS(scsi_id->ne->nodeid), hpsb_speedto_str[scsi_id->speed_code],
-		 scsi_id->max_payload_size, 1 << ((u32)scsi_id->max_payload_size + 2));
+		 1 << ((u32)scsi_id->max_payload_size + 2));
 
 	return(0);
 }
@@ -1747,13 +1784,15 @@
 				   unchar *scsi_cmd,
 				   unsigned int scsi_use_sg,
 				   unsigned int scsi_request_bufflen,
-				   void *scsi_request_buffer, int dma_dir)
+				   void *scsi_request_buffer, 
+				   unsigned char scsi_dir)
 {
 	struct scatterlist *sgpnt = (struct scatterlist *) scsi_request_buffer;
 	struct sbp2_command_orb *command_orb = &command->command_orb;
 	struct sbp2_unrestricted_page_table *scatter_gather_element =
 		&command->scatter_gather_element[0];
-	u32 sg_count, sg_len;
+	int dma_dir = scsi_to_pci_dma_dir (scsi_dir);
+	u32 sg_count, sg_len, orb_direction;
 	dma_addr_t sg_addr;
 	int i;
 
@@ -1765,25 +1804,49 @@
 	 * that data_size becomes the number of s/g elements, and
 	 * page_size should be zero (for unrestricted).
 	 */
-	command_orb->next_ORB_hi = 0xffffffff;
-	command_orb->next_ORB_lo = 0xffffffff;
+	command_orb->next_ORB_hi = ORB_SET_NULL_PTR(1);
+	command_orb->next_ORB_lo = 0x0;
 	command_orb->misc = ORB_SET_MAX_PAYLOAD(scsi_id->max_payload_size);
 	command_orb->misc |= ORB_SET_SPEED(scsi_id->speed_code);
 	command_orb->misc |= ORB_SET_NOTIFY(1);		/* Notify us when complete */
 
 	/*
+	 * Get the direction of the transfer. If the direction is unknown, then use our
+	 * goofy table as a back-up.
+	 */
+	switch (scsi_dir) {
+		case SCSI_DATA_NONE:
+			orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
+			break;
+		case SCSI_DATA_WRITE:
+			orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA;
+			break;
+		case SCSI_DATA_READ:
+			orb_direction = ORB_DIRECTION_READ_FROM_MEDIA;
+			break;
+		case SCSI_DATA_UNKNOWN:
+		default:
+			SBP2_ERR("SCSI data transfer direction not specified. "
+				 "Update the SBP2 direction table in sbp2.h if " 
+				 "necessary for your application");
+			print_command (scsi_cmd);
+			orb_direction = sbp2scsi_direction_table[*scsi_cmd];
+			break;
+	}
+
+	/*
 	 * Set-up our pagetable stuff... unfortunately, this has become
 	 * messier than I'd like. Need to clean this up a bit.   ;-)
 	 */
-	if (sbp2scsi_direction_table[*scsi_cmd] == ORB_DIRECTION_NO_DATA_TRANSFER) {
+	if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) {
 
 		SBP2_DEBUG("No data transfer");
 
 		/*
 		 * Handle no data transfer
 		 */
-		command_orb->data_descriptor_hi = 0xffffffff;
-		command_orb->data_descriptor_lo = 0xffffffff;
+		command_orb->data_descriptor_hi = 0x0;
+		command_orb->data_descriptor_lo = 0x0;
 		command_orb->misc |= ORB_SET_DIRECTION(1);
 
 	} else if (scsi_use_sg) {
@@ -1798,15 +1861,24 @@
 			SBP2_DEBUG("Only one s/g element");
 			command->dma_dir = dma_dir;
 			command->dma_size = sgpnt[0].length;
+			command->dma_type = CMD_DMA_PAGE;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13)
 			command->cmd_dma = pci_map_single (hi->host->pdev, sgpnt[0].address,
 							   command->dma_size,
 							   command->dma_dir);
-			SBP2_DMA_ALLOC("single scatter element");
+#else
+			command->cmd_dma = pci_map_page(hi->host->pdev,
+							sgpnt[0].page,
+							sgpnt[0].offset,
+							command->dma_size,
+							command->dma_dir);
+#endif /* Linux version < 2.4.13 */
+			SBP2_DMA_ALLOC("single page scatter element");
 
 			command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
 			command_orb->data_descriptor_lo = command->cmd_dma;
 			command_orb->misc |= ORB_SET_DATA_SIZE(command->dma_size);
-			command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+			command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
 
 		} else {
 			int count = pci_map_sg(hi->host->pdev, sgpnt, scsi_use_sg, dma_dir);
@@ -1818,7 +1890,7 @@
 
 			/* use page tables (s/g) */
 			command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1);
-			command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+			command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
 			command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
 			command_orb->data_descriptor_lo = command->sge_dma;
 
@@ -1863,6 +1935,7 @@
 
 		command->dma_dir = dma_dir;
 		command->dma_size = scsi_request_bufflen;
+		command->dma_type = CMD_DMA_SINGLE;
 		command->cmd_dma = pci_map_single (hi->host->pdev, scsi_request_buffer,
 						   command->dma_size,
 						   command->dma_dir);
@@ -1877,15 +1950,15 @@
 			command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
 			command_orb->data_descriptor_lo = command->cmd_dma;
 			command_orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen);
-			command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+			command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
 
 			/*
 			 * Sanity, in case our direction table is not
 			 * up-to-date
 			 */
 			if (!scsi_request_bufflen) {
-				command_orb->data_descriptor_hi = 0xffffffff;
-				command_orb->data_descriptor_lo = 0xffffffff;
+				command_orb->data_descriptor_hi = 0x0;
+				command_orb->data_descriptor_lo = 0x0;
 				command_orb->misc |= ORB_SET_DIRECTION(1);
 			}
 
@@ -1899,7 +1972,7 @@
 
 			/* Use page tables (s/g) */
 			command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1);
-			command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+			command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
 
 			/*
 			 * fill out our sbp-2 page tables (and split up
@@ -1964,6 +2037,12 @@
 	SBP2_ORB_DEBUG("sending command orb %p, linked = %x, total orbs = %x",
 			command_orb, command->linked, global_outstanding_command_orbs);
 
+	pci_dma_sync_single(hi->host->pdev, command->command_orb_dma,
+			    sizeof(struct sbp2_command_orb),
+			    PCI_DMA_BIDIRECTIONAL);
+	pci_dma_sync_single(hi->host->pdev, command->sge_dma,
+			    sizeof(command->scatter_gather_element),
+			    PCI_DMA_BIDIRECTIONAL);
 	/*
 	 * Check to see if there are any previous orbs to use
 	 */
@@ -2000,6 +2079,7 @@
 		}
 
 		scsi_id->last_orb = command_orb;
+		scsi_id->last_orb_dma = command->command_orb_dma;
 
 	} else {
 
@@ -2014,6 +2094,9 @@
 			cpu_to_be32(command->command_orb_dma);
 		/* Tells hardware that this pointer is valid */
 		scsi_id->last_orb->next_ORB_hi = 0x0;
+		pci_dma_sync_single(hi->host->pdev, scsi_id->last_orb_dma,
+				    sizeof(struct sbp2_command_orb),
+				    PCI_DMA_BIDIRECTIONAL);
 
 		/*
 		 * Only ring the doorbell if we need to (first parts of
@@ -2041,6 +2124,7 @@
 		}
 
 		scsi_id->last_orb = command_orb;
+		scsi_id->last_orb_dma = command->command_orb_dma;
 
 	}
        	return(0);
@@ -2053,12 +2137,17 @@
 			     Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	unchar *cmd = (unchar *) SCpnt->cmnd;
-	u32 device_type = (scsi_id->sbp2_device_type_and_lun & 0x00ff0000) >> 16;
+	unsigned int request_bufflen = SCpnt->request_bufflen;
+	u8 device_type
+		= SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun);
 	struct sbp2_command_info *command;
 
 	SBP2_DEBUG("sbp2_send_command");
-	SBP2_DEBUG("SCSI command = %02x", *cmd);
-	SBP2_DEBUG("SCSI transfer size = %x", SCpnt->request_bufflen);
+	SBP2_DEBUG("SCSI command:");
+#if CONFIG_IEEE1394_SBP2_DEBUG >= 2
+	print_command (cmd);
+#endif
+	SBP2_DEBUG("SCSI transfer size = %x", request_bufflen);
 	SBP2_DEBUG("SCSI s/g elements = %x", (unsigned int)SCpnt->use_sg);
 
 	/*
@@ -2069,7 +2158,7 @@
 	    (SCpnt->request_bufflen > SBP2_BROKEN_FIRMWARE_MAX_TRANSFER) && 
 	    (device_type == TYPE_DISK) &&
 	    (SCpnt->use_sg) &&
-	    (*cmd == 0x28 || *cmd == 0x2a || *cmd == 0x0a || *cmd == 0x08)) {
+	    (*cmd == READ_6 || *cmd == READ_10 || *cmd == WRITE_6 || *cmd == WRITE_10)) {
 
 		/*
 		 * Darn, a broken device. We'll need to split up the
@@ -2088,14 +2177,24 @@
 	}
 
 	/*
+	 * The scsi stack sends down a request_bufflen which does not match the
+	 * length field in the scsi cdb. This causes some sbp2 devices to 
+	 * reject this inquiry command. Fix is to fix request_bufflen to match
+	 * the value in the cdb.
+	 */
+	if (*cmd == INQUIRY) {
+		request_bufflen = cmd[4];
+	}
+
+	/*
 	 * Now actually fill in the comamnd orb and sbp2 s/g list
 	 */
 	sbp2_create_command_orb(hi, scsi_id, command, cmd, SCpnt->use_sg,
-			SCpnt->request_bufflen, SCpnt->request_buffer,
-			scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); 
+				request_bufflen, SCpnt->request_buffer,
+				SCpnt->sc_data_direction); 
 	/*
 	 * Update our cdb if necessary (to handle sbp2 RBC command set
-	 * differences).  This is where the command set hacks go!   =)
+	 * differences). This is where the command set hacks go!   =)
 	 */
 	if ((device_type == TYPE_DISK) ||
 	    (device_type == TYPE_SDAD) ||
@@ -2185,7 +2284,7 @@
 
 			sbp2_create_command_orb(hi, scsi_id, command, new_cmd, total_sg, 
 						total_transfer, &sgpnt[current_sg],
-						scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+						SCpnt->sc_data_direction);
 
 			/*
 			 * Link up the orb, and ring the doorbell if needed
@@ -2219,7 +2318,7 @@
 
 	sbp2_create_command_orb(hi, scsi_id, command, new_cmd, total_sg, 
 				total_transfer, &sgpnt[current_sg],
-				scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+				SCpnt->sc_data_direction);
 
 	/*
 	 * Link up the orb, and ring the doorbell if needed
@@ -2361,15 +2460,20 @@
 				     Scsi_Cmnd *SCpnt)
 {
 	u8 *scsi_buf = SCpnt->request_buffer;
-	u32 device_type = (scsi_id->sbp2_device_type_and_lun & 0x00ff0000) >> 16;
-        
+	u8 device_type = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun);
+
 	SBP2_DEBUG("sbp2_check_sbp2_response");
 
 	switch (SCpnt->cmnd[0]) {
 		
 		case INQUIRY:
 
-			SBP2_DEBUG("Check Inquiry data");
+			/*
+			 * Make sure data length is ok. Minimum length is 36 bytes
+			 */
+			if (scsi_buf[4] == 0) {
+				scsi_buf[4] = 36 - 5;
+			}
 
 			/*
 			 * Check for Simple Direct Access Device and change it to TYPE_DISK
@@ -2485,6 +2589,12 @@
 	if (command) {
 
 		SBP2_DEBUG("Found status for command ORB");
+		pci_dma_sync_single(hi->host->pdev, command->command_orb_dma,
+				    sizeof(struct sbp2_command_orb),
+				    PCI_DMA_BIDIRECTIONAL);
+		pci_dma_sync_single(hi->host->pdev, command->sge_dma,
+				    sizeof(command->scatter_gather_element),
+				    PCI_DMA_BIDIRECTIONAL);
 
 		SBP2_ORB_DEBUG("matched command orb %p", &command->command_orb);
 		outstanding_orb_decr;
@@ -2498,22 +2608,27 @@
 		if (SCpnt && !command->linked) {
 
 			/*
-			 * Handle check conditions
+			 * See if the target stored any scsi status information
 			 */
-			if (STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
-				SBP2_DEBUG("CHECK CONDITION");
-
+			if (length > 8) {
 				/*
 				 * Translate SBP-2 status to SCSI sense data
 				 */
 				scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer);
+			}
 
+			/*
+			 * Handle check conditions. If there is either SBP status or SCSI status
+			 * then we'll do a fetch agent reset and note that a check condition
+			 * occured.
+			 */
+			if (STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc) ||
+			    scsi_status) {
 				/*
 				 * Initiate a fetch agent reset. 
 				 */
+				SBP2_DEBUG("CHECK CONDITION");
 				sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT);
-
 			}
 
 			SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
@@ -2647,6 +2762,12 @@
 		SBP2_DEBUG("Found pending command to complete");
 		lh = scsi_id->sbp2_command_orb_inuse.next;
 		command = list_entry(lh, struct sbp2_command_info, list);
+		pci_dma_sync_single(hi->host->pdev, command->command_orb_dma,
+				    sizeof(struct sbp2_command_orb),
+				    PCI_DMA_BIDIRECTIONAL);
+		pci_dma_sync_single(hi->host->pdev, command->sge_dma,
+				    sizeof(command->scatter_gather_element),
+				    PCI_DMA_BIDIRECTIONAL);
 		sbp2util_mark_command_completed(scsi_id, command);
 		if (command->Current_SCpnt && !command->linked) {
 			void (*done)(Scsi_Cmnd *) = command->Current_done;
@@ -2704,13 +2825,17 @@
 			/*
 			 * Debug stuff
 			 */
+#if CONFIG_IEEE1394_SBP2_DEBUG >= 1
+			print_command (SCpnt->cmnd);
 			print_sense("bh", SCpnt);
+#endif
 
 			break;
 
 		case SBP2_SCSI_STATUS_SELECTION_TIMEOUT:
 			SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT");
 			SCpnt->result = DID_NO_CONNECT << 16;
+			print_command (SCpnt->cmnd);
 			break;
 
 		case SBP2_SCSI_STATUS_CONDITION_MET:
@@ -2718,6 +2843,7 @@
 		case SBP2_SCSI_STATUS_COMMAND_TERMINATED:
 			SBP2_ERR("Bad SCSI status = %x", scsi_status);
 			SCpnt->result = DID_ERROR << 16;
+			print_command (SCpnt->cmnd);
 			break;
 
 		default:
@@ -2733,19 +2859,6 @@
 	}
 
 	/*
-	 * One more quick hack (not enabled by default). Some sbp2 devices
-	 * do not support mode sense. Turn-on this hack to allow the
-	 * device to pass the sd driver's write-protect test (so that you
-	 * can mount the device rw).
-	 */
-	if (mode_sense_hack && SCpnt->result != DID_OK && SCpnt->cmnd[0] == MODE_SENSE) {
-		SBP2_INFO("Returning success to mode sense command");
-		SCpnt->result = DID_OK;
-		SCpnt->sense_buffer[0] = 0;
-		memset (SCpnt->request_buffer, 0, 8);
-	}
-
-	/*
 	 * If a bus reset is in progress and there was an error, complete
 	 * the command as busy so that it will get retried.
 	 */
@@ -2784,7 +2897,8 @@
 	unsigned long flags;
 
 	SBP2_ERR("aborting sbp2 command");
-
+	print_command (SCpnt->cmnd);
+        
 	if (scsi_id) {
 
 		/*
@@ -2797,6 +2911,14 @@
 			command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt);
 			if (command) {
 				SBP2_DEBUG("Found command to abort");
+				pci_dma_sync_single(hi->host->pdev,
+						    command->command_orb_dma,
+						    sizeof(struct sbp2_command_orb),
+						    PCI_DMA_BIDIRECTIONAL);
+				pci_dma_sync_single(hi->host->pdev,
+						    command->sge_dma,
+						    sizeof(command->scatter_gather_element),
+						    PCI_DMA_BIDIRECTIONAL);
 				sbp2util_mark_command_completed(scsi_id, command);
 				if (command->Current_SCpnt && !command->linked) {
 					void (*done)(Scsi_Cmnd *) = command->Current_done;
@@ -2814,13 +2936,13 @@
 		sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
 	}
 
-	return(SCSI_ABORT_SUCCESS);
+	return(SUCCESS);
 }
 
 /*
  * Called by scsi stack when something has really gone wrong.
  */
-static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) 
+static int sbp2scsi_reset (Scsi_Cmnd *SCpnt) 
 {
 	struct sbp2scsi_host_info *hi = (struct sbp2scsi_host_info *) SCpnt->host->hostdata[0];
 
@@ -2831,7 +2953,7 @@
 		hpsb_reset_bus(hi->host, LONG_RESET);
 	}
 
-	return(SCSI_RESET_SUCCESS);
+	return(SUCCESS);
 }
 
 /*
@@ -2861,126 +2983,146 @@
 }
 
 /*
- * This routine is called at setup (init) and does nothing. Not used here.   =)
+ * Called by scsi stack after scsi driver is registered
  */
-void sbp2scsi_setup( char *str, int *ints) 
+static int sbp2scsi_detect (Scsi_Host_Template *tpnt) 
 {
-	SBP2_DEBUG("sbp2scsi_setup");
-	return;
+	SBP2_DEBUG("sbp2scsi_detect");
+
+	/*
+	 * Call sbp2_init to register with the ieee1394 stack.  This
+	 * results in a callback to sbp2_add_host for each ieee1394
+	 * host controller currently registered, and for each of those
+	 * we register a scsi host with the scsi stack.
+	 */
+	sbp2_init();
+
+	/* We return the number of hosts registered. */
+	return scsi_driver_template.present;
 }
 
+
 /*
- * This is our detection routine, and is where we init everything.
+ * Called for contents of procfs
  */
-static int sbp2scsi_detect (Scsi_Host_Template *tpnt) 
+static const char *sbp2scsi_info (struct Scsi_Host *host)
 {
-	SBP2_DEBUG("sbp2scsi_detect");
+	struct sbp2scsi_host_info *hi = sbp2_find_host_info_scsi(host);
+	static char info[1024];
 
-	global_scsi_tpnt = tpnt;
+	if (!hi) /* shouldn't happen, but... */
+        	return "IEEE-1394 SBP-2 protocol driver";
+
+	sprintf(info, "IEEE-1394 SBP-2 protocol driver\nHost Driver: %s\nSerial I/O: %s",
+		hi->host->driver->name, serialize_io ? "yes" : "no");
+
+	return info;
+}
+
+
+MODULE_AUTHOR("James Goodwin <jamesg@filanet.com>");
+MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
+MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
+MODULE_LICENSE("GPL");
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,26)
+#define PROC_SCSI_SBP2 PROC_SCSI_NOT_PRESENT /* What should I use? */
+static struct proc_dir_entry proc_scsi_sbp2scsi = {
+	low_ino:	PROC_SCSI_SBP2,
+	namelen:	SBP2_DEVICE_NAME_SIZE,
+	name:		SBP2_DEVICE_NAME,
+	mode:		S_IFDIR | S_IRUGO | S_IXUGO,
+	nlink:		2
+};
+#endif
+
+/* SCSI host template */
+static Scsi_Host_Template scsi_driver_template = {
+	name:			"IEEE-1394 SBP-2 protocol driver",
+	info:			sbp2scsi_info,
+	detect:			sbp2scsi_detect,
+	queuecommand:		sbp2scsi_queuecommand,
+	eh_abort_handler:	sbp2scsi_abort,
+	eh_device_reset_handler:sbp2scsi_reset,
+	eh_bus_reset_handler:	sbp2scsi_reset,
+	eh_host_reset_handler:	sbp2scsi_reset,
+	bios_param:		sbp2scsi_biosparam,
+	can_queue:		SBP2SCSI_MAX_OUTSTANDING_CMDS,
+	this_id:		-1,
+	sg_tablesize:		SBP2_MAX_SG_ELEMENTS,
+	cmd_per_lun:		SBP2SCSI_MAX_CMDS_PER_LUN,
+	use_clustering:		SBP2_CLUSTERING,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	use_new_eh_code:	TRUE,
+#endif
+	emulated:		1,
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,26)
-	global_scsi_tpnt->proc_name = SBP2_DEVICE_NAME;
+	proc_name:	SBP2_DEVICE_NAME,
+#else
+	proc_dir:	&proc_scsi_sbp2scsi,
 #endif
+};
+
+static int sbp2_module_init(void)
+{
+	SBP2_DEBUG("sbp2_module_init");
+
 	/*
 	 * Module load option for force one command at a time
 	 */
 	if (serialize_io) {
 		SBP2_ERR("Driver forced to serialize I/O (serialize_io = 1)");
-		global_scsi_tpnt->can_queue = 1;
-		global_scsi_tpnt->cmd_per_lun = 1;
+		scsi_driver_template.can_queue = 1;
+		scsi_driver_template.cmd_per_lun = 1;
 	}
 
 	/*
-	 * Module load option to limit max size of requests from the scsi drivers
+	 * Module load option to limit max size of requests from the
+	 * scsi drivers
 	 */
 	if (no_large_packets) {
-		SBP2_ERR("Driver forced to limit max transfer size (no_large_packets = 1)");
-		global_scsi_tpnt->sg_tablesize = 0x1f;
-		global_scsi_tpnt->use_clustering = DISABLE_CLUSTERING;
+		SBP2_ERR("Driver forced to limit max transfer size "
+			 "(no_large_packets = 1)");
+		scsi_driver_template.sg_tablesize = 0x1f;
+		scsi_driver_template.use_clustering = DISABLE_CLUSTERING;
 	}
 
-	if (mode_sense_hack) {
-		SBP2_ERR("Mode sense emulation enabled (mode_sense_hack = 1)");
-	}
-
-	sbp2_init();
-
-	if (!sbp2_host_count) {
-		SBP2_ERR("Please load the lower level IEEE-1394 driver (e.g. ohci1394) before sbp2...");
+	/*
+	 * Ideally we would register our scsi_driver_template with the
+	 * scsi stack and after that register with the ieee1394 stack
+	 * and process the add_host callbacks.  However, the detect
+	 * function in the scsi host template requires that we find at
+	 * least one host, so we "nest" the registrations by calling
+	 * sbp2_init from the detect function.
+	 */
+	scsi_driver_template.module = THIS_MODULE;
+	if (SCSI_REGISTER_HOST(&scsi_driver_template) ||
+	    !scsi_driver_template.present) {
+		SBP2_ERR("Please load the lower level IEEE-1394 driver "
+			 "(e.g. ohci1394) before sbp2...");
 		sbp2_cleanup();
+		return -ENODEV;
 	}
 
-	/*
-	 * Since we are returning this count, it means that sbp2 must be
-	 * loaded "after" the host adapter module...
-	 */
-	return(sbp2_host_count);
+	return 0;
 }
 
-/*
- * This function is called from sbp2_add_host, and is where we register
- * our scsi host
- */
-static void sbp2scsi_register_scsi_host(struct sbp2scsi_host_info *hi)
+static void __exit sbp2_module_exit(void)
 {
-	struct Scsi_Host *shpnt = NULL;
-
-	SBP2_DEBUG("sbp2scsi_register_scsi_host");
-	SBP2_DEBUG("sbp2scsi_host_info = %p", hi);
+	SBP2_DEBUG("sbp2_module_exit");
 
 	/*
-	 * Let's register with the scsi stack
+	 * On module unload we unregister with the ieee1394 stack
+	 * which results in remove_host callbacks for all ieee1394
+	 * host controllers.  In the callbacks we unregister the
+	 * corresponding scsi hosts.
 	 */
-	if (global_scsi_tpnt) {
-
-		shpnt = scsi_register (global_scsi_tpnt, sizeof(void *));
-
-		/*
-		 * If successful, save off a context (to be used when SCSI
-		 * commands are received)
-		 */
-		if (shpnt) {
-			shpnt->hostdata[0] = (unsigned long)hi;
-		}
-	}
-
-	return;
-}
-
-/* Called when our module is released */
-static int sbp2scsi_release(struct Scsi_Host *host)
-{
-	SBP2_DEBUG("sbp2scsi_release");
 	sbp2_cleanup();
-	return(0);
-}
-
-/* Called for contents of procfs */
-static const char *sbp2scsi_info (struct Scsi_Host *host)
-{
-	return "IEEE-1394 SBP-2 protocol driver";
-}
 
-MODULE_AUTHOR("James Goodwin <jamesg@filanet.com>");
-MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
-MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
-MODULE_LICENSE("GPL");
+	if (SCSI_UNREGISTER_HOST(&scsi_driver_template))
+		SBP2_ERR("sbp2_module_exit: couldn't unregister scsi driver");
 
-/* SCSI host template */
-static Scsi_Host_Template driver_template = {
-	name:		"IEEE1394 SBP-2",
-	detect:		sbp2scsi_detect,
-	release:	sbp2scsi_release,
-	info:		sbp2scsi_info,
-	queuecommand:	sbp2scsi_queuecommand,
-	abort:		sbp2scsi_abort,
-	reset:		sbp2scsi_reset,
-	bios_param:	sbp2scsi_biosparam,
-	can_queue:	SBP2SCSI_MAX_OUTSTANDING_CMDS,
-	this_id:	-1,
-	sg_tablesize:	SBP2_MAX_SG_ELEMENTS,
-	cmd_per_lun:	SBP2SCSI_MAX_CMDS_PER_LUN,
-	use_clustering:	SBP2_CLUSTERING,
-	emulated:	1
-};
+}
 
-#include "../scsi/scsi_module.c"
+module_init(sbp2_module_init);
+module_exit(sbp2_module_exit);
diff -urN linux-2.5.3-pre2/drivers/ieee1394/sbp2.h linux/drivers/ieee1394/sbp2.h
--- linux-2.5.3-pre2/drivers/ieee1394/sbp2.h	Wed Oct 17 14:19:20 2001
+++ linux/drivers/ieee1394/sbp2.h	Mon Jan 21 17:23:22 2002
@@ -22,6 +22,15 @@
 #ifndef SBP2_H
 #define SBP2_H
 
+/* Some compatibility code */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#define SCSI_REGISTER_HOST(tmpl)	scsi_register_module(MODULE_SCSI_HA, tmpl)
+#define SCSI_UNREGISTER_HOST(tmpl)	scsi_unregister_module(MODULE_SCSI_HA, tmpl)
+#else
+#define SCSI_REGISTER_HOST(tmpl)	scsi_register_host(tmpl)
+#define SCSI_UNREGISTER_HOST(tmpl)	scsi_unregister_host(tmpl)
+#endif
+
 #define SBP2_DEVICE_NAME		"sbp2"
 #define SBP2_DEVICE_NAME_SIZE		4
 
@@ -36,6 +45,7 @@
 #define ORB_DIRECTION_READ_FROM_MEDIA   0x1
 #define ORB_DIRECTION_NO_DATA_TRANSFER  0x2
 
+#define ORB_SET_NULL_PTR(value)			((value & 0x1) << 31)
 #define ORB_SET_NOTIFY(value)                   ((value & 0x1) << 31)
 #define ORB_SET_RQ_FMT(value)                   ((value & 0x3) << 29)
 #define ORB_SET_NODE_ID(value)			((value & 0xffff) << 16)
@@ -199,6 +209,9 @@
 #define SBP2_DEVICE_TYPE_AND_LUN_KEY				0x14
 #define SBP2_FIRMWARE_REVISION_KEY				0x3c
 
+#define SBP2_DEVICE_TYPE(q)			(((q) >> 16) & 0x1f)
+#define SBP2_DEVICE_LUN(q)			((q) & 0xffff)
+
 #define SBP2_AGENT_STATE_OFFSET					0x00ULL
 #define SBP2_AGENT_RESET_OFFSET					0x04ULL
 #define SBP2_ORB_POINTER_OFFSET					0x08ULL
@@ -218,15 +231,6 @@
 #define SBP2_UNIT_SPEC_ID_ENTRY					0x0000609e
 #define SBP2_SW_VERSION_ENTRY					0x00010483
 
-/*
- * Miscellaneous general config rom related defines
- */
-
-#define CONFIG_ROM_INITIAL_MEMORY_SPACE 			0xfffff0000000ULL
-
-#define CONFIG_ROM_BASE_ADDRESS					0xfffff0000400ULL
-#define CONFIG_ROM_ROOT_DIR_BASE				0xfffff0000414ULL
-#define CONFIG_ROM_UNIT_DIRECTORY_OFFSET			0xfffff0000424ULL
 
 #define SBP2_128KB_BROKEN_FIRMWARE				0xa0b800
 #define SBP2_BROKEN_FIRMWARE_MAX_TRANSFER			0x20000
@@ -252,7 +256,8 @@
 #endif
 
 /*
- * SCSI direction table... since the scsi stack doesn't specify direction...   =(
+ * SCSI direction table... 
+ * (now used as a back-up in case the direction passed down from above is "unknown")
  *
  * DIN = IN data direction
  * DOU = OUT data direction
@@ -306,21 +311,27 @@
 
 };
 
+
+/* This is the two dma types we use for cmd_dma below */
+#define CMD_DMA_NONE   0x0
+#define CMD_DMA_PAGE   0x1
+#define CMD_DMA_SINGLE 0x2
+
 /* 
  * Encapsulates all the info necessary for an outstanding command. 
  */
 struct sbp2_command_info {
 
 	struct list_head list;
-	struct sbp2_command_orb command_orb;
-	dma_addr_t command_orb_dma;
+	struct sbp2_command_orb command_orb ____cacheline_aligned;
+	dma_addr_t command_orb_dma ____cacheline_aligned;
 	Scsi_Cmnd *Current_SCpnt;
 	void (*Current_done)(Scsi_Cmnd *);
 	unsigned int linked;
 
 	/* Also need s/g structure for each sbp2 command */
-	struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS];
-	dma_addr_t sge_dma;
+	struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS] ____cacheline_aligned;
+	dma_addr_t sge_dma ____cacheline_aligned;
 	void *sge_buffer;
 	dma_addr_t cmd_dma;
 	int dma_type;
@@ -340,6 +351,7 @@
 	 * Various sbp2 specific structures
 	 */
 	struct sbp2_command_orb *last_orb;
+	dma_addr_t last_orb_dma;
 	struct sbp2_login_orb *login_orb;
 	dma_addr_t login_orb_dma;
 	struct sbp2_login_response *login_response;
@@ -403,6 +415,13 @@
 	spinlock_t sbp2_request_packet_lock;
 
 	/*
+	 * This is the scsi host we register with the scsi mid level.
+	 * We keep a reference to it here, so we can unregister it
+	 * when the hpsb_host is removed.
+	 */
+	struct Scsi_Host *scsi_host;
+
+	/*
 	 * Lists keeping track of inuse/free sbp2_request_packets. These structures are
 	 * used for sending out sbp2 command and agent reset packets. We initially create
 	 * a pool of request packets so that we don't have to do any kmallocs while in critical
@@ -480,7 +499,8 @@
 				   unchar *scsi_cmd,
 				   unsigned int scsi_use_sg,
 				   unsigned int scsi_request_bufflen,
-				   void *scsi_request_buffer, int dma_dir);
+				   void *scsi_request_buffer, 
+				   unsigned char scsi_dir);
 static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
 				 struct sbp2_command_info *command);
 static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
@@ -498,17 +518,16 @@
 /*
  * Scsi interface related prototypes
  */
-static const char *sbp2scsi_info (struct Scsi_Host *host);
 static int sbp2scsi_detect (Scsi_Host_Template *tpnt);
+static const char *sbp2scsi_info (struct Scsi_Host *host);
 void sbp2scsi_setup(char *str, int *ints);
 static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]);
 static int sbp2scsi_abort (Scsi_Cmnd *SCpnt); 
-static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags); 
+static int sbp2scsi_reset (Scsi_Cmnd *SCpnt); 
 static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
 static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, 
 					   u32 status);
 static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, 
 				      u32 scsi_status, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
-static void sbp2scsi_register_scsi_host(struct sbp2scsi_host_info *hi);
 
 #endif /* SBP2_H */
diff -urN linux-2.5.3-pre2/drivers/ieee1394/video1394.c linux/drivers/ieee1394/video1394.c
--- linux-2.5.3-pre2/drivers/ieee1394/video1394.c	Sun Dec 30 10:31:51 2001
+++ linux/drivers/ieee1394/video1394.c	Mon Jan 21 17:23:22 2002
@@ -43,9 +43,12 @@
 #include <asm/pgtable.h>
 #include <asm/page.h>
 #include <linux/sched.h>
+#include <asm/segment.h>
 #include <linux/types.h>
 #include <linux/wrapper.h>
 #include <linux/vmalloc.h>
+#include <linux/timex.h>
+#include <linux/mm.h>
 
 #include "ieee1394.h"
 #include "ieee1394_types.h"
@@ -56,7 +59,6 @@
 
 #include "ohci1394.h"
 
-#define VIDEO1394_MAJOR 172
 #define ISO_CHANNELS 64
 #define ISO_RECEIVE 0
 #define ISO_TRANSMIT 1
@@ -78,6 +80,7 @@
 
 struct dma_iso_ctx {
 	struct ti_ohci *ohci;
+	int type; /* ISO_TRANSMIT or ISO_RECEIVE */
 	int ctx;
 	int channel;
 	int last_buffer;
@@ -94,6 +97,7 @@
         struct dma_cmd **ir_prg;
 	struct it_dma_prg **it_prg;
 	unsigned int *buffer_status;
+        struct timeval *buffer_time; /* time when the buffer was received */
 	unsigned int *last_used_cmd; /* For ISO Transmit with 
 					variable sized packets only ! */
 	int ctrlClear;
@@ -101,8 +105,8 @@
 	int cmdPtr;
 	int ctxMatch;
 	wait_queue_head_t waitq;
-        spinlock_t lock;
-    unsigned int syt_offset;
+	spinlock_t lock;
+	unsigned int syt_offset;
 	int flags;
 };
 
@@ -141,7 +145,7 @@
 printk(level "video1394_%d: " fmt "\n" , card , ## args)
 
 static void irq_handler(int card, quadlet_t isoRecvIntEvent, 
-		 quadlet_t isoXmitIntEvent);
+			quadlet_t isoXmitIntEvent, void *data);
 
 static LIST_HEAD(video1394_cards);
 static spinlock_t video1394_cards_lock = SPIN_LOCK_UNLOCKED;
@@ -149,8 +153,6 @@
 static devfs_handle_t devfs_handle;
 static struct hpsb_highlevel *hl_handle = NULL;
 
-static struct video_template video_tmpl = { irq_handler };
-
 /* Code taken from bttv.c */
 
 /*******************************/
@@ -277,6 +279,7 @@
 {
 	int i;
 	struct ti_ohci *ohci;
+	unsigned long *usage;
 	
 	if ((*d)==NULL) return -1;
 
@@ -303,11 +306,19 @@
 
 	if ((*d)->buffer_status)
 		kfree((*d)->buffer_status);
+	if ((*d)->buffer_time)
+		kfree((*d)->buffer_time);
 	if ((*d)->last_used_cmd)
 		kfree((*d)->last_used_cmd);
 	if ((*d)->next_buffer)
 		kfree((*d)->next_buffer);
 
+	usage = ((*d)->type == ISO_RECEIVE) ? &ohci->ir_ctx_usage :
+		&ohci->it_ctx_usage;
+       
+	/* clear the ISO context usage bit */
+	clear_bit((*d)->ctx, usage);
+	
 	kfree(*d);
 	*d = NULL;
 
@@ -315,12 +326,28 @@
 }
 
 static struct dma_iso_ctx *
-alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc,
+alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
 		  int buf_size, int channel, unsigned int packet_size)
 {
 	struct dma_iso_ctx *d=NULL;
 	int i;
 
+	unsigned long *usage = (type == ISO_RECEIVE) ? &ohci->ir_ctx_usage :
+		                                       &ohci->it_ctx_usage;
+
+	/* try to claim the ISO context usage bit */
+	for (i = 0; i < ohci->nb_iso_rcv_ctx; i++) {
+		if (!test_and_set_bit(i, usage)) {
+			PRINT(KERN_ERR, ohci->id, "Free iso ctx %d found", i);
+			break;
+		}
+	}
+
+	if (i == ohci->nb_iso_rcv_ctx) {
+		PRINT(KERN_ERR, ohci->id, "No DMA contexts available");
+		return NULL;
+	}
+	
 	d = (struct dma_iso_ctx *)kmalloc(sizeof(struct dma_iso_ctx), 
 					  GFP_KERNEL);
 	if (d==NULL) {
@@ -331,7 +358,8 @@
 	memset(d, 0, sizeof(struct dma_iso_ctx));
 
 	d->ohci = (void *)ohci;
-	d->ctx = ctx;
+	d->type = type;
+	d->ctx = i;
 	d->channel = channel;
 	d->num_desc = num_desc;
 	d->frame_size = buf_size;
@@ -436,6 +464,8 @@
 
 	d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int),
 				   GFP_KERNEL);
+	d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval),
+				   GFP_KERNEL);
 	d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int),
 				   GFP_KERNEL);
 	d->next_buffer = kmalloc(d->num_desc * sizeof(int),
@@ -446,6 +476,11 @@
 		free_dma_iso_ctx(&d);
 		return NULL;
 	}
+	if (d->buffer_time == NULL) {
+		PRINT(KERN_ERR, ohci->id, "Failed to allocate buffer_time");
+		free_dma_iso_ctx(&d);
+		return NULL;
+	}
 	if (d->last_used_cmd == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate last_used_cmd");
 		free_dma_iso_ctx(&d);
@@ -457,6 +492,7 @@
 		return NULL;
 	}
 	memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int));
+	memset(d->buffer_time, 0, d->num_desc * sizeof(struct timeval));
 	memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int));
 	memset(d->next_buffer, -1, d->num_desc * sizeof(int));
 	
@@ -554,38 +590,38 @@
 }
 
 /* find which context is listening to this channel */
-int ir_ctx_listening(struct video_card *video, int channel)
+static struct dma_iso_ctx **
+ir_ctx_listening(struct video_card *video, int channel)
 {
 	int i;
 	struct ti_ohci *ohci = video->ohci;
 
-	for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) 
-		if (video->ir_context[i]) {
-			if (video->ir_context[i]->channel==channel)
-				return i;
-		}
+	for (i = 0; i < ohci->nb_iso_rcv_ctx-1; i++) 
+		if (video->ir_context[i] &&
+		    video->ir_context[i]->channel == channel)
+			return &video->ir_context[i];
 		
 	PRINT(KERN_ERR, ohci->id, "No iso context is listening to channel %d",
 	      channel);
 
-	return -1;
+	return NULL;
 }
 
-int it_ctx_talking(struct video_card *video, int channel)
+static struct dma_iso_ctx **
+it_ctx_talking(struct video_card *video, int channel)
 {
 	int i;
 	struct ti_ohci *ohci = video->ohci;
 
-	for (i=0;i<ohci->nb_iso_xmit_ctx;i++) 
-		if (video->it_context[i]) {
-			if (video->it_context[i]->channel==channel)
-				return i;
-		}
+	for (i = 0; i < ohci->nb_iso_xmit_ctx; i++) 
+		if (video->it_context[i] &&
+		    video->it_context[i]->channel==channel)
+			return &video->ir_context[i];
 		
 	PRINT(KERN_ERR, ohci->id, "No iso context is talking to channel %d",
 	      channel);
 
-	return -1;
+	return NULL;
 }
 
 int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) 
@@ -603,6 +639,7 @@
 		if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) {
 			reset_ir_status(d, i);
 			d->buffer_status[i] = VIDEO1394_BUFFER_READY;
+			get_fast_time(&d->buffer_time[i]);
 		}
 	}
 	spin_unlock(&d->lock);
@@ -850,7 +887,7 @@
 		struct video_card *p;
 		list_for_each(lh, &video1394_cards) {
 			p = list_entry(lh, struct video_card, list);
-			if (p->id == MINOR(inode->i_rdev)) {
+			if (p->id == ieee1394_file_to_instance(file)) {
 				video = p;
 				ohci = video->ohci;
 				break;
@@ -860,7 +897,8 @@
 	spin_unlock_irqrestore(&video1394_cards_lock, flags);
 
 	if (video == NULL) {
-		PRINT_G(KERN_ERR, __FUNCTION__": Unknown video card for minor %d", MINOR(inode->i_rdev));
+		PRINT_G(KERN_ERR, "%s: Unknown video card for minor %d",
+			__FUNCTION__, ieee1394_file_to_instance(file));
 		return -EFAULT;
 	}
 
@@ -875,9 +913,23 @@
 
 		if(copy_from_user(&v, (void *)arg, sizeof(v)))
 			return -EFAULT;
+
+		/* if channel < 0, find lowest available one */
+		if (v.channel < 0) {
+		    mask = (u64)0x1;
+		    for (i=0; i<ISO_CHANNELS; i++) {
+			if (!(ohci->ISO_channel_usage & mask)) {
+			    v.channel = i;
+			    PRINT(KERN_INFO, ohci->id, "Found free channel %d", i);
+			    break;
+			}
+			mask = mask << 1;
+		    }
+		}
+		    
 		if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) {
 			PRINT(KERN_ERR, ohci->id, 
-			      "Iso channel %d out of bound", v.channel);
+			      "Iso channel %d out of bounds", v.channel);
 			return -EFAULT;
 		}
 		mask = (u64)0x1<<v.channel;
@@ -923,7 +975,7 @@
 			}
 
 			video->ir_context[i] = 
-				alloc_dma_iso_ctx(ohci, ISO_RECEIVE, i+1, 
+				alloc_dma_iso_ctx(ohci, ISO_RECEIVE,
 						  v.nb_buffers, v.buf_size, 
 						  v.channel, 0);
 
@@ -940,8 +992,8 @@
 			v.buf_size = video->ir_context[i]->buf_size;
 
 			PRINT(KERN_INFO, ohci->id, 
-			      "iso context %d listen on channel %d", i+1, 
-			      v.channel);
+			      "iso context %d listen on channel %d",
+			      video->current_ctx->ctx, v.channel);
 		}
 		else {
 			/* find a free iso transmit context */
@@ -955,7 +1007,7 @@
 			}
 			
 			video->it_context[i] = 
-				alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, i, 
+				alloc_dma_iso_ctx(ohci, ISO_TRANSMIT,
 						  v.nb_buffers, v.buf_size, 
 						  v.channel, v.packet_size);
 
@@ -986,7 +1038,6 @@
 	{
 		int channel;
 		u64 mask;
-		int i;
 
 		if(copy_from_user(&channel, (void *)arg, sizeof(int)))
 			return -EFAULT;
@@ -1005,24 +1056,23 @@
 		ohci->ISO_channel_usage &= ~mask;
 
 		if (cmd == VIDEO1394_UNLISTEN_CHANNEL) {
-			i = ir_ctx_listening(video, channel);
-			if (i<0) return -EFAULT;
-
-			free_dma_iso_ctx(&video->ir_context[i]);
-
+			struct dma_iso_ctx **d;
+			d = ir_ctx_listening(video, channel);
+			if (d == NULL) return -EFAULT;
 			PRINT(KERN_INFO, ohci->id, 
 			      "Iso context %d stop listening on channel %d", 
-			      i+1, channel);
+			      (*d)->ctx, channel);
+			free_dma_iso_ctx(d);
 		}
 		else {
-			i = it_ctx_talking(video, channel);
-			if (i<0) return -EFAULT;
-
-			free_dma_iso_ctx(&video->it_context[i]);
-
+			struct dma_iso_ctx **d;
+			d = it_ctx_talking(video, channel);
+			if (d == NULL) return -EFAULT;
 			PRINT(KERN_INFO, ohci->id, 
 			      "Iso context %d stop talking on channel %d", 
-			      i, channel);
+			      (*d)->ctx, channel);
+			free_dma_iso_ctx(d);
+
 		}
 		
 		return 0;
@@ -1030,15 +1080,14 @@
 	case VIDEO1394_LISTEN_QUEUE_BUFFER:
 	{
 		struct video1394_wait v;
-		struct dma_iso_ctx *d;
-		int i;
+		struct dma_iso_ctx *d, **dd;
 
 		if(copy_from_user(&v, (void *)arg, sizeof(v)))
 			return -EFAULT;
 
-		i = ir_ctx_listening(video, v.channel);
-		if (i<0) return -EFAULT;
-		d = video->ir_context[i];
+		dd = ir_ctx_listening(video, v.channel);
+		if (dd == NULL) return -EFAULT;
+		d = *dd;
 
 		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
 			PRINT(KERN_ERR, ohci->id, 
@@ -1091,17 +1140,18 @@
 		
 	}
 	case VIDEO1394_LISTEN_WAIT_BUFFER:
+	case VIDEO1394_LISTEN_POLL_BUFFER:
 	{
 		struct video1394_wait v;
-		struct dma_iso_ctx *d;
+		struct dma_iso_ctx *d, **dd;
 		int i;
 
 		if(copy_from_user(&v, (void *)arg, sizeof(v)))
 			return -EFAULT;
 
-		i = ir_ctx_listening(video, v.channel);
-		if (i<0) return -EFAULT;
-		d = video->ir_context[i];
+		dd = ir_ctx_listening(video, v.channel);
+		if (dd==NULL) return -EFAULT;
+		d = *dd;
 
 		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
 			PRINT(KERN_ERR, ohci->id, 
@@ -1119,6 +1169,12 @@
 			d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
 			break;
 		case VIDEO1394_BUFFER_QUEUED:
+			if (cmd == VIDEO1394_LISTEN_POLL_BUFFER) {
+			    /* for polling, return error code EINTR */
+			    spin_unlock_irqrestore(&d->lock, flags);
+			    return -EINTR;
+			}
+
 #if 1
 			while(d->buffer_status[v.buffer]!=
 			      VIDEO1394_BUFFER_READY) {
@@ -1146,6 +1202,10 @@
 			return -EFAULT;
 		}
 
+		/* set time of buffer */
+		v.filltime = d->buffer_time[v.buffer];
+//		printk("Buffer %d time %d\n", v.buffer, (d->buffer_time[v.buffer]).tv_usec);
+
 		/*
 		 * Look ahead to see how many more buffers have been received
 		 */
@@ -1167,15 +1227,14 @@
 	{
 		struct video1394_wait v;
 		struct video1394_queue_variable qv;
-		struct dma_iso_ctx *d;
-		int i;
+		struct dma_iso_ctx *d, **dd;
 
 		if(copy_from_user(&v, (void *)arg, sizeof(v)))
 			return -EFAULT;
 
-		i = it_ctx_talking(video, v.channel);
-		if (i<0) return -EFAULT;
-		d = video->it_context[i];
+		dd = it_ctx_talking(video, v.channel);
+		if (dd == NULL) return -EFAULT;
+		d = *dd;
 
 		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
 			PRINT(KERN_ERR, ohci->id, 
@@ -1259,15 +1318,14 @@
 	case VIDEO1394_TALK_WAIT_BUFFER:
 	{
 		struct video1394_wait v;
-		struct dma_iso_ctx *d;
-		int i;
+		struct dma_iso_ctx *d, **dd;
 
 		if(copy_from_user(&v, (void *)arg, sizeof(v)))
 			return -EFAULT;
 
-		i = it_ctx_talking(video, v.channel);
-		if (i<0) return -EFAULT;
-		d = video->it_context[i];
+		dd = it_ctx_talking(video, v.channel);
+		if (dd == NULL) return -EFAULT;
+		d = *dd;
 
 		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
 			PRINT(KERN_ERR, ohci->id, 
@@ -1328,7 +1386,7 @@
 		struct video_card *p;
 		list_for_each(lh, &video1394_cards) {
 			p = list_entry(lh, struct video_card, list);
-			if (p->id == MINOR(file->f_dentry->d_inode->i_rdev)) {
+			if (p->id == ieee1394_file_to_instance(file)) {
 				video = p;
 				break;
 			}
@@ -1337,8 +1395,8 @@
         spin_unlock_irqrestore(&video1394_cards_lock, flags);
 
 	if (video == NULL) {
-		PRINT_G(KERN_ERR, __FUNCTION__": Unknown video card for minor %d",
-			MINOR(file->f_dentry->d_inode->i_rdev));
+		PRINT_G(KERN_ERR, "%s: Unknown video card for minor %d",
+			__FUNCTION__, ieee1394_file_to_instance(file));
 		return -EFAULT;
 	}
 
@@ -1357,7 +1415,7 @@
 
 static int video1394_open(struct inode *inode, struct file *file)
 {
-	int i = MINOR(inode->i_rdev);
+	int i = ieee1394_file_to_instance(file);
 	unsigned long flags;
 	struct video_card *video = NULL;
 	struct list_head *lh;
@@ -1397,7 +1455,7 @@
 		struct video_card *p;
 		list_for_each(lh, &video1394_cards) {
 			p = list_entry(lh, struct video_card, list);
-			if (p->id == MINOR(inode->i_rdev)) {
+			if (p->id == ieee1394_file_to_instance(file)) {
 				video = p;
 				break;
 			}
@@ -1406,8 +1464,8 @@
         spin_unlock_irqrestore(&video1394_cards_lock, flags);
 
 	if (video == NULL) {
-		PRINT_G(KERN_ERR, __FUNCTION__": Unknown device for minor %d",
-				MINOR(inode->i_rdev));
+		PRINT_G(KERN_ERR, "%s: Unknown device for minor %d",
+			__FUNCTION__, ieee1394_file_to_instance(file));
 		return 1;
 	}
 
@@ -1419,13 +1477,13 @@
 			mask = (u64)0x1<<video->ir_context[i]->channel;
 			if (!(ohci->ISO_channel_usage & mask))
 				PRINT(KERN_ERR, ohci->id, 
-				      "Channel %d is not being used", 
+				      "On release: Channel %d is not being used", 
 				      video->ir_context[i]->channel);
 			else
 				ohci->ISO_channel_usage &= ~mask;
 			PRINT(KERN_INFO, ohci->id, 
 			      "Iso receive context %d stop listening "
-			      "on channel %d", i+1, 
+			      "on channel %d", video->ir_context[i]->ctx,
 			      video->ir_context[i]->channel);
 			free_dma_iso_ctx(&video->ir_context[i]);
 		}
@@ -1441,7 +1499,7 @@
 				ohci->ISO_channel_usage &= ~mask;
 			PRINT(KERN_INFO, ohci->id, 
 			      "Iso transmit context %d stop talking "
-			      "on channel %d", i+1, 
+			      "on channel %d", video->it_context[i]->ctx,
 			      video->it_context[i]->channel);
 			free_dma_iso_ctx(&video->it_context[i]);
 		}
@@ -1453,44 +1511,29 @@
 }
 
 static void irq_handler(int card, quadlet_t isoRecvIntEvent, 
-		 quadlet_t isoXmitIntEvent)
+			quadlet_t isoXmitIntEvent, void *data)
 {
 	int i;
-	unsigned long flags;
-	struct video_card *video = NULL;
-	struct list_head *lh;
-
-	spin_lock_irqsave(&video1394_cards_lock, flags);
-	if (!list_empty(&video1394_cards)) {
-		struct video_card *p;
-		list_for_each(lh, &video1394_cards) {
-			p = list_entry(lh, struct video_card, list);
-			if (p->id == card) {
-				video = p;
-				break;
-			}
-                }
-        }
-        spin_unlock_irqrestore(&video1394_cards_lock, flags);
+	struct video_card *video = (struct video_card*) data;
 
 	if (video == NULL) {
-		PRINT_G(KERN_ERR, __FUNCTION__": Unknown card number %d!!",
-				card);
+		PRINT_G(KERN_ERR, "%s: Unknown card number %d",
+			__FUNCTION__, card);
 		return;
 	}
 	
 	DBGMSG(card, "Iso event Recv: %08x Xmit: %08x",
 	       isoRecvIntEvent, isoXmitIntEvent);
 
-	for (i=0;i<video->ohci->nb_iso_rcv_ctx-1;i++)
-		if (isoRecvIntEvent & (1<<(i+1))) 
-			wakeup_dma_ir_ctx(video->ohci,
-					  video->ir_context[i]);
-
-	for (i=0;i<video->ohci->nb_iso_xmit_ctx;i++)
-		if (isoXmitIntEvent & (1<<i)) 
-			wakeup_dma_it_ctx(video->ohci,
-					  video->it_context[i]);
+	for (i = 0; i < video->ohci->nb_iso_rcv_ctx-1; i++)
+		if (video->ir_context[i] != NULL &&
+		    isoRecvIntEvent & (1<<(video->ir_context[i]->ctx)))
+			wakeup_dma_ir_ctx(video->ohci, video->ir_context[i]);
+
+	for (i = 0; i < video->ohci->nb_iso_xmit_ctx; i++)
+		if (video->it_context[i] != NULL &&
+		    isoXmitIntEvent & (1<<(video->it_context[i]->ctx)))
+			wakeup_dma_it_ctx(video->ohci, video->it_context[i]);
 }
 
 static struct file_operations video1394_fops=
@@ -1520,8 +1563,8 @@
 	list_add_tail(&video->list, &video1394_cards);
 	spin_unlock_irqrestore(&video1394_cards_lock, flags);
 
-	if (ohci1394_register_video(ohci, &video_tmpl)<0) {
-		PRINT(KERN_ERR, ohci->id, "Register_video failed");
+	if (ohci1394_hook_irq(ohci, irq_handler, (void*) video) != 0) {
+		PRINT(KERN_ERR, ohci->id, "ohci1394_hook_irq() failed");
 		return -1;
 	}
 
@@ -1555,7 +1598,8 @@
 	sprintf(name, "%d", video->id);
 	video->devfs = devfs_register(devfs_handle, name,
 				      DEVFS_FL_AUTO_OWNER,
-				      VIDEO1394_MAJOR, 0,
+				      IEEE1394_MAJOR,
+				      IEEE1394_MINOR_BLOCK_VIDEO1394*16+video->id,
 				      S_IFCHR | S_IRUSR | S_IWUSR,
 				      &video1394_fops, NULL);
 
@@ -1567,7 +1611,7 @@
 {
 	int i;
 
-	ohci1394_unregister_video(video->ohci, &video_tmpl);
+	ohci1394_unhook_irq(video->ohci, irq_handler, (void*) video);
 
 	devfs_unregister(video->devfs);
 
@@ -1595,23 +1639,21 @@
 {
 	struct ti_ohci *ohci;
 	unsigned long flags;
-	struct list_head *lh;
+	struct list_head *lh, *next;
+	struct video_card *p;
 
 	/* We only work with the OHCI-1394 driver */
-	if (strcmp(host->template->name, OHCI1394_DRIVER_NAME))
+	if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
 		return;
 
 	ohci = (struct ti_ohci *)host->hostdata;
 
         spin_lock_irqsave(&video1394_cards_lock, flags);
-	if (!list_empty(&video1394_cards)) {
-		struct video_card *p;
-		list_for_each(lh, &video1394_cards) {
-			p = list_entry(lh, struct video_card, list);
-			if (p ->ohci == ohci) {
-				remove_card(p);
-				break;
-			}
+	list_for_each_safe(lh, next, &video1394_cards) {
+		p = list_entry(lh, struct video_card, list);
+		if (p->ohci == ohci) {
+			remove_card(p);
+			break;
 		}
 	}
 	spin_unlock_irqrestore(&video1394_cards_lock, flags);
@@ -1624,7 +1666,7 @@
 	struct ti_ohci *ohci;
 
 	/* We only work with the OHCI-1394 driver */
-	if (strcmp(host->template->name, OHCI1394_DRIVER_NAME))
+	if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
 		return;
 
 	ohci = (struct ti_ohci *)host->hostdata;
@@ -1649,19 +1691,19 @@
 	hpsb_unregister_highlevel (hl_handle);
 
 	devfs_unregister(devfs_handle);
-	devfs_unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME);
-
-	PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module\n");
+	ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394);
+	
+	PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module");
 }
 
 static int __init video1394_init_module (void)
 {
-	if (devfs_register_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME,
-				&video1394_fops)) {
-		PRINT_G(KERN_ERR, "video1394: unable to get major %d\n",
-				VIDEO1394_MAJOR);
-		return -EIO;
-	}
+	if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394,
+				      THIS_MODULE, &video1394_fops)) {
+		PRINT_G(KERN_ERR, "video1394: unable to get minor device block");
+ 		return -EIO;
+ 	}
+	
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
 	devfs_handle = devfs_mk_dir(NULL, VIDEO1394_DRIVER_NAME,
 			strlen(VIDEO1394_DRIVER_NAME), NULL);
@@ -1673,10 +1715,11 @@
 	if (hl_handle == NULL) {
 		PRINT_G(KERN_ERR, "No more memory for driver\n");
 		devfs_unregister(devfs_handle);
-		devfs_unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME);
+		ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394);
 		return -ENOMEM;
 	}
 
+	PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module");
 	return 0;
 }
 
diff -urN linux-2.5.3-pre2/drivers/ieee1394/video1394.h linux/drivers/ieee1394/video1394.h
--- linux-2.5.3-pre2/drivers/ieee1394/video1394.h	Sun Aug 12 12:39:02 2001
+++ linux/drivers/ieee1394/video1394.h	Mon Jan 21 17:23:22 2002
@@ -35,11 +35,12 @@
 	VIDEO1394_LISTEN_CHANNEL = 0,
 	VIDEO1394_UNLISTEN_CHANNEL,
 	VIDEO1394_LISTEN_QUEUE_BUFFER,
-	VIDEO1394_LISTEN_WAIT_BUFFER,
+	VIDEO1394_LISTEN_WAIT_BUFFER,  // wait until buffer is ready
 	VIDEO1394_TALK_CHANNEL,
 	VIDEO1394_UNTALK_CHANNEL,
 	VIDEO1394_TALK_QUEUE_BUFFER,
-	VIDEO1394_TALK_WAIT_BUFFER
+	VIDEO1394_TALK_WAIT_BUFFER,
+	VIDEO1394_LISTEN_POLL_BUFFER   // return immediately with -EINTR if not ready
 };
 
 #define VIDEO1394_SYNC_FRAMES          0x00000001
@@ -47,7 +48,7 @@
 #define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004
 
 struct video1394_mmap {
-	unsigned int channel;
+	int channel;			/* -1 to find an open channel in LISTEN/TALK */
 	unsigned int sync_tag;
 	unsigned int nb_buffers;
 	unsigned int buf_size;
@@ -69,6 +70,7 @@
 struct video1394_wait {
 	unsigned int channel;
 	unsigned int buffer;
+	struct timeval filltime;	/* time of buffer full */
 };
 
 
diff -urN linux-2.5.3-pre2/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- linux-2.5.3-pre2/drivers/isdn/Config.in	Tue Jan  1 19:00:01 2002
+++ linux/drivers/isdn/Config.in	Mon Jan 21 17:23:22 2002
@@ -83,6 +83,7 @@
    dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA
    dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
    dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
+   dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL
 fi
 endmenu
 
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- linux-2.5.3-pre2/drivers/isdn/hisax/Makefile	Tue Jan  1 19:00:01 2002
+++ linux/drivers/isdn/hisax/Makefile	Mon Jan 21 17:23:22 2002
@@ -10,7 +10,7 @@
 
 # Objects that export symbols.
 
-export-objs	  := config.o fsm.o hisax_isac.o
+export-objs	  := config.o fsm.o hisax_isac.o hisax_hscx.o
 
 # Multipart objects.
 
@@ -63,6 +63,7 @@
 obj-$(CONFIG_HISAX_ELSA_CS)		+= elsa_cs.o
 obj-$(CONFIG_HISAX_ST5481)		+= hisax_st5481.o
 obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
+obj-$(CONFIG_HISAX_FRITZ_CLASSIC)       += hisax_isac.o hisax_hscx.o hisax_fcclassic.o
 
 CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
 CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/avm_pci.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/avm_pci.c	Mon Jan 21 17:23:22 2002
@@ -561,7 +561,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -577,7 +577,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			modehdlc(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -640,7 +640,7 @@
 	if (open_hdlcstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hdlc_l2l1;
+	st->l1.l2l1 = hdlc_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/callc.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/callc.c	Mon Jan 21 17:23:22 2002
@@ -250,7 +250,6 @@
 	}
 }
 
-
 /*
  * Dial out
  */
@@ -264,7 +263,7 @@
 		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);
+	L4L3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -281,7 +280,7 @@
 		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);
+		L4L3(chanp->d_st, CC_SETUP | REQUEST, chanp);
 	}
 }
 
@@ -299,7 +298,7 @@
 		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);
+		L4L3(chanp->d_st, CC_RESUME | REQUEST, chanp);
 	}
 }
 
@@ -366,33 +365,33 @@
 			case 1:	/* OK, someone likes this call */
 				FsmDelTimer(&chanp->drel_timer, 61);
 				FsmChangeState(fi, ST_IN_ALERT_SENT);
-				chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+				L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 				break;
 			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);
+				L4L3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
 				if (ret == 5) {
 					memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm));
-					chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+					L4L3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
 				}
 				break;
 			case 2:	/* Rejecting Call */
 				break;
 			case 3:	/* incomplete number */
 				FsmDelTimer(&chanp->drel_timer, 61);
-				chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
+				L4L3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
 				break;
 			case 0:	/* OK, nobody likes this call */
 			default:	/* statcallb problems */
-				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+				L4L3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
 				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 				FsmChangeState(fi, ST_NULL);
 				break;
 		}
 	} else {
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+		L4L3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
 		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 	}
 }
@@ -403,7 +402,7 @@
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+	L4L3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
 }
 
 static void
@@ -412,7 +411,7 @@
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_IN_ALERT_SENT);
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+	L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 }
 
 static void
@@ -420,7 +419,7 @@
 {
 	struct Channel *chanp = fi->userdata;
 
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+	L4L3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
 }
 
 static void
@@ -435,7 +434,7 @@
 	chanp->l2_active_protocol = chanp->l2_protocol;
 	chanp->incoming = !0;
 	init_b_st(chanp, !0);
-	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+	L4L3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -448,9 +447,9 @@
 	} else {
 		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
 #ifdef WANT_ALERT
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+		L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 #endif
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+		L4L3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
 	}
 }
 
@@ -461,7 +460,7 @@
 {
 	struct Channel *chanp = fi->userdata;
 
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
+	L4L3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
 }
 
 /* Call clearing */
@@ -493,8 +492,7 @@
 		FsmChangeState(fi, ST_WAIT_DRELEASE);
 		if (chanp->proc)
 			chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
-			chanp->proc);
+		L4L3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
 	}
 }
 
@@ -509,8 +507,7 @@
 		FsmChangeState(fi, ST_WAIT_DRELEASE);
 		if (chanp->proc)
 			chanp->proc->para.cause = 0x15;	/* Call Rejected */
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
-			chanp->proc);
+		L4L3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
 	}
 }
 
@@ -542,12 +539,12 @@
 #ifndef ALERT_REJECT
 	if (chanp->proc)
 		chanp->proc->para.cause = 0x15;	/* Call Rejected */
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
+	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);
+	L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 #endif
 }
 
@@ -558,7 +555,7 @@
 
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_WAIT_BRELEASE);
-	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
@@ -613,7 +610,7 @@
 
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_WAIT_BREL_DISC);
-	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 }
 
 
@@ -643,7 +640,7 @@
 	struct Channel *chanp = fi->userdata;
 
 	chanp->data_open = 0;
-	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	lli_bhup_dhup(fi, event, arg);
 }
  
@@ -656,8 +653,7 @@
 		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);
+		L4L3(chanp->d_st, CC_RELEASE | REQUEST,	chanp->proc);
 	}
 }
 
@@ -768,7 +764,7 @@
 	struct Channel *chanp = fi->userdata;
 
 	chanp->data_open = 0;
-	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	lli_bhup_fail(fi, event, arg);
 }
 
@@ -942,7 +938,7 @@
 	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);
+			L4L3(pc->st, CC_REJECT | REQUEST, pc);
 		} else {
 			chanp->proc = pc;
 			pc->chan = chanp;
@@ -1025,16 +1021,16 @@
 	if (!*stp)
 		return -ENOMEM;
 	(*stp)->next = NULL;
-	(*stp)->l1.l1l2 = dummy_pstack;
 	(*stp)->l1.l1hw = dummy_pstack;
 	(*stp)->l1.l1tei = dummy_pstack;
+	(*stp)->l1.l2l1 = dummy_pstack;
+	(*stp)->l2.l1l2 = dummy_pstack;
 	(*stp)->l2.l2tei = dummy_pstack;
-	(*stp)->l2.l2l1 = dummy_pstack;
-	(*stp)->l2.l2l3 = dummy_pstack;
-	(*stp)->l3.l3l2 = dummy_pstack;
+	(*stp)->l2.l3l2 = dummy_pstack;
+	(*stp)->l3.l2l3 = dummy_pstack;
 	(*stp)->l3.l3ml3 = dummy_pstack;
-	(*stp)->l3.l3l4 = dummy_pstack;
-	(*stp)->lli.l4l3 = dummy_pstack;
+	(*stp)->l3.l4l3 = dummy_pstack;
+	(*stp)->lli.l3l4 = dummy_pstack;
 	(*stp)->ma.layer = dummy_pstack;
 	return 0;
 }
@@ -1073,7 +1069,7 @@
 	setstack_l3dc(st, chanp);
 	st->lli.userdata = chanp;
 	st->lli.l2writewakeup = NULL;
-	st->l3.l3l4 = dchan_l3l4;
+	st->lli.l3l4 = dchan_l3l4;
 
 	return 0;
 }
@@ -1147,8 +1143,7 @@
 	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,
-			DL_ESTABLISH | REQUEST, NULL);
+		L4L3(csta->channel->d_st, DL_ESTABLISH | REQUEST, NULL);
 	}
 	return (0);
 }
@@ -1317,7 +1312,7 @@
 			sprintf(tmp, "Ch%d X.75", chanp->chan);
 			setstack_isdnl2(st, tmp);
 			setstack_l3bc(st, chanp);
-			st->l2.l2l3 = lldata_handler;
+			st->l3.l2l3 = lldata_handler;
 			st->lli.userdata = chanp;
 			st->lli.l1writewakeup = NULL;
 			st->lli.l2writewakeup = ll_writewakeup;
@@ -1329,7 +1324,7 @@
 		case (ISDN_PROTO_L2_TRANS):
 		case (ISDN_PROTO_L2_MODEM):
 		case (ISDN_PROTO_L2_FAX):
-			st->l1.l1l2 = lltrans_handler;
+			st->l2.l1l2 = lltrans_handler;
 			st->lli.userdata = chanp;
 			st->lli.l1writewakeup = ll_writewakeup;
 			setstack_transl2(st);
@@ -1352,7 +1347,7 @@
 			dev_kfree_skb(skb);
 			break;
 		case (DL_ESTABLISH | REQUEST):
-			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			L2L1(st, PH_ACTIVATE | REQUEST, NULL);
 			break;
 		case (DL_RELEASE | REQUEST):
 			break;
@@ -1650,16 +1645,15 @@
 						HiSax_putstatus(csta, "Card",
 							"%d channel %d set leased mode\n",
 							csta->cardnr + 1, num + 1);
-						chanp->d_st->l1.l1l2 = leased_l1l2;
-						chanp->d_st->lli.l4l3 = leased_l4l3;
-						chanp->d_st->lli.l4l3(chanp->d_st,
-							DL_ESTABLISH | REQUEST, NULL);
+						chanp->d_st->l2.l1l2 = leased_l1l2;
+						chanp->d_st->l3.l4l3 = leased_l4l3;
+						L4L3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 					}
 					break;
 				case (6):	/* set B-channel test loop */
 					num = *(unsigned int *) ic->parm.num;
 					if (csta->stlist)
-						csta->stlist->l2.l2l1(csta->stlist,
+						csta->stlist->l1.l2l1(csta->stlist,
 							PH_TESTLOOP | REQUEST, (void *) (long)num);
 					break;
 				case (7):	/* set card in PTP mode */
@@ -1673,8 +1667,7 @@
 						HiSax_putstatus(csta, "set card ", "in PTP mode");
 						printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
 						printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
-						csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
-							DL_ESTABLISH | REQUEST, NULL);
+						L4L3(csta->channel[0].d_st, DL_ESTABLISH | REQUEST, NULL);
 					} else {
 						test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
 						test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
@@ -1698,8 +1691,7 @@
 						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);
+					L4L3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 					break;
 #ifdef MODULE
 				case (55):
@@ -1764,7 +1756,7 @@
 		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(st->l3.l4l3_proto(st, ic));
 			return(-EINVAL);
 			break;
 		default:
@@ -1820,10 +1812,10 @@
 			if (!ack)
 				nskb->pkt_type = PACKET_NOACK;
 			if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
-				st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
+				L3L2(st, DL_DATA | REQUEST, nskb);
 			else {
 				chanp->bcs->tx_cnt += len;
-				st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
+				st->l1.l2l1(st, PH_DATA | REQUEST, nskb);
 			}
 			dev_kfree_skb(skb);
 		} else
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/config.c	Tue Jan  1 19:00:01 2002
+++ linux/drivers/isdn/hisax/config.c	Mon Jan 21 17:23:22 2002
@@ -1776,7 +1776,7 @@
 	cs->hw.hisax_d_if = hisax_d_if;
 	cs->cardmsg = hisax_cardmsg;
 	cs->tqueue.routine = (void *) (void *) hisax_bh;
-	cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1;
+	cs->channel[0].d_st->l1.l2l1 = hisax_d_l2l1;
 	for (i = 0; i < 2; i++) {
 		cs->bcs[i].BC_SetStack = hisax_bc_setstack;
 		cs->bcs[i].BC_Close = hisax_bc_close;
@@ -1823,7 +1823,7 @@
 		else
 			pr = PH_DEACTIVATE | INDICATION;
 		for (st = cs->stlist; st; st = st->next)
-			st->l1.l1l2(st, pr, NULL);
+			L1L2(st, pr, NULL);
 		
 	}
 }
@@ -1876,7 +1876,7 @@
 		clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 		for (st = cs->stlist; st; st = st->next) {
 			if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 				break;
 			}
 		}
@@ -1901,10 +1901,10 @@
 	// FIXME use isdnl1?
 	switch (pr) {
 	case PH_ACTIVATE | INDICATION:
-		st->l1.l1l2(st, pr, NULL);
+		L1L2(st, pr, NULL);
 		break;
 	case PH_DEACTIVATE | INDICATION:
-		st->l1.l1l2(st, pr, NULL);
+		L1L2(st, pr, NULL);
 		bcs->hw.b_if = NULL;
 		break;
 	case PH_DATA | INDICATION:
@@ -1922,7 +1922,7 @@
 		}
 		clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
-			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			L1L2(st, PH_PULL | CONFIRM, NULL);
 		}
 		break;
 	default:
@@ -1953,7 +1953,7 @@
 		break;
 	case PH_PULL | REQUEST:
 		if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
-			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			L1L2(st, PH_PULL | CONFIRM, NULL);
 		else
 			set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 		break;
@@ -1988,7 +1988,7 @@
 		break;
 	case PH_PULL | REQUEST:
 		if (!test_bit(BC_FLG_BUSY, &bcs->Flag))
-			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			L1L2(st, PH_PULL | CONFIRM, NULL);
 		else
 			set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 		break;
@@ -2009,7 +2009,7 @@
 	hisax_d_if->b_if[st->l1.bc]->bcs = bcs;
 
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hisax_b_l2l1;
+	st->l1.l2l1 = hisax_b_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/elsa_ser.c linux/drivers/isdn/hisax/elsa_ser.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/elsa_ser.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/elsa_ser.c	Mon Jan 21 17:23:22 2002
@@ -591,7 +591,7 @@
 		}
 	} else if (pr == (PH_ACTIVATE | REQUEST)) {
 		test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-		st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
 		set_arcofi(st->l1.bcs->cs, st->l1.bc);
 		mstartup(st->l1.bcs->cs);
 		modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
@@ -617,7 +617,7 @@
 		case L1_MODE_TRANS:
 			if (open_hscxstate(st->l1.hardware, bcs))
 				return (-1);
-			st->l2.l2l1 = hscx_l2l1;
+			st->l1.l2l1 = hscx_l2l1;
 			break;
 		case L1_MODE_MODEM:
 			bcs->mode = L1_MODE_MODEM;
@@ -632,7 +632,7 @@
 			bcs->hw.hscx.rcvidx = 0;
 			bcs->tx_cnt = 0;
 			bcs->cs->hw.elsa.bcs = bcs;
-			st->l2.l2l1 = modem_l2l1;
+			st->l1.l2l1 = modem_l2l1;
 			break;
 	}
 	st->l1.bcs = bcs;
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bds0.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/hfc_2bds0.c	Mon Jan 21 17:23:22 2002
@@ -545,7 +545,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -561,7 +561,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			mode_2bs0(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -602,7 +602,7 @@
 	if (open_hfcstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hfc_l2l1;
+	st->l1.l2l1 = hfc_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
@@ -1066,7 +1066,7 @@
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bs0.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/hfc_2bs0.c	Mon Jan 21 17:23:22 2002
@@ -507,7 +507,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -523,7 +523,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			mode_hfc(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -566,7 +566,7 @@
 	if (open_hfcstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hfc_l2l1;
+	st->l1.l2l1 = hfc_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_pci.c linux/drivers/isdn/hisax/hfc_pci.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_pci.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/hfc_pci.c	Mon Jan 21 17:23:22 2002
@@ -776,7 +776,7 @@
 			st->l1.l1hw(st, pr, arg);
 			break;
 		case (PH_ACTIVATE | REQUEST):
-			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
 			break;
 		case (PH_TESTLOOP | REQUEST):
 			if (1 & (long) arg)
@@ -821,7 +821,7 @@
 		cs->dc.hfcpci.ph_state = 1;
 		cs->hw.hfcpci.nt_mode = 1;
 		cs->hw.hfcpci.nt_timer = 0;
-		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		cs->stlist->l1.l2l1 = dch_nt_l2l1;
 		restore_flags(flags);
 		debugl1(cs, "NT mode activated");
 		return (0);
@@ -1211,7 +1211,7 @@
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -1462,7 +1462,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -1478,7 +1478,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			mode_hfcpci(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -1528,7 +1528,7 @@
 	if (open_hfcpcistate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hfcpci_l2l1;
+	st->l1.l2l1 = hfcpci_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_sx.c linux/drivers/isdn/hisax/hfc_sx.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_sx.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/hfc_sx.c	Mon Jan 21 17:23:22 2002
@@ -632,7 +632,7 @@
 			st->l1.l1hw(st, pr, arg);
 			break;
 		case (PH_ACTIVATE | REQUEST):
-			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
 			break;
 		case (PH_TESTLOOP | REQUEST):
 			if (1 & (long) arg)
@@ -676,7 +676,7 @@
 		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;
+		cs->stlist->l1.l2l1 = dch_nt_l2l1;
 		restore_flags(flags);
 		debugl1(cs, "NT mode activated");
 		return (0);
@@ -1008,7 +1008,7 @@
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -1248,7 +1248,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -1264,7 +1264,7 @@
 			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);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -1314,7 +1314,7 @@
 	if (open_hfcsxstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hfcsx_l2l1;
+	st->l1.l2l1 = hfcsx_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- linux-2.5.3-pre2/drivers/isdn/hisax/hisax.h	Tue Jan  1 19:00:01 2002
+++ linux/drivers/isdn/hisax/hisax.h	Mon Jan 21 17:23:22 2002
@@ -203,9 +203,9 @@
 	long Flags;
 	struct FsmInst l1m;
 	struct FsmTimer	timer;
-	void (*l1l2) (struct PStack *, int, void *);
 	void (*l1hw) (struct PStack *, int, void *);
 	void (*l1tei) (struct PStack *, int, void *);
+	void (*l2l1) (struct PStack *, int, void *);
 	int mode, bc;
 	int delay;
 };
@@ -247,8 +247,8 @@
 	struct sk_buff *windowar[MAX_WINDOW];
 	struct sk_buff_head i_queue;
 	struct sk_buff_head ui_queue;
-	void (*l2l1) (struct PStack *, int, void *);
-	void (*l2l3) (struct PStack *, int, void *);
+	void (*l3l2) (struct PStack *, int, void *);
+	void (*l1l2) (struct PStack *, int, void *);
 	void (*l2tei) (struct PStack *, int, void *);
 	struct FsmInst l2m;
 	struct FsmTimer t200, t203;
@@ -258,9 +258,10 @@
 };
 
 struct Layer3 {
-	void (*l3l4) (struct PStack *, int, void *);
+	void (*l4l3) (struct PStack *, int, void *);
+        int  (*l4l3_proto) (struct PStack *, isdn_ctrl *);
         void (*l3ml3) (struct PStack *, int, void *);
-	void (*l3l2) (struct PStack *, int, void *);
+	void (*l2l3) (struct PStack *, int, void *);
 	struct FsmInst l3m;
         struct FsmTimer l3m_timer;
 	struct sk_buff_head squeue;
@@ -272,8 +273,7 @@
 };
 
 struct LLInterface {
-	void (*l4l3) (struct PStack *, int, void *);
-        int  (*l4l3_proto) (struct PStack *, isdn_ctrl *);
+	void (*l3l4) (struct PStack *, int, void *);
 	void *userdata;
 	void (*l1writewakeup) (struct PStack *, int);
 	void (*l2writewakeup) (struct PStack *, int);
@@ -1348,3 +1348,40 @@
 int TeiNew(void);
 void TeiFree(void);
 int certification_check(int output);
+
+static inline void
+L2L1(struct PStack *st, int pr, void *arg)
+{
+	st->l1.l2l1(st, pr, arg);
+}
+
+static inline void
+L1L2(struct PStack *st, int pr, void *arg)
+{
+	st->l2.l1l2(st, pr, arg);
+}
+
+static inline void
+L3L2(struct PStack *st, int pr, void *arg)
+{
+	st->l2.l3l2(st, pr, arg);
+}
+
+static inline void
+L2L3(struct PStack *st, int pr, void *arg)
+{
+	st->l3.l2l3(st, pr, arg);
+}
+
+static inline void
+L3L4(struct PStack *st, int pr, void *arg)
+{
+	st->lli.l3l4(st, pr, arg);
+}
+
+static inline void
+L4L3(struct PStack *st, int pr, void *arg)
+{
+	st->l3.l4l3(st, pr, arg);
+}
+
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.c linux/drivers/isdn/hisax/hisax_fcclassic.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_fcclassic.c	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,384 @@
+/*
+ * Driver for AVM Fritz!classic (ISA) ISDN card
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original avm_a1.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include "hisax_fcclassic.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!Card classic ISDN driver");
+
+static int protocol = 2;       /* EURO-ISDN Default */
+MODULE_PARM(protocol, "i");
+
+// ----------------------------------------------------------------------
+
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
+
+// ----------------------------------------------------------------------
+
+static unsigned char
+fcclassic_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char val;
+
+	val = inb(adapter->isac_base + offset);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+	return val;
+}
+
+static void
+fcclassic_write_isac(struct isac *isac, unsigned char offset,
+		     unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	outb(value, adapter->isac_base + offset);
+}
+
+static void
+fcclassic_read_isac_fifo(struct isac *isac, unsigned char * data, int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+
+	insb(adapter->isac_fifo, data, size);
+}
+
+static void
+fcclassic_write_isac_fifo(struct isac *isac, unsigned char * data, int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+
+	outsb(adapter->isac_fifo, data, size);
+}
+
+static u_char
+fcclassic_read_hscx(struct hscx *hscx, u_char offset)
+{
+	struct fritz_adapter *adapter = hscx->priv;
+
+	return inb(adapter->hscx_base[hscx->channel] + offset);
+}
+
+static void
+fcclassic_write_hscx(struct hscx *hscx, u_char offset, u_char value)
+{
+	struct fritz_adapter *adapter = hscx->priv;
+
+	outb(value, adapter->hscx_base[hscx->channel] + offset);
+}
+
+static void
+fcclassic_read_hscx_fifo(struct hscx *hscx, unsigned char * data, int size)
+{
+	struct fritz_adapter *adapter = hscx->priv;
+
+	insb(adapter->hscx_fifo[hscx->channel], data, size);
+}
+
+static void
+fcclassic_write_hscx_fifo(struct hscx *hscx, unsigned char * data, int size)
+{
+	struct fritz_adapter *adapter = hscx->priv;
+
+	outsb(adapter->hscx_fifo[hscx->channel], data, size);
+}
+
+// ----------------------------------------------------------------------
+
+static void
+fcclassic_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char sval;
+
+	DBG(2, "");
+	while ((sval = inb(adapter->cfg_reg) & 0xf) != 0x7) {
+		DBG(2, "sval %#x", sval);
+		if (!(sval & AVM_A1_STAT_TIMER)) {
+			outb(0x1e, adapter->cfg_reg);
+		}
+		if (!(sval & AVM_A1_STAT_HSCX)) {
+			hscx_irq(adapter->hscx);
+		}
+		if (!(sval & AVM_A1_STAT_ISAC)) {
+			isac_irq(&adapter->isac);
+		}
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static int __init
+fcclassic_setup(struct fritz_adapter *adapter)
+{
+	u32 val = 0;
+	int i;
+	int retval;
+
+	DBG(1,"");
+
+	isac_init(&adapter->isac); // FIXME is this okay now
+
+	adapter->cfg_reg      = adapter->io + 0x1800;
+	adapter->isac_base    = adapter->io + 0x1400 - 0x20;
+	adapter->isac_fifo    = adapter->io + 0x1000;
+	adapter->hscx_base[0] = adapter->io + 0x0400 - 0x20;
+	adapter->hscx_fifo[0] = adapter->io;
+	adapter->hscx_base[1] = adapter->io + 0x0c00 - 0x20;
+	adapter->hscx_fifo[1] = adapter->io + 0x0800;
+
+	retval = -EBUSY;
+	if (!request_region(adapter->cfg_reg            ,  8,
+			    "fcclassic cfg"))
+		goto err;
+	if (!request_region(adapter->isac_base + 0x20   , 32,
+			    "fcclassic isac"))
+		goto err_cfg_reg;
+	if (!request_region(adapter->isac_fifo          ,  1,
+			    "fcclassic isac fifo"))
+		goto err_isac_base;
+	if (!request_region(adapter->hscx_base[0] + 0x20, 32,
+			    "fcclassic hscx"))
+		goto err_isac_fifo;
+	if (!request_region(adapter->hscx_fifo[0]       ,  1,
+			    "fcclassic hscx fifo"))
+		goto err_hscx_base_0;
+	if (!request_region(adapter->hscx_base[1] + 0x20, 32,
+			    "fcclassic hscx"))
+		goto err_hscx_fifo_0;
+	if (!request_region(adapter->hscx_fifo[1]       ,  1,
+			    "fcclassic hscx fifo"))
+		goto err_hscx_base_1;
+	retval = request_irq(adapter->irq, fcclassic_irq,  0,
+			     "fcclassic", adapter);
+	if (retval)
+		goto err_hscx_fifo_1;
+
+	// Reset
+	outb(0x00, adapter->cfg_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(200 * HZ / 1000); // 200 msec
+	outb(0x01, adapter->cfg_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(200 * HZ / 1000); // 200 msec
+	outb(0x00, adapter->cfg_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(200 * HZ / 1000); // 200 msec
+
+	val = adapter->irq;
+	if (val == 9)
+		val = 2;
+	outb(val, adapter->cfg_reg + 1);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(200 * HZ / 1000); // 200 msec
+	outb(0x00, adapter->cfg_reg);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(200 * HZ / 1000); // 200 msec
+
+	val = inb(adapter->cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       adapter->cfg_reg, val);
+	val = inb(adapter->cfg_reg + 3);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       adapter->cfg_reg + 3, val);
+	val = inb(adapter->cfg_reg + 2);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       adapter->cfg_reg + 2, val);
+	val = inb(adapter->cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       adapter->cfg_reg, val);
+
+	outb(0x16, adapter->cfg_reg);
+	outb(0x1e, adapter->cfg_reg);
+
+	adapter->isac.priv            = adapter;
+	adapter->isac.read_isac       = &fcclassic_read_isac;
+	adapter->isac.write_isac      = &fcclassic_write_isac;
+	adapter->isac.read_isac_fifo  = &fcclassic_read_isac_fifo;
+	adapter->isac.write_isac_fifo = &fcclassic_write_isac_fifo;
+	isac_setup(&adapter->isac);
+	for (i = 0; i < 2; i++) {
+		hscx_init(&adapter->hscx[i]);
+		adapter->hscx[i].priv            = adapter;
+		adapter->hscx[i].read_hscx       = &fcclassic_read_hscx;
+		adapter->hscx[i].write_hscx      = &fcclassic_write_hscx;
+		adapter->hscx[i].read_hscx_fifo  = &fcclassic_read_hscx_fifo;
+		adapter->hscx[i].write_hscx_fifo = &fcclassic_write_hscx_fifo;
+		hscx_setup(&adapter->hscx[i]);
+	}
+
+	return 0;
+
+ err_hscx_fifo_1:
+	release_region(adapter->hscx_fifo[1]       ,  1);
+ err_hscx_base_1:
+	release_region(adapter->hscx_base[1] + 0x20, 32);
+ err_hscx_fifo_0:
+	release_region(adapter->hscx_fifo[0]       ,  1);
+ err_hscx_base_0:
+	release_region(adapter->hscx_base[0] + 0x20, 32);
+ err_isac_fifo:
+	release_region(adapter->isac_fifo          ,  1);
+ err_isac_base:
+	release_region(adapter->isac_base    + 0x20, 32);
+ err_cfg_reg:
+	release_region(adapter->cfg_reg            ,  8);
+ err:
+	return retval;
+}
+
+static void __exit fcclassic_release(struct fritz_adapter *adapter)
+{
+	DBG(1,"");
+
+//	outb(0, adapter->io + AVM_STATUS0);
+	free_irq(adapter->irq, adapter);
+	release_region(adapter->hscx_fifo[1]       ,  1);
+	release_region(adapter->hscx_base[1] + 0x20, 32);
+	release_region(adapter->hscx_fifo[0]       ,  1);
+	release_region(adapter->hscx_base[0] + 0x20, 32);
+	release_region(adapter->isac_fifo          ,  1);
+	release_region(adapter->isac_base    + 0x20, 32);
+	release_region(adapter->cfg_reg            ,  8);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __init 
+new_adapter(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter;
+	struct hisax_b_if *b_if[2];
+	int i;
+
+	adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+	if (!adapter)
+		return NULL;
+
+	memset(adapter, 0, sizeof(struct fritz_adapter));
+
+	SET_MODULE_OWNER(&adapter->isac.hisax_d_if);
+	adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+	adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+
+	for (i = 0; i < 2; i++) {
+	  //		adapter->hscx[i].adapter = adapter;
+		adapter->hscx[i].channel = i;
+		adapter->hscx[i].b_if.ifc.priv = &adapter->hscx[i];
+		adapter->hscx[i].b_if.ifc.l2l1 = hscx_b_l2l1;
+	}
+	pci_set_drvdata(pdev, adapter);
+
+	for (i = 0; i < 2; i++)
+		b_if[i] = &adapter->hscx[i].b_if;
+
+	hisax_register(&adapter->isac.hisax_d_if, b_if, "fcclassic", protocol);
+
+	return adapter;
+}
+
+static void
+delete_adapter(struct fritz_adapter *adapter)
+{
+	hisax_unregister(&adapter->isac.hisax_d_if);
+	kfree(adapter);
+}
+
+static int __init
+fcclassic_probe(struct pci_dev *pdev, const struct isapnp_device_id *ent)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	retval = -ENOMEM;
+	adapter = new_adapter(pdev);
+	if (!adapter)
+		goto err;
+
+	adapter->io = pdev->resource[0].start;
+	adapter->irq = pdev->irq_resource[0].start;
+
+	printk(KERN_INFO "hisax_fcclassic: found Fritz!Card classic at IO %#x irq %d\n",
+	       adapter->io, adapter->irq);
+
+	retval = fcclassic_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+static int __exit 
+fcclassic_remove(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+	fcclassic_release(adapter);
+	delete_adapter(adapter);
+
+	return 0;
+}
+
+static struct pci_dev isa_dev[4];
+
+static int __init
+hisax_fcclassic_init(void)
+{
+	printk(KERN_INFO "hisax_fcclassic: Fritz!Card classic ISDN driver v0.0.1\n");
+
+	isa_dev[0].resource[0].start = 0x300;
+	isa_dev[0].irq_resource[0].start = 7;
+
+	fcclassic_probe(isa_dev, NULL);
+
+	return 0;
+}
+
+static void __exit
+hisax_fcclassic_exit(void)
+{
+	fcclassic_remove(isa_dev);
+}
+
+module_init(hisax_fcclassic_init);
+module_exit(hisax_fcclassic_exit);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.h linux/drivers/isdn/hisax/hisax_fcclassic.h
--- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_fcclassic.h	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,18 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include "hisax_hscx.h"
+
+#include <linux/pci.h>
+
+struct fritz_adapter {
+	unsigned int io;
+	unsigned int irq;
+	unsigned int cfg_reg;
+	unsigned int isac_base;
+	unsigned int isac_fifo;
+	unsigned int hscx_base[2];
+	unsigned int hscx_fifo[2];
+	struct isac isac;
+
+	struct hscx hscx[2];
+};
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.c linux/drivers/isdn/hisax/hisax_hscx.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_hscx.c	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,421 @@
+/*
+ * Driver for HSCX
+ * High-Level Serial Communcation Controller Extended
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+/* TODO:
+ * comments in .h
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_hscx.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+MODULE_PARM(debug, "i");
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("HSCX driver");
+
+#define DBG_WARN      0x0001
+#define DBG_IRQ       0x0002
+#define DBG_L1M       0x0004
+#define DBG_PR        0x0008
+#define DBG_RFIFO     0x0100
+#define DBG_RPACKET   0x0200
+#define DBG_XFIFO     0x1000
+#define DBG_XPACKET   0x2000
+
+#define HSCX_ISTA      0x20
+#define HSCX_ISTA_EXB  0x01
+#define HSCX_ISTA_EXA  0x02
+#define HSCX_ISTA_ICA  0x04
+#define HSCX_ISTA_TIN  0x08
+#define HSCX_ISTA_XPR  0x10
+#define HSCX_ISTA_RSC  0x20
+#define HSCX_ISTA_RPF  0x40
+#define HSCX_ISTA_RME  0x80
+
+#define HSCX_CMDR      0x21
+#define HSCX_CMDR_RMC  0x80
+#define HSCX_CMDR_RHR  0x40
+#define HSCX_CMDR_RNR  0x20
+#define HSCX_CMDR_STI  0x10
+#define HSCX_CMDR_XTF  0x08
+#define HSCX_CMDR_XIF  0x04
+#define HSCX_CMDR_XME  0x02
+#define HSCX_CMDR_XRES 0x01
+
+#define HSCX_EXIR      0x24
+#define HSCX_EXIR_XDU  0x40
+
+#define HSCX_RSTA      0x27
+#define HSCX_RSTA_VFR  0x80
+#define HSCX_RSTA_RDO  0x40
+#define HSCX_RSTA_CRC  0x20
+#define HSCX_RSTA_RAB  0x10
+
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+static inline void B_L1L2(struct hscx *hscx, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &hscx->b_if;
+
+	DBG(0x10, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void hscx_version(struct hscx *hscx)
+{
+	int val;
+
+	val = hscx->read_hscx(hscx, HSCX_VSTR) & 0xf;
+	DBG(1, "HSCX version (%x): %s", val, HSCXVer[val]);
+}
+
+static void hscx_empty_fifo(struct hscx *hscx, int count)
+{
+	u_char *ptr;
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if ((hscx->rcvidx + count) >= HSCX_BUFMAX) {
+		DBG(DBG_WARN, "overrun %d", hscx->rcvidx + count);
+		hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC);
+		hscx->rcvidx = 0;
+		return;
+	}
+	ptr = hscx->rcvbuf + hscx->rcvidx;
+	hscx->rcvidx += count;
+	hscx->read_hscx_fifo(hscx, ptr, count);
+	hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC);
+	DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void hscx_fill_fifo(struct hscx *hscx)
+{
+	int count;
+	unsigned char cmd;
+	int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32;
+	unsigned char *ptr;
+
+	if (!hscx->tx_skb)
+		BUG();
+
+	count = hscx->tx_skb->len;
+	if (count <= 0)
+		BUG();
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if (count > fifo_size || hscx->mode == L1_MODE_TRANS) {
+		count = fifo_size;
+		cmd = 0x8;
+	} else {
+		cmd = 0xa;
+	}
+
+	ptr = hscx->tx_skb->data;
+	skb_pull(hscx->tx_skb, count);
+	hscx->tx_cnt += count;
+	DBG_PACKET(DBG_XFIFO, ptr, count);
+	hscx->write_hscx_fifo(hscx, ptr, count);
+	hscx->write_hscx(hscx, HSCX_CMDR, cmd);
+}
+
+static void hscx_retransmit(struct hscx *hscx)
+{
+	if (!hscx->tx_skb) {
+		DBG(DBG_WARN, "no skb");
+		return;
+	}
+	skb_push(hscx->tx_skb, hscx->tx_cnt);
+	hscx->tx_cnt = 0;
+	hscx->write_hscx(hscx, HSCX_CMDR, 0x01);
+}
+
+static inline void hscx_rme_interrupt(struct hscx *hscx)
+{
+	unsigned char val;
+	int count;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32;
+	
+	val = hscx->read_hscx(hscx, HSCX_RSTA);
+	if ((val & (HSCX_RSTA_VFR | HSCX_RSTA_RDO | HSCX_RSTA_CRC | HSCX_RSTA_RAB) )
+	     != (HSCX_RSTA_VFR | HSCX_RSTA_CRC)) {
+		DBG(DBG_WARN, "RSTA %#x, dropped", val);
+		hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC);
+		goto out;
+	}
+	
+	count = hscx->read_hscx(hscx, HSCX_RBCL) & (fifo_size-1);
+	DBG(DBG_IRQ, "RBCL %#x", count);
+	if (count == 0)
+		count = fifo_size;
+
+	hscx_empty_fifo(hscx, count);
+
+	count = hscx->rcvidx;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = alloc_skb(count, GFP_ATOMIC);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping\n");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), hscx->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	B_L1L2(hscx, PH_DATA | INDICATION, skb);
+ out:
+	hscx->rcvidx = 0;
+}
+
+static inline void hscx_xpr_interrupt(struct hscx *hscx)
+{
+	struct sk_buff *skb;
+
+	skb = hscx->tx_skb;
+	if (!skb)
+		return;
+
+	if (skb->len > 0) {
+		hscx_fill_fifo(hscx);
+		return;
+	}
+	hscx->tx_cnt = 0;
+	hscx->tx_skb = NULL;
+	B_L1L2(hscx, PH_DATA | CONFIRM, (void *) skb->truesize);
+	dev_kfree_skb_irq(skb);
+}
+
+static inline void hscx_exi_interrupt(struct hscx *hscx)
+{
+	unsigned char val;
+
+	val = hscx->read_hscx(hscx, HSCX_EXIR);
+	DBG(2, "EXIR %#x", val);
+
+	if (val & HSCX_EXIR_XDU) {
+		DBG(DBG_WARN, "HSCX XDU");
+		if (hscx->mode == L1_MODE_TRANS) {
+			hscx_fill_fifo(hscx);
+		} else {
+			hscx_retransmit(hscx);
+		}
+	}
+}
+
+static void hscx_reg_interrupt(struct hscx *hscx, unsigned char val)
+{
+	struct sk_buff *skb;
+
+	if (val & HSCX_ISTA_XPR) {
+		DBG(DBG_IRQ, "XPR");
+		hscx_xpr_interrupt(hscx);
+	}
+	if (val & HSCX_ISTA_RME) {
+		DBG(DBG_IRQ, "RME");
+		hscx_rme_interrupt(hscx);
+	}
+	if (val & HSCX_ISTA_RPF) {
+		int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32;
+
+		DBG(DBG_IRQ, "RPF");
+		hscx_empty_fifo(hscx, fifo_size);
+		if (hscx->mode == L1_MODE_TRANS) {
+			skb = dev_alloc_skb(fifo_size);
+			if (!skb) {
+				DBG(DBG_WARN, "no memory, dropping\n");
+				goto out;
+			}
+			memcpy(skb_put(skb, fifo_size), hscx->rcvbuf, fifo_size);
+			DBG_SKB(DBG_RPACKET, skb);
+			B_L1L2(hscx, PH_DATA | INDICATION, skb);
+		out:
+			hscx->rcvidx = 0;
+		}
+	}
+}
+
+void hscx_irq(struct hscx *hscx_a)
+{
+	struct hscx *hscx_b = hscx_a + 1;
+	unsigned char val;
+
+	val = hscx_b->read_hscx(hscx_b, HSCX_ISTA);
+	DBG(DBG_IRQ, "ISTA B %#x", val);
+
+	if (val & HSCX_ISTA_EXB) {
+		DBG(DBG_IRQ, "EXI B");
+		hscx_exi_interrupt(hscx_b);
+	}
+	if (val & HSCX_ISTA_EXA) {
+		DBG(DBG_IRQ, "EXI A");
+		hscx_exi_interrupt(hscx_a);
+	}
+	if (val & 0xf8) {
+		hscx_reg_interrupt(hscx_b, val);
+	}
+	if (val & HSCX_ISTA_ICA) {
+		val = hscx_a->read_hscx(hscx_a, HSCX_ISTA);
+		DBG(DBG_IRQ, "ISTA A %#x", val);
+		hscx_reg_interrupt(hscx_a, val);
+		hscx_a->write_hscx(hscx_a, HSCX_MASK, 0xff);
+		hscx_a->write_hscx(hscx_a, HSCX_MASK, 0x00);
+	}
+	hscx_b->write_hscx(hscx_b, HSCX_MASK, 0xff);
+	hscx_b->write_hscx(hscx_b, HSCX_MASK, 0x00);
+}
+
+static void modehscx(struct hscx *hscx, int mode)
+{
+	int bc = hscx->channel;
+
+	DBG(0x40, "hscx %c mode %d --> %d",
+	    'A' + hscx->channel, hscx->mode, mode);
+
+	hscx->mode = mode;
+	hscx->write_hscx(hscx, HSCX_XAD1, 0xFF);
+	hscx->write_hscx(hscx, HSCX_XAD2, 0xFF);
+	hscx->write_hscx(hscx, HSCX_RAH2, 0xFF);
+	hscx->write_hscx(hscx, HSCX_XBCH, 0x0);
+	hscx->write_hscx(hscx, HSCX_RLCR, 0x0);
+	hscx->write_hscx(hscx, HSCX_CCR1, 
+			 test_bit(HSCX_IPAC, &hscx->flags) ? 0x82 : 0x85);
+	hscx->write_hscx(hscx, HSCX_CCR2, 0x30);
+	hscx->write_hscx(hscx, HSCX_XCCR, 7);
+	hscx->write_hscx(hscx, HSCX_RCCR, 7);
+
+	/* Switch IOM 1 SSI */
+	if (test_bit(HSCX_IOM1, &hscx->flags))
+		bc = 1;
+
+	hscx->write_hscx(hscx, HSCX_TSAX, hscx->tsaxr);
+	hscx->write_hscx(hscx, HSCX_TSAR, hscx->tsaxr);
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+			hscx->write_hscx(hscx, HSCX_TSAX, 0x1f);
+			hscx->write_hscx(hscx, HSCX_TSAR, 0x1f);
+			hscx->write_hscx(hscx, HSCX_MODE, 0x84);
+			break;
+		case (L1_MODE_TRANS):
+			hscx->write_hscx(hscx, HSCX_MODE, 0xe4);
+			break;
+		case (L1_MODE_HDLC):
+			hscx->write_hscx(hscx, HSCX_CCR1, test_bit(HSCX_IPAC, &hscx->flags) ? 0x8a : 0x8d);
+			hscx->write_hscx(hscx, HSCX_MODE, 0x8c);
+			break;
+	}
+	if (mode)
+		hscx->write_hscx(hscx, HSCX_CMDR, 0x41);
+
+	hscx->write_hscx(hscx, HSCX_ISTA, 0x00);
+}
+
+void hscx_init(struct hscx *hscx)
+{
+	if (hscx->channel)
+		hscx->tsaxr = 0x03;
+	else
+		hscx->tsaxr = 0x2f;
+}
+
+void hscx_setup(struct hscx *hscx)
+{
+	hscx_version(hscx);
+	hscx->mode = -1;
+	modehscx(hscx, L1_MODE_NULL);
+}
+
+void hscx_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hscx *hscx = ifc->priv;
+	struct sk_buff *skb = arg;
+	int mode;
+
+	DBG(0x10, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+		if (hscx->tx_skb)
+			BUG();
+		
+		hscx->tx_skb = skb;
+		DBG_SKB(1, skb);
+		hscx_fill_fifo(hscx);
+		break;
+	case PH_ACTIVATE | REQUEST:
+		mode = (int) arg;
+		DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", hscx->channel + 1, mode);
+		modehscx(hscx, mode);
+		B_L1L2(hscx, PH_ACTIVATE | INDICATION, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		DBG(4,"B%d,PH_DEACTIVATE_REQUEST", hscx->channel + 1);
+		modehscx(hscx, L1_MODE_NULL);
+		B_L1L2(hscx, PH_DEACTIVATE | INDICATION, NULL);
+		break;
+	}
+}
+
+static int __init hisax_hscx_init(void)
+{
+	printk(KERN_INFO "hisax_hscx: HSCX ISDN driver v0.1.0\n");
+	return 0;
+}
+
+static void __exit hisax_hscx_exit(void)
+{
+}
+
+EXPORT_SYMBOL(hscx_init);
+EXPORT_SYMBOL(hscx_b_l2l1);
+
+EXPORT_SYMBOL(hscx_setup);
+EXPORT_SYMBOL(hscx_irq);
+
+module_init(hisax_hscx_init);
+module_exit(hisax_hscx_exit);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.h linux/drivers/isdn/hisax/hisax_hscx.h
--- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_hscx.h	Mon Jan 21 17:23:22 2002
@@ -0,0 +1,37 @@
+#ifndef __HISAX_HSCX_H__
+#define __HISAX_HSCX_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define HSCX_BUFMAX	4096
+
+#define HSCX_IOM1 0
+#define HSCX_IPAC 1
+
+struct hscx {
+	void *priv;
+	u_long flags;
+	struct hisax_b_if b_if;
+	int mode;
+	int channel;
+	u_char tsaxr;
+	struct sk_buff *tx_skb;
+	int tx_cnt;
+	u_char rcvbuf[HSCX_BUFMAX];
+	int rcvidx;
+
+	u_char (*read_hscx)      (struct hscx *, u_char);
+	void   (*write_hscx)     (struct hscx *, u_char, u_char);
+	void   (*read_hscx_fifo) (struct hscx *, u_char *, int);
+	void   (*write_hscx_fifo)(struct hscx *, u_char *, int);
+};
+
+void hscx_init(struct hscx *hscx);
+void hscx_b_l2l1(struct hisax_if *hisax_b_if, int pr, void *arg);
+
+void hscx_setup(struct hscx *hscx);
+void hscx_irq(struct hscx *hscx);
+
+#endif
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hscx.c linux/drivers/isdn/hisax/hscx.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/hscx.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/hscx.c	Mon Jan 21 17:23:22 2002
@@ -134,7 +134,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -150,7 +150,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			modehscx(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -214,7 +214,7 @@
 	if (open_hscxstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = hscx_l2l1;
+	st->l1.l2l1 = hscx_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/icc.c linux/drivers/isdn/hisax/icc.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/icc.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/icc.c	Mon Jan 21 17:23:22 2002
@@ -89,7 +89,7 @@
 			debugl1(cs, "D-Channel Busy cleared");
 		stptr = cs->stlist;
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			L1L2(stptr, PH_PAUSE | CONFIRM, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -506,7 +506,7 @@
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -603,7 +603,7 @@
 			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 			stptr = cs->stlist;
 			while (stptr != NULL) {
-				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				L1L2(stptr, PH_PAUSE | INDICATION, NULL);
 				stptr = stptr->next;
 			}
 		} else {
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/isac.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/isac.c	Mon Jan 21 17:23:22 2002
@@ -93,7 +93,7 @@
 			debugl1(cs, "D-Channel Busy cleared");
 		stptr = cs->stlist;
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			L1L2(stptr, PH_PAUSE | CONFIRM, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -510,7 +510,7 @@
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -605,7 +605,7 @@
 			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 			stptr = cs->stlist;
 			while (stptr != NULL) {
-				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				L1L2(stptr, PH_PAUSE | INDICATION, NULL);
 				stptr = stptr->next;
 			}
 		} else {
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/isar.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/isdn/hisax/isar.c	Mon Jan 21 17:23:22 2002
@@ -1602,7 +1602,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -1637,7 +1637,7 @@
 			if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
 				debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY");
 			modeisar(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -1696,7 +1696,7 @@
 	if (open_isarstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = isar_l2l1;
+	st->l1.l2l1 = isar_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl1.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/isdnl1.c	Mon Jan 21 17:23:22 2002
@@ -160,9 +160,9 @@
 	st = cs->stlist;
 	while (st) {
 		if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
-			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
 		else
-			st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
+			L1L2(st, PH_ACTIVATE | INDICATION, NULL);
 		st = st->next;
 	}
 }
@@ -175,8 +175,8 @@
 	st = cs->stlist;
 	while (st) {
 		if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
-			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
-		st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
+			L1L2(st, PH_PAUSE | CONFIRM, NULL);
+		L1L2(st, PH_DEACTIVATE | INDICATION, NULL);
 		st = st->next;
 	}
 	test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
@@ -193,7 +193,7 @@
 	stptr = cs->stlist;
 	while (stptr != NULL)
 		if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
-			stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
+			L1L2(stptr, PH_PULL | CONFIRM, NULL);
 			break;
 		} else
 			stptr = stptr->next;
@@ -235,7 +235,7 @@
 			if (sapi == CTRL_SAPI) { /* sapi 0 */
 				while (stptr != NULL) {
 					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
-						stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
+						L1L2(stptr, PH_DATA | INDICATION, nskb);
 					else
 						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
 					stptr = stptr->next;
@@ -254,7 +254,7 @@
 			found = 0;
 			while (stptr != NULL)
 				if (tei == stptr->l2.tei) {
-					stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
+					L1L2(stptr, PH_DATA | INDICATION, skb);
 					found = !0;
 					break;
 				} else
@@ -277,10 +277,10 @@
 	}
 
 	if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
-		st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		L1L2(st, PH_PULL | CONFIRM, NULL);
 	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
 		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
-			st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L2L1(st, PH_DEACTIVATE | CONFIRM, NULL);
 		}
 	}
 }
@@ -295,7 +295,7 @@
 		FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
 	}
 	while ((skb = skb_dequeue(&bcs->rqueue))) {
-		bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
+		L1L2(bcs->st, PH_DATA | INDICATION, skb);
 	}
 }
 
@@ -717,7 +717,7 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L1_ACTIV);
-	st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+	L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
 }
 
 static void
@@ -726,7 +726,7 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L1_NULL);
-	st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+	L2L1(st, PH_DEACTIVATE | CONFIRM, NULL);
 }
 
 static struct FsmNode L1BFnList[] __initdata =
@@ -801,7 +801,7 @@
 				debugl1(cs, "PH_ACTIVATE_REQ %s",
 					st->l1.l1m.fsm->strState[st->l1.l1m.state]);
 			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
-				st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+				L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
 			else {
 				test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
 				FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
@@ -897,7 +897,7 @@
 	setstack_tei(st);
 	setstack_manager(st);
 	st->l1.stlistp = &(cs->stlist);
-	st->l2.l2l1  = dch_l2l1;
+	st->l1.l2l1  = dch_l2l1;
 	if (cs->setstack_d)
 		cs->setstack_d(st, cs);
 }
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/isdnl2.c	Mon Jan 21 17:23:22 2002
@@ -27,14 +27,14 @@
 static struct Fsm l2fsm;
 
 enum {
-	ST_L2_1,
-	ST_L2_2,
-	ST_L2_3,
-	ST_L2_4,
-	ST_L2_5,
-	ST_L2_6,
-	ST_L2_7,
-	ST_L2_8,
+	ST_L2_1, /* TEI unassigned */
+	ST_L2_2, /* Assign awaiting TEI */
+	ST_L2_3, /* Establish awaiting TEI */
+	ST_L2_4, /* TEI assigned */
+	ST_L2_5, /* Awaiting establishment */
+	ST_L2_6, /* Awaiting release */
+	ST_L2_7, /* Multiple frame established */
+	ST_L2_8, /* Timer recovery */
 };
 
 #define L2_STATE_COUNT (ST_L2_8+1)
@@ -219,7 +219,7 @@
 {
 	if (test_bit(FLG_LAPB, &st->l2.flag))
 		st->l1.bcs->tx_cnt += skb->len;
-	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+	L2L1(st, PH_DATA | REQUEST, skb);
 }
 
 #define enqueue_ui(a, b) enqueue_super(a, b)
@@ -494,15 +494,15 @@
 		else
 			pr = DL_RELEASE | INDICATION;
 
-		st->l2.l2l3(st, pr, NULL);
+		L2L3(st, pr, NULL);
 }
 
 inline void
 lapb_dl_release_l2l3(struct PStack *st, int f)
 {
 		if (test_bit(FLG_LAPB, &st->l2.flag))
-			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
-		st->l2.l2l3(st, DL_RELEASE | f, NULL);
+			L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
+		L2L3(st, DL_RELEASE | f, NULL);
 }
 
 static void
@@ -631,7 +631,7 @@
 	struct sk_buff *skb = arg;
 
 	skb_pull(skb, l2headersize(&st->l2, 1));
-	st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
+	L2L3(st, DL_UNIT_DATA | INDICATION, skb);
 /*	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *		in states 1-3 for broadcast
  */
@@ -673,7 +673,7 @@
 {
 	struct PStack *st = fi->userdata;
 
-	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	L2L3(st, DL_RELEASE | CONFIRM, NULL);
 }
 
 static void
@@ -714,7 +714,7 @@
 	FsmChangeState(fi, ST_L2_7);
 	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
 
-	st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+	L2L3(st, DL_ESTABLISH | INDICATION, NULL);
 }
 
 static void
@@ -763,11 +763,11 @@
 	FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
 
 	if (est)
-		st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+		L2L3(st, DL_ESTABLISH | INDICATION, NULL);
 
 	if ((ST_L2_7==state) || (ST_L2_8 == state))
 		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
-			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+			L2L1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -820,10 +820,10 @@
 	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
 
 	if (pr != -1)
-		st->l2.l2l3(st, pr, NULL);
+		L2L3(st, pr, NULL);
 
 	if (skb_queue_len(&st->l2.i_queue) && cansend(st))
-		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		L2L1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -866,7 +866,7 @@
 	 	if (!test_bit(FLG_L3_INIT, &st->l2.flag))
 			skb_queue_purge(&st->l2.i_queue);
 		if (test_bit(FLG_LAPB, &st->l2.flag))
-			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
 		st5_dl_release_l2l3(st);
 		FsmChangeState(fi, ST_L2_4);
 	}
@@ -962,7 +962,7 @@
 			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
 			l2->windowar[p1] = NULL;
 		}
-		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		L2L1(st, PH_PULL | REQUEST, NULL);
 	}
 }
 
@@ -1022,7 +1022,7 @@
 			restart_t200(st, 12);
 		}
 		if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
-			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+			L2L1(st, PH_PULL | REQUEST, NULL);
 	} else
 		nrerrorrecovery(fi);
 }
@@ -1050,7 +1050,7 @@
 	if (test_bit(FLG_LAPB, &st->l2.flag))
 		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
 	skb_queue_tail(&st->l2.i_queue, skb);
-	st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	L2L1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -1099,7 +1099,7 @@
 		else
 			test_and_set_bit(FLG_ACK_PEND, &l2->flag);
 		skb_pull(skb, l2headersize(l2, 0));
-		st->l2.l2l3(st, DL_DATA | INDICATION, skb);
+		L2L3(st, DL_DATA | INDICATION, skb);
 	} else {
 		/* n(s)!=v(r) */
 		FreeSkb(skb);
@@ -1128,7 +1128,7 @@
 	}
 
 	if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
-		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		L2L1(st, PH_PULL | REQUEST, NULL);
 	if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
 		enquiry_cr(st, RR, RSP, 0);
 }
@@ -1163,7 +1163,7 @@
 		skb_queue_purge(&st->l2.i_queue);
 		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
 		if (test_bit(FLG_LAPB, &st->l2.flag))
-			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
 		st5_dl_release_l2l3(st);
 	} else {
 		st->l2.rc++;
@@ -1304,14 +1304,14 @@
 		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
 		FreeSkb(oskb);
 	}
-	st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+	L2L1(st, PH_PULL | INDICATION, skb);
 	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
 	if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
 		FsmDelTimer(&st->l2.t203, 13);
 		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
 	}
 	if (skb_queue_len(&l2->i_queue) && cansend(st))
-		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		L2L1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -1357,7 +1357,7 @@
 			invoke_retransmission(st, nr);
 			FsmChangeState(fi, ST_L2_7);
 			if (skb_queue_len(&l2->i_queue) && cansend(st))
-				st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+				L2L1(st, PH_PULL | REQUEST, NULL);
 		} else
 			nrerrorrecovery(fi);
 	} else {
@@ -1404,7 +1404,7 @@
 
 	skb_queue_purge(&st->l2.ui_queue);
 	st->l2.tei = -1;
-	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	L2L3(st, DL_RELEASE | INDICATION, NULL);
 	FsmChangeState(fi, ST_L2_1);
 }
 
@@ -1430,7 +1430,7 @@
 	skb_queue_purge(&st->l2.ui_queue);
 	st->l2.tei = -1;
 	stop_t200(st, 18);
-	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	L2L3(st, DL_RELEASE | CONFIRM, NULL);
 	FsmChangeState(fi, ST_L2_1);
 }
 
@@ -1445,7 +1445,7 @@
 	st->l2.tei = -1;
 	stop_t200(st, 17);
 	FsmDelTimer(&st->l2.t203, 19);
-	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	L2L3(st, DL_RELEASE | INDICATION, NULL);
 	FsmChangeState(fi, ST_L2_1);
 }
 
@@ -1457,7 +1457,7 @@
 	skb_queue_purge(&st->l2.i_queue);
 	skb_queue_purge(&st->l2.ui_queue);
 	if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
-		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+		L2L3(st, DL_RELEASE | INDICATION, NULL);
 }
 
 static void
@@ -1480,7 +1480,7 @@
 
 	skb_queue_purge(&st->l2.ui_queue);
 	stop_t200(st, 20);
-	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	L2L3(st, DL_RELEASE | CONFIRM, NULL);
 	FsmChangeState(fi, ST_L2_4);
 }
 
@@ -1494,7 +1494,7 @@
 	freewin(st);
 	stop_t200(st, 19);
 	FsmDelTimer(&st->l2.t203, 19);
-	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	L2L3(st, DL_RELEASE | INDICATION, NULL);
 	FsmChangeState(fi, ST_L2_4);
 }
 
@@ -1739,12 +1739,12 @@
 					test_bit(FLG_ORIG, &st->l2.flag)) {
 					test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
 				}
-				st->l2.l2l1(st, PH_ACTIVATE, NULL);
+				L2L1(st, PH_ACTIVATE, NULL);
 			}
 			break;
 		case (DL_RELEASE | REQUEST):
 			if (test_bit(FLG_LAPB, &st->l2.flag)) {
-				st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+				L2L1(st, PH_DEACTIVATE, NULL);
 			}
 			FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg);
 			break;
@@ -1784,8 +1784,8 @@
 void
 setstack_isdnl2(struct PStack *st, char *debug_id)
 {
-	st->l1.l1l2 = isdnl2_l1l2;
-	st->l3.l3l2 = isdnl2_l3l2;
+	st->l2.l1l2 = isdnl2_l1l2;
+	st->l2.l3l2 = isdnl2_l3l2;
 
 	skb_queue_head_init(&st->l2.i_queue);
 	skb_queue_head_init(&st->l2.ui_queue);
@@ -1813,13 +1813,13 @@
 	switch (pr) {
 		case (DL_DATA | REQUEST):
 		case (DL_UNIT_DATA | REQUEST):
-			st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+			L2L1(st, PH_DATA | REQUEST, arg);
 			break;
 		case (DL_ESTABLISH | REQUEST):
-			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			L2L1(st, PH_ACTIVATE | REQUEST, NULL);
 			break;
 		case (DL_RELEASE | REQUEST):
-			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
 			break;
 	}
 }
@@ -1827,7 +1827,7 @@
 void
 setstack_transl2(struct PStack *st)
 {
-	st->l3.l3l2 = transl2_l3l2;
+	st->l2.l3l2 = transl2_l3l2;
 }
 
 void
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.h linux/drivers/isdn/hisax/isdnl2.h
--- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.h	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/isdnl2.h	Mon Jan 21 17:23:22 2002
@@ -23,3 +23,4 @@
 #define RSP    1
 
 #define LC_FLUSH_WAIT 1
+
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl3.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/isdnl3.c	Mon Jan 21 17:23:22 2002
@@ -163,7 +163,7 @@
 static void
 L3ExpireTimer(struct L3Timer *t)
 {
-	t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
+	t->pc->st->l3.l4l3(t->pc->st, t->event, t->pc);
 }
 
 void
@@ -355,7 +355,7 @@
 	st->l3.l3m.printdebug = l3m_debug;
         FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer);
 	strcpy(st->l3.debug_id, "L3DC ");
-	st->lli.l4l3_proto = no_l3_proto_spec;
+	st->l3.l4l3_proto = no_l3_proto_spec;
 
 #ifdef	CONFIG_HISAX_EURO
 	if (st->protocol == ISDN_PTYPE_EURO) {
@@ -373,13 +373,13 @@
 	} else
 #endif
 	if (st->protocol == ISDN_PTYPE_LEASED) {
-		st->lli.l4l3 = no_l3_proto;
-		st->l2.l2l3 = no_l3_proto;
+		st->l3.l4l3 = no_l3_proto;
+		st->l3.l2l3 = no_l3_proto;
                 st->l3.l3ml3 = no_l3_proto;
 		printk(KERN_INFO "HiSax: Leased line mode\n");
 	} else {
-		st->lli.l4l3 = no_l3_proto;
-		st->l2.l2l3 = no_l3_proto;
+		st->l3.l4l3 = no_l3_proto;
+		st->l3.l2l3 = no_l3_proto;
                 st->l3.l3ml3 = no_l3_proto;
 		sprintf(tmp, "protocol %s not supported",
 			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
@@ -393,7 +393,7 @@
 
 void
 isdnl3_trans(struct PStack *st, int pr, void *arg) {
-	st->l3.l3l2(st, pr, arg);
+	L3L2(st, pr, arg);
 }
 
 void
@@ -424,7 +424,7 @@
 	st->l3.l3m.userint = 0;
 	st->l3.l3m.printdebug = l3m_debug;
 	strcpy(st->l3.debug_id, "L3BC ");
-	st->lli.l4l3 = isdnl3_trans;
+	st->l3.l4l3 = isdnl3_trans;
 }
 
 #define DREL_TIMER_VALUE 40000
@@ -435,7 +435,7 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
-	st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+	L3L2(st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -447,7 +447,7 @@
 
 	FsmChangeState(fi, ST_L3_LC_ESTAB);
 	while ((skb = skb_dequeue(&st->l3.squeue))) {
-		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+		L3L2(st, DL_DATA | REQUEST, skb);
 		dequeued++;
 	}
 	if ((!st->l3.proc) &&  dequeued) {
@@ -468,7 +468,7 @@
 	FsmDelTimer(&st->l3.l3m_timer, 51);
 	FsmChangeState(fi, ST_L3_LC_ESTAB);
 	while ((skb = skb_dequeue(&st->l3.squeue))) {
-		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+		L3L2(st, DL_DATA | REQUEST, skb);
 		dequeued++;
 	}
 	if ((!st->l3.proc) &&  dequeued) {
@@ -512,7 +512,7 @@
 		FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
 	} else {
 		FsmChangeState(fi, ST_L3_LC_REL_WAIT);
-		st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+		L3L2(st, DL_RELEASE | REQUEST, NULL);
 	}
 }
 
@@ -565,7 +565,7 @@
 	switch (pr) {
 		case (DL_DATA | REQUEST):
 			if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
-				st->l3.l3l2(st, pr, arg);
+				L3L2(st, pr, arg);
 			} else {
 				struct sk_buff *skb = arg;
 
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/jade.c linux/drivers/isdn/hisax/jade.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/jade.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/jade.c	Mon Jan 21 17:23:22 2002
@@ -177,7 +177,7 @@
 	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);
+		    L1L2(st, PH_PULL | CONFIRM, NULL);
 		} else
 		    test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 		break;
@@ -193,7 +193,7 @@
 		test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 		test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 		modejade(st->l1.bcs, 0, st->l1.bc);
-		st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+		L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 		break;
     }
 }
@@ -258,7 +258,7 @@
 	if (open_jadestate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = jade_l2l1;
+	st->l1.l2l1 = jade_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/l3_1tr6.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Mon Jan 21 17:23:22 2002
@@ -223,7 +223,7 @@
 			l3_debug(pc->st, tmp);
 		}
 		newl3state(pc, 6);
-		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+		L3L4(pc->st, CC_SETUP | INDICATION, pc);
 	} else
 		release_l3_process(pc);
 }
@@ -253,7 +253,7 @@
 	}
 	dev_kfree_skb(skb);
 	L3AddTimer(&pc->timer, T304, CC_T304);
-	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+	L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
@@ -285,7 +285,7 @@
 	dev_kfree_skb(skb);
 	L3AddTimer(&pc->timer, T310, CC_T310);
 	newl3state(pc, 3);
-	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+	L3L4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
@@ -296,7 +296,7 @@
 	dev_kfree_skb(skb);
 	L3DelTimer(&pc->timer);	/* T304 */
 	newl3state(pc, 4);
-	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+	L3L4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
@@ -316,7 +316,7 @@
 		}
 		if (tmpcharge > pc->para.chargeinfo) {
 			pc->para.chargeinfo = tmpcharge;
-			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+			L3L4(pc->st, CC_CHARGE | INDICATION, pc);
 		}
 		if (pc->st->l3.debug & L3_DEB_CHARGE) {
 			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
@@ -349,7 +349,7 @@
 	newl3state(pc, 10);
 	dev_kfree_skb(skb);
 	pc->para.chargeinfo = 0;
-	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+	L3L4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
@@ -379,7 +379,7 @@
 	StopAllL3Timer(pc);
 	newl3state(pc, 0);
 	l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	release_l3_process(pc);
 }
 
@@ -392,7 +392,7 @@
 	StopAllL3Timer(pc);
 	newl3state(pc, 0);
 	pc->para.cause = NO_CAUSE;
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	L3L4(pc->st, CC_RELEASE | CONFIRM, pc);
 	release_l3_process(pc);
 }
 
@@ -414,7 +414,7 @@
 		}
 		if (tmpcharge > pc->para.chargeinfo) {
 			pc->para.chargeinfo = tmpcharge;
-			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+			L3L4(pc->st, CC_CHARGE | INDICATION, pc);
 		}
 		if (pc->st->l3.debug & L3_DEB_CHARGE) {
 			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
@@ -447,7 +447,7 @@
 	}
 	dev_kfree_skb(skb);
 	newl3state(pc, 12);
-	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+	L3L4(pc->st, CC_DISCONNECT | INDICATION, pc);
 }
 
 
@@ -464,7 +464,7 @@
 	newl3state(pc, 10);
 	pc->para.chargeinfo = 0;
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+	L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
@@ -573,7 +573,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3_1tr6_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -618,7 +618,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3_1tr6_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -627,7 +627,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3_1tr6_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+	L3L4(pc->st, CC_CONNECT_ERR, pc);
 }
 
 static void
@@ -643,7 +643,7 @@
 l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	L3L4(pc->st, CC_RELEASE_ERR, pc);
 	release_l3_process(pc);
 }
 
@@ -652,7 +652,7 @@
 {
         pc->para.cause = CAUSE_LocalProcErr;
         l3_1tr6_disconnect_req(pc, pr, NULL);
-        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+        L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -661,7 +661,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);
+        L3L4(pc->st, CC_RELEASE | INDICATION, pc);
         release_l3_process(pc);
 }
 
@@ -946,8 +946,8 @@
 {
 	char tmp[64];
 
-	st->lli.l4l3 = down1tr6;
-	st->l2.l2l3 = up1tr6;
+	st->l3.l4l3 = down1tr6;
+	st->l3.l2l3 = up1tr6;
 	st->l3.l3ml3 = man1tr6;
 	st->l3.N303 = 0;
 
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/l3dss1.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/l3dss1.c	Mon Jan 21 17:23:22 2002
@@ -449,7 +449,7 @@
                             pc->prot.dss1.remote_result = 0; /* success */     
                             pc->prot.dss1.invoke_id = 0;
                             pc->redir_result = pc->prot.dss1.remote_result; 
-                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
+                            L3L4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
                         else
                           l3_debug(st,"return error unknown identifier");
 			break;
@@ -494,7 +494,7 @@
                             pc->prot.dss1.remote_result = err_ret; /* result */
                             pc->prot.dss1.invoke_id = 0; 
                             pc->redir_result = pc->prot.dss1.remote_result; 
-                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                            L3L4(st, CC_REDIR | INDICATION, pc);  
                           } /* Deflection error */
                         else
                           l3_debug(st,"return result unknown identifier");
@@ -983,7 +983,7 @@
 		pc->para.cause = NO_CAUSE;
 	StopAllL3Timer(pc);
 	newl3state(pc, 0);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	L3L4(pc->st, CC_RELEASE | CONFIRM, pc);
 	dss1_release_l3_process(pc);
 }
 
@@ -1473,7 +1473,7 @@
 	L3AddTimer(&pc->timer, T310, CC_T310);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3dss1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+	L3L4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
@@ -1512,7 +1512,7 @@
 	L3AddTimer(&pc->timer, T304, CC_T304);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3dss1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+	L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
@@ -1544,7 +1544,7 @@
 	if (cause)
 		newl3state(pc, 19);
        	if (11 != ret)
-		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+		L3L4(pc->st, CC_DISCONNECT | INDICATION, pc);
        	else if (!cause)
 		   l3dss1_release_req(pc, pr, NULL);
 	if (cause) {
@@ -1570,7 +1570,7 @@
 	/* here should inserted COLP handling KKe */
 	if (ret)
 		l3dss1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+	L3L4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
@@ -1588,7 +1588,7 @@
 	newl3state(pc, 4);
 	if (ret)
 		l3dss1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+	L3L4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
@@ -1754,7 +1754,7 @@
 	newl3state(pc, 6);
 	if (err) /* STATUS for none mandatory IE errors after actions are taken */
 		l3dss1_std_ie_err(pc, err);
-	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+	L3L4(pc->st, CC_SETUP | INDICATION, pc);
 }
 
 static void
@@ -1833,7 +1833,7 @@
 	L3DelTimer(&pc->timer);
 	if (ret)
 		l3dss1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+	L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
@@ -1860,7 +1860,7 @@
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
 	l3_msg(pc->st, DL_DATA | REQUEST, skb);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	newl3state(pc, 0);
 	dss1_release_l3_process(pc);
 }
@@ -1894,7 +1894,7 @@
 		l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
 	else
 		l3dss1_message(pc, MT_RELEASE_COMPLETE);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	newl3state(pc, 0);
 	dss1_release_l3_process(pc);
 }
@@ -1916,7 +1916,7 @@
 {
 	newl3state(pc, 9);
 	l3dss1_message(pc, MT_CALL_PROCEEDING);
-	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+	L3L4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
 }
 
 static void
@@ -2009,7 +2009,7 @@
 	if (err)
 		l3dss1_std_ie_err(pc, err);
 	if (ERR_IE_COMPREHENSION != err)
-		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+		L3L4(pc->st, CC_PROGRESS | INDICATION, pc);
 }
 
 static void
@@ -2050,7 +2050,7 @@
 	if (err)
 		l3dss1_std_ie_err(pc, err);
 	if (ERR_IE_COMPREHENSION != err)
-		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+		L3L4(pc->st, CC_NOTIFY | INDICATION, pc);
 }
 
 static void
@@ -2082,7 +2082,7 @@
 		if ((p = findie(p, skb->len, 0x70, 0))) {
 			iecpy(tmp, p, 1);
 			strcat(pc->para.setup.eazmsn, tmp);
-			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+			L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
 		}
 		L3AddTimer(&pc->timer, T302, CC_T302);
 	}
@@ -2297,11 +2297,11 @@
 		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
 		 * set down layer 3 without sending any message
 		 */
-		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 		newl3state(pc, 0);
 		dss1_release_l3_process(pc);
 	} else {
-		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+		L3L4(pc->st, CC_IGNORE | INDICATION, pc);
 	}
 }
 
@@ -2317,7 +2317,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 28; /* invalid number */
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -2330,7 +2330,7 @@
 	} else {
 		L3DelTimer(&pc->timer);
 		l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
-		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		L3L4(pc->st, CC_NOSETUP_RSP, pc);
 		dss1_release_l3_process(pc);
 	}
 }
@@ -2342,7 +2342,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 
 }
 
@@ -2382,7 +2382,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -2392,7 +2392,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+	L3L4(pc->st, CC_CONNECT_ERR, pc);
 }
 
 static void
@@ -2408,7 +2408,7 @@
 l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	L3L4(pc->st, CC_RELEASE_ERR, pc);
 	dss1_release_l3_process(pc);
 }
 
@@ -2418,7 +2418,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 102;	/* Timer expiry */
 	pc->para.loc = 0;	/* local */
-	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	L3L4(pc->st, CC_RESUME_ERR, pc);
 	newl3state(pc, 19);
 	l3dss1_message(pc, MT_RELEASE);
 	L3AddTimer(&pc->timer, T308, CC_T308_1);
@@ -2430,7 +2430,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 102;	/* Timer expiry */
 	pc->para.loc = 0;	/* local */
-	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	L3L4(pc->st, CC_SUSPEND_ERR, pc);
 	newl3state(pc, 10);
 }
 
@@ -2438,7 +2438,7 @@
 l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	dss1_release_l3_process(pc);
 }
 
@@ -2494,7 +2494,7 @@
 		 * if received MT_STATUS with cause == 111 and call
 		 * state == 0, then we must set down layer 3
 		 */
-		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 		newl3state(pc, 0);
 		dss1_release_l3_process(pc);
 	}
@@ -2553,7 +2553,7 @@
 	L3DelTimer(&pc->timer);
 	newl3state(pc, 0);
 	pc->para.cause = NO_CAUSE;
-	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	L3L4(pc->st, CC_SUSPEND | CONFIRM, pc);
 	/* We don't handle suspend_ack for IE errors now */
 	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
 		if (pc->debug & L3_DEB_WARN)
@@ -2583,7 +2583,7 @@
 		return;
 	}
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	L3L4(pc->st, CC_SUSPEND_ERR, pc);
 	newl3state(pc, 10);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3dss1_std_ie_err(pc, ret);
@@ -2647,7 +2647,7 @@
 		return;
 	}
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	L3L4(pc->st, CC_RESUME | CONFIRM, pc);
 	newl3state(pc, 10);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3dss1_std_ie_err(pc, ret);
@@ -2675,7 +2675,7 @@
 		return;
 	}
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	L3L4(pc->st, CC_RESUME_ERR, pc);
 	newl3state(pc, 0);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3dss1_std_ie_err(pc, ret);
@@ -2713,9 +2713,9 @@
 	up = pc->st->l3.proc;
 	while (up) {
 		if ((ri & 7) == 7)
-			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+			L4L3(up->st, CC_RESTART | REQUEST, up);
 		else if (up->para.bchannel == chan)
-			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+			L4L3(up->st, CC_RESTART | REQUEST, up);
 		up = up->next;
 	}
 	p = tmp;
@@ -2742,7 +2742,7 @@
         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);
+        L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -2751,7 +2751,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);
+        L3L4(pc->st, CC_RELEASE | INDICATION, pc);
         release_l3_process(pc);
 }
 
@@ -3219,9 +3219,9 @@
 	char tmp[64];
 	int i;
 
-	st->lli.l4l3 = dss1down;
-	st->lli.l4l3_proto = l3dss1_cmd_global;
-	st->l2.l2l3 = dss1up;
+	st->l3.l4l3 = dss1down;
+	st->l3.l4l3_proto = l3dss1_cmd_global;
+	st->l3.l2l3 = dss1up;
 	st->l3.l3ml3 = dss1man;
 	st->l3.N303 = 1;
 	st->prot.dss1.last_invoke_id = 0;
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/l3ni1.c linux/drivers/isdn/hisax/l3ni1.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/l3ni1.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/l3ni1.c	Mon Jan 21 17:23:22 2002
@@ -377,7 +377,7 @@
                             pc->prot.ni1.remote_result = 0; /* success */     
                             pc->prot.ni1.invoke_id = 0;
                             pc->redir_result = pc->prot.ni1.remote_result; 
-                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
+                            L3L4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
                         else
                           l3_debug(st,"return error unknown identifier");
 			break;
@@ -422,7 +422,7 @@
                             pc->prot.ni1.remote_result = err_ret; /* result */
                             pc->prot.ni1.invoke_id = 0; 
                             pc->redir_result = pc->prot.ni1.remote_result; 
-                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                            L3L4(st, CC_REDIR | INDICATION, pc);  
                           } /* Deflection error */
                         else
                           l3_debug(st,"return result unknown identifier");
@@ -932,7 +932,7 @@
 		pc->para.cause = NO_CAUSE;
 	StopAllL3Timer(pc);
 	newl3state(pc, 0);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	L3L4(pc->st, CC_RELEASE | CONFIRM, pc);
 	ni1_release_l3_process(pc);
 }
 
@@ -1326,7 +1326,7 @@
 	L3AddTimer(&pc->timer, T310, CC_T310);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3ni1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+	L3L4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
@@ -1365,7 +1365,7 @@
 	L3AddTimer(&pc->timer, T304, CC_T304);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3ni1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+	L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
@@ -1397,7 +1397,7 @@
 	if (cause)
 		newl3state(pc, 19);
        	if (11 != ret)
-		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+		L3L4(pc->st, CC_DISCONNECT | INDICATION, pc);
        	else if (!cause)
 		   l3ni1_release_req(pc, pr, NULL);
 	if (cause) {
@@ -1423,7 +1423,7 @@
 	/* here should inserted COLP handling KKe */
 	if (ret)
 		l3ni1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+	L3L4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
@@ -1441,7 +1441,7 @@
 	newl3state(pc, 4);
 	if (ret)
 		l3ni1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+	L3L4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
@@ -1607,7 +1607,7 @@
 	newl3state(pc, 6);
 	if (err) /* STATUS for none mandatory IE errors after actions are taken */
 		l3ni1_std_ie_err(pc, err);
-	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+	L3L4(pc->st, CC_SETUP | INDICATION, pc);
 }
 
 static void
@@ -1688,7 +1688,7 @@
 	L3DelTimer(&pc->timer);
 	if (ret)
 		l3ni1_std_ie_err(pc, ret);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+	L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
@@ -1715,7 +1715,7 @@
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
 	l3_msg(pc->st, DL_DATA | REQUEST, skb);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	newl3state(pc, 0);
 	ni1_release_l3_process(pc);
 }
@@ -1749,7 +1749,7 @@
 		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
 	else
 		l3ni1_message(pc, MT_RELEASE_COMPLETE);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	newl3state(pc, 0);
 	ni1_release_l3_process(pc);
 }
@@ -1771,7 +1771,7 @@
 {
 	newl3state(pc, 9);
 	l3ni1_message(pc, MT_CALL_PROCEEDING);
-	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+	L3L4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
 }
 
 static void
@@ -1864,7 +1864,7 @@
 	if (err)
 		l3ni1_std_ie_err(pc, err);
 	if (ERR_IE_COMPREHENSION != err)
-		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+		L3L4(pc->st, CC_PROGRESS | INDICATION, pc);
 }
 
 static void
@@ -1905,7 +1905,7 @@
 	if (err)
 		l3ni1_std_ie_err(pc, err);
 	if (ERR_IE_COMPREHENSION != err)
-		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+		L3L4(pc->st, CC_NOTIFY | INDICATION, pc);
 }
 
 static void
@@ -1937,7 +1937,7 @@
 		if ((p = findie(p, skb->len, 0x70, 0))) {
 			iecpy(tmp, p, 1);
 			strcat(pc->para.setup.eazmsn, tmp);
-			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+			L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
 		}
 		L3AddTimer(&pc->timer, T302, CC_T302);
 	}
@@ -2152,11 +2152,11 @@
 		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
 		 * set down layer 3 without sending any message
 		 */
-		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 		newl3state(pc, 0);
 		ni1_release_l3_process(pc);
 	} else {
-		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+		L3L4(pc->st, CC_IGNORE | INDICATION, pc);
 	}
 }
 
@@ -2172,7 +2172,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 28; /* invalid number */
 	l3ni1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -2185,7 +2185,7 @@
 	} else {
 		L3DelTimer(&pc->timer);
 		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
-		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		L3L4(pc->st, CC_NOSETUP_RSP, pc);
 		ni1_release_l3_process(pc);
 	}
 }
@@ -2197,7 +2197,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3ni1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 
 }
 
@@ -2237,7 +2237,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3ni1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+	L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -2247,7 +2247,7 @@
 	pc->para.loc = 0;
 	pc->para.cause = 102;
 	l3ni1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+	L3L4(pc->st, CC_CONNECT_ERR, pc);
 }
 
 static void
@@ -2263,7 +2263,7 @@
 l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	L3L4(pc->st, CC_RELEASE_ERR, pc);
 	ni1_release_l3_process(pc);
 }
 
@@ -2273,7 +2273,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 102;	/* Timer expiry */
 	pc->para.loc = 0;	/* local */
-	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	L3L4(pc->st, CC_RESUME_ERR, pc);
 	newl3state(pc, 19);
 	l3ni1_message(pc, MT_RELEASE);
 	L3AddTimer(&pc->timer, T308, CC_T308_1);
@@ -2285,7 +2285,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 102;	/* Timer expiry */
 	pc->para.loc = 0;	/* local */
-	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	L3L4(pc->st, CC_SUSPEND_ERR, pc);
 	newl3state(pc, 10);
 }
 
@@ -2293,7 +2293,7 @@
 l3ni1_restart(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 	ni1_release_l3_process(pc);
 }
 
@@ -2349,7 +2349,7 @@
 		 * if received MT_STATUS with cause == 111 and call
 		 * state == 0, then we must set down layer 3
 		 */
-		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		L3L4(pc->st, CC_RELEASE | INDICATION, pc);
 		newl3state(pc, 0);
 		ni1_release_l3_process(pc);
 	}
@@ -2408,7 +2408,7 @@
 	L3DelTimer(&pc->timer);
 	newl3state(pc, 0);
 	pc->para.cause = NO_CAUSE;
-	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	L3L4(pc->st, CC_SUSPEND | CONFIRM, pc);
 	/* We don't handle suspend_ack for IE errors now */
 	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
 		if (pc->debug & L3_DEB_WARN)
@@ -2438,7 +2438,7 @@
 		return;
 	}
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	L3L4(pc->st, CC_SUSPEND_ERR, pc);
 	newl3state(pc, 10);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3ni1_std_ie_err(pc, ret);
@@ -2502,7 +2502,7 @@
 		return;
 	}
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	L3L4(pc->st, CC_RESUME | CONFIRM, pc);
 	newl3state(pc, 10);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3ni1_std_ie_err(pc, ret);
@@ -2530,7 +2530,7 @@
 		return;
 	}
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	L3L4(pc->st, CC_RESUME_ERR, pc);
 	newl3state(pc, 0);
 	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
 		l3ni1_std_ie_err(pc, ret);
@@ -2568,9 +2568,9 @@
 	up = pc->st->l3.proc;
 	while (up) {
 		if ((ri & 7) == 7)
-			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+			L4L3(up->st, CC_RESTART | REQUEST, up);
 		else if (up->para.bchannel == chan)
-			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+			L4L3(up->st, CC_RESTART | REQUEST, up);
 		
 		up = up->next;
 	}
@@ -2598,7 +2598,7 @@
         pc->para.cause = 0x29;          /* Temporary failure */
         pc->para.loc = 0;
         l3ni1_disconnect_req(pc, pr, NULL);
-        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+        L3L4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -2607,7 +2607,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);
+        L3L4(pc->st, CC_RELEASE | INDICATION, pc);
         release_l3_process(pc);
 }
 
@@ -2642,7 +2642,7 @@
 	{
 		printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn );
 		newl3state( pc, 0 );
-		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+		L3L2( pc->st, DL_RELEASE | REQUEST, NULL );
 		return;
 	}
 
@@ -2667,7 +2667,7 @@
 	L3DelTimer( &pc->timer );
 	L3AddTimer( &pc->timer, TSPID, CC_TSPID );
 
-	pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb );
+	L3L2( pc->st, DL_DATA | REQUEST, skb );
 }
 
 static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg )
@@ -2700,7 +2700,7 @@
 
 		printk( KERN_ERR "SPID not accepted\n" );
 		newl3state( pc, 0 );
-		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+		L3L2( pc->st, DL_RELEASE | REQUEST, NULL );
 	}
 }
 
@@ -3170,9 +3170,9 @@
 	char tmp[64];
 	int i;
 
-	st->lli.l4l3 = ni1down;
-	st->lli.l4l3_proto = l3ni1_cmd_global;
-	st->l2.l2l3 = ni1up;
+	st->l3.l4l3 = ni1down;
+	st->l3.l4l3_proto = l3ni1_cmd_global;
+	st->l3.l2l3 = ni1up;
 	st->l3.l3ml3 = ni1man;
 	st->l3.N303 = 1;
 	st->prot.ni1.last_invoke_id = 0;
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/netjet.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/isdn/hisax/netjet.c	Mon Jan 21 17:23:22 2002
@@ -847,7 +847,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -863,7 +863,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			mode_tiger(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -924,7 +924,7 @@
 	if (open_tigerstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = tiger_l2l1;
+	st->l1.l2l1 = tiger_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/st5481.h linux/drivers/isdn/hisax/st5481.h
--- linux-2.5.3-pre2/drivers/isdn/hisax/st5481.h	Sun Jan  6 18:49:21 2002
+++ linux/drivers/isdn/hisax/st5481.h	Mon Jan 21 17:23:22 2002
@@ -478,7 +478,7 @@
   if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb)
 
 static void __attribute__((unused))
-dump_iso_packet(const char *name,urb_t *urb)
+dump_iso_packet(const char *name, struct urb *urb)
 {
 	int i,j;
 	int len,ofs;
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/st5481_d.c linux/drivers/isdn/hisax/st5481_d.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/st5481_d.c	Sat Dec  8 20:28:45 2001
+++ linux/drivers/isdn/hisax/st5481_d.c	Mon Jan 21 17:23:22 2002
@@ -297,7 +297,7 @@
 	unsigned int num_packets, packet_offset;
 	int len, buf_size, bytes_sent;
 	struct sk_buff *skb;
-	iso_packet_descriptor_t *desc;
+	struct usb_iso_packet_descriptor *desc;
 
 	if (d_out->fsm.state != ST_DOUT_NORMAL)
 		return;
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/st5481_usb.c linux/drivers/isdn/hisax/st5481_usb.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/st5481_usb.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/isdn/hisax/st5481_usb.c	Mon Jan 21 17:23:22 2002
@@ -235,7 +235,7 @@
 	struct usb_interface_descriptor *altsetting;
 	struct usb_endpoint_descriptor *endpoint;
 	int status;
-	urb_t *urb;
+	struct urb *urb;
 	u_char *buf;
 	
 	DBG(1,"");
@@ -560,7 +560,7 @@
  */
 int st5481_isoc_flatten(struct urb *urb)
 {
-	iso_packet_descriptor_t *pipd,*pend;
+	struct usb_iso_packet_descriptor *pipd,*pend;
 	unsigned char *src,*dst;
 	unsigned int len;
 	
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/tei.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/isdn/hisax/tei.c	Mon Jan 21 17:23:22 2002
@@ -120,7 +120,7 @@
 	bp[2] = ri & 0xff;
 	bp[3] = m_id;
 	bp[4] = (tei << 1) | 1;
-	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+	L2L1(st, PH_DATA | REQUEST, skb);
 }
 
 static void
@@ -166,7 +166,7 @@
 	} else if (ri == st->ma.ri) {
 		FsmDelTimer(&st->ma.t202, 1);
 		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
-		st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
+		L3L2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
 		cs = (struct IsdnCardState *) st->l1.hardware;
 		cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
 	}
@@ -240,7 +240,7 @@
 	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
 		FsmDelTimer(&st->ma.t202, 5);
 		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
-		st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
+		L3L2(st, MDL_REMOVE | REQUEST, 0);
 		cs = (struct IsdnCardState *) st->l1.hardware;
 		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
 	}
@@ -276,7 +276,7 @@
 		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
 	} else {
 		st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
-		st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0);
+		L3L2(st, MDL_ERROR | RESPONSE, 0);
 		cs = (struct IsdnCardState *) st->l1.hardware;
 		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
 		FsmChangeState(fi, ST_TEI_NOP);
@@ -299,7 +299,7 @@
 	} else {
 		st->ma.tei_m.printdebug(&st->ma.tei_m,
 			"verify req for tei %d failed", st->l2.tei);
-		st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
+		L3L2(st, MDL_REMOVE | REQUEST, 0);
 		cs = (struct IsdnCardState *) st->l1.hardware;
 		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
 		FsmChangeState(fi, ST_TEI_NOP);
@@ -372,7 +372,7 @@
 			if (st->ma.debug)
 				st->ma.tei_m.printdebug(&st->ma.tei_m,
 					"fixed assign tei %d", st->l2.tei);
-			st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+			L3L2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
 			cs = (struct IsdnCardState *) st->l1.hardware;
 			cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
 		}
diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/w6692.c linux/drivers/isdn/hisax/w6692.c
--- linux-2.5.3-pre2/drivers/isdn/hisax/w6692.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/isdn/hisax/w6692.c	Mon Jan 21 17:23:22 2002
@@ -108,7 +108,7 @@
 			debugl1(cs, "D-Channel Busy cleared");
 		stptr = cs->stlist;
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			L1L2(stptr, PH_PAUSE | CONFIRM, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -614,7 +614,7 @@
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -687,7 +687,7 @@
 			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 			stptr = cs->stlist;
 			while (stptr != NULL) {
-				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				L1L2(stptr, PH_PAUSE | INDICATION, NULL);
 				stptr = stptr->next;
 			}
 		} else {
@@ -772,7 +772,7 @@
 		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);
+				L1L2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
@@ -788,7 +788,7 @@
 			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
 			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 			W6692Bmode(st->l1.bcs, 0, st->l1.bc);
-			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
 			break;
 	}
 }
@@ -852,7 +852,7 @@
 	if (open_w6692state(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
-	st->l2.l2l1 = W6692_l2l1;
+	st->l1.l2l1 = W6692_l2l1;
 	setstack_manager(st);
 	bcs->st = st;
 	setstack_l1_B(st);
diff -urN linux-2.5.3-pre2/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- linux-2.5.3-pre2/drivers/isdn/isdn_common.c	Mon Jan 21 17:22:32 2002
+++ linux/drivers/isdn/isdn_common.c	Mon Jan 21 17:23:22 2002
@@ -1124,6 +1124,18 @@
 #undef cfg
 }
 
+static struct file_operations isdn_status_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		isdn_status_read,
+	write:		isdn_status_write,
+	poll:		isdn_status_poll,
+	ioctl:		isdn_status_ioctl,
+	open:		isdn_status_open,
+	release:	isdn_status_release,
+};
+
 /*
  * /dev/isdnctrlX
  */
@@ -1650,155 +1662,62 @@
 #undef cfg
 }
 
-/*
- *
- */
-
-static ssize_t
-isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
-{
-	uint minor = minor(file->f_dentry->d_inode->i_rdev);
-
-	if (minor < ISDN_MINOR_CTRL)
-		return -ENODEV;
-	
-	if (minor <= ISDN_MINOR_CTRLMAX)
-		return isdn_ctrl_read(file, buf, count, off);
-
-#ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return isdn_ppp_read(file, buf, count, off);
-#endif
-
-	if (minor == ISDN_MINOR_STATUS)
-		return isdn_status_read(file, buf, count, off);
-
-	return -ENODEV;
-}
-
-static ssize_t
-isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
+static struct file_operations isdn_ctrl_fops =
 {
-	uint minor = minor(file->f_dentry->d_inode->i_rdev);
-
-	if (minor < ISDN_MINOR_CTRL)
-		return -ENODEV;
-
-	if (minor <= ISDN_MINOR_CTRLMAX)
-		return isdn_ctrl_write(file, buf, count, off);
-
-#ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return isdn_ppp_write(file, buf, count, off);
-#endif
-
-	if (minor == ISDN_MINOR_STATUS)
-		return isdn_status_write(file, buf, count, off);
-
-	return -ENODEV;
-}
-
-static unsigned int
-isdn_poll(struct file *file, poll_table * wait)
-{
-	unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
-
-	if (minor < ISDN_MINOR_CTRL)
-		return POLLERR;
-
-	if (minor <= ISDN_MINOR_CTRLMAX)
-		return isdn_ctrl_poll(file, wait);
-
-#ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return isdn_ppp_poll(file, wait);
-#endif
-
-	if (minor == ISDN_MINOR_STATUS)
-		return isdn_status_poll(file, wait);
-
-	return POLLERR;
-}
-
-static int
-isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
-{
-	uint minor = minor(inode->i_rdev);
-
-	if (minor == ISDN_MINOR_STATUS)
-		return isdn_status_ioctl(inode, file, cmd, arg);
-
-	if (minor < ISDN_MINOR_CTRL)
-		return -ENODEV;
-
-	if (minor <= ISDN_MINOR_CTRLMAX)
-		return isdn_ctrl_ioctl(inode, file, cmd, arg);
-
-#ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return isdn_ppp_ioctl(inode, file, cmd, arg);
-#endif
-
-	return -ENODEV;
-}
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		isdn_ctrl_read,
+	write:		isdn_ctrl_write,
+	poll:		isdn_ctrl_poll,
+	ioctl:		isdn_ctrl_ioctl,
+	open:		isdn_ctrl_open,
+	release:	isdn_ctrl_release,
+};
 
 /*
- * Open the device code.
+ * file_operations for major 43, /dev/isdn*
+ * stolen from drivers/char/misc.c
  */
-static int
-isdn_open(struct inode *ino, struct file *filep)
-{
-	uint minor = minor(ino->i_rdev);
-
-	if (minor < ISDN_MINOR_CTRL)
-		return -ENODEV;
-
-	if (minor <= ISDN_MINOR_CTRLMAX)
-		return isdn_ctrl_open(ino, filep);
-
-	if (minor == ISDN_MINOR_STATUS)
-		return isdn_status_open(ino, filep);
-
-#ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return isdn_ppp_open(ino, filep);
-#endif
-
-	return -ENODEV;
-}
 
 static int
-isdn_close(struct inode *ino, struct file *filep)
+isdn_open(struct inode * inode, struct file * file)
 {
-	uint minor = minor(ino->i_rdev);
-
-	if (minor == ISDN_MINOR_STATUS)
-		return isdn_status_release(ino, filep);
-
-	if (minor < ISDN_MINOR_CTRL)
-		return -ENODEV;
-
-	if (minor <= ISDN_MINOR_CTRLMAX)
-		return isdn_ctrl_release(ino, filep);
-
+	int minor = minor(inode->i_rdev);
+	int err = -ENODEV;
+	struct file_operations *old_fops, *new_fops = NULL;
+	
+	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX)
+		new_fops = fops_get(&isdn_ctrl_fops);
 #ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return isdn_ppp_release(ino, filep);
+	else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX)
+		new_fops = fops_get(&isdn_ppp_fops);
 #endif
+	else if (minor == ISDN_MINOR_STATUS)
+		new_fops = fops_get(&isdn_status_fops);
+
+	if (!new_fops)
+		goto out;
 
-	return -ENODEV;
+	err = 0;
+	old_fops = file->f_op;
+	file->f_op = new_fops;
+	if (file->f_op->open) {
+		err = file->f_op->open(inode,file);
+		if (err) {
+			fops_put(file->f_op);
+			file->f_op = fops_get(old_fops);
+		}
+	}
+	fops_put(old_fops);
+	
+ out:
+	return err;
 }
 
 static struct file_operations isdn_fops =
 {
 	owner:		THIS_MODULE,
-	llseek:		no_llseek,
-	read:		isdn_read,
-	write:		isdn_write,
-	poll:		isdn_poll,
-	ioctl:		isdn_ioctl,
 	open:		isdn_open,
-	release:	isdn_close,
 };
 
 char *
@@ -2253,7 +2172,7 @@
 	sprintf (buf, "isdn%d", k);
 	dev->devfs_handle_isdnX[k] =
 	    devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT,
-			    ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR,
+			    ISDN_MAJOR, k,0600 | S_IFCHR,
 			    &isdn_fops, NULL);
 	sprintf (buf, "isdnctrl%d", k);
 	dev->devfs_handle_isdnctrlX[k] =
diff -urN linux-2.5.3-pre2/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- linux-2.5.3-pre2/drivers/isdn/isdn_ppp.c	Mon Jan 21 17:22:32 2002
+++ linux/drivers/isdn/isdn_ppp.c	Mon Jan 21 17:23:22 2002
@@ -9,7 +9,7 @@
  *
  */
 
-#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/isdn.h>
 #include <linux/smp_lock.h>
 #include <linux/poll.h>
@@ -269,7 +269,7 @@
  * isdn_ppp_open
  */
 
-int
+static int
 isdn_ppp_open(struct inode *ino, struct file *file)
 {
 	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
@@ -322,7 +322,7 @@
 /*
  * release ippp device
  */
-int
+static int
 isdn_ppp_release(struct inode *ino, struct file *file)
 {
 	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
@@ -418,7 +418,7 @@
 /*
  * ippp device ioctl
  */
-int
+static int
 isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
@@ -579,7 +579,7 @@
 	return 0;
 }
 
-unsigned int
+static unsigned int
 isdn_ppp_poll(struct file *file, poll_table * wait)
 {
 	unsigned int mask;
@@ -690,8 +690,8 @@
  *           reports, that there is data
  */
 
-int
-isdn_ppp_read(struct file *file, char *buf, int count, loff_t *off)
+static int
+isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
 {
 	struct ippp_struct *is;
 	struct ippp_buf_queue *b;
@@ -745,8 +745,8 @@
  * ipppd wanna write a packet to the card .. non-blocking
  */
 
-int
-isdn_ppp_write(struct file *file, const char *buf, int count, loff_t *off)
+static int
+isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off)
 {
 	isdn_net_local *lp;
 	struct ippp_struct *is;
@@ -829,6 +829,18 @@
 	unlock_kernel();
 	return retval;
 }
+
+struct file_operations isdn_ppp_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		isdn_ppp_read,
+	write:		isdn_ppp_write,
+	poll:		isdn_ppp_poll,
+	ioctl:		isdn_ppp_ioctl,
+	open:		isdn_ppp_open,
+	release:	isdn_ppp_release,
+};
 
 /*
  * init memory, structures etc.
diff -urN linux-2.5.3-pre2/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h
--- linux-2.5.3-pre2/drivers/isdn/isdn_ppp.h	Mon Jan 21 17:22:32 2002
+++ linux/drivers/isdn/isdn_ppp.h	Mon Jan 21 17:23:22 2002
@@ -12,12 +12,7 @@
 #include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
 #include <linux/isdn_ppp.h>	/* for isdn_ppp info */
 
-extern int isdn_ppp_open(struct inode *, struct file *);
-extern int isdn_ppp_release(struct inode *, struct file *);
-extern int isdn_ppp_read(struct file *, char *, int, loff_t *off);
-extern int isdn_ppp_write(struct file *, const char *, int, loff_t *off);
-extern int isdn_ppp_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *);
+extern struct file_operations isdn_ppp_fops;
 
 extern int isdn_ppp_init(void);
 extern void isdn_ppp_cleanup(void);
diff -urN linux-2.5.3-pre2/drivers/media/video/cpia_usb.c linux/drivers/media/video/cpia_usb.c
--- linux-2.5.3-pre2/drivers/media/video/cpia_usb.c	Thu Oct 25 13:53:47 2001
+++ linux/drivers/media/video/cpia_usb.c	Mon Jan 21 17:23:22 2002
@@ -46,7 +46,7 @@
 
 struct cpia_sbuf {
 	char *data;
-	urb_t *urb;
+	struct urb *urb;
 };
 
 #define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
@@ -168,7 +168,7 @@
 static int cpia_usb_open(void *privdata)
 {
 	struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
-	urb_t *urb;
+	struct urb *urb;
 	int ret, retval = 0, fx, err;
   
 	if (!ucpia)
diff -urN linux-2.5.3-pre2/drivers/net/Config.in linux/drivers/net/Config.in
--- linux-2.5.3-pre2/drivers/net/Config.in	Mon Jan 21 17:22:32 2002
+++ linux/drivers/net/Config.in	Mon Jan 21 17:23:22 2002
@@ -292,7 +292,6 @@
 bool 'Wireless LAN (non-hamradio)' CONFIG_NET_RADIO
 if [ "$CONFIG_NET_RADIO" = "y" ]; then
    dep_tristate '  STRIP (Metricom starmode radio IP)' CONFIG_STRIP $CONFIG_INET
-   tristate '  AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
    tristate '  Aironet Arlan 655 & IC2200 DS support' CONFIG_ARLAN
    tristate '  Aironet 4500/4800 series adapters' CONFIG_AIRONET4500
    dep_tristate '   Aironet 4500/4800 ISA/PCI/PNP/365 support ' CONFIG_AIRONET4500_NONCS $CONFIG_AIRONET4500
diff -urN linux-2.5.3-pre2/drivers/net/Makefile linux/drivers/net/Makefile
--- linux-2.5.3-pre2/drivers/net/Makefile	Mon Dec 10 11:17:40 2001
+++ linux/drivers/net/Makefile	Mon Jan 21 17:23:22 2002
@@ -166,7 +166,6 @@
 obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
 obj-$(CONFIG_8139CP) += 8139cp.o
 obj-$(CONFIG_8139TOO) += 8139too.o
-obj-$(CONFIG_WAVELAN) += wavelan.o
 obj-$(CONFIG_ARLAN) += arlan.o arlan-proc.o
 obj-$(CONFIG_ZNET) += znet.o
 obj-$(CONFIG_LAN_SAA9730) += saa9730.o
diff -urN linux-2.5.3-pre2/drivers/net/au1000_eth.c linux/drivers/net/au1000_eth.c
--- linux-2.5.3-pre2/drivers/net/au1000_eth.c	Thu Jan 10 16:23:45 2002
+++ linux/drivers/net/au1000_eth.c	Mon Jan 21 17:23:22 2002
@@ -1141,6 +1141,8 @@
 		//data[3] = mdio_read(ioaddr, data[0], data[1]); 
 		return 0;
 		case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		//mdio_write(ioaddr, data[0], data[1], data[2]);
 		return 0;
 		default:
diff -urN linux-2.5.3-pre2/drivers/net/dl2k.c linux/drivers/net/dl2k.c
--- linux-2.5.3-pre2/drivers/net/dl2k.c	Thu Jan 10 16:23:45 2002
+++ linux/drivers/net/dl2k.c	Mon Jan 21 17:23:23 2002
@@ -1040,6 +1040,8 @@
 		miidata->out_value = mii_read (dev, phy_addr, miidata->reg_num);
 		break;
 	case SIOCDEVPRIVATE + 2:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		mii_write (dev, phy_addr, miidata->reg_num, miidata->in_value);
 		break;
 	case SIOCDEVPRIVATE + 3:
diff -urN linux-2.5.3-pre2/drivers/net/i82586.h linux/drivers/net/i82586.h
--- linux-2.5.3-pre2/drivers/net/i82586.h	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/i82586.h	Wed Dec 31 16:00:00 1969
@@ -1,413 +0,0 @@
-/*
- * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
- *
- * See:
- *	Intel Microcommunications 1991
- *	p1-1 to p1-37
- *	Intel order No. 231658
- *	ISBN 1-55512-119-5
- *
- *     Unfortunately, the above chapter mentions neither
- * the System Configuration Pointer (SCP) nor the
- * Intermediate System Configuration Pointer (ISCP),
- * so we probably need to look elsewhere for the
- * whole story -- some recommend the "Intel LAN
- * Components manual" but I have neither a copy
- * nor a full reference.  But "elsewhere" may be
- * in the same publication...
- *     The description of a later device, the
- * "82596CA High-Performance 32-Bit Local Area Network
- * Coprocessor", (ibid. p1-38 to p1-109) does mention
- * the SCP and ISCP and also has an i82586 compatibility
- * mode.  Even more useful is "AP-235 An 82586 Data Link
- * Driver" (ibid. p1-337 to p1-417).
- */
-
-#define	I82586_MEMZ	(64 * 1024)
-
-#define	I82586_SCP_ADDR	(I82586_MEMZ - sizeof(scp_t))
-
-#define	ADDR_LEN	6
-#define	I82586NULL	0xFFFF
-
-#define	toff(t,p,f) 	(unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
-
-/*
- * System Configuration Pointer (SCP).
- */
-typedef struct scp_t	scp_t;
-struct scp_t
-{
-	unsigned short	scp_sysbus;	/* 82586 bus width:	*/
-#define		SCP_SY_16BBUS	(0x0 << 0)	/* 16 bits */
-#define		SCP_SY_8BBUS	(0x1 << 0)	/*  8 bits. */
-	unsigned short	scp_junk[2];	/* Unused */
-	unsigned short	scp_iscpl;	/* lower 16 bits of ISCP_ADDR */
-	unsigned short	scp_iscph;	/* upper 16 bits of ISCP_ADDR */
-};
-
-/*
- * Intermediate System Configuration Pointer (ISCP).
- */
-typedef struct iscp_t	iscp_t;
-struct iscp_t
-{
-	unsigned short	iscp_busy;	/* set by CPU before first CA,	*/
-					/* cleared by 82586 after read.	*/
-	unsigned short	iscp_offset;	/* offset of SCB		*/
-	unsigned short	iscp_basel;	/* base of SCB			*/
-	unsigned short	iscp_baseh;	/*  "				*/
-};
-
-/*
- * System Control Block (SCB).
- *	The 82586 writes its status to scb_status and then
- *	raises an interrupt to alert the CPU.
- *	The CPU writes a command to scb_command and
- *	then issues a Channel Attention (CA) to alert the 82586.
- */
-typedef struct scb_t	scb_t;
-struct scb_t
-{
-	unsigned short	scb_status;	/* Status of 82586		*/
-#define		SCB_ST_INT	(0xF << 12)	/* Some of:		*/
-#define		SCB_ST_CX	(0x1 << 15)	/* Cmd completed	*/
-#define		SCB_ST_FR	(0x1 << 14)	/* Frame received	*/
-#define		SCB_ST_CNA	(0x1 << 13)	/* Cmd unit not active	*/
-#define		SCB_ST_RNR	(0x1 << 12)	/* Rcv unit not ready	*/
-#define		SCB_ST_JUNK0	(0x1 << 11)	/* 0			*/
-#define		SCB_ST_CUS	(0x7 <<  8)	/* Cmd unit status	*/
-#define			SCB_ST_CUS_IDLE	(0 << 8)	/* Idle		*/
-#define			SCB_ST_CUS_SUSP	(1 << 8)	/* Suspended	*/
-#define			SCB_ST_CUS_ACTV	(2 << 8)	/* Active	*/
-#define		SCB_ST_JUNK1	(0x1 <<  7)	/* 0			*/
-#define		SCB_ST_RUS	(0x7 <<  4)	/* Rcv unit status	*/
-#define			SCB_ST_RUS_IDLE	(0 << 4)	/* Idle		*/
-#define			SCB_ST_RUS_SUSP	(1 << 4)	/* Suspended	*/
-#define			SCB_ST_RUS_NRES	(2 << 4)	/* No resources	*/
-#define			SCB_ST_RUS_RDY	(4 << 4)	/* Ready	*/
-	unsigned short	scb_command;	/* Next command			*/
-#define		SCB_CMD_ACK_CX	(0x1 << 15)	/* Ack cmd completion	*/
-#define		SCB_CMD_ACK_FR	(0x1 << 14)	/* Ack frame received	*/
-#define		SCB_CMD_ACK_CNA	(0x1 << 13)	/* Ack CU not active	*/
-#define		SCB_CMD_ACK_RNR	(0x1 << 12)	/* Ack RU not ready	*/
-#define		SCB_CMD_JUNKX	(0x1 << 11)	/* Unused		*/
-#define		SCB_CMD_CUC	(0x7 <<  8)	/* Command Unit command	*/
-#define			SCB_CMD_CUC_NOP	(0 << 8)	/* Nop		*/
-#define			SCB_CMD_CUC_GO	(1 << 8)	/* Start cbl_offset */
-#define			SCB_CMD_CUC_RES	(2 << 8)	/* Resume execution */
-#define			SCB_CMD_CUC_SUS	(3 << 8)	/* Suspend   "	*/
-#define			SCB_CMD_CUC_ABT	(4 << 8)	/* Abort     "	*/
-#define		SCB_CMD_RESET	(0x1 <<  7)	/* Reset chip (hardware) */
-#define		SCB_CMD_RUC	(0x7 <<  4)	/* Receive Unit command	*/
-#define			SCB_CMD_RUC_NOP	(0 << 4)	/* Nop		*/
-#define			SCB_CMD_RUC_GO	(1 << 4)	/* Start rfa_offset */
-#define			SCB_CMD_RUC_RES	(2 << 4)	/* Resume reception */
-#define			SCB_CMD_RUC_SUS	(3 << 4)	/* Suspend   "	*/
-#define			SCB_CMD_RUC_ABT	(4 << 4)	/* Abort     "	*/
-	unsigned short	scb_cbl_offset;	/* Offset of first command unit	*/
-					/* Action Command		*/
-	unsigned short	scb_rfa_offset;	/* Offset of first Receive	*/
-					/* Frame Descriptor in the	*/
-					/* Receive Frame Area		*/
-	unsigned short	scb_crcerrs;	/* Properly aligned frames	*/
-					/* received with a CRC error	*/
-	unsigned short	scb_alnerrs;	/* Misaligned frames received	*/
-					/* with a CRC error		*/
-	unsigned short	scb_rscerrs;	/* Frames lost due to no space	*/
-	unsigned short	scb_ovrnerrs;	/* Frames lost due to slow bus	*/
-};
-
-#define	scboff(p,f) 	toff(scb_t, p, f)
-
-/*
- * The eight Action Commands.
- */
-typedef enum acmd_e	acmd_e;
-enum acmd_e
-{
-	acmd_nop	= 0,	/* Do nothing				*/
-	acmd_ia_setup	= 1,	/* Load an (ethernet) address into the	*/
-				/* 82586				*/
-	acmd_configure	= 2,	/* Update the 82586 operating parameters */
-	acmd_mc_setup	= 3,	/* Load a list of (ethernet) multicast	*/
-				/* addresses into the 82586		*/
-	acmd_transmit	= 4,	/* Transmit a frame			*/
-	acmd_tdr	= 5,	/* Perform a Time Domain Reflectometer	*/
-				/* test on the serial link		*/
-	acmd_dump	= 6,	/* Copy 82586 registers to memory	*/
-	acmd_diagnose	= 7,	/* Run an internal self test		*/
-};
-
-/*
- * Generic Action Command header.
- */
-typedef struct ach_t	ach_t;
-struct ach_t
-{
-	unsigned short	ac_status;		/* Command status:	*/
-#define		AC_SFLD_C	(0x1 << 15)	/* Command completed	*/
-#define		AC_SFLD_B	(0x1 << 14)	/* Busy executing	*/
-#define		AC_SFLD_OK	(0x1 << 13)	/* Completed error free	*/
-#define		AC_SFLD_A	(0x1 << 12)	/* Command aborted	*/
-#define		AC_SFLD_FAIL	(0x1 << 11)	/* Selftest failed	*/
-#define		AC_SFLD_S10	(0x1 << 10)	/* No carrier sense	*/
-						/* during transmission	*/
-#define		AC_SFLD_S9	(0x1 <<  9)	/* Tx unsuccessful:	*/
-						/* (stopped) lost CTS	*/
-#define		AC_SFLD_S8	(0x1 <<  8)	/* Tx unsuccessful:	*/
-						/* (stopped) slow DMA	*/
-#define		AC_SFLD_S7	(0x1 <<  7)	/* Tx deferred:		*/
-						/* other link traffic	*/
-#define		AC_SFLD_S6	(0x1 <<  6)	/* Heart Beat: collision */
-						/* detect after last tx	*/
-#define		AC_SFLD_S5	(0x1 <<  5)	/* Tx stopped:		*/
-						/* excessive collisions	*/
-#define		AC_SFLD_MAXCOL	(0xF <<  0)	/* Collision count  	*/
-	unsigned short	ac_command;		/* Command specifier:	*/
-#define		AC_CFLD_EL	(0x1 << 15)	/* End of command list	*/
-#define		AC_CFLD_S	(0x1 << 14)	/* Suspend on completion */
-#define		AC_CFLD_I	(0x1 << 13)	/* Interrupt on completion */
-#define		AC_CFLD_CMD	(0x7 <<  0)	/* acmd_e		*/
-	unsigned short	ac_link;		/* Next Action Command	*/
-};
-
-#define	acoff(p,f) 	toff(ach_t, p, f)
-
-/*
- * The Nop Action Command.
- */
-typedef struct ac_nop_t	ac_nop_t;
-struct ac_nop_t
-{
-	ach_t	nop_h;
-};
-
-/*
- * The IA-Setup Action Command.
- */
-typedef struct ac_ias_t	ac_ias_t;
-struct ac_ias_t
-{
-	ach_t		ias_h;
-	unsigned char	ias_addr[ADDR_LEN]; /* The (ethernet) address	*/
-};
-
-/*
- * The Configure Action Command.
- */
-typedef struct ac_cfg_t	ac_cfg_t;
-struct ac_cfg_t
-{
-	ach_t		cfg_h;
-	unsigned char	cfg_byte_cnt;	/* Size foll data: 4-12	*/
-#define	AC_CFG_BYTE_CNT(v)	(((v) & 0xF) << 0)
-	unsigned char	cfg_fifolim;	/* FIFO threshold	*/
-#define	AC_CFG_FIFOLIM(v)	(((v) & 0xF) << 0)
-	unsigned char	cfg_byte8;
-#define	AC_CFG_SAV_BF(v) 	(((v) & 0x1) << 7)	/* Save rxd bad frames	*/
-#define	AC_CFG_SRDY(v) 		(((v) & 0x1) << 6)	/* SRDY/ARDY pin means	*/
-							/* external sync.	*/
-	unsigned char	cfg_byte9;
-#define	AC_CFG_ELPBCK(v)	(((v) & 0x1) << 7)	/* External loopback	*/
-#define	AC_CFG_ILPBCK(v)	(((v) & 0x1) << 6)	/* Internal loopback	*/
-#define	AC_CFG_PRELEN(v)	(((v) & 0x3) << 4)	/* Preamble length	*/
-#define		AC_CFG_PLEN_2		0		/*  2 bytes	*/
-#define		AC_CFG_PLEN_4		1		/*  4 bytes	*/
-#define		AC_CFG_PLEN_8		2		/*  8 bytes	*/
-#define		AC_CFG_PLEN_16		3		/* 16 bytes	*/
-#define	AC_CFG_ALOC(v)		(((v) & 0x1) << 3)	/* Addr/len data is	*/
-							/* explicit in buffers	*/
-#define	AC_CFG_ADDRLEN(v)	(((v) & 0x7) << 0)	/* Bytes per address	*/
-	unsigned char	cfg_byte10;
-#define	AC_CFG_BOFMET(v)	(((v) & 0x1) << 7)	/* Use alternate expo.	*/
-							/* backoff method	*/
-#define	AC_CFG_ACR(v)		(((v) & 0x7) << 4)	/* Accelerated cont. res. */
-#define	AC_CFG_LINPRIO(v)	(((v) & 0x7) << 0)	/* Linear priority	*/
-	unsigned char	cfg_ifs;	/* Interframe spacing		*/
-	unsigned char	cfg_slotl;	/* Slot time (low byte)		*/
-	unsigned char	cfg_byte13;
-#define	AC_CFG_RETRYNUM(v)	(((v) & 0xF) << 4)	/* Max. collision retry	*/
-#define	AC_CFG_SLTTMHI(v)	(((v) & 0x7) << 0)	/* Slot time (high bits) */
-	unsigned char	cfg_byte14;
-#define	AC_CFG_FLGPAD(v)	(((v) & 0x1) << 7)	/* Pad with HDLC flags	*/
-#define	AC_CFG_BTSTF(v)		(((v) & 0x1) << 6)	/* Do HDLC bitstuffing	*/
-#define	AC_CFG_CRC16(v)		(((v) & 0x1) << 5)	/* 16 bit CCITT CRC	*/
-#define	AC_CFG_NCRC(v)		(((v) & 0x1) << 4)	/* Insert no CRC	*/
-#define	AC_CFG_TNCRS(v)		(((v) & 0x1) << 3)	/* Tx even if no carrier */
-#define	AC_CFG_MANCH(v)		(((v) & 0x1) << 2)	/* Manchester coding	*/
-#define	AC_CFG_BCDIS(v)		(((v) & 0x1) << 1)	/* Disable broadcast	*/
-#define	AC_CFG_PRM(v)		(((v) & 0x1) << 0)	/* Promiscuous mode	*/
-	unsigned char	cfg_byte15;
-#define	AC_CFG_ICDS(v)		(((v) & 0x1) << 7)	/* Internal collision	*/
-							/* detect source	*/
-#define	AC_CFG_CDTF(v)		(((v) & 0x7) << 4)	/* Collision detect	*/
-							/* filter in bit times	*/
-#define	AC_CFG_ICSS(v)		(((v) & 0x1) << 3)	/* Internal carrier	*/
-							/* sense source		*/
-#define	AC_CFG_CSTF(v)		(((v) & 0x7) << 0)	/* Carrier sense	*/
-							/* filter in bit times	*/
-	unsigned short	cfg_min_frm_len;
-#define	AC_CFG_MNFRM(v)		(((v) & 0xFF) << 0)	/* Min. bytes/frame (<= 255) */
-};
-
-/*
- * The MC-Setup Action Command.
- */
-typedef struct ac_mcs_t	ac_mcs_t;
-struct ac_mcs_t
-{
-	ach_t		mcs_h;
-	unsigned short	mcs_cnt;	/* No. of bytes of MC addresses	*/
-#if 0
-	unsigned char	mcs_data[ADDR_LEN]; /* The first MC address ..	*/
-	...
-#endif
-};
-
-#define I82586_MAX_MULTICAST_ADDRESSES	128	/* Hardware hashed filter */
-
-/*
- * The Transmit Action Command.
- */
-typedef struct ac_tx_t	ac_tx_t;
-struct ac_tx_t
-{
-	ach_t		tx_h;
-	unsigned short	tx_tbd_offset;	/* Address of list of buffers.	*/
-#if	0
-Linux packets are passed down with the destination MAC address
-and length/type field already prepended to the data,
-so we do not need to insert it.  Consistent with this
-we must also set the AC_CFG_ALOC(..) flag during the
-ac_cfg_t action command.
-	unsigned char	tx_addr[ADDR_LEN]; /* The frame dest. address	*/
-	unsigned short	tx_length;	/* The frame length		*/
-#endif	/* 0 */
-};
-
-/*
- * The Time Domain Reflectometer Action Command.
- */
-typedef struct ac_tdr_t	ac_tdr_t;
-struct ac_tdr_t
-{
-	ach_t		tdr_h;
-	unsigned short	tdr_result;	/* Result.	*/
-#define		AC_TDR_LNK_OK	(0x1 << 15)	/* No link problem	*/
-#define		AC_TDR_XCVR_PRB	(0x1 << 14)	/* Txcvr cable problem	*/
-#define		AC_TDR_ET_OPN	(0x1 << 13)	/* Open on the link	*/
-#define		AC_TDR_ET_SRT	(0x1 << 12)	/* Short on the link	*/
-#define		AC_TDR_TIME	(0x7FF << 0)	/* Distance to problem	*/
-						/* site	in transmit	*/
-						/* clock cycles		*/
-};
-
-/*
- * The Dump Action Command.
- */
-typedef struct ac_dmp_t	ac_dmp_t;
-struct ac_dmp_t
-{
-	ach_t		dmp_h;
-	unsigned short	dmp_offset;	/* Result.	*/
-};
-
-/*
- * Size of the result of the dump command.
- */
-#define	DUMPBYTES	170
-
-/*
- * The Diagnose Action Command.
- */
-typedef struct ac_dgn_t	ac_dgn_t;
-struct ac_dgn_t
-{
-	ach_t		dgn_h;
-};
-
-/*
- * Transmit Buffer Descriptor (TBD).
- */
-typedef struct tbd_t	tbd_t;
-struct tbd_t
-{
-	unsigned short	tbd_status;		/* Written by the CPU	*/
-#define		TBD_STATUS_EOF	(0x1 << 15)	/* This TBD is the	*/
-						/* last for this frame	*/
-#define		TBD_STATUS_ACNT	(0x3FFF << 0)	/* Actual count of data	*/
-						/* bytes in this buffer	*/
-	unsigned short	tbd_next_bd_offset;	/* Next in list		*/
-	unsigned short	tbd_bufl;		/* Buffer address (low)	*/
-	unsigned short	tbd_bufh;		/*    "	     "	(high)	*/
-};
-
-/*
- * Receive Buffer Descriptor (RBD).
- */
-typedef struct rbd_t	rbd_t;
-struct rbd_t
-{
-	unsigned short	rbd_status;		/* Written by the 82586	*/
-#define		RBD_STATUS_EOF	(0x1 << 15)	/* This RBD is the	*/
-						/* last for this frame	*/
-#define		RBD_STATUS_F	(0x1 << 14)	/* ACNT field is valid	*/
-#define		RBD_STATUS_ACNT	(0x3FFF << 0)	/* Actual no. of data	*/
-						/* bytes in this buffer	*/
-	unsigned short	rbd_next_rbd_offset;	/* Next rbd in list	*/
-	unsigned short	rbd_bufl;		/* Data pointer (low)	*/
-	unsigned short	rbd_bufh;		/*  "	   "    (high)	*/
-	unsigned short	rbd_el_size;		/* EL+Data buf. size	*/
-#define		RBD_EL	(0x1 << 15)		/* This BD is the	*/
-						/* last in the list	*/
-#define		RBD_SIZE (0x3FFF << 0)		/* No. of bytes the	*/
-						/* buffer can hold	*/
-};
-
-#define	rbdoff(p,f) 	toff(rbd_t, p, f)
-
-/*
- * Frame Descriptor (FD).
- */
-typedef struct fd_t	fd_t;
-struct fd_t
-{
-	unsigned short	fd_status;		/* Written by the 82586	*/
-#define		FD_STATUS_C	(0x1 << 15)	/* Completed storing frame */
-#define		FD_STATUS_B	(0x1 << 14)	/* FD was consumed by RU */
-#define		FD_STATUS_OK	(0x1 << 13)	/* Frame rxd successfully */
-#define		FD_STATUS_S11	(0x1 << 11)	/* CRC error		*/
-#define		FD_STATUS_S10	(0x1 << 10)	/* Alignment error	*/
-#define		FD_STATUS_S9	(0x1 <<  9)	/* Ran out of resources	*/
-#define		FD_STATUS_S8	(0x1 <<  8)	/* Rx DMA overrun	*/
-#define		FD_STATUS_S7	(0x1 <<  7)	/* Frame too short	*/
-#define		FD_STATUS_S6	(0x1 <<  6)	/* No EOF flag		*/
-	unsigned short	fd_command;		/* Command		*/
-#define		FD_COMMAND_EL	(0x1 << 15)	/* Last FD in list	*/
-#define		FD_COMMAND_S	(0x1 << 14)	/* Suspend RU after rx	*/
-	unsigned short	fd_link_offset;		/* Next FD		*/
-	unsigned short	fd_rbd_offset;		/* First RBD (data)	*/
-						/* Prepared by CPU,	*/
-						/* updated by 82586	*/
-#if	0
-I think the rest is unused since we
-have set AC_CFG_ALOC(..).  However, just
-in case, we leave the space.
-#endif	/* 0 */
-	unsigned char	fd_dest[ADDR_LEN];	/* Destination address	*/
-						/* Written by 82586	*/
-	unsigned char	fd_src[ADDR_LEN];	/* Source address	*/
-						/* Written by 82586	*/
-	unsigned short	fd_length;		/* Frame length or type	*/
-						/* Written by 82586	*/
-};
-
-#define	fdoff(p,f) 	toff(fd_t, p, f)
-
-/*
- * This software may only be used and distributed
- * according to the terms of the GNU General Public License.
- *
- * For more details, see wavelan.c.
- */
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in
--- linux-2.5.3-pre2/drivers/net/pcmcia/Config.in	Mon Nov 12 09:35:43 2001
+++ linux/drivers/net/pcmcia/Config.in	Mon Jan 21 17:23:23 2002
@@ -28,8 +28,6 @@
    bool '  Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
    if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then
       dep_tristate '    Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
-      dep_tristate '    Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
-      dep_tristate '    AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
       dep_tristate '    Aironet 4500/4800 PCMCIA support' CONFIG_AIRONET4500_CS $CONFIG_AIRONET4500 $CONFIG_PCMCIA
    fi
 fi
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile
--- linux-2.5.3-pre2/drivers/net/pcmcia/Makefile	Mon Nov 12 09:35:43 2001
+++ linux/drivers/net/pcmcia/Makefile	Mon Jan 21 17:23:23 2002
@@ -27,8 +27,6 @@
 
 # 16-bit wireless client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
-obj-$(CONFIG_PCMCIA_NETWAVE)	+= netwave_cs.o
-obj-$(CONFIG_PCMCIA_WAVELAN)	+= wavelan_cs.o
 obj-$(CONFIG_AIRONET4500_CS)	+= aironet4500_cs.o
 
 # Cardbus client drivers
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/i82593.h linux/drivers/net/pcmcia/i82593.h
--- linux-2.5.3-pre2/drivers/net/pcmcia/i82593.h	Fri Mar  2 11:02:15 2001
+++ linux/drivers/net/pcmcia/i82593.h	Wed Dec 31 16:00:00 1969
@@ -1,224 +0,0 @@
-/*
- * Definitions for Intel 82593 CSMA/CD Core LAN Controller
- * The definitions are taken from the 1992 users manual with Intel
- * order number 297125-001.
- *
- * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
- *
- * Copyright 1994, Anders Klemets <klemets@it.kth.se>
- *
- * This software may be freely distributed for noncommercial purposes
- * as long as this notice is retained.
- * 
- * HISTORY
- * i82593.h,v
- * Revision 1.1  1996/07/17 15:23:12  root
- * Initial revision
- *
- * Revision 1.3  1995/04/05  15:13:58  adj
- * Initial alpha release
- *
- * Revision 1.2  1994/06/16  23:57:31  klemets
- * Mirrored all the fields in the configuration block.
- *
- * Revision 1.1  1994/06/02  20:25:34  klemets
- * Initial revision
- *
- *
- */
-#ifndef	_I82593_H
-#define	_I82593_H
-
-/* Intel 82593 CSMA/CD Core LAN Controller */
-
-/* Port 0 Command Register definitions */
-
-/* Execution operations */
-#define OP0_NOP			0	/* CHNL = 0 */
-#define OP0_SWIT_TO_PORT_1	0	/* CHNL = 1 */
-#define OP0_IA_SETUP		1
-#define OP0_CONFIGURE		2
-#define OP0_MC_SETUP		3
-#define OP0_TRANSMIT		4
-#define OP0_TDR			5
-#define OP0_DUMP		6
-#define OP0_DIAGNOSE		7
-#define OP0_TRANSMIT_NO_CRC	9
-#define OP0_RETRANSMIT		12
-#define OP0_ABORT		13
-/* Reception operations */
-#define OP0_RCV_ENABLE		8
-#define OP0_RCV_DISABLE		10
-#define OP0_STOP_RCV		11
-/* Status pointer control operations */
-#define OP0_FIX_PTR		15	/* CHNL = 1 */
-#define OP0_RLS_PTR		15	/* CHNL = 0 */
-#define OP0_RESET		14
-
-#define CR0_CHNL		(1 << 4)	/* 0=Channel 0, 1=Channel 1 */
-#define CR0_STATUS_0		0x00
-#define CR0_STATUS_1		0x20
-#define CR0_STATUS_2		0x40
-#define CR0_STATUS_3		0x60
-#define CR0_INT_ACK		(1 << 7)	/* 0=No ack, 1=acknowledge */
-
-/* Port 0 Status Register definitions */
-
-#define SR0_NO_RESULT		0		/* dummy */
-#define SR0_EVENT_MASK		0x0f
-#define SR0_IA_SETUP_DONE	1
-#define SR0_CONFIGURE_DONE	2
-#define SR0_MC_SETUP_DONE	3
-#define SR0_TRANSMIT_DONE	4
-#define SR0_TDR_DONE		5
-#define SR0_DUMP_DONE		6
-#define SR0_DIAGNOSE_PASSED	7
-#define SR0_TRANSMIT_NO_CRC_DONE 9
-#define SR0_RETRANSMIT_DONE	12
-#define SR0_EXECUTION_ABORTED	13
-#define SR0_END_OF_FRAME	8
-#define SR0_RECEPTION_ABORTED	10
-#define SR0_DIAGNOSE_FAILED	15
-#define SR0_STOP_REG_HIT	11
-
-#define SR0_CHNL		(1 << 4)
-#define SR0_EXECUTION		(1 << 5)
-#define SR0_RECEPTION		(1 << 6)
-#define SR0_INTERRUPT		(1 << 7)
-#define SR0_BOTH_RX_TX		(SR0_EXECUTION | SR0_RECEPTION)
-
-#define SR3_EXEC_STATE_MASK	0x03
-#define SR3_EXEC_IDLE		0
-#define SR3_TX_ABORT_IN_PROGRESS 1
-#define SR3_EXEC_ACTIVE		2
-#define SR3_ABORT_IN_PROGRESS	3
-#define SR3_EXEC_CHNL		(1 << 2)
-#define SR3_STP_ON_NO_RSRC	(1 << 3)
-#define SR3_RCVING_NO_RSRC	(1 << 4)
-#define SR3_RCV_STATE_MASK	0x60
-#define SR3_RCV_IDLE		0x00
-#define SR3_RCV_READY		0x20
-#define SR3_RCV_ACTIVE		0x40
-#define SR3_RCV_STOP_IN_PROG	0x60
-#define SR3_RCV_CHNL		(1 << 7)
-
-/* Port 1 Command Register definitions */
-
-#define OP1_NOP			0
-#define OP1_SWIT_TO_PORT_0	1
-#define OP1_INT_DISABLE		2
-#define OP1_INT_ENABLE		3
-#define OP1_SET_TS		5
-#define OP1_RST_TS		7
-#define OP1_POWER_DOWN		8
-#define OP1_RESET_RING_MNGMT	11
-#define OP1_RESET		14
-#define OP1_SEL_RST		15
-
-#define CR1_STATUS_4		0x00
-#define CR1_STATUS_5		0x20
-#define CR1_STATUS_6		0x40
-#define CR1_STOP_REG_UPDATE	(1 << 7)
-
-/* Receive frame status bits */
-
-#define	RX_RCLD			(1 << 0)
-#define RX_IA_MATCH		(1 << 1)
-#define	RX_NO_AD_MATCH		(1 << 2)
-#define RX_NO_SFD		(1 << 3)
-#define RX_SRT_FRM		(1 << 7)
-#define RX_OVRRUN		(1 << 8)
-#define RX_ALG_ERR		(1 << 10)
-#define RX_CRC_ERR		(1 << 11)
-#define RX_LEN_ERR		(1 << 12)
-#define RX_RCV_OK		(1 << 13)
-#define RX_TYP_LEN		(1 << 15)
-
-/* Transmit status bits */
-
-#define TX_NCOL_MASK		0x0f
-#define TX_FRTL			(1 << 4)
-#define TX_MAX_COL		(1 << 5)
-#define TX_HRT_BEAT		(1 << 6)
-#define TX_DEFER		(1 << 7)
-#define TX_UND_RUN		(1 << 8)
-#define TX_LOST_CTS		(1 << 9)
-#define TX_LOST_CRS		(1 << 10)
-#define TX_LTCOL		(1 << 11)
-#define TX_OK			(1 << 13)
-#define TX_COLL			(1 << 15)
-
-struct i82593_conf_block {
-  u_char fifo_limit : 4,
-  	 forgnesi   : 1,
-  	 fifo_32    : 1,
-  	 d6mod      : 1,
-  	 throttle_enb : 1;
-  u_char throttle   : 6,
-	 cntrxint   : 1,
-	 contin	    : 1;
-  u_char addr_len   : 3,
-  	 acloc 	    : 1,
- 	 preamb_len : 2,
-  	 loopback   : 2;
-  u_char lin_prio   : 3,
-	 tbofstop   : 1,
-	 exp_prio   : 3,
-	 bof_met    : 1;
-  u_char	    : 4,
-	 ifrm_spc   : 4;
-  u_char	    : 5,
-	 slottim_low : 3;
-  u_char slottim_hi : 3,
-		    : 1,
-	 max_retr   : 4;
-  u_char prmisc     : 1,
-	 bc_dis     : 1,
-  		    : 1,
-	 crs_1	    : 1,
-	 nocrc_ins  : 1,
-	 crc_1632   : 1,
-  	 	    : 1,
-  	 crs_cdt    : 1;
-  u_char cs_filter  : 3,
-	 crs_src    : 1,
-	 cd_filter  : 3,
-		    : 1;
-  u_char	    : 2,
-  	 min_fr_len : 6;
-  u_char lng_typ    : 1,
-	 lng_fld    : 1,
-	 rxcrc_xf   : 1,
-	 artx	    : 1,
-	 sarec	    : 1,
-	 tx_jabber  : 1,	/* why is this called max_len in the manual? */
-	 hash_1	    : 1,
-  	 lbpkpol    : 1;
-  u_char	    : 6,
-  	 fdx	    : 1,
-  	  	    : 1;
-  u_char dummy_6    : 6,	/* supposed to be ones */
-  	 mult_ia    : 1,
-  	 dis_bof    : 1;
-  u_char dummy_1    : 1,	/* supposed to be one */
-	 tx_ifs_retrig : 2,
-	 mc_all     : 1,
-	 rcv_mon    : 2,
-	 frag_acpt  : 1,
-  	 tstrttrs   : 1;
-  u_char fretx	    : 1,
-	 runt_eop   : 1,
-	 hw_sw_pin  : 1,
-	 big_endn   : 1,
-	 syncrqs    : 1,
-	 sttlen     : 1,
-	 tx_eop     : 1,
-  	 rx_eop	    : 1;
-  u_char rbuf_size  : 5,
-	 rcvstop    : 1,
-  	 	    : 2;
-};
-
-#define I82593_MAX_MULTICAST_ADDRESSES	128	/* Hardware hashed filter */
-
-#endif /* _I82593_H */
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/netwave_cs.c linux/drivers/net/pcmcia/netwave_cs.c
--- linux-2.5.3-pre2/drivers/net/pcmcia/netwave_cs.c	Fri Oct 12 14:21:18 2001
+++ linux/drivers/net/pcmcia/netwave_cs.c	Wed Dec 31 16:00:00 1969
@@ -1,1600 +0,0 @@
-/*********************************************************************
- *                
- * Filename:      netwave_cs.c
- * Version:       0.4.1
- * Description:   Netwave AirSurfer Wireless LAN PC Card driver
- * Status:        Experimental.
- * Authors:       John Markus Bjørndalen <johnm@cs.uit.no>
- *                Dag Brattli <dagb@cs.uit.no>
- *                David Hinds <dahinds@users.sourceforge.net>
- * Created at:    A long time ago!
- * Modified at:   Mon Nov 10 11:54:37 1997
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * 
- *     Copyright (c) 1997 University of Tromsø, Norway
- *
- * Revision History:
- *
- *   08-Nov-97 15:14:47   John Markus Bjørndalen <johnm@cs.uit.no>
- *    - Fixed some bugs in netwave_rx and cleaned it up a bit. 
- *      (One of the bugs would have destroyed packets when receiving
- *      multiple packets per interrupt). 
- *    - Cleaned up parts of newave_hw_xmit. 
- *    - A few general cleanups. 
- *   24-Oct-97 13:17:36   Dag Brattli <dagb@cs.uit.no>
- *    - Fixed netwave_rx receive function (got updated docs)
- *   Others:
- *    - Changed name from xircnw to netwave, take a look at 
- *      http://www.netwave-wireless.com
- *    - Some reorganizing of the code
- *    - Removed possible race condition between interrupt handler and transmit
- *      function
- *    - Started to add wireless extensions, but still needs some coding
- *    - Added watchdog for better handling of transmission timeouts 
- *      (hopefully this works better)
- ********************************************************************/
-
-/* To have statistics (just packets sent) define this */
-#undef NETWAVE_STATS
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#ifdef CONFIG_NET_PCMCIA_RADIO
-#include <linux/wireless.h>
-#endif
-
-#include <pcmcia/version.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/mem_op.h>
-
-#define NETWAVE_REGOFF         0x8000
-/* The Netwave IO registers, offsets to iobase */
-#define NETWAVE_REG_COR        0x0
-#define NETWAVE_REG_CCSR       0x2
-#define NETWAVE_REG_ASR        0x4
-#define NETWAVE_REG_IMR        0xa
-#define NETWAVE_REG_PMR        0xc
-#define NETWAVE_REG_IOLOW      0x6
-#define NETWAVE_REG_IOHI       0x7
-#define NETWAVE_REG_IOCONTROL  0x8
-#define NETWAVE_REG_DATA       0xf
-/* The Netwave Extended IO registers, offsets to RamBase */
-#define NETWAVE_EREG_ASCC      0x114
-#define NETWAVE_EREG_RSER      0x120
-#define NETWAVE_EREG_RSERW     0x124
-#define NETWAVE_EREG_TSER      0x130
-#define NETWAVE_EREG_TSERW     0x134
-#define NETWAVE_EREG_CB        0x100
-#define NETWAVE_EREG_SPCQ      0x154
-#define NETWAVE_EREG_SPU       0x155
-#define NETWAVE_EREG_LIF       0x14e
-#define NETWAVE_EREG_ISPLQ     0x156
-#define NETWAVE_EREG_HHC       0x158
-#define NETWAVE_EREG_NI        0x16e
-#define NETWAVE_EREG_MHS       0x16b
-#define NETWAVE_EREG_TDP       0x140
-#define NETWAVE_EREG_RDP       0x150
-#define NETWAVE_EREG_PA        0x160
-#define NETWAVE_EREG_EC        0x180
-#define NETWAVE_EREG_CRBP      0x17a
-#define NETWAVE_EREG_ARW       0x166
-
-/*
- * Commands used in the extended command buffer
- * NETWAVE_EREG_CB (0x100-0x10F) 
- */
-#define NETWAVE_CMD_NOP        0x00
-#define NETWAVE_CMD_SRC        0x01
-#define NETWAVE_CMD_STC        0x02
-#define NETWAVE_CMD_AMA        0x03
-#define NETWAVE_CMD_DMA        0x04
-#define NETWAVE_CMD_SAMA       0x05
-#define NETWAVE_CMD_ER         0x06
-#define NETWAVE_CMD_DR         0x07
-#define NETWAVE_CMD_TL         0x08
-#define NETWAVE_CMD_SRP        0x09
-#define NETWAVE_CMD_SSK        0x0a
-#define NETWAVE_CMD_SMD        0x0b
-#define NETWAVE_CMD_SAPD       0x0c
-#define NETWAVE_CMD_SSS        0x11
-/* End of Command marker */
-#define NETWAVE_CMD_EOC        0x00
-
-/* ASR register bits */
-#define NETWAVE_ASR_RXRDY   0x80
-#define NETWAVE_ASR_TXBA    0x01
-
-#define TX_TIMEOUT		((32*HZ)/100)
-
-static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */
-static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */
-
-static const unsigned int corConfIENA   = 0x01; /* Interrupt enable */
-static const unsigned int corConfLVLREQ = 0x40; /* Keep high */
-
-static const unsigned int rxConfRxEna  = 0x80; /* Receive Enable */
-static const unsigned int rxConfMAC    = 0x20; /* MAC host receive mode*/ 
-static const unsigned int rxConfPro    = 0x10; /* Promiscuous */
-static const unsigned int rxConfAMP    = 0x08; /* Accept Multicast Packets */
-static const unsigned int rxConfBcast  = 0x04; /* Accept Broadcast Packets */
-
-static const unsigned int txConfTxEna  = 0x80; /* Transmit Enable */
-static const unsigned int txConfMAC    = 0x20; /* Host sends MAC mode */
-static const unsigned int txConfEUD    = 0x10; /* Enable Uni-Data packets */
-static const unsigned int txConfKey    = 0x02; /* Scramble data packets */
-static const unsigned int txConfLoop   = 0x01; /* Loopback mode */
-
-/*
-   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
-   you do not define PCMCIA_DEBUG at all, all the debug code will be
-   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
-   be present but disabled -- but it can then be enabled for specific
-   modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#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 =
-"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
-#else
-#define DEBUG(n, args...)
-#endif
-
-static dev_info_t dev_info = "netwave_cs";
-
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
-/* Choose the domain, default is 0x100 */
-static u_int  domain = 0x100;
-
-/* Scramble key, range from 0x0 to 0xffff.  
- * 0x0 is no scrambling. 
- */
-static u_int  scramble_key = 0x0;
-
-/* Shared memory speed, in ns. The documentation states that 
- * the card should not be read faster than every 400ns. 
- * This timing should be provided by the HBA. If it becomes a 
- * problem, try setting mem_speed to 400. 
- */
-static int mem_speed;
-
-/* Bit map of interrupts to choose from */
-/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
-static u_int irq_mask = 0xdeb8;
-static int irq_list[4] = { -1 };
-
-MODULE_PARM(domain, "i");
-MODULE_PARM(scramble_key, "i");
-MODULE_PARM(mem_speed, "i");
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-
-/*====================================================================*/
-
-/* PCMCIA (Card Services) related functions */
-static void netwave_release(u_long arg);     /* Card removal */
-static int  netwave_event(event_t event, int priority, 
-					      event_callback_args_t *args);
-static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card 
-													   insertion */
-static dev_link_t *netwave_attach(void);     /* Create instance */
-static void netwave_detach(dev_link_t *);    /* Destroy instance */
-static void netwave_flush_stale_links(void);	     /* Destroy all staled instances */
-
-/* Hardware configuration */
-static void netwave_doreset(ioaddr_t iobase, u_char* ramBase);
-static void netwave_reset(struct net_device *dev);
-
-/* Misc device stuff */
-static int netwave_open(struct net_device *dev);  /* Open the device */
-static int netwave_close(struct net_device *dev); /* Close the device */
-static int netwave_config(struct net_device *dev, struct ifmap *map);
-
-/* Packet transmission and Packet reception */
-static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev);
-static int netwave_rx( struct net_device *dev);
-
-/* Interrupt routines */
-static void netwave_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void netwave_watchdog(struct net_device *);
-
-/* Statistics */
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *netwave_get_stats(struct net_device *dev);
-
-/* Wireless extensions */
-#ifdef WIRELESS_EXT
-static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev);
-#endif
-static int netwave_ioctl(struct net_device *, struct ifreq *, int);
-
-static void set_multicast_list(struct net_device *dev);
-
-/*
-   A linked list of "instances" of the skeleton device.  Each actual
-   PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
-
-   You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
-   device numbers are used to derive the corresponding array index.
-*/
-static dev_link_t *dev_list;
-
-/*
-   A dev_link_t structure has fields for most things that are needed
-   to keep track of a socket, but there will usually be some device
-   specific information that also needs to be kept track of.  The
-   'priv' pointer in a dev_link_t structure can be used to point to
-   a device-specific private data structure, like this.
-
-   A driver needs to provide a dev_node_t structure for each device
-   on a card.  In some cases, there is only one device per card (for
-   example, ethernet cards, modems).  In other cases, there may be
-   many actual or logical devices (SCSI adapters, memory cards with
-   multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
-   structure.  We allocate them in the card's private data structure,
-   because they generally can't be allocated dynamically.
-*/
-
-/* Wireless Extension Backward compatibility - Jean II
- * If the new wireless device private ioctl range is not defined,
- * default to standard device private ioctl range */
-#ifndef SIOCIWFIRSTPRIV
-#define SIOCIWFIRSTPRIV	SIOCDEVPRIVATE
-#endif /* SIOCIWFIRSTPRIV */
-
-#define SIOCGIPSNAP	SIOCIWFIRSTPRIV		/* Site Survey Snapshot */
-/*#define SIOCGIPQTHR	SIOCIWFIRSTPRIV + 1*/
-
-#define MAX_ESA 10
-
-typedef struct net_addr {
-    u_char addr48[6];
-} net_addr;
-
-struct site_survey {
-    u_short length;
-    u_char  struct_revision;
-    u_char  roaming_state;
-	
-    u_char  sp_existsFlag;
-    u_char  sp_link_quality;
-    u_char  sp_max_link_quality;
-    u_char  linkQualityGoodFairBoundary;
-    u_char  linkQualityFairPoorBoundary;
-    u_char  sp_utilization;
-    u_char  sp_goodness;
-    u_char  sp_hotheadcount;
-    u_char  roaming_condition;
-	
-    net_addr sp;
-    u_char   numAPs;
-    net_addr nearByAccessPoints[MAX_ESA];
-};	
-   
-typedef struct netwave_private {
-    dev_link_t link;
-    struct net_device      dev;
-    dev_node_t node;
-    u_char     *ramBase;
-    int        timeoutCounter;
-    int        lastExec;
-    struct timer_list      watchdog;	/* To avoid blocking state */
-    struct site_survey     nss;
-    struct net_device_stats stats;
-#ifdef WIRELESS_EXT
-    struct iw_statistics   iw_stats;    /* Wireless stats */
-#endif
-} netwave_private;
-
-#ifdef NETWAVE_STATS
-static struct net_device_stats *netwave_get_stats(struct net_device *dev);
-#endif
-
-/*
- * The Netwave card is little-endian, so won't work for big endian
- * systems.
- */
-static inline unsigned short get_uint16(u_char* staddr) 
-{
-    return readw(staddr); /* Return only 16 bits */
-}
-
-static inline short get_int16(u_char* staddr)
-{
-    return readw(staddr);
-}
-
-/**************************************************************************/
-
-static void cs_error(client_handle_t handle, int func, int ret)
-{
-    error_info_t err = { func, ret };
-    CardServices(ReportError, handle, &err);
-}
-
-/* 
- * Wait until the WOC (Write Operation Complete) bit in the 
- * ASR (Adapter Status Register) is asserted. 
- * This should have aborted if it takes too long time. 
- */
-static inline void wait_WOC(unsigned int iobase)
-{
-    /* Spin lock */
-    while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ; 
-}
-
-#ifdef WIRELESS_EXT
-static void netwave_snapshot(netwave_private *priv, u_char *ramBase, 
-			     ioaddr_t iobase) { 
-    u_short resultBuffer;
-
-    /* if time since last snapshot is > 1 sec. (100 jiffies?)  then take 
-     * new snapshot, else return cached data. This is the recommended rate.  
-     */
-    if ( jiffies - priv->lastExec > 100) { 
-	/* Take site survey  snapshot */ 
-	/*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies -
-	  priv->lastExec); */
-	wait_WOC(iobase); 
-	writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0); 
-	writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); 
-	wait_WOC(iobase); 
-
-	/* Get result and copy to cach */ 
-	resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP); 
-	copy_from_pc( &priv->nss, ramBase+resultBuffer, 
-		      sizeof(struct site_survey)); 
-    } 
-}
-#endif
-
-#ifdef WIRELESS_EXT
-/*
- * Function netwave_get_wireless_stats (dev)
- *
- *    Wireless extensions statistics
- *
- */
-static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev)
-{	
-    unsigned long flags;
-    ioaddr_t iobase = dev->base_addr;
-    netwave_private *priv = (netwave_private *) dev->priv;
-    u_char *ramBase = priv->ramBase;
-    struct iw_statistics* wstats;
-	
-    wstats = &priv->iw_stats;
-
-    save_flags(flags);
-    cli();
-	
-    netwave_snapshot( priv, ramBase, iobase);
-
-    wstats->status = priv->nss.roaming_state;
-    wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ); 
-    wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ);
-    wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f;
-    wstats->discard.nwid = 0L;
-    wstats->discard.code = 0L;
-    wstats->discard.misc = 0L;
-
-    restore_flags(flags);
-    
-    return &priv->iw_stats;
-}
-#endif
-
-/*
- * Function netwave_attach (void)
- *
- *     Creates an "instance" of the driver, allocating local data 
- *     structures for one device.  The device is registered with Card 
- *     Services.
- *
- *     The dev_link structure is initialized, but we don't actually
- *     configure the card at this point -- we wait until we receive a
- *     card insertion event.
- */
-static dev_link_t *netwave_attach(void)
-{
-    client_reg_t client_reg;
-    dev_link_t *link;
-    struct net_device *dev;
-    netwave_private *priv;
-    int i, ret;
-    
-    DEBUG(0, "netwave_attach()\n");
-    
-    /* Perform some cleanup */
-    netwave_flush_stale_links();
-
-    /* Initialize the dev_link_t structure */
-    priv = kmalloc(sizeof(*priv), GFP_KERNEL);
-    if (!priv) return NULL;
-    memset(priv, 0, sizeof(*priv));
-    link = &priv->link; dev = &priv->dev;
-    link->priv = dev->priv = priv;
-    link->release.function = &netwave_release;
-    link->release.data = (u_long)link;
-	
-    /* The io structure describes IO port mapping */
-    link->io.NumPorts1 = 16;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
-    /* link->io.NumPorts2 = 16; 
-       link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */
-    link->io.IOAddrLines = 5;
-    
-    /* Interrupt setup */
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
-    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->irq.Handler = &netwave_interrupt;
-    
-    /* General socket configuration */
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    link->conf.ConfigIndex = 1;
-    link->conf.Present = PRESENT_OPTION;
-
-    /* Netwave specific entries in the device structure */
-    dev->hard_start_xmit = &netwave_start_xmit;
-    dev->set_config = &netwave_config;
-    dev->get_stats  = &netwave_get_stats;
-    dev->set_multicast_list = &set_multicast_list;
-    /* wireless extensions */
-#ifdef WIRELESS_EXT
-    dev->get_wireless_stats = &netwave_get_wireless_stats;
-#endif
-    dev->do_ioctl = &netwave_ioctl;
-
-    dev->tx_timeout = &netwave_watchdog;
-    dev->watchdog_timeo = TX_TIMEOUT;
-
-    ether_setup(dev);
-    dev->open = &netwave_open;
-    dev->stop = &netwave_close;
-    link->irq.Instance = dev;
-    
-    /* Register with Card Services */
-    link->next = dev_list;
-    dev_list = link;
-    client_reg.dev_info = &dev_info;
-    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
-    client_reg.EventMask =
-	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
-	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
-	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-    client_reg.event_handler = &netwave_event;
-    client_reg.Version = 0x0210;
-    client_reg.event_callback_args.client_data = link;
-    ret = CardServices(RegisterClient, &link->handle, &client_reg);
-    if (ret != 0) {
-	cs_error(link->handle, RegisterClient, ret);
-	netwave_detach(link);
-	return NULL;
-    }
-
-    return link;
-} /* netwave_attach */
-
-/*
- * Function netwave_detach (link)
- *
- *    This deletes a driver "instance".  The device is de-registered
- *    with Card Services.  If it has been released, all local data
- *    structures are freed.  Otherwise, the structures will be freed
- *    when the device is released.
- */
-static void netwave_detach(dev_link_t *link)
-{
-    netwave_private *priv = link->priv;
-    dev_link_t **linkp;
-
-    DEBUG(0, "netwave_detach(0x%p)\n", link);
-  
-    /*
-	  If the device is currently configured and active, we won't
-	  actually delete it yet.  Instead, it is marked so that when
-	  the release() function is called, that will trigger a proper
-	  detach().
-	*/
-    del_timer(&link->release);
-    if (link->state & DEV_CONFIG) {
-	netwave_release((u_long) link);
-	if (link->state & DEV_STALE_CONFIG) {
-	    DEBUG(1, "netwave_cs: detach postponed, '%s' still "
-		  "locked\n", link->dev->dev_name);
-	    link->state |= DEV_STALE_LINK;
-	    return;
-	}
-    }
-	
-    /* Break the link with Card Services */
-    if (link->handle)
-	CardServices(DeregisterClient, link->handle);
-    
-    /* Locate device structure */
-    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
-	if (*linkp == link) break;
-    if (*linkp == NULL)
-      {
-	DEBUG(1, "netwave_cs: detach fail, '%s' not in list\n",
-	      link->dev->dev_name);
-	return;
-      }
-
-    /* Unlink device structure, free pieces */
-    *linkp = link->next;
-    if (link->dev)
-	unregister_netdev(&priv->dev);
-    kfree(priv);
-    
-} /* netwave_detach */
-
-/*
- * Function netwave_flush_stale_links (void)
- *
- *    This deletes all driver "instances" that need to be deleted.
- *    Sometimes, netwave_detach can't be performed following a call from
- *    cardmgr (device still open) and the device is put in a STALE_LINK
- *    state.
- *    This function is in charge of making the cleanup...
- */
-static void netwave_flush_stale_links(void)
-{
-    dev_link_t *	link;		/* Current node in linked list */
-    dev_link_t *	next;		/* Next node in linked list */
-
-    DEBUG(1, "netwave_flush_stale_links(0x%p)\n", dev_list);
-
-    /* Go through the list */
-    for (link = dev_list; link; link = next) {
-        next = link->next;
-        /* Check if in need of being removed */
-        if(link->state & DEV_STALE_LINK)
-	    netwave_detach(link);
-    }
-} /* netwave_flush_stale_links */
-
-/*
- * Function netwave_ioctl (dev, rq, cmd)
- *
- *     Perform ioctl : config & info stuff
- *     This is the stuff that are treated the wireless extensions (iwconfig)
- *
- */
-static int netwave_ioctl(struct net_device *dev, /* ioctl device */
-			 struct ifreq *rq,	 /* Data passed */
-			 int	cmd)	     /* Ioctl number */
-{
-    unsigned long flags;
-    int			ret = 0;
-#ifdef WIRELESS_EXT
-    ioaddr_t iobase = dev->base_addr;
-    netwave_private *priv = (netwave_private *) dev->priv;
-    u_char *ramBase = priv->ramBase;
-    struct iwreq *wrq = (struct iwreq *) rq;
-#endif
-	
-    DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
-	
-    /* Disable interrupts & save flags */
-    save_flags(flags);
-    cli();
-
-    /* Look what is the request */
-    switch(cmd) {
-	/* --------------- WIRELESS EXTENSIONS --------------- */
-#ifdef WIRELESS_EXT
-    case SIOCGIWNAME:
-	/* Get name */
-	strcpy(wrq->u.name, "Netwave");
-	break;
-    case SIOCSIWNWID:
-	/* Set domain */
-#if WIRELESS_EXT > 8
-	if(!wrq->u.nwid.disabled) {
-	    domain = wrq->u.nwid.value;
-#else	/* WIRELESS_EXT > 8 */
-	if(wrq->u.nwid.on) {
-	    domain = wrq->u.nwid.nwid;
-#endif	/* WIRELESS_EXT > 8 */
-	    printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", 
-		    (domain >> 8) & 0x01, domain & 0xff);
-	    wait_WOC(iobase);
-	    writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
-	    writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
-	    writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2);
-	    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-	} break;
-    case SIOCGIWNWID:
-	/* Read domain*/
-#if WIRELESS_EXT > 8
-	wrq->u.nwid.value = domain;
-	wrq->u.nwid.disabled = 0;
-	wrq->u.nwid.fixed = 1;
-#else	/* WIRELESS_EXT > 8 */
-	wrq->u.nwid.nwid = domain;
-	wrq->u.nwid.on = 1;
-#endif	/* WIRELESS_EXT > 8 */
-	break;
-#if WIRELESS_EXT > 8	/* Note : The API did change... */
-    case SIOCGIWENCODE:
-	/* Get scramble key */
-	if(wrq->u.encoding.pointer != (caddr_t) 0)
-	  {
-	    char	key[2];
-	    key[1] = scramble_key & 0xff;
-	    key[0] = (scramble_key>>8) & 0xff;
-	    wrq->u.encoding.flags = IW_ENCODE_ENABLED;
-	    wrq->u.encoding.length = 2;
-	    if(copy_to_user(wrq->u.encoding.pointer, key, 2))
-	      ret = -EFAULT;
-	  }
-	break;
-    case SIOCSIWENCODE:
-	/* Set  scramble key */
-	if(wrq->u.encoding.pointer != (caddr_t) 0)
-	  {
-	    char	key[2];
-	    if(copy_from_user(key, wrq->u.encoding.pointer, 2))
-	      {
-		ret = -EFAULT;
-		break;
-	      }
-	    scramble_key = (key[0] << 8) | key[1];
-	    wait_WOC(iobase);
-	    writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
-	    writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
-	    writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
-	    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-	  }
-	break;
-    case SIOCGIWMODE:
-      /* Mode of operation */
-	if(domain & 0x100)
-	  wrq->u.mode = IW_MODE_INFRA;
-	else
-	  wrq->u.mode = IW_MODE_ADHOC;
-      break;
-#else /* WIRELESS_EXT > 8 */
-    case SIOCGIWENCODE:
-	/* Get scramble key */
-	wrq->u.encoding.code = scramble_key;
-	wrq->u.encoding.method = 1;
-	break;
-    case SIOCSIWENCODE:
-	/* Set  scramble key */
-	scramble_key = wrq->u.encoding.code;
-	wait_WOC(iobase);
-	writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
-	writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
-	writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
-	writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-	break;
-#endif /* WIRELESS_EXT > 8 */
-   case SIOCGIWRANGE:
-       /* Basic checking... */
-       if(wrq->u.data.pointer != (caddr_t) 0) {
-	   struct iw_range	range;
-		   
-	   /* Set the length (very important for backward compatibility) */
-	   wrq->u.data.length = sizeof(struct iw_range);
-
-	   /* Set all the info we don't care or don't know about to zero */
-	   memset(&range, 0, sizeof(range));
-
-#if WIRELESS_EXT > 10
-	   /* Set the Wireless Extension versions */
-	   range.we_version_compiled = WIRELESS_EXT;
-	   range.we_version_source = 9;	/* Nothing for us in v10 and v11 */
-#endif /* WIRELESS_EXT > 10 */
-		   
-	   /* Set information in the range struct */
-	   range.throughput = 450 * 1000;	/* don't argue on this ! */
-	   range.min_nwid = 0x0000;
-	   range.max_nwid = 0x01FF;
-
-	   range.num_channels = range.num_frequency = 0;
-		   
-	   range.sensitivity = 0x3F;
-	   range.max_qual.qual = 255;
-	   range.max_qual.level = 255;
-	   range.max_qual.noise = 0;
-		   
-#if WIRELESS_EXT > 7
-	   range.num_bitrates = 1;
-	   range.bitrate[0] = 1000000;	/* 1 Mb/s */
-#endif /* WIRELESS_EXT > 7 */
-
-#if WIRELESS_EXT > 8
-	   range.encoding_size[0] = 2;		/* 16 bits scrambling */
-	   range.num_encoding_sizes = 1;
-	   range.max_encoding_tokens = 1;	/* Only one key possible */
-#endif /* WIRELESS_EXT > 8 */
-
-	   /* Copy structure to the user buffer */
-	   if(copy_to_user(wrq->u.data.pointer, &range,
-			sizeof(struct iw_range)))
-	     ret = -EFAULT;
-       }
-       break;
-    case SIOCGIWPRIV:
-	/* Basic checking... */
-	if(wrq->u.data.pointer != (caddr_t) 0) {
-	    struct iw_priv_args	priv[] =
-	    {	/* cmd,		set_args,	get_args,	name */
-		{ SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, 
-		  sizeof(struct site_survey), 
-		  "getsitesurvey" },
-	    };
-			
-	    /* Set the number of ioctl available */
-	    wrq->u.data.length = 1;
-			
-	    /* Copy structure to the user buffer */
-	    if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
-			 sizeof(priv)))
-	      ret = -EFAULT;
-	} 
-	break;
-    case SIOCGIPSNAP:
-	if(wrq->u.data.pointer != (caddr_t) 0) {
-	    /* Take snapshot of environment */
-	    netwave_snapshot( priv, ramBase, iobase);
-	    wrq->u.data.length = priv->nss.length;
-	    /* Copy structure to the user buffer */
-	    if(copy_to_user(wrq->u.data.pointer, 
-			 (u_char *) &priv->nss,
-			 sizeof( struct site_survey)))
-	      {
-		printk(KERN_DEBUG "Bad buffer!\n");
-		break;
-	      }
-
-	    priv->lastExec = jiffies;
-	}
-	break;
-#endif
-    default:
-	ret = -EOPNOTSUPP;
-    }
-	
-    /* ReEnable interrupts & restore flags */
-    restore_flags(flags);
-    
-    return ret;
-}
-
-/*
- * Function netwave_pcmcia_config (link)
- *
- *     netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION 
- *     event is received, to configure the PCMCIA socket, and to make the
- *     device available to the system. 
- *
- */
-
-#define CS_CHECK(fn, args...) \
-while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
-
-static void netwave_pcmcia_config(dev_link_t *link) {
-    client_handle_t handle = link->handle;
-    netwave_private *priv = link->priv;
-    struct net_device *dev = &priv->dev;
-    tuple_t tuple;
-    cisparse_t parse;
-    int i, j, last_ret, last_fn;
-    u_char buf[64];
-    win_req_t req;
-    memreq_t mem;
-    u_char *ramBase = NULL;
-
-    DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
-
-    /*
-      This reads the card's CONFIG tuple to find its configuration
-      registers.
-    */
-    tuple.Attributes = 0;
-    tuple.TupleData = (cisdata_t *) buf;
-    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;
-    link->conf.Present = parse.config.rmask[0];
-
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /*
-     *  Try allocating IO ports.  This tries a few fixed addresses.
-     *  If you want, you can also read the card's config table to
-     *  pick addresses -- see the serial driver for an example.
-     */
-    for (i = j = 0x0; j < 0x400; j += 0x20) {
-	link->io.BasePort1 = j ^ 0x300;
-	i = CardServices(RequestIO, link->handle, &link->io);
-	if (i == CS_SUCCESS) break;
-    }
-    if (i != CS_SUCCESS) {
-	cs_error(link->handle, RequestIO, i);
-	goto failed;
-    }
-
-    /*
-     *  Now allocate an interrupt line.  Note that this does not
-     *  actually assign a handler to the interrupt.
-     */
-    CS_CHECK(RequestIRQ, handle, &link->irq);
-
-    /*
-     *  This actually configures the PCMCIA socket -- setting up
-     *  the I/O windows and the interrupt mapping.
-     */
-    CS_CHECK(RequestConfiguration, handle, &link->conf);
-
-    /*
-     *  Allocate a 32K memory window.  Note that the dev_link_t
-     *  structure provides space for one window handle -- if your
-     *  device needs several windows, you'll need to keep track of
-     *  the handles in your private data structure, link->priv.
-     */
-    DEBUG(1, "Setting mem speed of %d\n", mem_speed);
-
-    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
-    req.Base = 0; req.Size = 0x8000;
-    req.AccessSpeed = mem_speed;
-    link->win = (window_handle_t)link->handle;
-    CS_CHECK(RequestWindow, &link->win, &req);
-    mem.CardOffset = 0x20000; mem.Page = 0; 
-    CS_CHECK(MapMemPage, link->win, &mem);
-
-    /* Store base address of the common window frame */
-    ramBase = ioremap(req.Base, 0x8000);
-    ((netwave_private*)dev->priv)->ramBase = ramBase;
-
-    dev->irq = link->irq.AssignedIRQ;
-    dev->base_addr = link->io.BasePort1;
-    if (register_netdev(dev) != 0) {
-	printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
-	goto failed;
-    }
-
-    strcpy(priv->node.dev_name, dev->name);
-    link->dev = &priv->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-
-    /* Reset card before reading physical address */
-    netwave_doreset(dev->base_addr, ramBase);
-
-    /* Read the ethernet address and fill in the Netwave registers. */
-    for (i = 0; i < 6; i++) 
-	dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i);
-
-    printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx id "
-	   "%c%c, hw_addr ", dev->name, dev->base_addr, dev->irq,
-	   (u_long) ramBase, (int) readb(ramBase+NETWAVE_EREG_NI),
-	   (int) readb(ramBase+NETWAVE_EREG_NI+1));
-    for (i = 0; i < 6; i++)
-	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
-
-    /* get revision words */
-    printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", 
-	   get_uint16(ramBase + NETWAVE_EREG_ARW),
-	   get_uint16(ramBase + NETWAVE_EREG_ARW+2));
-    return;
-
-cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
-failed:
-    netwave_release((u_long)link);
-} /* netwave_pcmcia_config */
-
-/*
- * Function netwave_release (arg)
- *
- *    After a card is removed, netwave_release() will unregister the net
- *    device, and release the PCMCIA configuration.  If the device is
- *    still open, this will be postponed until it is closed.
- */
-static void netwave_release(u_long arg) {
-    dev_link_t *link = (dev_link_t *)arg;
-    netwave_private *priv = link->priv;
-
-    DEBUG(0, "netwave_release(0x%p)\n", link);
-
-    /*
-      If the device is currently in use, we won't release until it
-      is actually closed.
-      */
-    if (link->open) {
-	printk(KERN_DEBUG "netwave_cs: release postponed, '%s' still open\n",
-	       link->dev->dev_name);
-	link->state |= DEV_STALE_CONFIG;
-	return;
-    }
-
-    /* Don't bother checking to see if these succeed or not */
-    if (link->win) {
-	iounmap(priv->ramBase);
-	CardServices(ReleaseWindow, link->win);
-    }
-    CardServices(ReleaseConfiguration, link->handle);
-    CardServices(ReleaseIO, link->handle, &link->io);
-    CardServices(ReleaseIRQ, link->handle, &link->irq);
-
-    link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
-
-} /* netwave_release */
-
-/*
- * Function netwave_event (event, priority, args)
- *
- *    The card status event handler.  Mostly, this schedules other
- *    stuff to run after an event is received.  A CARD_REMOVAL event
- *    also sets some flags to discourage the net drivers from trying
- *    to talk to the card any more.
- *
- *    When a CARD_REMOVAL event is received, we immediately set a flag
- *    to block future accesses to this device.  All the functions that
- *    actually access the device should check this flag to make sure
- *    the card is still present.
- *
- */
-static int netwave_event(event_t event, int priority,
-			 event_callback_args_t *args) {
-    dev_link_t *link = args->client_data;
-    netwave_private *priv = link->priv;
-    struct net_device *dev = &priv->dev;
-	
-    DEBUG(1, "netwave_event(0x%06x)\n", event);
-  
-    switch (event) {
-    case CS_EVENT_REGISTRATION_COMPLETE:
-	DEBUG(0, "netwave_cs: registration complete\n");
-	break;
-
-    case CS_EVENT_CARD_REMOVAL:
-	link->state &= ~DEV_PRESENT;
-	if (link->state & DEV_CONFIG) {
-	    netif_device_detach(dev);
-	    mod_timer(&link->release, jiffies + HZ/20);
-	}
-	break;
-    case CS_EVENT_CARD_INSERTION:
-	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-	netwave_pcmcia_config( link);
-	break;
-    case CS_EVENT_PM_SUSPEND:
-	link->state |= DEV_SUSPEND;
-	/* Fall through... */
-    case CS_EVENT_RESET_PHYSICAL:
-	if (link->state & DEV_CONFIG) {
-	    if (link->open)
-		netif_device_detach(dev);
-	    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);
-	    if (link->open) {
-		netwave_reset(dev);
-		netif_device_attach(dev);
-	    }
-	}
-	break;
-    }
-    return 0;
-} /* netwave_event */
-
-/*
- * Function netwave_doreset (ioBase, ramBase)
- *
- *    Proper hardware reset of the card.
- */
-static void netwave_doreset(ioaddr_t ioBase, u_char* ramBase) {
-    /* Reset card */
-    wait_WOC(ioBase);
-    outb(0x80, ioBase + NETWAVE_REG_PMR);
-    writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */
-    outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */
-}
-
-/*
- * Function netwave_reset (dev)
- *
- *    Reset and restore all of the netwave registers 
- */
-static void netwave_reset(struct net_device *dev) {
-    /* u_char state; */
-    netwave_private *priv = (netwave_private*) dev->priv;
-    u_char *ramBase = priv->ramBase;
-    ioaddr_t iobase = dev->base_addr;
-
-    DEBUG(0, "netwave_reset: Done with hardware reset\n");
-
-    priv->timeoutCounter = 0;
-
-    /* Reset card */
-    netwave_doreset(iobase, ramBase);
-    printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n");
-	
-    /* Write a NOP to check the card */
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
-	
-    /* Set receive conf */
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
-    
-    /* Set transmit conf */
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
-    
-    /* Now set the MU Domain */
-    printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff);
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
-    writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-	
-    /* Set scramble key */
-    printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key);
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
-    writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-
-    /* Enable interrupts, bit 4 high to keep unused
-     * source from interrupting us, bit 2 high to 
-     * set interrupt enable, 567 to enable TxDN, 
-     * RxErr and RxRdy
-     */
-    wait_WOC(iobase);
-    outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR);
-
-    /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36
-     * waitWOC
-     * skriv 80 til d000:3688
-     * sjekk om det ble 80
-     */
-    
-    /* Enable Receiver */
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
-	
-    /* Set the IENA bit in COR */
-    wait_WOC(iobase);
-    outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR);
-}
-
-/*
- * Function netwave_config (dev, map)
- *
- *    Configure device, this work is done by netwave_pcmcia_config when a
- *    card is inserted
- */
-static int netwave_config(struct net_device *dev, struct ifmap *map) {
-    return 0; 
-}
-
-/*
- * Function netwave_hw_xmit (data, len, dev)    
- */
-static int netwave_hw_xmit(unsigned char* data, int len,
-			   struct net_device* dev) {
-    unsigned long flags;
-    unsigned int TxFreeList,
-	         curBuff,
-	         MaxData, 
-                 DataOffset;
-    int tmpcount; 
-	
-    netwave_private *priv = (netwave_private *) dev->priv;
-    u_char* ramBase = priv->ramBase;
-    ioaddr_t iobase = dev->base_addr;
-
-    /* Disable interrupts & save flags */
-    save_flags(flags);
-    cli();
-
-    /* Check if there are transmit buffers available */
-    wait_WOC(iobase);
-    if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) {
-	/* No buffers available */
-	printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n",
-	       dev->name);
-	restore_flags(flags);
-	return 1;
-    }
-
-    priv->stats.tx_bytes += len;
-
-    DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
-	  readb(ramBase + NETWAVE_EREG_SPCQ),
-	  readb(ramBase + NETWAVE_EREG_SPU),
-	  readb(ramBase + NETWAVE_EREG_LIF),
-	  readb(ramBase + NETWAVE_EREG_ISPLQ));
-
-    /* Now try to insert it into the adapters free memory */
-    wait_WOC(iobase);
-    TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP);
-    MaxData    = get_uint16(ramBase + NETWAVE_EREG_TDP+2);
-    DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4);
-	
-    DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n",
-	  TxFreeList, MaxData, DataOffset);
-
-    /* Copy packet to the adapter fragment buffers */
-    curBuff = TxFreeList; 
-    tmpcount = 0; 
-    while (tmpcount < len) {
-	int tmplen = len - tmpcount; 
-	copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount, 
-		   (tmplen < MaxData) ? tmplen : MaxData);
-	tmpcount += MaxData;
-			
-	/* Advance to next buffer */
-	curBuff = get_uint16(ramBase + curBuff);
-    }
-    
-    /* Now issue transmit list */
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1);
-    writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-
-    restore_flags( flags);
-    return 0;
-}
-
-static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) {
-	/* This flag indicate that the hardware can't perform a transmission.
-	 * Theoritically, NET3 check it before sending a packet to the driver,
-	 * but in fact it never do that and pool continuously.
-	 * As the watchdog will abort too long transmissions, we are quite safe...
-	 */
-
-    netif_stop_queue(dev);
-
-    {
-	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-	unsigned char* buf = skb->data;
-	
-	if (netwave_hw_xmit( buf, length, dev) == 1) {
-	    /* Some error, let's make them call us another time? */
-	    netif_start_queue(dev);
-	}
-	dev->trans_start = jiffies;
-    }
-    dev_kfree_skb(skb);
-    
-    return 0;
-} /* netwave_start_xmit */
-
-/*
- * Function netwave_interrupt (irq, dev_id, regs)
- *
- *    This function is the interrupt handler for the Netwave card. This
- *    routine will be called whenever: 
- *	  1. A packet is received.
- *	  2. A packet has successfully been transferred and the unit is
- *	     ready to transmit another packet.
- *	  3. A command has completed execution.
- */
-static void netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs) {
-    ioaddr_t iobase;
-    u_char *ramBase;
-    struct net_device *dev = (struct net_device *)dev_id;
-    struct netwave_private *priv = dev->priv;
-    dev_link_t *link = &priv->link;
-    int i;
-    
-    if (!netif_device_present(dev))
-	return;
-    
-    iobase = dev->base_addr;
-    ramBase = priv->ramBase;
-	
-    /* Now find what caused the interrupt, check while interrupts ready */
-    for (i = 0; i < 10; i++) {
-	u_char status;
-		
-	wait_WOC(iobase);	
-	if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02))
-	    break; /* None of the interrupt sources asserted */
-	
-        status = inb(iobase + NETWAVE_REG_ASR);
-		
-	if (!DEV_OK(link)) {
-	    DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x "
-		  "from removed or suspended card!\n", status);
-	    break;
-	}
-		
-	/* RxRdy */
-	if (status & 0x80) {
-	    netwave_rx(dev);
-	    /* wait_WOC(iobase); */
-	    /* RxRdy cannot be reset directly by the host */
-	}
-	/* RxErr */
-	if (status & 0x40) {
-	    u_char rser;
-			
-	    rser = readb(ramBase + NETWAVE_EREG_RSER);			
-	    
-	    if (rser & 0x04) {
-		++priv->stats.rx_dropped; 
-		++priv->stats.rx_crc_errors;
-	    }
-	    if (rser & 0x02)
-		++priv->stats.rx_frame_errors;
-			
-	    /* Clear the RxErr bit in RSER. RSER+4 is the
-	     * write part. Also clear the RxCRC (0x04) and 
-	     * RxBig (0x02) bits if present */
-	    wait_WOC(iobase);
-	    writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4);
-
-	    /* Write bit 6 high to ASCC to clear RxErr in ASR,
-	     * WOC must be set first! 
-	     */
-	    wait_WOC(iobase);
-	    writeb(0x40, ramBase + NETWAVE_EREG_ASCC);
-
-	    /* Remember to count up priv->stats on error packets */
-	    ++priv->stats.rx_errors;
-	}
-	/* TxDN */
-	if (status & 0x20) {
-	    int txStatus;
-
-	    txStatus = readb(ramBase + NETWAVE_EREG_TSER);
-	    DEBUG(3, "Transmit done. TSER = %x id %x\n", 
-		  txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1));
-	    
-	    if (txStatus & 0x20) {
-		/* Transmitting was okay, clear bits */
-		wait_WOC(iobase);
-		writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4);
-		++priv->stats.tx_packets;
-	    }
-			
-	    if (txStatus & 0xd0) {
-		if (txStatus & 0x80) {
-		    ++priv->stats.collisions; /* Because of /proc/net/dev*/
-		    /* ++priv->stats.tx_aborted_errors; */
-		    /* printk("Collision. %ld\n", jiffies - dev->trans_start); */
-		}
-		if (txStatus & 0x40) 
-		    ++priv->stats.tx_carrier_errors;
-		/* 0x80 TxGU Transmit giveup - nine times and no luck
-		 * 0x40 TxNOAP No access point. Discarded packet.
-		 * 0x10 TxErr Transmit error. Always set when 
-		 *      TxGU and TxNOAP is set. (Those are the only ones
-		 *      to set TxErr).
-		 */
-		DEBUG(3, "netwave_interrupt: TxDN with error status %x\n", 
-		      txStatus);
-		
-		/* Clear out TxGU, TxNOAP, TxErr and TxTrys */
-		wait_WOC(iobase);
-		writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4);
-		++priv->stats.tx_errors;
-	    }
-	    DEBUG(3, "New status is TSER %x ASR %x\n",
-		  readb(ramBase + NETWAVE_EREG_TSER),
-		  inb(iobase + NETWAVE_REG_ASR));
-
-	    netif_wake_queue(dev);
-	}
-	/* TxBA, this would trigger on all error packets received */
-	/* if (status & 0x01) {
-	   DEBUG(4, "Transmit buffers available, %x\n", status);
-	   }
-	   */
-    }
-} /* netwave_interrupt */
-
-/*
- * Function netwave_watchdog (a)
- *
- *    Watchdog : when we start a transmission, we set a timer in the
- *    kernel.  If the transmission complete, this timer is disabled. If
- *    it expire, we reset the card.
- *
- */
-static void netwave_watchdog(struct net_device *dev) {
-
-    DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
-    netwave_reset(dev);
-    dev->trans_start = jiffies;
-    netif_wake_queue(dev);
-} /* netwave_watchdog */
-
-static struct net_device_stats *netwave_get_stats(struct net_device *dev) {
-    netwave_private *priv = (netwave_private*)dev->priv;
-
-    update_stats(dev);
-
-    DEBUG(2, "netwave: SPCQ %x SPU %x LIF %x ISPLQ %x MHS %x rxtx %x"
-	  " %x tx %x %x %x %x\n", 
-	  readb(priv->ramBase + NETWAVE_EREG_SPCQ),
-	  readb(priv->ramBase + NETWAVE_EREG_SPU),
-	  readb(priv->ramBase + NETWAVE_EREG_LIF),
-	  readb(priv->ramBase + NETWAVE_EREG_ISPLQ),
-	  readb(priv->ramBase + NETWAVE_EREG_MHS),
-	  readb(priv->ramBase + NETWAVE_EREG_EC + 0xe),
-	  readb(priv->ramBase + NETWAVE_EREG_EC + 0xf),
-	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x18),
-	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x19),
-	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x1a),
-	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x1b));
-
-    return &priv->stats;
-}
-
-static void update_stats(struct net_device *dev) {
-    unsigned long flags;
-
-    save_flags(flags);
-    cli();
-
-/*     netwave_private *priv = (netwave_private*) dev->priv;
-    priv->stats.rx_packets = readb(priv->ramBase + 0x18e); 
-    priv->stats.tx_packets = readb(priv->ramBase + 0x18f); */
-
-    restore_flags(flags);
-}
-
-static int netwave_rx(struct net_device *dev) {
-    netwave_private *priv = (netwave_private*)(dev->priv);
-    u_char *ramBase = priv->ramBase;
-    ioaddr_t iobase = dev->base_addr;
-    u_char rxStatus;
-    struct sk_buff *skb = NULL;
-    unsigned int curBuffer,
-		rcvList;
-    int rcvLen;
-    int tmpcount = 0;
-    int dataCount, dataOffset;
-    int i;
-    u_char *ptr;
-	
-    DEBUG(3, "xinw_rx: Receiving ... \n");
-
-    /* Receive max 10 packets for now. */
-    for (i = 0; i < 10; i++) {
-	/* Any packets? */
-	wait_WOC(iobase);
-	rxStatus = readb(ramBase + NETWAVE_EREG_RSER);		
-	if ( !( rxStatus & 0x80)) /* No more packets */
-	    break;
-		
-	/* Check if multicast/broadcast or other */
-	/* multicast = (rxStatus & 0x20);  */
-		
-	/* The receive list pointer and length of the packet */
-	wait_WOC(iobase);
-	rcvLen  = get_int16( ramBase + NETWAVE_EREG_RDP);
-	rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2);
-		
-	if (rcvLen < 0) {
-	    printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n", 
-		   rcvLen);
-	    return 0;
-	}
-		
-	skb = dev_alloc_skb(rcvLen+5);
-	if (skb == NULL) {
-	    DEBUG(1, "netwave_rx: Could not allocate an sk_buff of "
-		  "length %d\n", rcvLen);
-	    ++priv->stats.rx_dropped; 
-	    /* Tell the adapter to skip the packet */
-	    wait_WOC(iobase);
-	    writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
-	    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
-	    return 0;
-	}
-
-	skb_reserve( skb, 2);  /* Align IP on 16 byte */
-	skb_put( skb, rcvLen);
-	skb->dev = dev;
-
-	/* Copy packet fragments to the skb data area */
-	ptr = (u_char*) skb->data;
-	curBuffer = rcvList;
-	tmpcount = 0; 
-	while ( tmpcount < rcvLen) {
-	    /* Get length and offset of current buffer */
-	    dataCount  = get_uint16( ramBase+curBuffer+2);
-	    dataOffset = get_uint16( ramBase+curBuffer+4);
-		
-	    copy_from_pc( ptr + tmpcount,
-			  ramBase+curBuffer+dataOffset, dataCount);
-
-	    tmpcount += dataCount;
-		
-	    /* Point to next buffer */
-	    curBuffer = get_uint16(ramBase + curBuffer);
-	}
-	
-	skb->protocol = eth_type_trans(skb,dev);
-	/* Queue packet for network layer */
-	netif_rx(skb);
-
-	dev->last_rx = jiffies;
-	priv->stats.rx_packets++;
-	priv->stats.rx_bytes += rcvLen;
-
-	/* Got the packet, tell the adapter to skip it */
-	wait_WOC(iobase);
-	writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
-	writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
-	DEBUG(3, "Packet reception ok\n");
-    }
-    return 0;
-}
-
-static int netwave_open(struct net_device *dev) {
-    netwave_private *priv = dev->priv;
-    dev_link_t *link = &priv->link;
-
-    DEBUG(1, "netwave_open: starting.\n");
-    
-    if (!DEV_OK(link))
-	return -ENODEV;
-
-    link->open++;
-    MOD_INC_USE_COUNT;
-
-    netif_start_queue(dev);
-    netwave_reset(dev);
-	
-    return 0;
-}
-
-static int netwave_close(struct net_device *dev) {
-    netwave_private *priv = (netwave_private *)dev->priv;
-    dev_link_t *link = &priv->link;
-
-    DEBUG(1, "netwave_close: finishing.\n");
-
-    link->open--;
-    netif_stop_queue(dev);
-    if (link->state & DEV_STALE_CONFIG)
-	mod_timer(&link->release, jiffies + HZ/20);
-	
-    MOD_DEC_USE_COUNT;
-    return 0;
-}
-
-static int __init init_netwave_cs(void) {
-    servinfo_t serv;
-
-    DEBUG(0, "%s\n", version);
-
-    CardServices(GetCardServicesInfo, &serv);
-    if (serv.Revision != CS_RELEASE_CODE) {
-	printk("netwave_cs: Card Services release does not match!\n");
-	return -1;
-    }
- 
-    register_pccard_driver(&dev_info, &netwave_attach, &netwave_detach);
-	
-    return 0;
-}
-
-static void __exit exit_netwave_cs(void) {
-    DEBUG(1, "netwave_cs: unloading\n");
-
-    unregister_pccard_driver(&dev_info);
-
-    /* Do some cleanup of the device list */
-    netwave_flush_stale_links();
-    if(dev_list != NULL)	/* Critical situation */
-        printk("netwave_cs: devices remaining when removing module\n");
-}
-
-module_init(init_netwave_cs);
-module_exit(exit_netwave_cs);
-
-/* Set or clear the multicast filter for this adaptor.
-   num_addrs == -1	Promiscuous mode, receive all packets
-   num_addrs == 0	Normal mode, clear multicast list
-   num_addrs > 0	Multicast mode, receive normal and MC packets, and do
-   best-effort filtering.
- */
-static void set_multicast_list(struct net_device *dev)
-{
-    ioaddr_t iobase = dev->base_addr;
-    u_char* ramBase = ((netwave_private*) dev->priv)->ramBase;
-    u_char  rcvMode = 0;
-   
-#ifdef PCMCIA_DEBUG
-    if (pc_debug > 2) {
-	static int old;
-	if (old != dev->mc_count) {
-	    old = dev->mc_count;
-	    DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
-		  dev->name, dev->mc_count);
-	}
-    }
-#endif
-	
-    if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
-	/* Multicast Mode */
-	rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast;
-    } else if (dev->flags & IFF_PROMISC) {
-	/* Promiscous mode */
-	rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast;
-    } else {
-	/* Normal mode */
-	rcvMode = rxConfRxEna + rxConfBcast;
-    }
-	
-    /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/
-    /* Now set receive mode */
-    wait_WOC(iobase);
-    writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
-    writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1);
-    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
-}
-MODULE_LICENSE("GPL");
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/wavelan.h linux/drivers/net/pcmcia/wavelan.h
--- linux-2.5.3-pre2/drivers/net/pcmcia/wavelan.h	Mon Jan 21 17:22:32 2002
+++ linux/drivers/net/pcmcia/wavelan.h	Wed Dec 31 16:00:00 1969
@@ -1,386 +0,0 @@
-/*
- *	Wavelan Pcmcia driver
- *
- *		Jean II - HPLB '96
- *
- * Reorganization and extension of the driver.
- * Original copyright follow. See wavelan_cs.h for details.
- *
- * This file contain the declarations of the Wavelan hardware. Note that
- * the Pcmcia Wavelan include a i82593 controller (see definitions in
- * file i82593.h).
- *
- * The main difference between the pcmcia hardware and the ISA one is
- * the Ethernet Controller (i82593 instead of i82586). The i82593 allow
- * only one send buffer. The PSA (Parameter Storage Area : EEprom for
- * permanent storage of various info) is memory mapped, but not the
- * MMI (Modem Management Interface).
- */
-
-/*
- * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card: 
- *   An Ethernet-like radio transceiver controlled by an Intel 82593
- *   coprocessor.
- *
- *
- ****************************************************************************
- *   Copyright 1995
- *   Anthony D. Joseph
- *   Massachusetts Institute of Technology
- *
- *   Permission to use, copy, modify, and distribute this program
- *   for any purpose and without fee is hereby granted, provided
- *   that this copyright and permission notice appear on all copies
- *   and supporting documentation, the name of M.I.T. not be used
- *   in advertising or publicity pertaining to distribution of the
- *   program without specific prior permission, and notice be given
- *   in supporting documentation that copying and distribution is
- *   by permission of M.I.T.  M.I.T. makes no representations about
- *   the suitability of this software for any purpose.  It is pro-
- *   vided "as is" without express or implied warranty.         
- ****************************************************************************
- *
- *
- * Credits:
- *     Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for
- *       providing extremely useful information about WaveLAN PCMCIA hardware
- *
- *     This driver is based upon several other drivers, in particular:
- *       David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
- *       Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
- *	 Anders Klemets' PCMCIA WaveLAN adapter driver
- *       Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
- */
-
-#ifndef _WAVELAN_H
-#define	_WAVELAN_H
-
-/************************** MAGIC NUMBERS ***************************/
-
-/* The detection of the wavelan card is made by reading the MAC address
- * from the card and checking it. If you have a non AT&T product (OEM,
- * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
- * part to accomodate your hardware...
- */
-const unsigned char	MAC_ADDRESSES[][3] =
-{
-  { 0x08, 0x00, 0x0E },		/* AT&T Wavelan (standard) & DEC RoamAbout */
-  { 0x08, 0x00, 0x6A },		/* AT&T Wavelan (alternate) */
-  { 0x00, 0x00, 0xE1 },		/* Hitachi Wavelan */
-  { 0x00, 0x60, 0x1D }		/* Lucent Wavelan (another one) */
-  /* Add your card here and send me the patch ! */
-};
-
-/*
- * Constants used to convert channels to frequencies
- */
-
-/* Frequency available in the 2.0 modem, in units of 250 kHz
- * (as read in the offset register of the dac area).
- * Used to map channel numbers used by `wfreqsel' to frequencies
- */
-const short	channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
-				    0xD0, 0xF0, 0xF8, 0x150 };
-
-/* Frequencies of the 1.0 modem (fixed frequencies).
- * Use to map the PSA `subband' to a frequency
- * Note : all frequencies apart from the first one need to be multiplied by 10
- */
-const int	fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
-
-
-/*************************** PC INTERFACE ****************************/
-
-/* WaveLAN host interface definitions */
-
-#define	LCCR(base)	(base)		/* LAN Controller Command Register */
-#define	LCSR(base)	(base)		/* LAN Controller Status Register */
-#define	HACR(base)	(base+0x1)	/* Host Adapter Command Register */
-#define	HASR(base)	(base+0x1)	/* Host Adapter Status Register */
-#define PIORL(base)	(base+0x2)	/* Program I/O Register Low */
-#define RPLL(base)	(base+0x2)	/* Receive Pointer Latched Low */
-#define PIORH(base)	(base+0x3)	/* Program I/O Register High */
-#define RPLH(base)	(base+0x3)	/* Receive Pointer Latched High */
-#define PIOP(base)	(base+0x4)	/* Program I/O Port */
-#define MMR(base)	(base+0x6)	/* MMI Address Register */
-#define MMD(base)	(base+0x7)	/* MMI Data Register */
-
-/* Host Adaptor Command Register bit definitions */
-
-#define HACR_LOF	  (1 << 3)	/* Lock Out Flag, toggle every 250ms */
-#define HACR_PWR_STAT	  (1 << 4)	/* Power State, 1=active, 0=sleep */
-#define HACR_TX_DMA_RESET (1 << 5)	/* Reset transmit DMA ptr on high */
-#define HACR_RX_DMA_RESET (1 << 6)	/* Reset receive DMA ptr on high */
-#define HACR_ROM_WEN	  (1 << 7)	/* EEPROM write enabled when true */
-
-#define HACR_RESET              (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET)
-#define	HACR_DEFAULT		(HACR_PWR_STAT)
-
-/* Host Adapter Status Register bit definitions */
-
-#define HASR_MMI_BUSY	(1 << 2)	/* MMI is busy when true */
-#define HASR_LOF	(1 << 3)	/* Lock out flag status */
-#define HASR_NO_CLK	(1 << 4)	/* active when modem not connected */
-
-/* Miscellaneous bit definitions */
-
-#define PIORH_SEL_TX	(1 << 5)	/* PIOR points to 0=rx/1=tx buffer */
-#define MMR_MMI_WR	(1 << 0)	/* Next MMI cycle is 0=read, 1=write */
-#define PIORH_MASK	0x1f		/* only low 5 bits are significant */
-#define RPLH_MASK	0x1f		/* only low 5 bits are significant */
-#define MMI_ADDR_MASK	0x7e		/* Bits 1-6 of MMR are significant */
-
-/* Attribute Memory map */
-
-#define CIS_ADDR	0x0000		/* Card Information Status Register */
-#define PSA_ADDR	0x0e00		/* Parameter Storage Area address */
-#define EEPROM_ADDR	0x1000		/* EEPROM address (unused ?) */
-#define COR_ADDR	0x4000		/* Configuration Option Register */
-
-/* Configuration Option Register bit definitions */
-
-#define COR_CONFIG	(1 << 0)	/* Config Index, 0 when unconfigured */
-#define COR_SW_RESET	(1 << 7)	/* Software Reset on true */
-#define COR_LEVEL_IRQ	(1 << 6)	/* Level IRQ */
-
-/* Local Memory map */
-
-#define RX_BASE		0x0000		/* Receive memory, 8 kB */
-#define TX_BASE		0x2000		/* Transmit memory, 2 kB */
-#define UNUSED_BASE	0x2800		/* Unused, 22 kB */
-#define RX_SIZE		(TX_BASE-RX_BASE)	/* Size of receive area */
-#define RX_SIZE_SHIFT	6		/* Bits to shift in stop register */
-
-#define TRUE  1
-#define FALSE 0
-
-#define MOD_ENAL 1
-#define MOD_PROM 2
-
-/* Size of a MAC address */
-#define WAVELAN_ADDR_SIZE	6
-
-/* Maximum size of Wavelan packet */
-#define WAVELAN_MTU	1500
-
-#define	MAXDATAZ		(6 + 6 + 2 + WAVELAN_MTU)
-
-/********************** PARAMETER STORAGE AREA **********************/
-
-/*
- * Parameter Storage Area (PSA).
- */
-typedef struct psa_t	psa_t;
-struct psa_t
-{
-  /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */
-  unsigned char	psa_io_base_addr_1;	/* [0x00] Base address 1 ??? */
-  unsigned char	psa_io_base_addr_2;	/* [0x01] Base address 2 */
-  unsigned char	psa_io_base_addr_3;	/* [0x02] Base address 3 */
-  unsigned char	psa_io_base_addr_4;	/* [0x03] Base address 4 */
-  unsigned char	psa_rem_boot_addr_1;	/* [0x04] Remote Boot Address 1 */
-  unsigned char	psa_rem_boot_addr_2;	/* [0x05] Remote Boot Address 2 */
-  unsigned char	psa_rem_boot_addr_3;	/* [0x06] Remote Boot Address 3 */
-  unsigned char	psa_holi_params;	/* [0x07] HOst Lan Interface (HOLI) Parameters */
-  unsigned char	psa_int_req_no;		/* [0x08] Interrupt Request Line */
-  unsigned char	psa_unused0[7];		/* [0x09-0x0F] unused */
-
-  unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x10-0x15] Universal (factory) MAC Address */
-  unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x16-1B] Local MAC Address */
-  unsigned char	psa_univ_local_sel;	/* [0x1C] Universal Local Selection */
-#define		PSA_UNIVERSAL	0		/* Universal (factory) */
-#define		PSA_LOCAL	1		/* Local */
-  unsigned char	psa_comp_number;	/* [0x1D] Compatability Number: */
-#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz	*/
-#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	*/
-#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	*/
-#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz	*/
-#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz or 2.0 */
-  unsigned char	psa_thr_pre_set;	/* [0x1E] Modem Threshold Preset */
-  unsigned char	psa_feature_select;	/* [0x1F] Call code required (1=on) */
-#define		PSA_FEATURE_CALL_CODE	0x01 	/* Call code required (Japan) */
-  unsigned char	psa_subband;		/* [0x20] Subband	*/
-#define		PSA_SUBBAND_915		0	/* 915 MHz or 2.0 */
-#define		PSA_SUBBAND_2425	1	/* 2425 MHz	*/
-#define		PSA_SUBBAND_2460	2	/* 2460 MHz	*/
-#define		PSA_SUBBAND_2484	3	/* 2484 MHz	*/
-#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	*/
-  unsigned char	psa_quality_thr;	/* [0x21] Modem Quality Threshold */
-  unsigned char	psa_mod_delay;		/* [0x22] Modem Delay ??? (reserved) */
-  unsigned char	psa_nwid[2];		/* [0x23-0x24] Network ID */
-  unsigned char	psa_nwid_select;	/* [0x25] Network ID Select On Off */
-  unsigned char	psa_encryption_select;	/* [0x26] Encryption On Off */
-  unsigned char	psa_encryption_key[8];	/* [0x27-0x2E] Encryption Key */
-  unsigned char	psa_databus_width;	/* [0x2F] AT bus width select 8/16 */
-  unsigned char	psa_call_code[8];	/* [0x30-0x37] (Japan) Call Code */
-  unsigned char	psa_nwid_prefix[2];	/* [0x38-0x39] Roaming domain */
-  unsigned char	psa_reserved[2];	/* [0x3A-0x3B] Reserved - fixed 00 */
-  unsigned char	psa_conf_status;	/* [0x3C] Conf Status, bit 0=1:config*/
-  unsigned char	psa_crc[2];		/* [0x3D] CRC-16 over PSA */
-  unsigned char	psa_crc_status;		/* [0x3F] CRC Valid Flag */
-};
-
-/* Size for structure checking (if padding is correct) */
-#define	PSA_SIZE	64
-
-/* Calculate offset of a field in the above structure
- * Warning : only even addresses are used */
-#define	psaoff(p,f) 	((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
-
-/******************** MODEM MANAGEMENT INTERFACE ********************/
-
-/*
- * Modem Management Controller (MMC) write structure.
- */
-typedef struct mmw_t	mmw_t;
-struct mmw_t
-{
-  unsigned char	mmw_encr_key[8];	/* encryption key */
-  unsigned char	mmw_encr_enable;	/* enable/disable encryption */
-#define	MMW_ENCR_ENABLE_MODE	0x02	/* Mode of security option */
-#define	MMW_ENCR_ENABLE_EN	0x01	/* Enable security option */
-  unsigned char	mmw_unused0[1];		/* unused */
-  unsigned char	mmw_des_io_invert;	/* Encryption option */
-#define	MMW_DES_IO_INVERT_RES	0x0F	/* Reserved */
-#define	MMW_DES_IO_INVERT_CTRL	0xF0	/* Control ??? (set to 0) */
-  unsigned char	mmw_unused1[5];		/* unused */
-  unsigned char	mmw_loopt_sel;		/* looptest selection */
-#define	MMW_LOOPT_SEL_DIS_NWID	0x40	/* disable NWID filtering */
-#define	MMW_LOOPT_SEL_INT	0x20	/* activate Attention Request */
-#define	MMW_LOOPT_SEL_LS	0x10	/* looptest w/o collision avoidance */
-#define MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */
-#define	MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */
-#define	MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */
-#define	MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */
-  unsigned char	mmw_jabber_enable;	/* jabber timer enable */
-  /* Abort transmissions > 200 ms */
-  unsigned char	mmw_freeze;		/* freeze / unfreeeze signal level */
-  /* 0 : signal level & qual updated for every new message, 1 : frozen */
-  unsigned char	mmw_anten_sel;		/* antenna selection */
-#define MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */
-#define	MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algo. enable */
-  unsigned char	mmw_ifs;		/* inter frame spacing */
-  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
-  unsigned char	mmw_mod_delay;	 	/* modem delay (synchro) */
-  unsigned char	mmw_jam_time;		/* jamming time (after collision) */
-  unsigned char	mmw_unused2[1];		/* unused */
-  unsigned char	mmw_thr_pre_set;	/* level threshold preset */
-  /* Discard all packet with signal < this value (4) */
-  unsigned char	mmw_decay_prm;		/* decay parameters */
-  unsigned char	mmw_decay_updat_prm;	/* decay update parameterz */
-  unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
-  /* Discard all packet with quality < this value (3) */
-  unsigned char	mmw_netw_id_l;		/* NWID low order byte */
-  unsigned char	mmw_netw_id_h;		/* NWID high order byte */
-  /* Network ID or Domain : create virtual net on the air */
-
-  /* 2.0 Hardware extension - frequency selection support */
-  unsigned char	mmw_mode_select;	/* for analog tests (set to 0) */
-  unsigned char	mmw_unused3[1];		/* unused */
-  unsigned char	mmw_fee_ctrl;		/* frequency eeprom control */
-#define	MMW_FEE_CTRL_PRE	0x10	/* Enable protected instructions */
-#define	MMW_FEE_CTRL_DWLD	0x08	/* Download eeprom to mmc */
-#define	MMW_FEE_CTRL_CMD	0x07	/* EEprom commands : */
-#define	MMW_FEE_CTRL_READ	0x06	/* Read */
-#define	MMW_FEE_CTRL_WREN	0x04	/* Write enable */
-#define	MMW_FEE_CTRL_WRITE	0x05	/* Write data to address */
-#define	MMW_FEE_CTRL_WRALL	0x04	/* Write data to all addresses */
-#define	MMW_FEE_CTRL_WDS	0x04	/* Write disable */
-#define	MMW_FEE_CTRL_PRREAD	0x16	/* Read addr from protect register */
-#define	MMW_FEE_CTRL_PREN	0x14	/* Protect register enable */
-#define	MMW_FEE_CTRL_PRCLEAR	0x17	/* Unprotect all registers */
-#define	MMW_FEE_CTRL_PRWRITE	0x15	/* Write addr in protect register */
-#define	MMW_FEE_CTRL_PRDS	0x14	/* Protect register disable */
-  /* Never issue this command (PRDS) : it's irreversible !!! */
-
-  unsigned char	mmw_fee_addr;		/* EEprom address */
-#define	MMW_FEE_ADDR_CHANNEL	0xF0	/* Select the channel */
-#define	MMW_FEE_ADDR_OFFSET	0x0F	/* Offset in channel data */
-#define	MMW_FEE_ADDR_EN		0xC0	/* FEE_CTRL enable operations */
-#define	MMW_FEE_ADDR_DS		0x00	/* FEE_CTRL disable operations */
-#define	MMW_FEE_ADDR_ALL	0x40	/* FEE_CTRL all operations */
-#define	MMW_FEE_ADDR_CLEAR	0xFF	/* FEE_CTRL clear operations */
-
-  unsigned char	mmw_fee_data_l;		/* Write data to EEprom */
-  unsigned char	mmw_fee_data_h;		/* high octet */
-  unsigned char	mmw_ext_ant;		/* Setting for external antenna */
-#define	MMW_EXT_ANT_EXTANT	0x01	/* Select external antenna */
-#define	MMW_EXT_ANT_POL		0x02	/* Polarity of the antenna */
-#define	MMW_EXT_ANT_INTERNAL	0x00	/* Internal antenna */
-#define	MMW_EXT_ANT_EXTERNAL	0x03	/* External antenna */
-#define	MMW_EXT_ANT_IQ_TEST	0x1C	/* IQ test pattern (set to 0) */
-};
-
-/* Size for structure checking (if padding is correct) */
-#define	MMW_SIZE	37
-
-/* Calculate offset of a field in the above structure */
-#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
-
-
-/*
- * Modem Management Controller (MMC) read structure.
- */
-typedef struct mmr_t	mmr_t;
-struct mmr_t
-{
-  unsigned char	mmr_unused0[8];		/* unused */
-  unsigned char	mmr_des_status;		/* encryption status */
-  unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */
-#define	MMR_DES_AVAIL_DES	0x55		/* DES available */
-#define	MMR_DES_AVAIL_AES	0x33		/* AES (AT&T) available */
-  unsigned char	mmr_des_io_invert;	/* des I/O invert register */
-  unsigned char	mmr_unused1[5];		/* unused */
-  unsigned char	mmr_dce_status;		/* DCE status */
-#define	MMR_DCE_STATUS_RX_BUSY		0x01	/* receiver busy */
-#define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
-#define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */
-#define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
-#define MMR_DCE_STATUS			0x0F	/* mask to get the bits */
-  unsigned char	mmr_dsp_id;		/* DSP id (AA = Daedalus rev A) */
-  unsigned char	mmr_unused2[2];		/* unused */
-  unsigned char	mmr_correct_nwid_l;	/* # of correct NWID's rxd (low) */
-  unsigned char	mmr_correct_nwid_h;	/* # of correct NWID's rxd (high) */
-  /* Warning : Read high order octet first !!! */
-  unsigned char	mmr_wrong_nwid_l;	/* # of wrong NWID's rxd (low) */
-  unsigned char	mmr_wrong_nwid_h;	/* # of wrong NWID's rxd (high) */
-  unsigned char	mmr_thr_pre_set;	/* level threshold preset */
-#define	MMR_THR_PRE_SET		0x3F		/* level threshold preset */
-#define	MMR_THR_PRE_SET_CUR	0x80		/* Current signal above it */
-  unsigned char	mmr_signal_lvl;		/* signal level */
-#define	MMR_SIGNAL_LVL		0x3F		/* signal level */
-#define	MMR_SIGNAL_LVL_VALID	0x80		/* Updated since last read */
-  unsigned char	mmr_silence_lvl;	/* silence level (noise) */
-#define	MMR_SILENCE_LVL		0x3F		/* silence level */
-#define	MMR_SILENCE_LVL_VALID	0x80		/* Updated since last read */
-  unsigned char	mmr_sgnl_qual;		/* signal quality */
-#define	MMR_SGNL_QUAL		0x0F		/* signal quality */
-#define	MMR_SGNL_QUAL_ANT	0x80		/* current antenna used */
-  unsigned char	mmr_netw_id_l;		/* NWID low order byte ??? */
-  unsigned char	mmr_unused3[3];		/* unused */
-
-  /* 2.0 Hardware extension - frequency selection support */
-  unsigned char	mmr_fee_status;		/* Status of frequency eeprom */
-#define	MMR_FEE_STATUS_ID	0xF0		/* Modem revision id */
-#define	MMR_FEE_STATUS_DWLD	0x08		/* Download in progress */
-#define	MMR_FEE_STATUS_BUSY	0x04		/* EEprom busy */
-  unsigned char	mmr_unused4[1];		/* unused */
-  unsigned char	mmr_fee_data_l;		/* Read data from eeprom (low) */
-  unsigned char	mmr_fee_data_h;		/* Read data from eeprom (high) */
-};
-
-/* Size for structure checking (if padding is correct) */
-#define	MMR_SIZE	36
-
-/* Calculate offset of a field in the above structure */
-#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
-
-
-/* Make the two above structures one */
-typedef union mm_t
-{
-  struct mmw_t	w;	/* Write to the mmc */
-  struct mmr_t	r;	/* Read from the mmc */
-} mm_t;
-
-#endif /* _WAVELAN_H */
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.c linux/drivers/net/pcmcia/wavelan_cs.c
--- linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.c	Mon Jan 21 17:22:32 2002
+++ linux/drivers/net/pcmcia/wavelan_cs.c	Wed Dec 31 16:00:00 1969
@@ -1,4837 +0,0 @@
-/*
- *	Wavelan Pcmcia driver
- *
- *		Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- * Original copyright follow. See wavelan_cs.h for details.
- *
- * This code is derived from Anthony D. Joseph's code and all the changes here
- * are also under the original copyright below.
- *
- * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and
- * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services
- *
- * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added
- * critical code in the routine to initialize the Modem Management Controller.
- *
- * Thanks to Alan Cox and Bruce Janson for their advice.
- *
- *	-- Yunzhou Li (scip4166@nus.sg)
- *
-#ifdef WAVELAN_ROAMING	
- * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu)
- * based on patch by Joe Finney from Lancaster University.
-#endif
- *
- * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An
- * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor.
- *
- *   A non-shared memory PCMCIA ethernet driver for linux
- *
- * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu)
- *
- *
- * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu)
- *
- * Apr 2 '98  made changes to bring the i82593 control/int handling in line
- *             with offical specs...
- *
- ****************************************************************************
- *   Copyright 1995
- *   Anthony D. Joseph
- *   Massachusetts Institute of Technology
- *
- *   Permission to use, copy, modify, and distribute this program
- *   for any purpose and without fee is hereby granted, provided
- *   that this copyright and permission notice appear on all copies
- *   and supporting documentation, the name of M.I.T. not be used
- *   in advertising or publicity pertaining to distribution of the
- *   program without specific prior permission, and notice be given
- *   in supporting documentation that copying and distribution is
- *   by permission of M.I.T.  M.I.T. makes no representations about
- *   the suitability of this software for any purpose.  It is pro-
- *   vided "as is" without express or implied warranty.         
- ****************************************************************************
- *
- */
-
-#include "wavelan_cs.h"		/* Private header */
-
-/************************* MISC SUBROUTINES **************************/
-/*
- * Subroutines which won't fit in one of the following category
- * (wavelan modem or i82593)
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for disabling interrupts.
- * (note : inline, so optimised away)
- */
-static inline void
-wv_splhi(net_local *		lp,
-	 unsigned long *	pflags)
-{
-  spin_lock_irqsave(&lp->spinlock, *pflags);
-  /* Note : above does the cli(); itself */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for re-enabling interrupts.
- */
-static inline void
-wv_splx(net_local *		lp,
-	unsigned long *		pflags)
-{
-  spin_unlock_irqrestore(&lp->spinlock, *pflags);
-
-  /* Note : enabling interrupts on the hardware is done in wv_ru_start()
-   * via : outb(OP1_INT_ENABLE, LCCR(base));
-   */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for reporting error to cardservices
- */
-static void cs_error(client_handle_t handle, int func, int ret)
-{
-    error_info_t err = { func, ret };
-    CardServices(ReportError, handle, &err);
-}
-
-#ifdef STRUCT_CHECK
-/*------------------------------------------------------------------*/
-/*
- * Sanity routine to verify the sizes of the various WaveLAN interface
- * structures.
- */
-static char *
-wv_structuct_check(void)
-{
-#define	SC(t,s,n)	if (sizeof(t) != s) return(n);
-
-  SC(psa_t, PSA_SIZE, "psa_t");
-  SC(mmw_t, MMW_SIZE, "mmw_t");
-  SC(mmr_t, MMR_SIZE, "mmr_t");
-
-#undef	SC
-
-  return((char *) NULL);
-} /* wv_structuct_check */
-#endif	/* STRUCT_CHECK */
-
-/******************* MODEM MANAGEMENT SUBROUTINES *******************/
-/*
- * Useful subroutines to manage the modem of the wavelan
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read from card's Host Adaptor Status Register.
- */
-static inline u_char
-hasr_read(u_long	base)
-{
-  return(inb(HASR(base)));
-} /* hasr_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register.
- */
-static inline void
-hacr_write(u_long	base,
-	   u_char	hacr)
-{
-  outb(hacr, HACR(base));
-} /* hacr_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register. Include a delay for
- * those times when it is needed.
- */
-static inline void
-hacr_write_slow(u_long	base,
-		u_char	hacr)
-{
-  hacr_write(base, hacr);
-  /* delay might only be needed sometimes */
-  mdelay(1);
-} /* hacr_write_slow */
-
-/*------------------------------------------------------------------*/
-/*
- * Read the Parameter Storage Area from the WaveLAN card's memory
- */
-static void
-psa_read(device *	dev,
-	 int		o,	/* offset in PSA */
-	 u_char *	b,	/* buffer to fill */
-	 int		n)	/* size to read */
-{
-  u_char *	ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1);
-
-  while(n-- > 0)
-    {
-      *b++ = readb(ptr);
-      /* Due to a lack of address decode pins, the WaveLAN PCMCIA card
-       * only supports reading even memory addresses. That means the
-       * increment here MUST be two.
-       * Because of that, we can't use memcpy_fromio()...
-       */
-      ptr += 2;
-    }
-} /* psa_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write the Paramter Storage Area to the WaveLAN card's memory
- */
-static void
-psa_write(device *	dev,
-	  int		o,	/* Offset in psa */
-	  u_char *	b,	/* Buffer in memory */
-	  int		n)	/* Length of buffer */
-{
-  u_char *	ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1);
-  int		count = 0;
-  ioaddr_t	base = dev->base_addr;
-  /* As there seem to have no flag PSA_BUSY as in the ISA model, we are
-   * oblige to verify this address to know when the PSA is ready... */
-  volatile u_char *	verify = ((u_char *) dev->mem_start) + PSA_ADDR +
-    (psaoff(0, psa_comp_number) << 1);
-
-  /* Authorize writting to PSA */
-  hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN);
-
-  while(n-- > 0)
-    {
-      /* write to PSA */
-      writeb(*b++, ptr);
-      ptr += 2;
-
-      /* I don't have the spec, so I don't know what the correct
-       * sequence to write is. This hack seem to work for me... */
-      count = 0;
-      while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100))
-	mdelay(1);
-    }
-
-  /* Put the host interface back in standard state */
-  hacr_write(base, HACR_DEFAULT);
-} /* psa_write */
-
-#ifdef SET_PSA_CRC
-/*------------------------------------------------------------------*/
-/*
- * Calculate the PSA CRC
- * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
- * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA)
- *
- * The Windows drivers don't use the CRC, but the AP and the PtP tool
- * depend on it.
- */
-static u_short
-psa_crc(unsigned char *	psa,	/* The PSA */
-	int		size)	/* Number of short for CRC */
-{
-  int		byte_cnt;	/* Loop on the PSA */
-  u_short	crc_bytes = 0;	/* Data in the PSA */
-  int		bit_cnt;	/* Loop on the bits of the short */
-
-  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
-    {
-      crc_bytes ^= psa[byte_cnt];	/* Its an xor */
-
-      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
-	{
-	  if(crc_bytes & 0x0001)
-	    crc_bytes = (crc_bytes >> 1) ^ 0xA001;
-	  else
-	    crc_bytes >>= 1 ;
-        }
-    }
-
-  return crc_bytes;
-} /* psa_crc */
-#endif	/* SET_PSA_CRC */
-
-/*------------------------------------------------------------------*/
-/*
- * update the checksum field in the Wavelan's PSA
- */
-static void
-update_psa_checksum(device *	dev)
-{
-#ifdef SET_PSA_CRC
-  psa_t		psa;
-  u_short	crc;
-
-  /* read the parameter storage area */
-  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
-  /* update the checksum */
-  crc = psa_crc((unsigned char *) &psa,
-		sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
-		- sizeof(psa.psa_crc_status));
-
-  psa.psa_crc[0] = crc & 0xFF;
-  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
-
-  /* Write it ! */
-  psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
-	    (unsigned char *)&psa.psa_crc, 2);
-
-#ifdef DEBUG_IOCTL_INFO
-  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
-          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
-
-  /* Check again (luxury !) */
-  crc = psa_crc((unsigned char *) &psa,
-		 sizeof(psa) - sizeof(psa.psa_crc_status));
-
-  if(crc != 0)
-    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
-#endif /* DEBUG_IOCTL_INFO */
-#endif	/* SET_PSA_CRC */
-} /* update_psa_checksum */
-
-/*------------------------------------------------------------------*/
-/*
- * Write 1 byte to the MMC.
- */
-static inline void
-mmc_out(u_long		base,
-	u_short		o,
-	u_char		d)
-{
-  /* Wait for MMC to go idle */
-  while(inb(HASR(base)) & HASR_MMI_BUSY)
-    ;
-
-  outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
-  outb(d, MMD(base));
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to write bytes to the Modem Management Controller.
- * We start by the end because it is the way it should be !
- */
-static inline void
-mmc_write(u_long	base,
-	  u_char	o,
-	  u_char *	b,
-	  int		n)
-{
-  o += n;
-  b += n;
-
-  while(n-- > 0 )
-    mmc_out(base, --o, *(--b));
-} /* mmc_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Read 1 byte from the MMC.
- * Optimised version for 1 byte, avoid using memory...
- */
-static inline u_char
-mmc_in(u_long	base,
-       u_short	o)
-{
-  while(inb(HASR(base)) & HASR_MMI_BUSY)
-    ;
-  outb(o << 1, MMR(base));		/* Set the read address */
-
-  outb(0, MMD(base));			/* Required dummy write */
-
-  while(inb(HASR(base)) & HASR_MMI_BUSY)
-    ;
-  return (u_char) (inb(MMD(base)));	/* Now do the actual read */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read bytes from the Modem Management Controller.
- * The implementation is complicated by a lack of address lines,
- * which prevents decoding of the low-order bit.
- * (code has just been moved in the above function)
- * We start by the end because it is the way it should be !
- */
-static inline void
-mmc_read(u_long		base,
-	 u_char		o,
-	 u_char *	b,
-	 int		n)
-{
-  o += n;
-  b += n;
-
-  while(n-- > 0)
-    *(--b) = mmc_in(base, --o);
-} /* mmc_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the type of encryption available...
- */
-static inline int
-mmc_encr(u_long		base)	/* i/o port of the card */
-{
-  int	temp;
-
-  temp = mmc_in(base, mmroff(0, mmr_des_avail));
-  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
-    return 0;
-  else
-    return temp;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wait for the frequency EEprom to complete a command...
- * I hope this one will be optimally inlined...
- */
-static inline void
-fee_wait(u_long		base,	/* i/o port of the card */
-	 int		delay,	/* Base delay to wait for */
-	 int		number)	/* Number of time to wait */
-{
-  int		count = 0;	/* Wait only a limited time */
-
-  while((count++ < number) &&
-	(mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
-    udelay(delay);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the Frequency EEprom (frequency select cards).
- */
-static void
-fee_read(u_long		base,	/* i/o port of the card */
-	 u_short	o,	/* destination offset */
-	 u_short *	b,	/* data buffer */
-	 int		n)	/* number of registers */
-{
-  b += n;		/* Position at the end of the area */
-
-  /* Write the address */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
-
-  /* Loop on all buffer */
-  while(n-- > 0)
-    {
-      /* Write the read command */
-      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
-
-      /* Wait until EEprom is ready (should be quick !) */
-      fee_wait(base, 10, 100);
-
-      /* Read the value */
-      *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
-	      mmc_in(base, mmroff(0, mmr_fee_data_l)));
-    }
-}
-
-#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes from the Frequency EEprom (frequency select cards).
- * This is a bit complicated, because the frequency eeprom has to
- * be unprotected and the write enabled.
- * Jean II
- */
-static void
-fee_write(u_long	base,	/* i/o port of the card */
-	  u_short	o,	/* destination offset */
-	  u_short *	b,	/* data buffer */
-	  int		n)	/* number of registers */
-{
-  b += n;		/* Position at the end of the area */
-
-#ifdef EEPROM_IS_PROTECTED	/* disabled */
-#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
-  /* Ask to read the protected register */
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
-
-  fee_wait(base, 10, 100);
-
-  /* Read the protected register */
-  printk("Protected 2 : %02X-%02X\n",
-	 mmc_in(base, mmroff(0, mmr_fee_data_h)),
-	 mmc_in(base, mmroff(0, mmr_fee_data_l)));
-#endif	/* DOESNT_SEEM_TO_WORK */
-
-  /* Enable protected register */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
-
-  fee_wait(base, 10, 100);
-
-  /* Unprotect area */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
-  /* Or use : */
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
-#endif	/* DOESNT_SEEM_TO_WORK */
-
-  fee_wait(base, 10, 100);
-#endif	/* EEPROM_IS_PROTECTED */
-
-  /* Write enable */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
-
-  fee_wait(base, 10, 100);
-
-  /* Write the EEprom address */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
-
-  /* Loop on all buffer */
-  while(n-- > 0)
-    {
-      /* Write the value */
-      mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
-      mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
-
-      /* Write the write command */
-      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
-
-      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
-      mdelay(10);
-      fee_wait(base, 10, 100);
-    }
-
-  /* Write disable */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
-
-  fee_wait(base, 10, 100);
-
-#ifdef EEPROM_IS_PROTECTED	/* disabled */
-  /* Reprotect EEprom */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-
-  fee_wait(base, 10, 100);
-#endif	/* EEPROM_IS_PROTECTED */
-}
-#endif	/* WIRELESS_EXT */
-
-/******************* WaveLAN Roaming routines... ********************/
-
-#ifdef WAVELAN_ROAMING	/* Conditional compile, see wavelan_cs.h */
-
-unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
-  
-void wv_roam_init(struct net_device *dev)
-{
-  net_local  *lp= (net_local *)dev->priv;
-
-  /* Do not remove this unless you have a good reason */
-  printk(KERN_NOTICE "%s: Warning, you have enabled roaming on"
-	 " device %s !\n", dev->name, dev->name);
-  printk(KERN_NOTICE "Roaming is currently an experimental unsupported feature"
-	 " of the Wavelan driver.\n");
-  printk(KERN_NOTICE "It may work, but may also make the driver behave in"
-	 " erratic ways or crash.\n");
-
-  lp->wavepoint_table.head=NULL;           /* Initialise WavePoint table */
-  lp->wavepoint_table.num_wavepoints=0;
-  lp->wavepoint_table.locked=0;
-  lp->curr_point=NULL;                        /* No default WavePoint */
-  lp->cell_search=0;
-  
-  lp->cell_timer.data=(long)lp;               /* Start cell expiry timer */
-  lp->cell_timer.function=wl_cell_expiry;
-  lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
-  add_timer(&lp->cell_timer);
-  
-  wv_nwid_filter(NWID_PROMISC,lp) ;    /* Enter NWID promiscuous mode */
-  /* to build up a good WavePoint */
-                                           /* table... */
-  printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
-}
- 
-void wv_roam_cleanup(struct net_device *dev)
-{
-  wavepoint_history *ptr,*old_ptr;
-  net_local *lp= (net_local *)dev->priv;
-  
-  printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name);
-  
-  /* Fixme : maybe we should check that the timer exist before deleting it */
-  del_timer(&lp->cell_timer);          /* Remove cell expiry timer       */
-  ptr=lp->wavepoint_table.head;        /* Clear device's WavePoint table */
-  while(ptr!=NULL)
-    {
-      old_ptr=ptr;
-      ptr=ptr->next;	
-      wl_del_wavepoint(old_ptr,lp);	
-    }
-}
-
-/* Enable/Disable NWID promiscuous mode on a given device */
-void wv_nwid_filter(unsigned char mode, net_local *lp)
-{
-  mm_t                  m;
-  unsigned long         flags;
-  
-#ifdef WAVELAN_ROAMING_DEBUG
-  printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name);
-#endif
-  
-  /* Disable interrupts & save flags */
-  wv_splhi(lp, &flags);
-  
-  m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00;
-  mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1);
-  
-  if(mode==NWID_PROMISC)
-    lp->cell_search=1;
-  else
-    lp->cell_search=0;
-
-  /* ReEnable interrupts & restore flags */
-  wv_splx(lp, &flags);
-}
-
-/* Find a record in the WavePoint table matching a given NWID */
-wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
-{
-  wavepoint_history	*ptr=lp->wavepoint_table.head;
-  
-  while(ptr!=NULL){
-    if(ptr->nwid==nwid)
-      return ptr;	
-    ptr=ptr->next;
-  }
-  return NULL;
-}
-
-/* Create a new wavepoint table entry */
-wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
-{
-  wavepoint_history *new_wavepoint;
-
-#ifdef WAVELAN_ROAMING_DEBUG	
-  printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid);
-#endif
-  
-  if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS)
-    return NULL;
-  
-  new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
-  if(new_wavepoint==NULL)
-    return NULL;
-  
-  new_wavepoint->nwid=nwid;                       /* New WavePoints NWID */
-  new_wavepoint->average_fast=0;                    /* Running Averages..*/
-  new_wavepoint->average_slow=0;
-  new_wavepoint->qualptr=0;                       /* Start of ringbuffer */
-  new_wavepoint->last_seq=seq-1;                /* Last sequence no.seen */
-  memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */
-  
-  new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */
-  new_wavepoint->prev=NULL;
-  
-  if(lp->wavepoint_table.head!=NULL)
-    lp->wavepoint_table.head->prev=new_wavepoint;
-  
-  lp->wavepoint_table.head=new_wavepoint;
-  
-  lp->wavepoint_table.num_wavepoints++;     /* no. of visible wavepoints */
-  
-  return new_wavepoint;
-}
-
-/* Remove a wavepoint entry from WavePoint table */
-void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
-{
-  if(wavepoint==NULL)
-    return;
-  
-  if(lp->curr_point==wavepoint)
-    lp->curr_point=NULL;
-  
-  if(wavepoint->prev!=NULL)
-    wavepoint->prev->next=wavepoint->next;
-  
-  if(wavepoint->next!=NULL)
-    wavepoint->next->prev=wavepoint->prev;
-  
-  if(lp->wavepoint_table.head==wavepoint)
-    lp->wavepoint_table.head=wavepoint->next;
-  
-  lp->wavepoint_table.num_wavepoints--;
-  kfree(wavepoint);
-}
-
-/* Timer callback function - checks WavePoint table for stale entries */ 
-void wl_cell_expiry(unsigned long data)
-{
-  net_local *lp=(net_local *)data;
-  wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
-  
-#if WAVELAN_ROAMING_DEBUG > 1
-  printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name);
-#endif
-  
-  if(lp->wavepoint_table.locked)
-    {
-#if WAVELAN_ROAMING_DEBUG > 1
-      printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n");
-#endif
-      
-      lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */
-      add_timer(&lp->cell_timer);
-      return;
-    }
-  
-  while(wavepoint!=NULL)
-    {
-      if(wavepoint->last_seen < jiffies-CELL_TIMEOUT)
-	{
-#ifdef WAVELAN_ROAMING_DEBUG
-	  printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid);
-#endif
-	  
-	  old_point=wavepoint;
-	  wavepoint=wavepoint->next;
-	  wl_del_wavepoint(old_point,lp);
-	}
-      else
-	wavepoint=wavepoint->next;
-    }
-  lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
-  add_timer(&lp->cell_timer);
-}
-
-/* Update SNR history of a wavepoint */
-void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)	
-{
-  int i=0,num_missed=0,ptr=0;
-  int average_fast=0,average_slow=0;
-  
-  num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed
-							    any beacons? */
-  if(num_missed)
-    for(i=0;i<num_missed;i++)
-      {
-	wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
-	wavepoint->qualptr %=WAVEPOINT_HISTORY;    /* in the ringbuffer. */
-      }
-  wavepoint->last_seen=jiffies;                 /* Add beacon to history */
-  wavepoint->last_seq=seq;	
-  wavepoint->sigqual[wavepoint->qualptr++]=sigqual;          
-  wavepoint->qualptr %=WAVEPOINT_HISTORY;
-  ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
-  
-  for(i=0;i<WAVEPOINT_FAST_HISTORY;i++)       /* Update running averages */
-    {
-      average_fast+=wavepoint->sigqual[ptr++];
-      ptr %=WAVEPOINT_HISTORY;
-    }
-  
-  average_slow=average_fast;
-  for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
-    {
-      average_slow+=wavepoint->sigqual[ptr++];
-      ptr %=WAVEPOINT_HISTORY;
-    }
-  
-  wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
-  wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;	
-}
-
-/* Perform a handover to a new WavePoint */
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
-{
-  ioaddr_t              base = lp->dev->base_addr;  
-  mm_t                  m;
-  unsigned long         flags;
-
-  if(wavepoint==lp->curr_point)          /* Sanity check... */
-    {
-      wv_nwid_filter(!NWID_PROMISC,lp);
-      return;
-    }
-  
-#ifdef WAVELAN_ROAMING_DEBUG
-  printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name);
-#endif
- 	
-  /* Disable interrupts & save flags */
-  wv_splhi(lp, &flags);
-
-  m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF;
-  m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8;
-  
-  mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2);
-  
-  /* ReEnable interrupts & restore flags */
-  wv_splx(lp, &flags);
-
-  wv_nwid_filter(!NWID_PROMISC,lp);
-  lp->curr_point=wavepoint;
-}
-
-/* Called when a WavePoint beacon is received */
-static inline void wl_roam_gather(device *  dev,
-				  u_char *  hdr,   /* Beacon header */
-				  u_char *  stats) /* SNR, Signal quality 
-						      of packet */
-{
-  wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */
-  unsigned short nwid=ntohs(beacon->nwid);  
-  unsigned short sigqual=stats[2] & MMR_SGNL_QUAL;   /* SNR of beacon */
-  wavepoint_history *wavepoint=NULL;                /* WavePoint table entry */
-  net_local *lp=(net_local *)dev->priv;              /* Device info */
-
-#if 0
-  /* Some people don't need this, some other may need it */
-  nwid=nwid^ntohs(beacon->domain_id);
-#endif
-
-#if WAVELAN_ROAMING_DEBUG > 1
-  printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name);
-  printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual);
-#endif
-  
-  lp->wavepoint_table.locked=1;                            /* <Mutex> */
-  
-  wavepoint=wl_roam_check(nwid,lp);            /* Find WavePoint table entry */
-  if(wavepoint==NULL)                    /* If no entry, Create a new one... */
-    {
-      wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
-      if(wavepoint==NULL)
-	goto out;
-    }
-  if(lp->curr_point==NULL)             /* If this is the only WavePoint, */
-    wv_roam_handover(wavepoint, lp);	         /* Jump on it! */
-  
-  wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
-							 stats. */
-  
-  if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
-    if(!lp->cell_search)                  /* WavePoint is getting faint, */
-      wv_nwid_filter(NWID_PROMISC,lp);    /* start looking for a new one */
-  
-  if(wavepoint->average_slow > 
-     lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
-    wv_roam_handover(wavepoint, lp);   /* Handover to a better WavePoint */
-  
-  if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
-    if(lp->cell_search)  /* getting better, drop out of cell search mode */
-      wv_nwid_filter(!NWID_PROMISC,lp);
-  
-out:
-  lp->wavepoint_table.locked=0;                        /* </MUTEX>   :-) */
-}
-
-/* Test this MAC frame a WavePoint beacon */
-static inline int WAVELAN_BEACON(unsigned char *data)
-{
-  wavepoint_beacon *beacon= (wavepoint_beacon *)data;
-  static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
-  
-  if(memcmp(beacon,&beacon_template,9)==0)
-    return 1;
-  else
-    return 0;
-}
-#endif	/* WAVELAN_ROAMING */
-
-/************************ I82593 SUBROUTINES *************************/
-/*
- * Useful subroutines to manage the Ethernet controller
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to synchronously send a command to the i82593 chip. 
- * Should be called with interrupts disabled.
- * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(),
- *  wv_82593_config() & wv_diag())
- */
-static int
-wv_82593_cmd(device *	dev,
-	     char *	str,
-	     int	cmd,
-	     int	result)
-{
-  ioaddr_t	base = dev->base_addr;
-  int		status;
-  int		wait_completed;
-  long		spin;
-
-  /* Spin until the chip finishes executing its current command (if any) */
-  spin = 1000;
-  do
-    {
-      /* Time calibration of the loop */
-      udelay(10);
-
-      /* Read the interrupt register */
-      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
-      status = inb(LCSR(base));
-    }
-  while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
-
-  /* If the interrupt hasn't be posted */
-  if(spin <= 0)
-    {
-#ifdef DEBUG_INTERRUPT_ERROR
-      printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n",
-	     str, status);
-#endif
-      return(FALSE);
-    }
-
-  /* Issue the command to the controller */
-  outb(cmd, LCCR(base));
-
-  /* If we don't have to check the result of the command
-   * Note : this mean that the irq handler will deal with that */
-  if(result == SR0_NO_RESULT)
-    return(TRUE);
-
-  /* We are waiting for command completion */
-  wait_completed = TRUE;
-
-  /* Busy wait while the LAN controller executes the command. */
-  spin = 1000;
-  do
-    {
-      /* Time calibration of the loop */
-      udelay(10);
-
-      /* Read the interrupt register */
-      outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
-      status = inb(LCSR(base));
-
-      /* Check if there was an interrupt posted */
-      if((status & SR0_INTERRUPT))
-	{
-	  /* Acknowledge the interrupt */
-	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
-
-	  /* Check if interrupt is a command completion */
-	  if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) &&
-	     ((status & SR0_BOTH_RX_TX) != 0x0) &&
-	     !(status & SR0_RECEPTION))
-	    {
-	      /* Signal command completion */
-	      wait_completed = FALSE;
-	    }
-	  else
-	    {
-	      /* Note : Rx interrupts will be handled later, because we can
-	       * handle multiple Rx packets at once */
-#ifdef DEBUG_INTERRUPT_INFO
-	      printk(KERN_INFO "wv_82593_cmd: not our interrupt\n");
-#endif
-	    }
-	}
-    }
-  while(wait_completed && (spin-- > 0));
-
-  /* If the interrupt hasn't be posted */
-  if(wait_completed)
-    {
-#ifdef DEBUG_INTERRUPT_ERROR
-      printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n",
-	     str, status);
-#endif
-      return(FALSE);
-    }
-
-  /* Check the return code returned by the card (see above) against
-   * the expected return code provided by the caller */
-  if((status & SR0_EVENT_MASK) != result)
-    {
-#ifdef DEBUG_INTERRUPT_ERROR
-      printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n",
-	     str, status);
-#endif
-      return(FALSE);
-    }
-
-  return(TRUE);
-} /* wv_82593_cmd */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a 593 op-code number 7, and obtains the diagnose
- * status for the WaveLAN.
- */
-static inline int
-wv_diag(device *	dev)
-{
-  int		ret = FALSE;
-
-  if(wv_82593_cmd(dev, "wv_diag(): diagnose",
-		  OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
-    ret = TRUE;
-
-#ifdef DEBUG_CONFIG_ERROR
-  printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
-#endif
-  return(ret);
-} /* wv_diag */
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read len bytes from the i82593's ring buffer, starting at
- * chip address addr. The results read from the chip are stored in buf.
- * The return value is the address to use for next the call.
- */
-static int
-read_ringbuf(device *	dev,
-	     int	addr,
-	     char *	buf,
-	     int	len)
-{
-  ioaddr_t	base = dev->base_addr;
-  int		ring_ptr = addr;
-  int		chunk_len;
-  char *	buf_ptr = buf;
-
-  /* Get all the buffer */
-  while(len > 0)
-    {
-      /* Position the Program I/O Register at the ring buffer pointer */
-      outb(ring_ptr & 0xff, PIORL(base));
-      outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base));
-
-      /* First, determine how much we can read without wrapping around the
-	 ring buffer */
-      if((addr + len) < (RX_BASE + RX_SIZE))
-	chunk_len = len;
-      else
-	chunk_len = RX_BASE + RX_SIZE - addr;
-      insb(PIOP(base), buf_ptr, chunk_len);
-      buf_ptr += chunk_len;
-      len -= chunk_len;
-      ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE;
-    }
-  return(ring_ptr);
-} /* read_ringbuf */
-
-/*------------------------------------------------------------------*/
-/*
- * Reconfigure the i82593, or at least ask for it...
- * Because wv_82593_config use the transmission buffer, we must do it
- * when we are sure that there is no transmission, so we do it now
- * or in wavelan_packet_xmit() (I can't find any better place,
- * wavelan_interrupt is not an option...), so you may experience
- * some delay sometime...
- */
-static inline void
-wv_82593_reconfig(device *	dev)
-{
-  net_local *		lp = (net_local *)dev->priv;
-  dev_link_t *		link = ((net_local *) dev->priv)->link;
-  unsigned long		flags;
-
-  /* Arm the flag, will be cleard in wv_82593_config() */
-  lp->reconfig_82593 = TRUE;
-
-  /* Check if we can do it now ! */
-  if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev)))
-    {
-      wv_splhi(lp, &flags);	/* Disable interrupts */
-      wv_82593_config(dev);
-      wv_splx(lp, &flags);	/* Re-enable interrupts */
-    }
-  else
-    {
-#ifdef DEBUG_IOCTL_INFO
-      printk(KERN_DEBUG
-	     "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n",
-	     dev->name, dev->state, link->open);
-#endif
-    }
-}
-
-/********************* DEBUG & INFO SUBROUTINES *********************/
-/*
- * This routines are used in the code to show debug informations.
- * Most of the time, it dump the content of hardware structures...
- */
-
-#ifdef DEBUG_PSA_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted contents of the Parameter Storage Area.
- */
-static void
-wv_psa_show(psa_t *	p)
-{
-  printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
-  printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
-	 p->psa_io_base_addr_1,
-	 p->psa_io_base_addr_2,
-	 p->psa_io_base_addr_3,
-	 p->psa_io_base_addr_4);
-  printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
-	 p->psa_rem_boot_addr_1,
-	 p->psa_rem_boot_addr_2,
-	 p->psa_rem_boot_addr_3);
-  printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
-  printk("psa_int_req_no: %d\n", p->psa_int_req_no);
-#ifdef DEBUG_SHOW_UNUSED
-  printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
-	 p->psa_unused0[0],
-	 p->psa_unused0[1],
-	 p->psa_unused0[2],
-	 p->psa_unused0[3],
-	 p->psa_unused0[4],
-	 p->psa_unused0[5],
-	 p->psa_unused0[6]);
-#endif	/* DEBUG_SHOW_UNUSED */
-  printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
-	 p->psa_univ_mac_addr[0],
-	 p->psa_univ_mac_addr[1],
-	 p->psa_univ_mac_addr[2],
-	 p->psa_univ_mac_addr[3],
-	 p->psa_univ_mac_addr[4],
-	 p->psa_univ_mac_addr[5]);
-  printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
-	 p->psa_local_mac_addr[0],
-	 p->psa_local_mac_addr[1],
-	 p->psa_local_mac_addr[2],
-	 p->psa_local_mac_addr[3],
-	 p->psa_local_mac_addr[4],
-	 p->psa_local_mac_addr[5]);
-  printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
-  printk("psa_comp_number: %d, ", p->psa_comp_number);
-  printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
-  printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
-	 p->psa_feature_select);
-  printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
-  printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
-  printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
-  printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
-  printk("psa_nwid_select: %d\n", p->psa_nwid_select);
-  printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
-  printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
-	 p->psa_encryption_key[0],
-	 p->psa_encryption_key[1],
-	 p->psa_encryption_key[2],
-	 p->psa_encryption_key[3],
-	 p->psa_encryption_key[4],
-	 p->psa_encryption_key[5],
-	 p->psa_encryption_key[6],
-	 p->psa_encryption_key[7]);
-  printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
-  printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
-	 p->psa_call_code[0]);
-  printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
-	 p->psa_call_code[0],
-	 p->psa_call_code[1],
-	 p->psa_call_code[2],
-	 p->psa_call_code[3],
-	 p->psa_call_code[4],
-	 p->psa_call_code[5],
-	 p->psa_call_code[6],
-	 p->psa_call_code[7]);
-#ifdef DEBUG_SHOW_UNUSED
-  printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
-	 p->psa_reserved[0],
-	 p->psa_reserved[1],
-	 p->psa_reserved[2],
-	 p->psa_reserved[3]);
-#endif	/* DEBUG_SHOW_UNUSED */
-  printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
-  printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
-  printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
-} /* wv_psa_show */
-#endif	/* DEBUG_PSA_SHOW */
-
-#ifdef DEBUG_MMC_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the Modem Management Controller.
- * This function need to be completed...
- */
-static void
-wv_mmc_show(device *	dev)
-{
-  ioaddr_t	base = dev->base_addr;
-  net_local *	lp = (net_local *)dev->priv;
-  mmr_t		m;
-
-  /* Basic check */
-  if(hasr_read(base) & HASR_NO_CLK)
-    {
-      printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
-	     dev->name);
-      return;
-    }
-
-  wv_splhi(lp, &flags);
-
-  /* Read the mmc */
-  mmc_out(base, mmwoff(0, mmw_freeze), 1);
-  mmc_read(base, 0, (u_char *)&m, sizeof(m));
-  mmc_out(base, mmwoff(0, mmw_freeze), 0);
-
-#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
-  /* Don't forget to update statistics */
-  lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-#endif	/* WIRELESS_EXT */
-
-  wv_splx(lp, &flags);
-
-  printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
-#ifdef DEBUG_SHOW_UNUSED
-  printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
-	 m.mmr_unused0[0],
-	 m.mmr_unused0[1],
-	 m.mmr_unused0[2],
-	 m.mmr_unused0[3],
-	 m.mmr_unused0[4],
-	 m.mmr_unused0[5],
-	 m.mmr_unused0[6],
-	 m.mmr_unused0[7]);
-#endif	/* DEBUG_SHOW_UNUSED */
-  printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
-	 m.mmr_des_avail, m.mmr_des_status);
-#ifdef DEBUG_SHOW_UNUSED
-  printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
-	 m.mmr_unused1[0],
-	 m.mmr_unused1[1],
-	 m.mmr_unused1[2],
-	 m.mmr_unused1[3],
-	 m.mmr_unused1[4]);
-#endif	/* DEBUG_SHOW_UNUSED */
-  printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
-	 m.mmr_dce_status,
-	 (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
-	 (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
-	 "loop test indicated," : "",
-	 (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
-	 (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
-	 "jabber timer expired," : "");
-  printk(KERN_DEBUG "Dsp ID: %02X\n",
-	 m.mmr_dsp_id);
-#ifdef DEBUG_SHOW_UNUSED
-  printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
-	 m.mmr_unused2[0],
-	 m.mmr_unused2[1]);
-#endif	/* DEBUG_SHOW_UNUSED */
-  printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
-	 (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
-	 (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
-  printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
-	 m.mmr_thr_pre_set & MMR_THR_PRE_SET,
-	 (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
-  printk(KERN_DEBUG "signal_lvl: %d [%s], ",
-	 m.mmr_signal_lvl & MMR_SIGNAL_LVL,
-	 (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
-  printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
-	 (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
-  printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
-	 (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
-#ifdef DEBUG_SHOW_UNUSED
-  printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
-#endif	/* DEBUG_SHOW_UNUSED */
-} /* wv_mmc_show */
-#endif	/* DEBUG_MMC_SHOW */
-
-#ifdef DEBUG_I82593_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the i82593's receive unit.
- */
-static void
-wv_ru_show(device *	dev)
-{
-  net_local *lp = (net_local *) dev->priv;
-
-  printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n");
-  printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop);
-  /*
-   * Not implemented yet...
-   */
-  printk("\n");
-} /* wv_ru_show */
-#endif	/* DEBUG_I82593_SHOW */
-
-#ifdef DEBUG_DEVICE_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver.
- */
-static void
-wv_dev_show(device *	dev)
-{
-  printk(KERN_DEBUG "dev:");
-  printk(" state=%lX,", dev->state);
-  printk(" trans_start=%ld,", dev->trans_start);
-  printk(" flags=0x%x,", dev->flags);
-  printk("\n");
-} /* wv_dev_show */
-
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver's
- * private information.
- */
-static void
-wv_local_show(device *	dev)
-{
-  net_local *lp;
-
-  lp = (net_local *)dev->priv;
-
-  printk(KERN_DEBUG "local:");
-  /*
-   * Not implemented yet...
-   */
-  printk("\n");
-} /* wv_local_show */
-#endif	/* DEBUG_DEVICE_SHOW */
-
-#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
-/*------------------------------------------------------------------*/
-/*
- * Dump packet header (and content if necessary) on the screen
- */
-static inline void
-wv_packet_info(u_char *		p,		/* Packet to dump */
-	       int		length,		/* Length of the packet */
-	       char *		msg1,		/* Name of the device */
-	       char *		msg2)		/* Name of the function */
-{
-  int		i;
-  int		maxi;
-
-  printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
-	 msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
-  printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
-	 msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
-
-#ifdef DEBUG_PACKET_DUMP
-
-  printk(KERN_DEBUG "data=\"");
-
-  if((maxi = length) > DEBUG_PACKET_DUMP)
-    maxi = DEBUG_PACKET_DUMP;
-  for(i = 14; i < maxi; i++)
-    if(p[i] >= ' ' && p[i] <= '~')
-      printk(" %c", p[i]);
-    else
-      printk("%02X", p[i]);
-  if(maxi < length)
-    printk("..");
-  printk("\"\n");
-  printk(KERN_DEBUG "\n");
-#endif	/* DEBUG_PACKET_DUMP */
-}
-#endif	/* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
-
-/*------------------------------------------------------------------*/
-/*
- * This is the information which is displayed by the driver at startup
- * There  is a lot of flag to configure it at your will...
- */
-static inline void
-wv_init_info(device *	dev)
-{
-  ioaddr_t	base = dev->base_addr;
-  psa_t		psa;
-  int		i;
-
-  /* Read the parameter storage area */
-  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
-#ifdef DEBUG_PSA_SHOW
-  wv_psa_show(&psa);
-#endif
-#ifdef DEBUG_MMC_SHOW
-  wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82593_SHOW
-  wv_ru_show(dev);
-#endif
-
-#ifdef DEBUG_BASIC_SHOW
-  /* Now, let's go for the basic stuff */
-  printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr",
-	 dev->name, base, dev->irq);
-  for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
-    printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
-
-  /* Print current network id */
-  if(psa.psa_nwid_select)
-    printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
-  else
-    printk(", nwid off");
-
-  /* If 2.00 card */
-  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
-       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
-    {
-      unsigned short	freq;
-
-      /* Ask the EEprom to read the frequency from the first area */
-      fee_read(base, 0x00 /* 1st area - frequency... */,
-	       &freq, 1);
-
-      /* Print frequency */
-      printk(", 2.00, %ld", (freq >> 6) + 2400L);
-
-      /* Hack !!! */
-      if(freq & 0x20)
-	printk(".5");
-    }
-  else
-    {
-      printk(", PCMCIA, ");
-      switch (psa.psa_subband)
-	{
-	case PSA_SUBBAND_915:
-	  printk("915");
-	  break;
-	case PSA_SUBBAND_2425:
-	  printk("2425");
-	  break;
-	case PSA_SUBBAND_2460:
-	  printk("2460");
-	  break;
-	case PSA_SUBBAND_2484:
-	  printk("2484");
-	  break;
-	case PSA_SUBBAND_2430_5:
-	  printk("2430.5");
-	  break;
-	default:
-	  printk("???");
-	}
-    }
-
-  printk(" MHz\n");
-#endif	/* DEBUG_BASIC_SHOW */
-
-#ifdef DEBUG_VERSION_SHOW
-  /* Print version information */
-  printk(KERN_NOTICE "%s", version);
-#endif
-} /* wv_init_info */
-
-/********************* IOCTL, STATS & RECONFIG *********************/
-/*
- * We found here routines that are called by Linux on differents
- * occasions after the configuration and not for transmitting data
- * These may be called when the user use ifconfig, /proc/net/dev
- * or wireless extensions
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the current ethernet statistics. This may be called with the
- * card open or closed.
- * Used when the user read /proc/net/dev
- */
-static en_stats	*
-wavelan_get_stats(device *	dev)
-{
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
-#endif
-
-  return(&((net_local *) dev->priv)->stats);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Set or clear the multicast filter for this adaptor.
- * num_addrs == -1	Promiscuous mode, receive all packets
- * num_addrs == 0	Normal mode, clear multicast list
- * num_addrs > 0	Multicast mode, receive normal and MC packets,
- *			and do best-effort filtering.
- */
-
-static void
-wavelan_set_multicast_list(device *	dev)
-{
-  net_local *	lp = (net_local *) dev->priv;
-
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
-#endif
-
-#ifdef DEBUG_IOCTL_INFO
-  printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
-	 dev->name, dev->flags, dev->mc_count);
-#endif
-
-  if(dev->flags & IFF_PROMISC)
-    {
-      /*
-       * Enable promiscuous mode: receive all packets.
-       */
-      if(!lp->promiscuous)
-	{
-	  lp->promiscuous = 1;
-	  lp->allmulticast = 0;
-	  lp->mc_count = 0;
-
-	  wv_82593_reconfig(dev);
-
-	  /* Tell the kernel that we are doing a really bad job... */
-	  dev->flags |= IFF_PROMISC;
-	}
-    }
-  else
-    /* If all multicast addresses
-     * or too much multicast addresses for the hardware filter */
-    if((dev->flags & IFF_ALLMULTI) ||
-       (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES))
-      {
-	/*
-	 * Disable promiscuous mode, but active the all multicast mode
-	 */
-	if(!lp->allmulticast)
-	  {
-	    lp->promiscuous = 0;
-	    lp->allmulticast = 1;
-	    lp->mc_count = 0;
-
-	    wv_82593_reconfig(dev);
-
-	    /* Tell the kernel that we are doing a really bad job... */
-	    dev->flags |= IFF_ALLMULTI;
-	  }
-      }
-    else
-      /* If there is some multicast addresses to send */
-      if(dev->mc_list != (struct dev_mc_list *) NULL)
-	{
-	  /*
-	   * Disable promiscuous mode, but receive all packets
-	   * in multicast list
-	   */
-#ifdef MULTICAST_AVOID
-	  if(lp->promiscuous || lp->allmulticast ||
-	     (dev->mc_count != lp->mc_count))
-#endif
-	    {
-	      lp->promiscuous = 0;
-	      lp->allmulticast = 0;
-	      lp->mc_count = dev->mc_count;
-
-	      wv_82593_reconfig(dev);
-	    }
-	}
-      else
-	{
-	  /*
-	   * Switch to normal mode: disable promiscuous mode and 
-	   * clear the multicast list.
-	   */
-	  if(lp->promiscuous || lp->mc_count == 0)
-	    {
-	      lp->promiscuous = 0;
-	      lp->allmulticast = 0;
-	      lp->mc_count = 0;
-
-	      wv_82593_reconfig(dev);
-	    }
-	}
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This function doesn't exist...
- * (Note : it was a nice way to test the reconfigure stuff...)
- */
-#ifdef SET_MAC_ADDRESS
-static int
-wavelan_set_mac_address(device *	dev,
-			void *		addr)
-{
-  struct sockaddr *	mac = addr;
-
-  /* Copy the address */
-  memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
-
-  /* Reconfig the beast */
-  wv_82593_reconfig(dev);
-
-  return 0;
-}
-#endif	/* SET_MAC_ADDRESS */
-
-#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Frequency setting (for hardware able of it)
- * It's a bit complicated and you don't really want to look into it...
- * (called in wavelan_ioctl)
- */
-static inline int
-wv_set_frequency(u_long		base,	/* i/o port of the card */
-		 iw_freq *	frequency)
-{
-  const int	BAND_NUM = 10;	/* Number of bands */
-  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz */
-#ifdef DEBUG_IOCTL_INFO
-  int		i;
-#endif
-
-  /* Setting by frequency */
-  /* Theoritically, you may set any frequency between
-   * the two limits with a 0.5 MHz precision. In practice,
-   * I don't want you to have trouble with local
-   * regulations... */
-  if((frequency->e == 1) &&
-     (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8))
-    {
-      freq = ((frequency->m / 10000) - 24000L) / 5;
-    }
-
-  /* Setting by channel (same as wfreqsel) */
-  /* Warning : each channel is 22MHz wide, so some of the channels
-   * will interfere... */
-  if((frequency->e == 0) &&
-     (frequency->m >= 0) && (frequency->m < BAND_NUM))
-    {
-      /* Get frequency offset. */
-      freq = channel_bands[frequency->m] >> 1;
-    }
-
-  /* Verify if the frequency is allowed */
-  if(freq != 0L)
-    {
-      u_short	table[10];	/* Authorized frequency table */
-
-      /* Read the frequency table */
-      fee_read(base, 0x71 /* frequency table */,
-	       table, 10);
-
-#ifdef DEBUG_IOCTL_INFO
-      printk(KERN_DEBUG "Frequency table :");
-      for(i = 0; i < 10; i++)
-	{
-	  printk(" %04X",
-		 table[i]);
-	}
-      printk("\n");
-#endif
-
-      /* Look in the table if the frequency is allowed */
-      if(!(table[9 - ((freq - 24) / 16)] &
-	   (1 << ((freq - 24) % 16))))
-	return -EINVAL;		/* not allowed */
-    }
-  else
-    return -EINVAL;
-
-  /* If we get a usable frequency */
-  if(freq != 0L)
-    {
-      unsigned short	area[16];
-      unsigned short	dac[2];
-      unsigned short	area_verify[16];
-      unsigned short	dac_verify[2];
-      /* Corresponding gain (in the power adjust value table)
-       * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
-       * & WCIN062D.DOC, page 6.2.9 */
-      unsigned short	power_limit[] = { 40, 80, 120, 160, 0 };
-      int		power_band = 0;		/* Selected band */
-      unsigned short	power_adjust;		/* Correct value */
-
-      /* Search for the gain */
-      power_band = 0;
-      while((freq > power_limit[power_band]) &&
-	    (power_limit[++power_band] != 0))
-	;
-
-      /* Read the first area */
-      fee_read(base, 0x00,
-	       area, 16);
-
-      /* Read the DAC */
-      fee_read(base, 0x60,
-	       dac, 2);
-
-      /* Read the new power adjust value */
-      fee_read(base, 0x6B - (power_band >> 1),
-	       &power_adjust, 1);
-      if(power_band & 0x1)
-	power_adjust >>= 8;
-      else
-	power_adjust &= 0xFF;
-
-#ifdef DEBUG_IOCTL_INFO
-      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
-      for(i = 0; i < 16; i++)
-	{
-	  printk(" %04X",
-		 area[i]);
-	}
-      printk("\n");
-
-      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
-	     dac[0], dac[1]);
-#endif
-
-      /* Frequency offset (for info only...) */
-      area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
-
-      /* Receiver Principle main divider coefficient */
-      area[3] = (freq >> 1) + 2400L - 352L;
-      area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
-      /* Transmitter Main divider coefficient */
-      area[13] = (freq >> 1) + 2400L;
-      area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
-      /* Others part of the area are flags, bit streams or unused... */
-
-      /* Set the value in the DAC */
-      dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
-      dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
-
-      /* Write the first area */
-      fee_write(base, 0x00,
-		area, 16);
-
-      /* Write the DAC */
-      fee_write(base, 0x60,
-		dac, 2);
-
-      /* We now should verify here that the EEprom writting was ok */
-
-      /* ReRead the first area */
-      fee_read(base, 0x00,
-	       area_verify, 16);
-
-      /* ReRead the DAC */
-      fee_read(base, 0x60,
-	       dac_verify, 2);
-
-      /* Compare */
-      if(memcmp(area, area_verify, 16 * 2) ||
-	 memcmp(dac, dac_verify, 2 * 2))
-	{
-#ifdef DEBUG_IOCTL_ERROR
-	  printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
-#endif
-	  return -EOPNOTSUPP;
-	}
-
-      /* We must download the frequency parameters to the
-       * synthetisers (from the EEprom - area 1)
-       * Note : as the EEprom is auto decremented, we set the end
-       * if the area... */
-      mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F);
-      mmc_out(base, mmwoff(0, mmw_fee_ctrl),
-	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
-      /* Wait until the download is finished */
-      fee_wait(base, 100, 100);
-
-      /* We must now download the power adjust value (gain) to
-       * the synthetisers (from the EEprom - area 7 - DAC) */
-      mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61);
-      mmc_out(base, mmwoff(0, mmw_fee_ctrl),
-	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
-      /* Wait until the download is finished */
-      fee_wait(base, 100, 100);
-
-#ifdef DEBUG_IOCTL_INFO
-      /* Verification of what we have done... */
-
-      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
-      for(i = 0; i < 16; i++)
-	{
-	  printk(" %04X",
-		 area_verify[i]);
-	}
-      printk("\n");
-
-      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
-	     dac_verify[0], dac_verify[1]);
-#endif
-
-      return 0;
-    }
-  else
-    return -EINVAL;		/* Bah, never get there... */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Give the list of available frequencies
- */
-static inline int
-wv_frequency_list(u_long	base,	/* i/o port of the card */
-		  iw_freq *	list,	/* List of frequency to fill */
-		  int		max)	/* Maximum number of frequencies */
-{
-  u_short	table[10];	/* Authorized frequency table */
-  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz + 12 MHz */
-  int		i;		/* index in the table */
-#if WIRELESS_EXT > 7
-  const int	BAND_NUM = 10;	/* Number of bands */
-  int		c = 0;		/* Channel number */
-#endif /* WIRELESS_EXT */
-
-  /* Read the frequency table */
-  fee_read(base, 0x71 /* frequency table */,
-	   table, 10);
-
-  /* Look all frequencies */
-  i = 0;
-  for(freq = 0; freq < 150; freq++)
-    /* Look in the table if the frequency is allowed */
-    if(table[9 - (freq / 16)] & (1 << (freq % 16)))
-      {
-#if WIRELESS_EXT > 7
-	/* Compute approximate channel number */
-	while((((channel_bands[c] >> 1) - 24) < freq) &&
-	      (c < BAND_NUM))
-	  c++;
-	list[i].i = c;	/* Set the list index */
-#endif /* WIRELESS_EXT */
-
-	/* put in the list */
-	list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
-	list[i++].e = 1;
-
-	/* Check number */
-	if(i >= max)
-	  return(i);
-      }
-
-  return(i);
-}
-
-#ifdef WIRELESS_SPY
-/*------------------------------------------------------------------*/
-/*
- * Gather wireless spy statistics : for each packet, compare the source
- * address with out list, and if match, get the stats...
- * Sorry, but this function really need wireless extensions...
- */
-static inline void
-wl_spy_gather(device *	dev,
-	      u_char *	mac,		/* MAC address */
-	      u_char *	stats)		/* Statistics to gather */
-{
-  net_local *	lp = (net_local *) dev->priv;
-  int		i;
-
-  /* Look all addresses */
-  for(i = 0; i < lp->spy_number; i++)
-    /* If match */
-    if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
-      {
-	/* Update statistics */
-	lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
-	lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
-	lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
-	lp->spy_stat[i].updated = 0x7;
-      }
-}
-#endif	/* WIRELESS_SPY */
-
-#ifdef HISTOGRAM
-/*------------------------------------------------------------------*/
-/*
- * This function calculate an histogram on the signal level.
- * As the noise is quite constant, it's like doing it on the SNR.
- * We have defined a set of interval (lp->his_range), and each time
- * the level goes in that interval, we increment the count (lp->his_sum).
- * With this histogram you may detect if one wavelan is really weak,
- * or you may also calculate the mean and standard deviation of the level...
- */
-static inline void
-wl_his_gather(device *	dev,
-	      u_char *	stats)		/* Statistics to gather */
-{
-  net_local *	lp = (net_local *) dev->priv;
-  u_char	level = stats[0] & MMR_SIGNAL_LVL;
-  int		i;
-
-  /* Find the correct interval */
-  i = 0;
-  while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
-    ;
-
-  /* Increment interval counter */
-  (lp->his_sum[i])++;
-}
-#endif	/* HISTOGRAM */
-
-/*------------------------------------------------------------------*/
-/*
- * Perform ioctl : config & info stuff
- * This is here that are treated the wireless extensions (iwconfig)
- */
-static int
-wavelan_ioctl(struct net_device *	dev,	/* Device on wich the ioctl apply */
-	      struct ifreq *	rq,	/* Data passed */
-	      int		cmd)	/* Ioctl number */
-{
-  ioaddr_t		base = dev->base_addr;
-  net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */
-  struct iwreq *	wrq = (struct iwreq *) rq;
-  psa_t			psa;
-  mm_t			m;
-  unsigned long		flags;
-  int			ret = 0;
-
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
-#endif
-
-  /* Disable interrupts & save flags */
-  wv_splhi(lp, &flags);
-
-  /* Look what is the request */
-  switch(cmd)
-    {
-      /* --------------- WIRELESS EXTENSIONS --------------- */
-
-    case SIOCGIWNAME:
-      strcpy(wrq->u.name, "Wavelan");
-      break;
-
-    case SIOCSIWNWID:
-      /* Set NWID in wavelan */
-#if WIRELESS_EXT > 8
-      if(!wrq->u.nwid.disabled)
-	{
-	  /* Set NWID in psa */
-	  psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
-	  psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
-#else	/* WIRELESS_EXT > 8 */
-      if(wrq->u.nwid.on)
-	{
-	  /* Set NWID in psa */
-	  psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
-	  psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
-#endif	/* WIRELESS_EXT > 8 */
-	  psa.psa_nwid_select = 0x01;
-	  psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
-		    (unsigned char *)psa.psa_nwid, 3);
-
-	  /* Set NWID in mmc */
-	  m.w.mmw_netw_id_l = psa.psa_nwid[1];
-	  m.w.mmw_netw_id_h = psa.psa_nwid[0];
-	  mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m,
-		    (unsigned char *)&m.w.mmw_netw_id_l, 2);
-	  mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00);
-	}
-      else
-	{
-	  /* Disable nwid in the psa */
-	  psa.psa_nwid_select = 0x00;
-	  psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa,
-		    (unsigned char *)&psa.psa_nwid_select, 1);
-
-	  /* Disable nwid in the mmc (no filtering) */
-	  mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
-	}
-      /* update the Wavelan checksum */
-      update_psa_checksum(dev);
-      break;
-
-    case SIOCGIWNWID:
-      /* Read the NWID */
-      psa_read(dev, (char *)psa.psa_nwid - (char *)&psa,
-	       (unsigned char *)psa.psa_nwid, 3);
-#if WIRELESS_EXT > 8
-      wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
-      wrq->u.nwid.disabled = !(psa.psa_nwid_select);
-      wrq->u.nwid.fixed = 1;	/* Superfluous */
-#else	/* WIRELESS_EXT > 8 */
-      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
-      wrq->u.nwid.on = psa.psa_nwid_select;
-#endif	/* WIRELESS_EXT > 8 */
-      break;
-
-    case SIOCSIWFREQ:
-      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
-      if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
-	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
-	ret = wv_set_frequency(base, &(wrq->u.freq));
-      else
-	ret = -EOPNOTSUPP;
-      break;
-
-    case SIOCGIWFREQ:
-      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
-       * (does it work for everybody ??? - especially old cards...) */
-      if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
-	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
-	{
-	  unsigned short	freq;
-
-	  /* Ask the EEprom to read the frequency from the first area */
-	  fee_read(base, 0x00 /* 1st area - frequency... */,
-		   &freq, 1);
-	  wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
-	  wrq->u.freq.e = 1;
-	}
-      else
-	{
-	  psa_read(dev, (char *)&psa.psa_subband - (char *)&psa,
-		   (unsigned char *)&psa.psa_subband, 1);
-
-	  if(psa.psa_subband <= 4)
-	    {
-	      wrq->u.freq.m = fixed_bands[psa.psa_subband];
-	      wrq->u.freq.e = (psa.psa_subband != 0);
-	    }
-	  else
-	    ret = -EOPNOTSUPP;
-	}
-      break;
-
-    case SIOCSIWSENS:
-      /* Set the level threshold */
-#if WIRELESS_EXT > 7
-      /* We should complain loudly if wrq->u.sens.fixed = 0, because we
-       * can't set auto mode... */
-      psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
-#else	/* WIRELESS_EXT > 7 */
-      psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
-#endif	/* WIRELESS_EXT > 7 */
-      psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
-	       (unsigned char *)&psa.psa_thr_pre_set, 1);
-      /* update the Wavelan checksum */
-      update_psa_checksum(dev);
-      mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
-      break;
-
-    case SIOCGIWSENS:
-      /* Read the level threshold */
-      psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
-	       (unsigned char *)&psa.psa_thr_pre_set, 1);
-#if WIRELESS_EXT > 7
-      wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
-      wrq->u.sens.fixed = 1;
-#else	/* WIRELESS_EXT > 7 */
-      wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
-#endif	/* WIRELESS_EXT > 7 */
-      break;
-
-#if WIRELESS_EXT > 8
-    case SIOCSIWENCODE:
-      /* Set encryption key */
-      if(!mmc_encr(base))
-	{
-	  ret = -EOPNOTSUPP;
-	  break;
-	}
-
-      /* Basic checking... */
-      if(wrq->u.encoding.pointer != (caddr_t) 0)
-	{
-	  /* Check the size of the key */
-	  if(wrq->u.encoding.length != 8)
-	    {
-	      ret = -EINVAL;
-	      break;
-	    }
-
-	  /* Copy the key in the driver */
-	  if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
-			    wrq->u.encoding.length))
-	    {
-	      ret = -EFAULT;
-	      break;
-	    }
-
-	  psa.psa_encryption_select = 1;
-	  psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
-		    (unsigned char *) &psa.psa_encryption_select, 8+1);
-
-	  mmc_out(base, mmwoff(0, mmw_encr_enable),
-		  MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
-	  mmc_write(base, mmwoff(0, mmw_encr_key),
-		    (unsigned char *) &psa.psa_encryption_key, 8);
-	}
-
-      if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
-	{	/* disable encryption */
-	  psa.psa_encryption_select = 0;
-	  psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
-		    (unsigned char *) &psa.psa_encryption_select, 1);
-
-	  mmc_out(base, mmwoff(0, mmw_encr_enable), 0);
-	}
-      /* update the Wavelan checksum */
-      update_psa_checksum(dev);
-      break;
-
-    case SIOCGIWENCODE:
-      /* Read the encryption key */
-      if(!mmc_encr(base))
-	{
-	  ret = -EOPNOTSUPP;
-	  break;
-	}
-
-      /* only super-user can see encryption key */
-      if(!capable(CAP_NET_ADMIN))
-	{
-	  ret = -EPERM;
-	  break;
-	}
-
-      /* Basic checking... */
-      if(wrq->u.encoding.pointer != (caddr_t) 0)
-	{
-	  psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
-		   (unsigned char *) &psa.psa_encryption_select, 1+8);
-
-	  /* encryption is enabled ? */
-	  if(psa.psa_encryption_select)
-	    wrq->u.encoding.flags = IW_ENCODE_ENABLED;
-	  else
-	    wrq->u.encoding.flags = IW_ENCODE_DISABLED;
-	  wrq->u.encoding.flags |= mmc_encr(base);
-
-	  /* Copy the key to the user buffer */
-	  wrq->u.encoding.length = 8;
-	  if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
-	    ret = -EFAULT;
-	}
-      break;
-#endif	/* WIRELESS_EXT > 8 */
-
-#ifdef WAVELAN_ROAMING_EXT
-#if WIRELESS_EXT > 5
-    case SIOCSIWESSID:
-      /* Check if disable */
-      if(wrq->u.data.flags == 0)
-	lp->filter_domains = 0;
-      else
-	/* Basic checking... */
-	if(wrq->u.data.pointer != (caddr_t) 0)
-	  {
-	    char	essid[IW_ESSID_MAX_SIZE + 1];
-	    char *	endp;
-
-	    /* Check the size of the string */
-	    if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1)
-	      {
-		ret = -E2BIG;
-		break;
-	      }
-
-	    /* Copy the string in the driver */
-	    if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length))
-	      {
-		ret = -EFAULT;
-		break;
-	      }
-	    essid[IW_ESSID_MAX_SIZE] = '\0';
-
-#ifdef DEBUG_IOCTL_INFO
-	    printk(KERN_DEBUG "SetEssid : ``%s''\n", essid);
-#endif	/* DEBUG_IOCTL_INFO */
-
-	    /* Convert to a number (note : Wavelan specific) */
-	    lp->domain_id = simple_strtoul(essid, &endp, 16);
-	    /* Has it worked  ? */
-	    if(endp > essid)
-	      lp->filter_domains = 1;
-	    else
-	      {
-		lp->filter_domains = 0;
-		ret = -EINVAL;
-	      }
-	  }
-      break;
-
-    case SIOCGIWESSID:
-      /* Basic checking... */
-      if(wrq->u.data.pointer != (caddr_t) 0)
-	{
-	  char		essid[IW_ESSID_MAX_SIZE + 1];
-
-	  /* Is the domain ID active ? */
-	  wrq->u.data.flags = lp->filter_domains;
-
-	  /* Copy Domain ID into a string (Wavelan specific) */
-	  /* Sound crazy, be we can't have a snprintf in the kernel !!! */
-	  sprintf(essid, "%lX", lp->domain_id);
-	  essid[IW_ESSID_MAX_SIZE] = '\0';
-
-	  /* Set the length */
-	  wrq->u.data.length = strlen(essid) + 1;
-
-	  /* Copy structure to the user buffer */
-	  if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length))
-	    ret = -EFAULT;
-	}
-      break;
-
-    case SIOCSIWAP:
-#ifdef DEBUG_IOCTL_INFO
-      printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n",
-	     wrq->u.ap_addr.sa_data[0],
-	     wrq->u.ap_addr.sa_data[1],
-	     wrq->u.ap_addr.sa_data[2],
-	     wrq->u.ap_addr.sa_data[3],
-	     wrq->u.ap_addr.sa_data[4],
-	     wrq->u.ap_addr.sa_data[5]);
-#endif	/* DEBUG_IOCTL_INFO */
-
-      ret = -EOPNOTSUPP;	/* Not supported yet */
-      break;
-
-    case SIOCGIWAP:
-      /* Should get the real McCoy instead of own Ethernet address */
-      memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE);
-      wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
-
-      ret = -EOPNOTSUPP;	/* Not supported yet */
-      break;
-#endif	/* WIRELESS_EXT > 5 */
-#endif	/* WAVELAN_ROAMING_EXT */
-
-#if WIRELESS_EXT > 8
-#ifdef WAVELAN_ROAMING
-    case SIOCSIWMODE:
-      switch(wrq->u.mode)
-	{
-	case IW_MODE_ADHOC:
-	  if(do_roaming)
-	    {
-	      wv_roam_cleanup(dev);
-	      do_roaming = 0;
-	    }
-	  break;
-	case IW_MODE_INFRA:
-	  if(!do_roaming)
-	    {
-	      wv_roam_init(dev);
-	      do_roaming = 1;
-	    }
-	  break;
-	default:
-	  ret = -EINVAL;
-	}
-      break;
-
-    case SIOCGIWMODE:
-      if(do_roaming)
-	wrq->u.mode = IW_MODE_INFRA;
-      else
-	wrq->u.mode = IW_MODE_ADHOC;
-      break;
-#endif	/* WAVELAN_ROAMING */
-#endif /* WIRELESS_EXT > 8 */
-
-    case SIOCGIWRANGE:
-      /* Basic checking... */
-      if(wrq->u.data.pointer != (caddr_t) 0)
-	{
-	  struct iw_range	range;
-
-	   /* Set the length (very important for backward compatibility) */
-	   wrq->u.data.length = sizeof(struct iw_range);
-
-	   /* Set all the info we don't care or don't know about to zero */
-	   memset(&range, 0, sizeof(range));
-
-#if WIRELESS_EXT > 10
-	   /* Set the Wireless Extension versions */
-	   range.we_version_compiled = WIRELESS_EXT;
-	   range.we_version_source = 9;	/* Nothing for us in v10 and v11 */
-#endif /* WIRELESS_EXT > 10 */
-
-	  /* Set information in the range struct */
-	  range.throughput = 1.4 * 1000 * 1000;	/* don't argue on this ! */
-	  range.min_nwid = 0x0000;
-	  range.max_nwid = 0xFFFF;
-
-	  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
-	  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
-	       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
-	    {
-	      range.num_channels = 10;
-	      range.num_frequency = wv_frequency_list(base, range.freq,
-						      IW_MAX_FREQUENCIES);
-	    }
-	  else
-	    range.num_channels = range.num_frequency = 0;
-
-	  range.sensitivity = 0x3F;
-	  range.max_qual.qual = MMR_SGNL_QUAL;
-	  range.max_qual.level = MMR_SIGNAL_LVL;
-	  range.max_qual.noise = MMR_SILENCE_LVL;
-#if WIRELESS_EXT > 11
-	  range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
-	  /* Need to get better values for those two */
-	  range.avg_qual.level = 30;
-	  range.avg_qual.noise = 8;
-#endif /* WIRELESS_EXT > 11 */
-
-#if WIRELESS_EXT > 7
-	  range.num_bitrates = 1;
-	  range.bitrate[0] = 2000000;	/* 2 Mb/s */
-#endif /* WIRELESS_EXT > 7 */
-
-#if WIRELESS_EXT > 8
-	  /* Encryption supported ? */
-	  if(mmc_encr(base))
-	    {
-	      range.encoding_size[0] = 8;	/* DES = 64 bits key */
-	      range.num_encoding_sizes = 1;
-	      range.max_encoding_tokens = 1;	/* Only one key possible */
-	    }
-	  else
-	    {
-	      range.num_encoding_sizes = 0;
-	      range.max_encoding_tokens = 0;
-	    }
-#endif /* WIRELESS_EXT > 8 */
-
-	  /* Copy structure to the user buffer */
-	  if(copy_to_user(wrq->u.data.pointer, &range,
-			  sizeof(struct iw_range)))
-	    ret = -EFAULT;
-	}
-      break;
-
-    case SIOCGIWPRIV:
-      /* Basic checking... */
-      if(wrq->u.data.pointer != (caddr_t) 0)
-	{
-	  struct iw_priv_args	priv[] =
-	  {	/* cmd,		set_args,	get_args,	name */
-	    { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
-	    { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
-	    { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,	0, "sethisto" },
-	    { SIOCGIPHISTO, 0,	    IW_PRIV_TYPE_INT | 16, "gethisto" },
-	    { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" },
-	    { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" },
-	  };
-
-	  /* Set the number of ioctl available */
-	  wrq->u.data.length = 6;
-
-	  /* Copy structure to the user buffer */
-	  if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
-		       sizeof(priv)))
-	    ret = -EFAULT;
-	}
-      break;
-
-#ifdef WIRELESS_SPY
-    case SIOCSIWSPY:
-      /* Set the spy list */
-
-      /* Check the number of addresses */
-      if(wrq->u.data.length > IW_MAX_SPY)
-	{
-	  ret = -E2BIG;
-	  break;
-	}
-      lp->spy_number = wrq->u.data.length;
-
-      /* If there is some addresses to copy */
-      if(lp->spy_number > 0)
-	{
-	  struct sockaddr	address[IW_MAX_SPY];
-	  int			i;
-
-	  /* Copy addresses to the driver */
-	  if(copy_from_user(address, wrq->u.data.pointer,
-			    sizeof(struct sockaddr) * lp->spy_number))
-	    {
-	      ret = -EFAULT;
-	      break;
-	    }
-
-	  /* Copy addresses to the lp structure */
-	  for(i = 0; i < lp->spy_number; i++)
-	    {
-	      memcpy(lp->spy_address[i], address[i].sa_data,
-		     WAVELAN_ADDR_SIZE);
-	    }
-
-	  /* Reset structure... */
-	  memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
-
-#ifdef DEBUG_IOCTL_INFO
-	  printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
-	  for(i = 0; i < wrq->u.data.length; i++)
-	    printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n",
-		   lp->spy_address[i][0],
-		   lp->spy_address[i][1],
-		   lp->spy_address[i][2],
-		   lp->spy_address[i][3],
-		   lp->spy_address[i][4],
-		   lp->spy_address[i][5]);
-#endif	/* DEBUG_IOCTL_INFO */
-	}
-
-      break;
-
-    case SIOCGIWSPY:
-      /* Get the spy list and spy stats */
-
-      /* Set the number of addresses */
-      wrq->u.data.length = lp->spy_number;
-
-      /* If the user want to have the addresses back... */
-      if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
-	{
-	  struct sockaddr	address[IW_MAX_SPY];
-	  int			i;
-
-	  /* Copy addresses from the lp structure */
-	  for(i = 0; i < lp->spy_number; i++)
-	    {
-	      memcpy(address[i].sa_data, lp->spy_address[i],
-		     WAVELAN_ADDR_SIZE);
-	      address[i].sa_family = ARPHRD_ETHER;
-	    }
-
-	  /* Copy addresses to the user buffer */
-	  if(copy_to_user(wrq->u.data.pointer, address,
-		       sizeof(struct sockaddr) * lp->spy_number))
-	    {
-	      ret = -EFAULT;
-	      break;
-	    }
-
-	  /* Copy stats to the user buffer (just after) */
-	  if(copy_to_user(wrq->u.data.pointer +
-		       (sizeof(struct sockaddr) * lp->spy_number),
-		       lp->spy_stat, sizeof(iw_qual) * lp->spy_number))
-	    {
-	      ret = -EFAULT;
-	      break;
-	    }
-
-	  /* Reset updated flags */
-	  for(i = 0; i < lp->spy_number; i++)
-	    lp->spy_stat[i].updated = 0x0;
-	}	/* if(pointer != NULL) */
-
-      break;
-#endif	/* WIRELESS_SPY */
-
-      /* ------------------ PRIVATE IOCTL ------------------ */
-
-    case SIOCSIPQTHR:
-      if(!capable(CAP_NET_ADMIN))
-	{
-	  ret = -EPERM;
-	  break;
-	}
-      psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
-      psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
-	       (unsigned char *)&psa.psa_quality_thr, 1);
-      /* update the Wavelan checksum */
-      update_psa_checksum(dev);
-      mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
-      break;
-
-    case SIOCGIPQTHR:
-      psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
-	       (unsigned char *)&psa.psa_quality_thr, 1);
-      *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
-      break;
-
-#ifdef WAVELAN_ROAMING
-    case SIOCSIPROAM:
-      /* Note : should check if user == root */
-      if(do_roaming && (*wrq->u.name)==0)
-	wv_roam_cleanup(dev);
-      else if(do_roaming==0 && (*wrq->u.name)!=0)
-	wv_roam_init(dev);
-
-      do_roaming = (*wrq->u.name);
-	  
-      break;
-
-    case SIOCGIPROAM:
-      *(wrq->u.name) = do_roaming;
-      break;
-#endif	/* WAVELAN_ROAMING */
-
-#ifdef HISTOGRAM
-    case SIOCSIPHISTO:
-      /* Verif if the user is root */
-      if(!capable(CAP_NET_ADMIN))
-	{
-	  ret = -EPERM;
-	}
-
-      /* Check the number of intervals */
-      if(wrq->u.data.length > 16)
-	{
-	  ret = -E2BIG;
-	  break;
-	}
-      lp->his_number = wrq->u.data.length;
-
-      /* If there is some addresses to copy */
-      if(lp->his_number > 0)
-	{
-	  /* Copy interval ranges to the driver */
-	  if(copy_from_user(lp->his_range, wrq->u.data.pointer,
-			 sizeof(char) * lp->his_number))
-	    {
-	      ret = -EFAULT;
-	      break;
-	    }
-
-	  /* Reset structure... */
-	  memset(lp->his_sum, 0x00, sizeof(long) * 16);
-	}
-      break;
-
-    case SIOCGIPHISTO:
-      /* Set the number of intervals */
-      wrq->u.data.length = lp->his_number;
-
-      /* Give back the distribution statistics */
-      if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
-	{
-	  /* Copy data to the user buffer */
-	  if(copy_to_user(wrq->u.data.pointer, lp->his_sum,
-		       sizeof(long) * lp->his_number))
-	    ret = -EFAULT;
-
-	}	/* if(pointer != NULL) */
-      break;
-#endif	/* HISTOGRAM */
-
-      /* ------------------- OTHER IOCTL ------------------- */
-
-    default:
-      ret = -EOPNOTSUPP;
-    }
-
-  /* ReEnable interrupts & restore flags */
-  wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
-#endif
-  return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Get wireless statistics
- * Called by /proc/net/wireless...
- */
-static iw_stats *
-wavelan_get_wireless_stats(device *	dev)
-{
-  ioaddr_t		base = dev->base_addr;
-  net_local *		lp = (net_local *) dev->priv;
-  mmr_t			m;
-  iw_stats *		wstats;
-  unsigned long		flags;
-
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
-#endif
-
-  /* Disable interrupts & save flags */
-  wv_splhi(lp, &flags);
-
-  wstats = &lp->wstats;
-
-  /* Get data from the mmc */
-  mmc_out(base, mmwoff(0, mmw_freeze), 1);
-
-  mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
-  mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
-  mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
-
-  mmc_out(base, mmwoff(0, mmw_freeze), 0);
-
-  /* Copy data to wireless stuff */
-  wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
-  wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
-  wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
-  wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
-  wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
-			  ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
-			  ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
-  wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-  wstats->discard.code = 0L;
-  wstats->discard.misc = 0L;
-
-  /* ReEnable interrupts & restore flags */
-  wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
-#endif
-  return &lp->wstats;
-}
-#endif	/* WIRELESS_EXT */
-
-/************************* PACKET RECEPTION *************************/
-/*
- * This part deal with receiving the packets.
- * The interrupt handler get an interrupt when a packet has been
- * successfully received and called this part...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Calculate the starting address of the frame pointed to by the receive
- * frame pointer and verify that the frame seem correct
- * (called by wv_packet_rcv())
- */
-static inline int
-wv_start_of_frame(device *	dev,
-		  int		rfp,	/* end of frame */
-		  int		wrap)	/* start of buffer */
-{
-  ioaddr_t	base = dev->base_addr;
-  int		rp;
-  int		len;
-
-  rp = (rfp - 5 + RX_SIZE) % RX_SIZE;
-  outb(rp & 0xff, PIORL(base));
-  outb(((rp >> 8) & PIORH_MASK), PIORH(base));
-  len = inb(PIOP(base));
-  len |= inb(PIOP(base)) << 8;
-
-  /* Sanity checks on size */
-  /* Frame too big */
-  if(len > MAXDATAZ + 100)
-    {
-#ifdef DEBUG_RX_ERROR
-      printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n",
-	     dev->name, rfp, len);
-#endif
-      return(-1);
-    }
-  
-  /* Frame too short */
-  if(len < 7)
-    {
-#ifdef DEBUG_RX_ERROR
-      printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n",
-	     dev->name, rfp, len);
-#endif
-      return(-1);
-    }
-  
-  /* Wrap around buffer */
-  if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE))	/* magic formula ! */
-    {
-#ifdef DEBUG_RX_ERROR
-      printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",
-	     dev->name, wrap, rfp, len);
-#endif
-      return(-1);
-    }
-
-  return((rp - len + RX_SIZE) % RX_SIZE);
-} /* wv_start_of_frame */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does the actual copy of data (including the ethernet
- * header structure) from the WaveLAN card to an sk_buff chain that
- * will be passed up to the network interface layer. NOTE: We
- * currently don't handle trailer protocols (neither does the rest of
- * the network interface), so if that is needed, it will (at least in
- * part) be added here.  The contents of the receive ring buffer are
- * copied to a message chain that is then passed to the kernel.
- *
- * Note: if any errors occur, the packet is "dropped on the floor"
- * (called by wv_packet_rcv())
- */
-static inline void
-wv_packet_read(device *		dev,
-	       int		fd_p,
-	       int		sksize)
-{
-  net_local *		lp = (net_local *) dev->priv;
-  struct sk_buff *	skb;
-
-#ifdef DEBUG_RX_TRACE
-  printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
-	 dev->name, fd_p, sksize);
-#endif
-
-  /* Allocate some buffer for the new packet */
-  if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL)
-    {
-#ifdef DEBUG_RX_ERROR
-      printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n",
-	     dev->name, sksize);
-#endif
-      lp->stats.rx_dropped++;
-      /*
-       * Not only do we want to return here, but we also need to drop the
-       * packet on the floor to clear the interrupt.
-       */
-      return;
-    }
-
-  skb->dev = dev;
-
-  skb_reserve(skb, 2);
-  fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
-  skb->protocol = eth_type_trans(skb, dev);
-
-#ifdef DEBUG_RX_INFO
-  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
-#endif	/* DEBUG_RX_INFO */
-     
-  /* Statistics gathering & stuff associated.
-   * It seem a bit messy with all the define, but it's really simple... */
-  if(
-#ifdef WIRELESS_SPY
-     (lp->spy_number > 0) ||
-#endif	/* WIRELESS_SPY */
-#ifdef HISTOGRAM
-     (lp->his_number > 0) ||
-#endif	/* HISTOGRAM */
-#ifdef WAVELAN_ROAMING
-     (do_roaming) ||
-#endif	/* WAVELAN_ROAMING */
-     0)
-    {
-      u_char	stats[3];	/* Signal level, Noise level, Signal quality */
-
-      /* read signal level, silence level and signal quality bytes */
-      fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE,
-			  stats, 3);
-#ifdef DEBUG_RX_INFO
-      printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
-	     dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
-#endif
-
-#ifdef WAVELAN_ROAMING
-      if(do_roaming)
-	if(WAVELAN_BEACON(skb->data))
-	  wl_roam_gather(dev, skb->data, stats);
-#endif	/* WAVELAN_ROAMING */
-	  
-#ifdef WIRELESS_SPY
-      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
-#endif	/* WIRELESS_SPY */
-#ifdef HISTOGRAM
-      wl_his_gather(dev, stats);
-#endif	/* HISTOGRAM */
-    }
-
-  /*
-   * Hand the packet to the Network Module
-   */
-  netif_rx(skb);
-
-  /* Keep stats up to date */
-  dev->last_rx = jiffies;
-  lp->stats.rx_packets++;
-  lp->stats.rx_bytes += sksize;
-
-#ifdef DEBUG_RX_TRACE
-  printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
-#endif
-  return;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called by the interrupt handler to initiate a
- * packet transfer from the card to the network interface layer above
- * this driver.  This routine checks if a buffer has been successfully
- * received by the WaveLAN card.  If so, the routine wv_packet_read is
- * called to do the actual transfer of the card's data including the
- * ethernet header into a packet consisting of an sk_buff chain.
- * (called by wavelan_interrupt())
- * Note : the spinlock is already grabbed for us and irq are disabled.
- */
-static inline void
-wv_packet_rcv(device *	dev)
-{
-  ioaddr_t	base = dev->base_addr;
-  net_local *	lp = (net_local *) dev->priv;
-  int		newrfp;
-  int		rp;
-  int		len;
-  int		f_start;
-  int		status;
-  int		i593_rfp;
-  int		stat_ptr;
-  u_char	c[4];
-
-#ifdef DEBUG_RX_TRACE
-  printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name);
-#endif
-
-  /* Get the new receive frame pointer from the i82593 chip */
-  outb(CR0_STATUS_2 | OP0_NOP, LCCR(base));
-  i593_rfp = inb(LCSR(base));
-  i593_rfp |= inb(LCSR(base)) << 8;
-  i593_rfp %= RX_SIZE;
-
-  /* Get the new receive frame pointer from the WaveLAN card.
-   * It is 3 bytes more than the increment of the i82593 receive
-   * frame pointer, for each packet. This is because it includes the
-   * 3 roaming bytes added by the mmc.
-   */
-  newrfp = inb(RPLL(base));
-  newrfp |= inb(RPLH(base)) << 8;
-  newrfp %= RX_SIZE;
-
-#ifdef DEBUG_RX_INFO
-  printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
-	 dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
-#endif
-
-#ifdef DEBUG_RX_ERROR
-  /* If no new frame pointer... */
-  if(lp->overrunning || newrfp == lp->rfp)
-    printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
-	   dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
-#endif
-
-  /* Read all frames (packets) received */
-  while(newrfp != lp->rfp)
-    {
-      /* A frame is composed of the packet, followed by a status word,
-       * the length of the frame (word) and the mmc info (SNR & qual).
-       * It's because the length is at the end that we can only scan
-       * frames backward. */
-
-      /* Find the first frame by skipping backwards over the frames */
-      rp = newrfp;	/* End of last frame */
-      while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) &&
-	    (f_start != -1))
-	  rp = f_start;
-
-      /* If we had a problem */
-      if(f_start == -1)
-	{
-#ifdef DEBUG_RX_ERROR
-	  printk(KERN_INFO "wavelan_cs: cannot find start of frame ");
-	  printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
-		 i593_rfp, lp->stop, newrfp, lp->rfp);
-#endif
-	  lp->rfp = rp;		/* Get to the last usable frame */
-	  continue;
-	}
-
-      /* f_start point to the beggining of the first frame received
-       * and rp to the beggining of the next one */
-
-      /* Read status & length of the frame */
-      stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE;
-      stat_ptr = read_ringbuf(dev, stat_ptr, c, 4);
-      status = c[0] | (c[1] << 8);
-      len = c[2] | (c[3] << 8);
-
-      /* Check status */
-      if((status & RX_RCV_OK) != RX_RCV_OK)
-	{
-	  lp->stats.rx_errors++;
-	  if(status & RX_NO_SFD)
-	    lp->stats.rx_frame_errors++;
-	  if(status & RX_CRC_ERR)
-	    lp->stats.rx_crc_errors++;
-	  if(status & RX_OVRRUN)
-	    lp->stats.rx_over_errors++;
-
-#ifdef DEBUG_RX_FAIL
-	  printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n",
-		 dev->name, status);
-#endif
-	}
-      else
-	/* Read the packet and transmit to Linux */
-	wv_packet_read(dev, f_start, len - 2);
-
-      /* One frame has been processed, skip it */
-      lp->rfp = rp;
-    }
-
-  /*
-   * Update the frame stop register, but set it to less than
-   * the full 8K to allow space for 3 bytes of signal strength
-   * per packet.
-   */
-  lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
-  outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
-  outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
-  outb(OP1_SWIT_TO_PORT_0, LCCR(base));
-
-#ifdef DEBUG_RX_TRACE
-  printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name);
-#endif
-}
-
-/*********************** PACKET TRANSMISSION ***********************/
-/*
- * This part deal with sending packet through the wavelan
- * We copy the packet to the send buffer and then issue the send
- * command to the i82593. The result of this operation will be
- * checked in wavelan_interrupt()
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine fills in the appropriate registers and memory
- * locations on the WaveLAN card and starts the card off on
- * the transmit.
- * (called in wavelan_packet_xmit())
- */
-static inline void
-wv_packet_write(device *	dev,
-		void *		buf,
-		short		length)
-{
-  net_local *		lp = (net_local *) dev->priv;
-  ioaddr_t		base = dev->base_addr;
-  unsigned long		flags;
-  int			clen = length;
-  register u_short	xmtdata_base = TX_BASE;
-
-#ifdef DEBUG_TX_TRACE
-  printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
-#endif
-
-  wv_splhi(lp, &flags);
-
-  /* Check if we need some padding */
-  if(clen < ETH_ZLEN)
-    clen = ETH_ZLEN;
-
-  /* Write the length of data buffer followed by the buffer */
-  outb(xmtdata_base & 0xff, PIORL(base));
-  outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
-  outb(clen & 0xff, PIOP(base));	/* lsb */
-  outb(clen >> 8, PIOP(base));  	/* msb */
-
-  /* Send the data */
-  outsb(PIOP(base), buf, clen);
-
-  /* Indicate end of transmit chain */
-  outb(OP0_NOP, PIOP(base));
-  /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */
-  outb(OP0_NOP, PIOP(base));
-
-  /* Reset the transmit DMA pointer */
-  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
-  hacr_write(base, HACR_DEFAULT);
-  /* Send the transmit command */
-  wv_82593_cmd(dev, "wv_packet_write(): transmit",
-	       OP0_TRANSMIT, SR0_NO_RESULT);
-
-  /* Keep stats up to date */
-  lp->stats.tx_bytes += length;
-
-  wv_splx(lp, &flags);
-
-#ifdef DEBUG_TX_INFO
-  wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
-#endif	/* DEBUG_TX_INFO */
-
-#ifdef DEBUG_TX_TRACE
-  printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called when we want to send a packet (NET3 callback)
- * In this routine, we check if the harware is ready to accept
- * the packet. We also prevent reentrance. Then, we call the function
- * to send the packet...
- */
-static int
-wavelan_packet_xmit(struct sk_buff *	skb,
-		    device *		dev)
-{
-  net_local *		lp = (net_local *)dev->priv;
-  unsigned long		flags;
-
-#ifdef DEBUG_TX_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
-	 (unsigned) skb);
-#endif
-
-  /*
-   * Block a timer-based transmit from overlapping a previous transmit.
-   * In other words, prevent reentering this routine.
-   */
-  netif_stop_queue(dev);
-
-  /* If somebody has asked to reconfigure the controller,
-   * we can do it now */
-  if(lp->reconfig_82593)
-    {
-      wv_splhi(lp, &flags);	/* Disable interrupts */
-      wv_82593_config(dev);
-      wv_splx(lp, &flags);	/* Re-enable interrupts */
-      /* Note : the configure procedure was totally synchronous,
-       * so the Tx buffer is now free */
-    }
-
-#ifdef DEBUG_TX_ERROR
-	if (skb->next)
-		printk(KERN_INFO "skb has next\n");
-#endif
-
-  wv_packet_write(dev, skb->data, skb->len);
-
-  dev_kfree_skb(skb);
-
-#ifdef DEBUG_TX_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
-#endif
-  return(0);
-}
-
-/********************** HARDWARE CONFIGURATION **********************/
-/*
- * This part do the real job of starting and configuring the hardware.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to initialize the Modem Management Controller.
- * (called by wv_hw_config())
- */
-static inline int
-wv_mmc_init(device *	dev)
-{
-  ioaddr_t	base = dev->base_addr;
-  psa_t		psa;
-  mmw_t		m;
-  int		configured;
-  int		i;		/* Loop counter */
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
-#endif
-
-  /* Read the parameter storage area */
-  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
-  /*
-   * Check the first three octets of the MAC addr for the manufacturer's code.
-   * Note: If you get the error message below, you've got a
-   * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on
-   * how to configure your card...
-   */
-  for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
-    if((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) &&
-       (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) &&
-       (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2]))
-      break;
-
-  /* If we have not found it... */
-  if(i == (sizeof(MAC_ADDRESSES) / sizeof(char) / 3))
-    {
-#ifdef DEBUG_CONFIG_ERRORS
-      printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n",
-	     dev->name, psa.psa_univ_mac_addr[0],
-	     psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]);
-#endif
-      return FALSE;
-    }
-
-  /* Get the MAC address */
-  memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
-
-#ifdef USE_PSA_CONFIG
-  configured = psa.psa_conf_status & 1;
-#else
-  configured = 0;
-#endif
-
-  /* Is the PSA is not configured */
-  if(!configured)
-    {
-      /* User will be able to configure NWID after (with iwconfig) */
-      psa.psa_nwid[0] = 0;
-      psa.psa_nwid[1] = 0;
-
-      /* As NWID is not set : no NWID checking */
-      psa.psa_nwid_select = 0;
-
-      /* Disable encryption */
-      psa.psa_encryption_select = 0;
-
-      /* Set to standard values
-       * 0x04 for AT,
-       * 0x01 for MCA,
-       * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
-       */
-      if (psa.psa_comp_number & 1)
-	psa.psa_thr_pre_set = 0x01;
-      else
-	psa.psa_thr_pre_set = 0x04;
-      psa.psa_quality_thr = 0x03;
-
-      /* It is configured */
-      psa.psa_conf_status |= 1;
-
-#ifdef USE_PSA_CONFIG
-      /* Write the psa */
-      psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
-		(unsigned char *)psa.psa_nwid, 4);
-      psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
-		(unsigned char *)&psa.psa_thr_pre_set, 1);
-      psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
-		(unsigned char *)&psa.psa_quality_thr, 1);
-      psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa,
-		(unsigned char *)&psa.psa_conf_status, 1);
-      /* update the Wavelan checksum */
-      update_psa_checksum(dev);
-#endif	/* USE_PSA_CONFIG */
-    }
-
-  /* Zero the mmc structure */
-  memset(&m, 0x00, sizeof(m));
-
-  /* Copy PSA info to the mmc */
-  m.mmw_netw_id_l = psa.psa_nwid[1];
-  m.mmw_netw_id_h = psa.psa_nwid[0];
-  
-  if(psa.psa_nwid_select & 1)
-    m.mmw_loopt_sel = 0x00;
-  else
-    m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
-
-  memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, 
-	 sizeof(m.mmw_encr_key));
-
-  if(psa.psa_encryption_select)
-    m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
-  else
-    m.mmw_encr_enable = 0;
-
-  m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
-  m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
-
-  /*
-   * Set default modem control parameters.
-   * See NCR document 407-0024326 Rev. A.
-   */
-  m.mmw_jabber_enable = 0x01;
-  m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
-  m.mmw_ifs = 0x20;
-  m.mmw_mod_delay = 0x04;
-  m.mmw_jam_time = 0x38;
-
-  m.mmw_des_io_invert = 0;
-  m.mmw_freeze = 0;
-  m.mmw_decay_prm = 0;
-  m.mmw_decay_updat_prm = 0;
-
-  /* Write all info to mmc */
-  mmc_write(base, 0, (u_char *)&m, sizeof(m));
-
-  /* The following code start the modem of the 2.00 frequency
-   * selectable cards at power on. It's not strictly needed for the
-   * following boots...
-   * The original patch was by Joe Finney for the PCMCIA driver, but
-   * I've cleaned it a bit and add documentation.
-   * Thanks to Loeke Brederveld from Lucent for the info.
-   */
-
-  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
-   * (does it work for everybody ??? - especially old cards...) */
-  /* Note : WFREQSEL verify that it is able to read from EEprom
-   * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
-   * is 0xA (Xilinx version) or 0xB (Ariadne version).
-   * My test is more crude but do work... */
-  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
-       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
-    {
-      /* We must download the frequency parameters to the
-       * synthetisers (from the EEprom - area 1)
-       * Note : as the EEprom is auto decremented, we set the end
-       * if the area... */
-      m.mmw_fee_addr = 0x0F;
-      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
-      mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
-		(unsigned char *)&m.mmw_fee_ctrl, 2);
-
-      /* Wait until the download is finished */
-      fee_wait(base, 100, 100);
-
-#ifdef DEBUG_CONFIG_INFO
-      /* The frequency was in the last word downloaded... */
-      mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m,
-	       (unsigned char *)&m.mmw_fee_data_l, 2);
-
-      /* Print some info for the user */
-      printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
-	     dev->name,
-	     ((m.mmw_fee_data_h << 4) |
-	      (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
-#endif
-
-      /* We must now download the power adjust value (gain) to
-       * the synthetisers (from the EEprom - area 7 - DAC) */
-      m.mmw_fee_addr = 0x61;
-      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
-      mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
-		(unsigned char *)&m.mmw_fee_ctrl, 2);
-
-      /* Wait until the download is finished */
-    }	/* if 2.00 card */
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
-#endif
-  return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to gracefully turn off reception, and wait for any commands
- * to complete.
- * (called in wv_ru_start() and wavelan_close() and wavelan_event())
- */
-static int
-wv_ru_stop(device *	dev)
-{
-  ioaddr_t	base = dev->base_addr;
-  net_local *	lp = (net_local *) dev->priv;
-  unsigned long	flags;
-  int		status;
-  int		spin;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
-#endif
-
-  wv_splhi(lp, &flags);
-
-  /* First, send the LAN controller a stop receive command */
-  wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv",
-	       OP0_STOP_RCV, SR0_NO_RESULT);
-
-  /* Then, spin until the receive unit goes idle */
-  spin = 300;
-  do
-    {
-      udelay(10);
-      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
-      status = inb(LCSR(base));
-    }
-  while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0));
-
-  /* Now, spin until the chip finishes executing its current command */
-  do
-    {
-      udelay(10);
-      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
-      status = inb(LCSR(base));
-    }
-  while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
-
-  wv_splx(lp, &flags);
-
-  /* If there was a problem */
-  if(spin <= 0)
-    {
-#ifdef DEBUG_CONFIG_ERROR
-      printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n",
-	     dev->name);
-#endif
-      return FALSE;
-    }
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name);
-#endif
-  return TRUE;
-} /* wv_ru_stop */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine starts the receive unit running.  First, it checks if
- * the card is actually ready. Then the card is instructed to receive
- * packets again.
- * (called in wv_hw_reset() & wavelan_open())
- */
-static int
-wv_ru_start(device *	dev)
-{
-  ioaddr_t	base = dev->base_addr;
-  net_local *	lp = (net_local *) dev->priv;
-  unsigned long	flags;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
-#endif
-
-  /*
-   * We need to start from a quiescent state. To do so, we could check
-   * if the card is already running, but instead we just try to shut
-   * it down. First, we disable reception (in case it was already enabled).
-   */
-  if(!wv_ru_stop(dev))
-    return FALSE;
-
-  wv_splhi(lp, &flags);
-
-  /* Now we know that no command is being executed. */
-
-  /* Set the receive frame pointer and stop pointer */
-  lp->rfp = 0;
-  outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
-
-  /* Reset ring management.  This sets the receive frame pointer to 1 */
-  outb(OP1_RESET_RING_MNGMT, LCCR(base));
-
-#if 0
-  /* XXX the i82593 manual page 6-4 seems to indicate that the stop register
-     should be set as below */
-  /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/
-#elif 0
-  /* but I set it 0 instead */
-  lp->stop = 0;
-#else
-  /* but I set it to 3 bytes per packet less than 8K */
-  lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
-#endif
-  outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
-  outb(OP1_INT_ENABLE, LCCR(base));
-  outb(OP1_SWIT_TO_PORT_0, LCCR(base));
-
-  /* Reset receive DMA pointer */
-  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
-  hacr_write_slow(base, HACR_DEFAULT);
-
-  /* Receive DMA on channel 1 */
-  wv_82593_cmd(dev, "wv_ru_start(): rcv-enable",
-	       CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT);
-
-#ifdef DEBUG_I82593_SHOW
-  {
-    int	status;
-    int	opri;
-    int	spin = 10000;
-
-    /* spin until the chip starts receiving */
-    do
-      {
-	outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
-	status = inb(LCSR(base));
-	if(spin-- <= 0)
-	  break;
-      }
-    while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) &&
-	  ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY));
-    printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n",
-	   (status & SR3_RCV_STATE_MASK), i);
-  }
-#endif
-
-  wv_splx(lp, &flags);
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
-#endif
-  return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a standard config of the WaveLAN controller (i82593).
- * In the ISA driver, this is integrated in wavelan_hardware_reset()
- * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit())
- */
-static int
-wv_82593_config(device *	dev)
-{
-  ioaddr_t			base = dev->base_addr;
-  net_local *			lp = (net_local *) dev->priv;
-  struct i82593_conf_block	cfblk;
-  int				ret = TRUE;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name);
-#endif
-
-  /* Create & fill i82593 config block
-   *
-   * Now conform to Wavelan document WCIN085B
-   */
-  memset(&cfblk, 0x00, sizeof(struct i82593_conf_block));
-  cfblk.d6mod = FALSE;  	/* Run in i82593 advanced mode */
-  cfblk.fifo_limit = 5;         /* = 56 B rx and 40 B tx fifo thresholds */
-  cfblk.forgnesi = FALSE;       /* 0=82C501, 1=AMD7992B compatibility */
-  cfblk.fifo_32 = 1;
-  cfblk.throttle_enb = FALSE;
-  cfblk.contin = TRUE;          /* enable continuous mode */
-  cfblk.cntrxint = FALSE;       /* enable continuous mode receive interrupts */
-  cfblk.addr_len = WAVELAN_ADDR_SIZE;
-  cfblk.acloc = TRUE;           /* Disable source addr insertion by i82593 */
-  cfblk.preamb_len = 0;         /* 2 bytes preamble (SFD) */
-  cfblk.loopback = FALSE;
-  cfblk.lin_prio = 0;   	/* conform to 802.3 backoff algoritm */
-  cfblk.exp_prio = 5;	        /* conform to 802.3 backoff algoritm */
-  cfblk.bof_met = 1;	        /* conform to 802.3 backoff algoritm */
-  cfblk.ifrm_spc = 0x20;	/* 32 bit times interframe spacing */
-  cfblk.slottim_low = 0x20;	/* 32 bit times slot time */
-  cfblk.slottim_hi = 0x0;
-  cfblk.max_retr = 15;
-  cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE);	/* Promiscuous mode */
-  cfblk.bc_dis = FALSE;         /* Enable broadcast reception */
-  cfblk.crs_1 = TRUE;		/* Transmit without carrier sense */
-  cfblk.nocrc_ins = FALSE;	/* i82593 generates CRC */	
-  cfblk.crc_1632 = FALSE;	/* 32-bit Autodin-II CRC */
-  cfblk.crs_cdt = FALSE;	/* CD not to be interpreted as CS */
-  cfblk.cs_filter = 0;  	/* CS is recognized immediately */
-  cfblk.crs_src = FALSE;	/* External carrier sense */
-  cfblk.cd_filter = 0;  	/* CD is recognized immediately */
-  cfblk.min_fr_len = ETH_ZLEN >> 2;     /* Minimum frame length 64 bytes */
-  cfblk.lng_typ = FALSE;	/* Length field > 1500 = type field */
-  cfblk.lng_fld = TRUE; 	/* Disable 802.3 length field check */
-  cfblk.rxcrc_xf = TRUE;	/* Don't transfer CRC to memory */
-  cfblk.artx = TRUE;		/* Disable automatic retransmission */
-  cfblk.sarec = TRUE;		/* Disable source addr trig of CD */
-  cfblk.tx_jabber = TRUE;	/* Disable jabber jam sequence */
-  cfblk.hash_1 = FALSE; 	/* Use bits 0-5 in mc address hash */
-  cfblk.lbpkpol = TRUE; 	/* Loopback pin active high */
-  cfblk.fdx = FALSE;		/* Disable full duplex operation */
-  cfblk.dummy_6 = 0x3f; 	/* all ones */
-  cfblk.mult_ia = FALSE;	/* No multiple individual addresses */
-  cfblk.dis_bof = FALSE;	/* Disable the backoff algorithm ?! */
-  cfblk.dummy_1 = TRUE; 	/* set to 1 */
-  cfblk.tx_ifs_retrig = 3;	/* Hmm... Disabled */
-#ifdef MULTICAST_ALL
-  cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE);	/* Allow all multicasts */
-#else
-  cfblk.mc_all = FALSE;		/* No multicast all mode */
-#endif
-  cfblk.rcv_mon = 0;		/* Monitor mode disabled */
-  cfblk.frag_acpt = TRUE;	/* Do not accept fragments */
-  cfblk.tstrttrs = FALSE;	/* No start transmission threshold */
-  cfblk.fretx = TRUE;		/* FIFO automatic retransmission */
-  cfblk.syncrqs = FALSE; 	/* Synchronous DRQ deassertion... */
-  cfblk.sttlen = TRUE;  	/* 6 byte status registers */
-  cfblk.rx_eop = TRUE;  	/* Signal EOP on packet reception */
-  cfblk.tx_eop = TRUE;  	/* Signal EOP on packet transmission */
-  cfblk.rbuf_size = RX_SIZE>>11;	/* Set receive buffer size */
-  cfblk.rcvstop = TRUE; 	/* Enable Receive Stop Register */
-
-#ifdef DEBUG_I82593_SHOW
-  {
-    u_char *c = (u_char *) &cfblk;
-    int i;
-    printk(KERN_DEBUG "wavelan_cs: config block:");
-    for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++)
-      {
-	if((i % 16) == 0) printk("\n" KERN_DEBUG);
-	printk("%02x ", *c);
-      }
-    printk("\n");
-  }
-#endif
-
-  /* Copy the config block to the i82593 */
-  outb(TX_BASE & 0xff, PIORL(base));
-  outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
-  outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base));    /* lsb */
-  outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base));	/* msb */
-  outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block));
-
-  /* reset transmit DMA pointer */
-  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
-  hacr_write(base, HACR_DEFAULT);
-  if(!wv_82593_cmd(dev, "wv_82593_config(): configure",
-		   OP0_CONFIGURE, SR0_CONFIGURE_DONE))
-    ret = FALSE;
-
-  /* Initialize adapter's ethernet MAC address */
-  outb(TX_BASE & 0xff, PIORL(base));
-  outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
-  outb(WAVELAN_ADDR_SIZE, PIOP(base));	/* byte count lsb */
-  outb(0, PIOP(base));			/* byte count msb */
-  outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE);
-
-  /* reset transmit DMA pointer */
-  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
-  hacr_write(base, HACR_DEFAULT);
-  if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup",
-		   OP0_IA_SETUP, SR0_IA_SETUP_DONE))
-    ret = FALSE;
-
-#ifdef WAVELAN_ROAMING
-    /* If roaming is enabled, join the "Beacon Request" multicast group... */
-    /* But only if it's not in there already! */
-  if(do_roaming)
-    dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1);
-#endif	/* WAVELAN_ROAMING */
-
-  /* If any multicast address to set */
-  if(lp->mc_count)
-    {
-      struct dev_mc_list *	dmi;
-      int			addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count;
-
-#ifdef DEBUG_CONFIG_INFO
-      printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n",
-	     dev->name, lp->mc_count);
-      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
-	printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
-	       dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
-#endif
-
-      /* Initialize adapter's ethernet multicast addresses */
-      outb(TX_BASE & 0xff, PIORL(base));
-      outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
-      outb(addrs_len & 0xff, PIOP(base));	/* byte count lsb */
-      outb((addrs_len >> 8), PIOP(base));	/* byte count msb */
-      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
-	outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen);
-
-      /* reset transmit DMA pointer */
-      hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
-      hacr_write(base, HACR_DEFAULT);
-      if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup",
-		       OP0_MC_SETUP, SR0_MC_SETUP_DONE))
-	ret = FALSE;
-      lp->mc_count = dev->mc_count;	/* remember to avoid repeated reset */
-    }
-
-  /* Job done, clear the flag */
-  lp->reconfig_82593 = FALSE;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
-#endif
-  return(ret);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read the Access Configuration Register, perform a software reset,
- * and then re-enable the card's software.
- *
- * If I understand correctly : reset the pcmcia interface of the
- * wavelan.
- * (called by wv_config())
- */
-static inline int
-wv_pcmcia_reset(device *	dev)
-{
-  int		i;
-  conf_reg_t	reg = { 0, CS_READ, CISREG_COR, 0 };
-  dev_link_t *	link = ((net_local *) dev->priv)->link;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name);
-#endif
-
-  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, AccessConfigurationRegister, i);
-      return FALSE;
-    }
-      
-#ifdef DEBUG_CONFIG_INFO
-  printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
-	 dev->name, (u_int) reg.Value);
-#endif
-
-  reg.Action = CS_WRITE;
-  reg.Value = reg.Value | COR_SW_RESET;
-  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, AccessConfigurationRegister, i);
-      return FALSE;
-    }
-      
-  reg.Action = CS_WRITE;
-  reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
-  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, AccessConfigurationRegister, i);
-      return FALSE;
-    }
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
-#endif
-  return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wavelan_hw_config() is called after a CARD_INSERTION event is
- * received, to configure the wavelan hardware.
- * Note that the reception will be enabled in wavelan->open(), so the
- * device is configured but idle...
- * Performs the following actions:
- * 	1. A pcmcia software reset (using wv_pcmcia_reset())
- *	2. A power reset (reset DMA)
- *	3. Reset the LAN controller
- *	4. Initialize the radio modem (using wv_mmc_init)
- *	5. Configure LAN controller (using wv_82593_config)
- *	6. Perform a diagnostic on the LAN controller
- * (called by wavelan_event() & wv_hw_reset())
- */
-static int
-wv_hw_config(device *	dev)
-{
-  net_local *		lp = (net_local *) dev->priv;
-  ioaddr_t		base = dev->base_addr;
-  unsigned long		flags;
-  int			ret = FALSE;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name);
-#endif
-
-#ifdef STRUCT_CHECK
-  if(wv_structuct_check() != (char *) NULL)
-    {
-      printk(KERN_WARNING "%s: wv_hw_config: structure/compiler botch: \"%s\"\n",
-	     dev->name, wv_structuct_check());
-      return FALSE;
-    }
-#endif	/* STRUCT_CHECK == 1 */
-
-  /* Reset the pcmcia interface */
-  if(wv_pcmcia_reset(dev) == FALSE)
-    return FALSE;
-
-  /* Disable interrupts */
-  wv_splhi(lp, &flags);
-
-  /* Disguised goto ;-) */
-  do
-    {
-      /* Power UP the module + reset the modem + reset host adapter
-       * (in fact, reset DMA channels) */
-      hacr_write_slow(base, HACR_RESET);
-      hacr_write(base, HACR_DEFAULT);
-
-      /* Check if the module has been powered up... */
-      if(hasr_read(base) & HASR_NO_CLK)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
-		 dev->name);
-#endif
-	  break;
-	}
-
-      /* initialize the modem */
-      if(wv_mmc_init(dev) == FALSE)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n",
-		 dev->name);
-#endif
-	  break;
-	}
-
-      /* reset the LAN controller (i82593) */
-      outb(OP0_RESET, LCCR(base));
-      mdelay(1);	/* A bit crude ! */
-
-      /* Initialize the LAN controller */
-      if(wv_82593_config(dev) == FALSE)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n",
-		 dev->name);
-#endif
-	  break;
-	}
-
-      /* Diagnostic */
-      if(wv_diag(dev) == FALSE)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n",
-		 dev->name);
-#endif
-	  break;
-	}
-
-      /* 
-       * insert code for loopback test here
-       */
-
-      /* The device is now configured */
-      lp->configured = 1;
-      ret = TRUE;
-    }
-  while(0);
-
-  /* Re-enable interrupts */
-  wv_splx(lp, &flags);
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
-#endif
-  return(ret);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Totally reset the wavelan and restart it.
- * Performs the following actions:
- * 	1. Call wv_hw_config()
- *	2. Start the LAN controller's receive unit
- * (called by wavelan_event(), wavelan_watchdog() and wavelan_open())
- */
-static inline void
-wv_hw_reset(device *	dev)
-{
-  net_local *	lp = (net_local *) dev->priv;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name);
-#endif
-
-  lp->nresets++;
-  lp->configured = 0;
-  
-  /* Call wv_hw_config() for most of the reset & init stuff */
-  if(wv_hw_config(dev) == FALSE)
-    return;
-
-  /* start receive unit */
-  wv_ru_start(dev);
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wv_pcmcia_config() is called after a CARD_INSERTION event is
- * received, to configure the PCMCIA socket, and to make the ethernet
- * device available to the system.
- * (called by wavelan_event())
- */
-static inline int
-wv_pcmcia_config(dev_link_t *	link)
-{
-  client_handle_t	handle;
-  tuple_t		tuple;
-  cisparse_t		parse;
-  struct net_device *	dev;
-  int			i;
-  u_char		buf[64];
-  win_req_t		req;
-  memreq_t		mem;
-
-  handle = link->handle;
-  dev = (device *) link->priv;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link);
-#endif
-
-  /*
-   * This reads the card's CONFIG tuple to find its configuration
-   * registers.
-   */
-  do
-    {
-      tuple.Attributes = 0;
-      tuple.DesiredTuple = CISTPL_CONFIG;
-      i = CardServices(GetFirstTuple, handle, &tuple);
-      if(i != CS_SUCCESS)
-	break;
-      tuple.TupleData = (cisdata_t *)buf;
-      tuple.TupleDataMax = 64;
-      tuple.TupleOffset = 0;
-      i = CardServices(GetTupleData, handle, &tuple);
-      if(i != CS_SUCCESS)
-	break;
-      i = CardServices(ParseTuple, handle, &tuple, &parse);
-      if(i != CS_SUCCESS)
-	break;
-      link->conf.ConfigBase = parse.config.base;
-      link->conf.Present = parse.config.rmask[0];
-    }
-  while(0);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, ParseTuple, i);
-      link->state &= ~DEV_CONFIG_PENDING;
-      return FALSE;
-    }
-    
-  /* Configure card */
-  link->state |= DEV_CONFIG;
-  do
-    {
-      i = CardServices(RequestIO, link->handle, &link->io);
-      if(i != CS_SUCCESS)
-	{
-	  cs_error(link->handle, RequestIO, i);
-	  break;
-	}
-
-      /*
-       * Now allocate an interrupt line.  Note that this does not
-       * actually assign a handler to the interrupt.
-       */
-      i = CardServices(RequestIRQ, link->handle, &link->irq);
-      if(i != CS_SUCCESS)
-	{
-	  cs_error(link->handle, RequestIRQ, i);
-	  break;
-	}
-
-      /*
-       * This actually configures the PCMCIA socket -- setting up
-       * the I/O windows and the interrupt mapping.
-       */
-      link->conf.ConfigIndex = 1;
-      i = CardServices(RequestConfiguration, link->handle, &link->conf);
-      if(i != CS_SUCCESS)
-	{
-	  cs_error(link->handle, RequestConfiguration, i);
-	  break;
-	}
-
-      /*
-       * Allocate a small memory window.  Note that the dev_link_t
-       * structure provides space for one window handle -- if your
-       * device needs several windows, you'll need to keep track of
-       * the handles in your private data structure, link->priv.
-       */
-      req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
-      req.Base = req.Size = 0;
-      req.AccessSpeed = mem_speed;
-      link->win = (window_handle_t)link->handle;
-      i = CardServices(RequestWindow, &link->win, &req);
-      if(i != CS_SUCCESS)
-	{
-	  cs_error(link->handle, RequestWindow, i);
-	  break;
-	}
-
-      dev->rmem_start = dev->mem_start =
-	  (u_long)ioremap(req.Base, req.Size);
-      dev->rmem_end = dev->mem_end = dev->mem_start + req.Size;
-
-      mem.CardOffset = 0; mem.Page = 0;
-      i = CardServices(MapMemPage, link->win, &mem);
-      if(i != CS_SUCCESS)
-	{
-	  cs_error(link->handle, MapMemPage, i);
-	  break;
-	}
-
-      /* Feed device with this info... */
-      dev->irq = link->irq.AssignedIRQ;
-      dev->base_addr = link->io.BasePort1;
-      netif_start_queue(dev);
-
-#ifdef DEBUG_CONFIG_INFO
-      printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n",
-	     (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr);
-#endif
-
-      i = register_netdev(dev);
-      if(i != 0)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n");
-#endif
-	  break;
-	}
-    }
-  while(0);		/* Humm... Disguised goto !!! */
-
-  link->state &= ~DEV_CONFIG_PENDING;
-  /* If any step failed, release any partially configured state */
-  if(i != 0)
-    {
-      wv_pcmcia_release((u_long) link);
-      return FALSE;
-    }
-
-  strcpy(((net_local *) dev->priv)->node.dev_name, dev->name);
-  link->dev = &((net_local *) dev->priv)->node;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "<-wv_pcmcia_config()\n");
-#endif
-  return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * After a card is removed, wv_pcmcia_release() will unregister the net
- * device, and release the PCMCIA configuration.  If the device is
- * still open, this will be postponed until it is closed.
- */
-static void
-wv_pcmcia_release(u_long	arg)	/* Address of the interface struct */
-{
-  dev_link_t *	link = (dev_link_t *) arg;
-  device *	dev = (device *) link->priv;
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
-#endif
-
-  /* If the device is currently in use, we won't release until it is
-   * actually closed. */
-  if(link->open)
-    {
-#ifdef DEBUG_CONFIG_INFO
-      printk(KERN_DEBUG "%s: wv_pcmcia_release: release postponed, device still open\n",
-	     dev->name);
-#endif
-      link->state |= DEV_STALE_CONFIG;
-      return;
-    }
-
-  /* Don't bother checking to see if these succeed or not */
-  iounmap((u_char *)dev->mem_start);
-  CardServices(ReleaseWindow, link->win);
-  CardServices(ReleaseConfiguration, link->handle);
-  CardServices(ReleaseIO, link->handle, &link->io);
-  CardServices(ReleaseIRQ, link->handle, &link->irq);
-
-  link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
-#endif
-} /* wv_pcmcia_release */
-
-/*------------------------------------------------------------------*/
-/*
- * Sometimes, wavelan_detach can't be performed following a call from
- * cardmgr (device still open, pcmcia_release not done) and the device
- * is put in a STALE_LINK state and remains in memory.
- *
- * This function run through our current list of device and attempt
- * another time to remove them. We hope that since last time the
- * device has properly been closed.
- *
- * (called by wavelan_attach() & cleanup_module())
- */
-static void
-wv_flush_stale_links(void)
-{
-  dev_link_t *	link;		/* Current node in linked list */
-  dev_link_t *	next;		/* Next node in linked list */
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "-> wv_flush_stale_links(0x%p)\n", dev_list);
-#endif
-
-  /* Go through the list */
-  for (link = dev_list; link; link = next)
-    {
-      next = link->next;
-
-      /* Check if in need of being removed */
-      if((link->state & DEV_STALE_LINK) ||
-	 (! (link->state & DEV_PRESENT)))
-	wavelan_detach(link);
-
-    }
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "<- wv_flush_stale_links()\n");
-#endif
-}
-
-/************************ INTERRUPT HANDLING ************************/
-
-/*
- * This function is the interrupt handler for the WaveLAN card. This
- * routine will be called whenever: 
- *	1. A packet is received.
- *	2. A packet has successfully been transferred and the unit is
- *	   ready to transmit another packet.
- *	3. A command has completed execution.
- */
-static void
-wavelan_interrupt(int		irq,
-		  void *	dev_id,
-		  struct pt_regs * regs)
-{
-  device *	dev;
-  net_local *	lp;
-  ioaddr_t	base;
-  int		status0;
-  u_int		tx_status;
-
-  if((dev = (device *)dev_id) == (device *) NULL)
-    {
-#ifdef DEBUG_INTERRUPT_ERROR
-      printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
-	     irq);
-#endif
-      return;
-    }
-
-#ifdef DEBUG_INTERRUPT_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
-#endif
-
-  lp = (net_local *) dev->priv;
-  base = dev->base_addr;
-
-#ifdef DEBUG_INTERRUPT_INFO
-  /* Check state of our spinlock (it should be cleared) */
-  if(spin_is_locked(&lp->spinlock))
-    printk(KERN_DEBUG
-	   "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
-	   dev->name);
-#endif
-
-  /* Prevent reentrancy. We need to do that because we may have
-   * multiple interrupt handler running concurently.
-   * It is safe because wv_splhi() disable interrupts before aquiring
-   * the spinlock. */
-  spin_lock(&lp->spinlock);
-
-  /* Treat all pending interrupts */
-  while(1)
-    {
-      /* ---------------- INTERRUPT CHECKING ---------------- */
-      /*
-       * Look for the interrupt and verify the validity
-       */
-      outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
-      status0 = inb(LCSR(base));
-
-#ifdef DEBUG_INTERRUPT_INFO
-      printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0, 
-	     (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT);
-      if(status0&SR0_INTERRUPT)
-	{
-	  printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" :
-		 ((status0 & SR0_EXECUTION) ? "cmd" :
-		  ((status0 & SR0_RECEPTION) ? "recv" : "unknown")),
-		 (status0 & SR0_EVENT_MASK));
-	}
-      else
-	printk("\n");
-#endif
-
-      /* Return if no actual interrupt from i82593 (normal exit) */
-      if(!(status0 & SR0_INTERRUPT))
-	break;
-
-      /* If interrupt is both Rx and Tx or none...
-       * This code in fact is there to catch the spurious interrupt
-       * when you remove the wavelan pcmcia card from the socket */
-      if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) ||
-	 ((status0 & SR0_BOTH_RX_TX) == 0x0))
-	{
-#ifdef DEBUG_INTERRUPT_INFO
-	  printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n",
-		 dev->name, status0);
-#endif
-	  /* Acknowledge the interrupt */
-	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
-	  break;
-	}
-
-      /* ----------------- RECEIVING PACKET ----------------- */
-      /*
-       * When the wavelan signal the reception of a new packet,
-       * we call wv_packet_rcv() to copy if from the buffer and
-       * send it to NET3
-       */
-      if(status0 & SR0_RECEPTION)
-	{
-#ifdef DEBUG_INTERRUPT_INFO
-	  printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name);
-#endif
-
-	  if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT)
-	    {
-#ifdef DEBUG_INTERRUPT_ERROR
-	      printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n",
-		     dev->name);
-#endif
-	      lp->stats.rx_over_errors++;
-	      lp->overrunning = 1;
-      	    }
-
-	  /* Get the packet */
-	  wv_packet_rcv(dev);
-	  lp->overrunning = 0;
-
-	  /* Acknowledge the interrupt */
-	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
-	  continue;
-    	}
-
-      /* ---------------- COMMAND COMPLETION ---------------- */
-      /*
-       * Interrupts issued when the i82593 has completed a command.
-       * Most likely : transmission done
-       */
-
-      /* If a transmission has been done */
-      if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
-	 (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
-	 (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
-	{
-#ifdef DEBUG_TX_ERROR
-	  if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
-	    printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n",
-		   dev->name);
-#endif
-
-	  /* Get transmission status */
-	  tx_status = inb(LCSR(base));
-	  tx_status |= (inb(LCSR(base)) << 8);
-#ifdef DEBUG_INTERRUPT_INFO
-	  printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n",
-		 dev->name);
-	  {
-	    u_int	rcv_bytes;
-	    u_char	status3;
-	    rcv_bytes = inb(LCSR(base));
-	    rcv_bytes |= (inb(LCSR(base)) << 8);
-	    status3 = inb(LCSR(base));
-	    printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n",
-		   tx_status, rcv_bytes, (u_int) status3);
-	  }
-#endif
-	  /* Check for possible errors */
-	  if((tx_status & TX_OK) != TX_OK)
-	    {
-	      lp->stats.tx_errors++;
-
-	      if(tx_status & TX_FRTL)
-		{
-#ifdef DEBUG_TX_ERROR
-		  printk(KERN_INFO "%s: wv_interrupt(): frame too long\n",
-			 dev->name);
-#endif
-		}
-	      if(tx_status & TX_UND_RUN)
-		{
-#ifdef DEBUG_TX_FAIL
-		  printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n",
-			 dev->name);
-#endif
-		  lp->stats.tx_aborted_errors++;
-		}
-	      if(tx_status & TX_LOST_CTS)
-		{
-#ifdef DEBUG_TX_FAIL
-		  printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name);
-#endif
-		  lp->stats.tx_carrier_errors++;
-		}
-	      if(tx_status & TX_LOST_CRS)
-		{
-#ifdef DEBUG_TX_FAIL
-		  printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n",
-			 dev->name);
-#endif
-		  lp->stats.tx_carrier_errors++;
-		}
-	      if(tx_status & TX_HRT_BEAT)
-		{
-#ifdef DEBUG_TX_FAIL
-		  printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name);
-#endif
-		  lp->stats.tx_heartbeat_errors++;
-		}
-	      if(tx_status & TX_DEFER)
-		{
-#ifdef DEBUG_TX_FAIL
-		  printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n",
-			 dev->name);
-#endif
-		}
-	      /* Ignore late collisions since they're more likely to happen
-	       * here (the WaveLAN design prevents the LAN controller from
-	       * receiving while it is transmitting). We take action only when
-	       * the maximum retransmit attempts is exceeded.
-	       */
-	      if(tx_status & TX_COLL)
-		{
-		  if(tx_status & TX_MAX_COL)
-		    {
-#ifdef DEBUG_TX_FAIL
-		      printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n",
-			     dev->name);
-#endif
-		      if(!(tx_status & TX_NCOL_MASK))
-			{
-			  lp->stats.collisions += 0x10;
-			}
-		    }
-		}
-	    }	/* if(!(tx_status & TX_OK)) */
-
-	  lp->stats.collisions += (tx_status & TX_NCOL_MASK);
-	  lp->stats.tx_packets++;
-
-	  netif_wake_queue(dev);
-	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));	/* Acknowledge the interrupt */
-    	} 
-      else	/* if interrupt = transmit done or retransmit done */
-	{
-#ifdef DEBUG_INTERRUPT_ERROR
-	  printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n",
-		 status0);
-#endif
-	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));	/* Acknowledge the interrupt */
-    	}
-    }	/* while(1) */
-
-  spin_unlock(&lp->spinlock);
-
-#ifdef DEBUG_INTERRUPT_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
-#endif
-} /* wv_interrupt */
-
-/*------------------------------------------------------------------*/
-/*
- * Watchdog: when we start a transmission, a timer is set for us in the
- * kernel.  If the transmission completes, this timer is disabled. If
- * the timer expires, we are called and we try to unlock the hardware.
- *
- * Note : This watchdog is move clever than the one in the ISA driver,
- * because it try to abort the current command before reseting
- * everything...
- * On the other hand, it's a bit simpler, because we don't have to
- * deal with the multiple Tx buffers...
- */
-static void
-wavelan_watchdog(device *	dev)
-{
-  net_local *		lp = (net_local *) dev->priv;
-  ioaddr_t		base = dev->base_addr;
-  unsigned long		flags;
-  int			aborted = FALSE;
-
-#ifdef DEBUG_INTERRUPT_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
-#endif
-
-#ifdef DEBUG_INTERRUPT_ERROR
-  printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
-	 dev->name);
-#endif
-
-  wv_splhi(lp, &flags);
-
-  /* Ask to abort the current command */
-  outb(OP0_ABORT, LCCR(base));
-
-  /* Wait for the end of the command (a bit hackish) */
-  if(wv_82593_cmd(dev, "wavelan_watchdog(): abort",
-		  OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED))
-    aborted = TRUE;
-
-  /* Release spinlock here so that wv_hw_reset() can grab it */
-  wv_splx(lp, &flags);
-
-  /* Check if we were successful in aborting it */
-  if(!aborted)
-    {
-      /* It seem that it wasn't enough */
-#ifdef DEBUG_INTERRUPT_ERROR
-      printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n",
-	     dev->name);
-#endif
-      wv_hw_reset(dev);
-    }
-
-#ifdef DEBUG_PSA_SHOW
-  {
-    psa_t		psa;
-    psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-    wv_psa_show(&psa);
-  }
-#endif
-#ifdef DEBUG_MMC_SHOW
-  wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82593_SHOW
-  wv_ru_show(dev);
-#endif
-
-  /* We are no more waiting for something... */
-  netif_wake_queue(dev);
-
-#ifdef DEBUG_INTERRUPT_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
-#endif
-}
-
-/********************* CONFIGURATION CALLBACKS *********************/
-/*
- * Here are the functions called by the pcmcia package (cardmgr) and
- * linux networking (NET3) for initialization, configuration and
- * deinstallations of the Wavelan Pcmcia Hardware.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Configure and start up the WaveLAN PCMCIA adaptor.
- * Called by NET3 when it "open" the device.
- */
-static int
-wavelan_open(device *	dev)
-{
-  dev_link_t *	link = ((net_local *) dev->priv)->link;
-  net_local *	lp = (net_local *)dev->priv;
-  ioaddr_t	base = dev->base_addr;
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
-	 (unsigned int) dev);
-#endif
-
-  /* Check if the modem is powered up (wavelan_close() power it down */
-  if(hasr_read(base) & HASR_NO_CLK)
-    {
-      /* Power up (power up time is 250us) */
-      hacr_write(base, HACR_DEFAULT);
-
-      /* Check if the module has been powered up... */
-      if(hasr_read(base) & HASR_NO_CLK)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n",
-		 dev->name);
-#endif
-	  return FALSE;
-	}
-    }
-
-  /* Start reception and declare the driver ready */
-  if(!lp->configured)
-    return FALSE;
-  if(!wv_ru_start(dev))
-    wv_hw_reset(dev);		/* If problem : reset */
-  netif_start_queue(dev);
-
-  /* Mark the device as used */
-  link->open++;
-  MOD_INC_USE_COUNT;
-
-#ifdef WAVELAN_ROAMING
-  if(do_roaming)
-    wv_roam_init(dev);
-#endif	/* WAVELAN_ROAMING */
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
-#endif
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Shutdown the WaveLAN PCMCIA adaptor.
- * Called by NET3 when it "close" the device.
- */
-static int
-wavelan_close(device *	dev)
-{
-  dev_link_t *	link = ((net_local *) dev->priv)->link;
-  ioaddr_t	base = dev->base_addr;
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
-	 (unsigned int) dev);
-#endif
-
-  /* If the device isn't open, then nothing to do */
-  if(!link->open)
-    {
-#ifdef DEBUG_CONFIG_INFO
-      printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name);
-#endif
-      return 0;
-    }
-
-#ifdef WAVELAN_ROAMING
-  /* Cleanup of roaming stuff... */
-  if(do_roaming)
-    wv_roam_cleanup(dev);
-#endif	/* WAVELAN_ROAMING */
-
-  link->open--;
-  MOD_DEC_USE_COUNT;
-
-  /* If the card is still present */
-  if(netif_running(dev))
-    {
-      netif_stop_queue(dev);
-
-      /* Stop receiving new messages and wait end of transmission */
-      wv_ru_stop(dev);
-
-      /* Power down the module */
-      hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT));
-    }
-  else
-    /* The card is no more there (flag is activated in wv_pcmcia_release) */
-    if(link->state & DEV_STALE_CONFIG)
-      wv_pcmcia_release((u_long)link);
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
-#endif
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wavelan_attach() creates an "instance" of the driver, allocating
- * local data structures for one device (one interface).  The device
- * is registered with Card Services.
- *
- * The dev_link structure is initialized, but we don't actually
- * configure the card at this point -- we wait until we receive a
- * card insertion event.
- */
-static dev_link_t *
-wavelan_attach(void)
-{
-  client_reg_t	client_reg;	/* Register with cardmgr */
-  dev_link_t *	link;		/* Info for cardmgr */
-  device *	dev;		/* Interface generic data */
-  net_local *	lp;		/* Interface specific data */
-  int		i, ret;
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "-> wavelan_attach()\n");
-#endif
-
-  /* Perform some cleanup */
-  wv_flush_stale_links();
-
-  /* Initialize the dev_link_t structure */
-  link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-  if (!link) return NULL;
-  memset(link, 0, sizeof(struct dev_link_t));
-
-  /* Unused for the Wavelan */
-  link->release.function = &wv_pcmcia_release;
-  link->release.data = (u_long) link;
-
-  /* The io structure describes IO port mapping */
-  link->io.NumPorts1 = 8;
-  link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-  link->io.IOAddrLines = 3;
-
-  /* Interrupt setup */
-  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
-  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->irq.Handler = wavelan_interrupt;
-
-  /* General socket configuration */
-  link->conf.Attributes = CONF_ENABLE_IRQ;
-  link->conf.Vcc = 50;
-  link->conf.IntType = INT_MEMORY_AND_IO;
-
-  /* Chain drivers */
-  link->next = dev_list;
-  dev_list = link;
-
-  /* Allocate the generic data structure */
-  dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
-  if (!dev) {
-      kfree(link);
-      return NULL;
-  }
-  memset(dev, 0x00, sizeof(struct net_device));
-  link->priv = link->irq.Instance = dev;
-
-  /* Allocate the wavelan-specific data structure. */
-  dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
-  if (!lp) {
-      kfree(link);
-      kfree(dev);
-      return NULL;
-  }
-  memset(lp, 0x00, sizeof(net_local));
-
-  /* Init specific data */
-  lp->configured = 0;
-  lp->reconfig_82593 = FALSE;
-  lp->nresets = 0;
-  /* Multicast stuff */
-  lp->promiscuous = 0;
-  lp->allmulticast = 0;
-  lp->mc_count = 0;
-
-  /* Init spinlock */
-  spin_lock_init(&lp->spinlock);
-
-  /* back links */
-  lp->link = link;
-  lp->dev = dev;
-
-  /* Standard setup for generic data */
-  ether_setup(dev);
-
-  /* wavelan NET3 callbacks */
-  dev->open = &wavelan_open;
-  dev->stop = &wavelan_close;
-  dev->hard_start_xmit = &wavelan_packet_xmit;
-  dev->get_stats = &wavelan_get_stats;
-  dev->set_multicast_list = &wavelan_set_multicast_list;
-#ifdef SET_MAC_ADDRESS
-  dev->set_mac_address = &wavelan_set_mac_address;
-#endif	/* SET_MAC_ADDRESS */
-
-  /* Set the watchdog timer */
-  dev->tx_timeout	= &wavelan_watchdog;
-  dev->watchdog_timeo	= WATCHDOG_JIFFIES;
-
-#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
-  dev->do_ioctl = wavelan_ioctl;	/* wireless extensions */
-  dev->get_wireless_stats = wavelan_get_wireless_stats;
-#endif
-
-  /* Other specific data */
-  dev->mtu = WAVELAN_MTU;
-
-  /* Register with Card Services */
-  client_reg.dev_info = &dev_info;
-  client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
-  client_reg.EventMask = 
-    CS_EVENT_REGISTRATION_COMPLETE |
-    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
-    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
-    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-  client_reg.event_handler = &wavelan_event;
-  client_reg.Version = 0x0210;
-  client_reg.event_callback_args.client_data = link;
-
-#ifdef DEBUG_CONFIG_INFO
-  printk(KERN_DEBUG "wavelan_attach(): almost done, calling CardServices\n");
-#endif
-
-  ret = CardServices(RegisterClient, &link->handle, &client_reg);
-  if(ret != 0)
-    {
-      cs_error(link->handle, RegisterClient, ret);
-      wavelan_detach(link);
-      return NULL;
-    }
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "<- wavelan_attach()\n");
-#endif
-
-  return link;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This deletes a driver "instance".  The device is de-registered with
- * Card Services.  If it has been released, all local data structures
- * are freed.  Otherwise, the structures will be freed when the device
- * is released.
- */
-static void
-wavelan_detach(dev_link_t *	link)
-{
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link);
-#endif
-
-  /*
-   * If the device is currently configured and active, we won't
-   * actually delete it yet.  Instead, it is marked so that when the
-   * release() function is called, that will trigger a proper
-   * detach().
-   */
-  if(link->state & DEV_CONFIG)
-    {
-      /* Some others haven't done their job : give them another chance */
-      wv_pcmcia_release((u_long) link);
-      if(link->state & DEV_STALE_CONFIG)
-	{
-#ifdef DEBUG_CONFIG_INFO
-	  printk(KERN_DEBUG "wavelan_detach: detach postponed,"
-		 " '%s' still locked\n", link->dev->dev_name);
-#endif
-	  link->state |= DEV_STALE_LINK;
-	  return;
-	}
-    }
-
-  /* Break the link with Card Services */
-  if(link->handle)
-    CardServices(DeregisterClient, link->handle);
-    
-  /* Remove the interface data from the linked list */
-  if(dev_list == link)
-    dev_list = link->next;
-  else
-    {
-      dev_link_t *	prev = dev_list;
-
-      while((prev != (dev_link_t *) NULL) && (prev->next != link))
-	prev = prev->next;
-
-      if(prev == (dev_link_t *) NULL)
-	{
-#ifdef DEBUG_CONFIG_ERRORS
-	  printk(KERN_WARNING "wavelan_detach : Attempting to remove a nonexistent device.\n");
-#endif
-	  return;
-	}
-
-      prev->next = link->next;
-    }
-
-  /* Free pieces */
-  if(link->priv)
-    {
-      device *	dev = (device *) link->priv;
-
-      /* Remove ourselves from the kernel list of ethernet devices */
-      /* Warning : can't be called from interrupt, timer or wavelan_close() */
-      if(link->dev != NULL)
-	unregister_netdev(dev);
-      link->dev = NULL;
-
-      if(dev->priv)
-	{
-	  /* Sound strange, but safe... */
-	  ((net_local *) dev->priv)->link = (dev_link_t *) NULL;
-	  ((net_local *) dev->priv)->dev = (device *) NULL;
-	  kfree(dev->priv);
-	}
-      kfree(link->priv);
-    }
-  kfree(link);
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "<- wavelan_detach()\n");
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * The card status event handler. Mostly, this schedules other stuff
- * to run after an event is received. A CARD_REMOVAL event also sets
- * some flags to discourage the net drivers from trying to talk to the
- * card any more.
- */
-static int
-wavelan_event(event_t		event,		/* The event received */
-	      int		priority,
-	      event_callback_args_t *	args)
-{
-  dev_link_t *	link = (dev_link_t *) args->client_data;
-  device *	dev = (device *) link->priv;
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "->wavelan_event(): %s\n",
-	 ((event == CS_EVENT_REGISTRATION_COMPLETE)?"registration complete" :
-	  ((event == CS_EVENT_CARD_REMOVAL) ? "card removal" :
-	   ((event == CS_EVENT_CARD_INSERTION) ? "card insertion" :
-	    ((event == CS_EVENT_PM_SUSPEND) ? "pm suspend" :
-	     ((event == CS_EVENT_RESET_PHYSICAL) ? "physical reset" :
-	      ((event == CS_EVENT_PM_RESUME) ? "pm resume" :
-	       ((event == CS_EVENT_CARD_RESET) ? "card reset" :
-		"unknown"))))))));
-#endif
-
-    switch(event)
-      {
-      case CS_EVENT_REGISTRATION_COMPLETE:
-#ifdef DEBUG_CONFIG_INFO
-	printk(KERN_DEBUG "wavelan_cs: registration complete\n");
-#endif
-	break;
-
-      case CS_EVENT_CARD_REMOVAL:
-	/* Oups ! The card is no more there */
-	link->state &= ~DEV_PRESENT;
-	if(link->state & DEV_CONFIG)
-	  {
-	    /* Accept no more transmissions */
-	    netif_device_detach(dev);
-
-	    /* Release the card */
-	    wv_pcmcia_release((u_long) link);
-	  }
-	break;
-
-      case CS_EVENT_CARD_INSERTION:
-	/* Reset and configure the card */
-	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-	if(wv_pcmcia_config(link) &&
-	   wv_hw_config(dev))
-	  wv_init_info(dev);
-	else
-	  dev->irq = 0;
-	break;
-
-      case CS_EVENT_PM_SUSPEND:
-	/* NB: wavelan_close will be called, but too late, so we are
-	 * obliged to close nicely the wavelan here. David, could you
-	 * close the device before suspending them ? And, by the way,
-	 * could you, on resume, add a "route add -net ..." after the
-	 * ifconfig up ??? Thanks... */
-
-	/* Stop receiving new messages and wait end of transmission */
-	wv_ru_stop(dev);
-
-	/* Power down the module */
-	hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT));
-
-	/* The card is now suspended */
-	link->state |= DEV_SUSPEND;
-	/* Fall through... */
-      case CS_EVENT_RESET_PHYSICAL:
-    	if(link->state & DEV_CONFIG)
-	  {
-      	    if(link->open)
-	      netif_device_detach(dev);
-      	    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);
-      	    if(link->open)	/* If RESET -> True, If RESUME -> False ??? */
-	      {
-		wv_hw_reset(dev);
-		netif_device_attach(dev);
-	      }
-	  }
-	break;
-    }
-
-#ifdef DEBUG_CALLBACK_TRACE
-  printk(KERN_DEBUG "<-wavelan_event()\n");
-#endif
-  return 0;
-}
-
-/****************************** MODULE ******************************/
-/*
- * Module entry points : insertion & removal
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Module insertion : initialisation of the module.
- * Register the card with cardmgr...
- */
-static int __init
-init_wavelan_cs(void)
-{
-  servinfo_t	serv;
-
-#ifdef DEBUG_MODULE_TRACE
-  printk(KERN_DEBUG "-> init_wavelan_cs()\n");
-#ifdef DEBUG_VERSION_SHOW
-  printk(KERN_DEBUG "%s", version);
-#endif
-#endif
-
-  CardServices(GetCardServicesInfo, &serv);
-  if(serv.Revision != CS_RELEASE_CODE)
-    {
-#ifdef DEBUG_CONFIG_ERRORS
-      printk(KERN_WARNING "init_wavelan_cs: Card Services release does not match!\n");
-#endif
-      return -1;
-    }
-
-  register_pccard_driver(&dev_info, &wavelan_attach, &wavelan_detach);
-
-#ifdef DEBUG_MODULE_TRACE
-  printk(KERN_DEBUG "<- init_wavelan_cs()\n");
-#endif
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module removal
- */
-static void __exit
-exit_wavelan_cs(void)
-{
-#ifdef DEBUG_MODULE_TRACE
-  printk(KERN_DEBUG "-> cleanup_module()\n");
-#endif
-#ifdef DEBUG_BASIC_SHOW
-  printk(KERN_NOTICE "wavelan_cs: unloading\n");
-#endif
-
-  /* Do some cleanup of the device list */
-  wv_flush_stale_links();
-
-  /* If there remain some devices... */
-#ifdef DEBUG_CONFIG_ERRORS
-  if(dev_list != NULL)
-    {
-      /* Honestly, if this happen we are in a deep s**t */
-      printk(KERN_INFO "wavelan_cs: devices remaining when removing module\n");
-      printk(KERN_INFO "Please flush your disks and reboot NOW !\n");
-    }
-#endif
-
-  unregister_pccard_driver(&dev_info);
-
-#ifdef DEBUG_MODULE_TRACE
-  printk(KERN_DEBUG "<- cleanup_module()\n");
-#endif
-}
-
-module_init(init_wavelan_cs);
-module_exit(exit_wavelan_cs);
diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.h linux/drivers/net/pcmcia/wavelan_cs.h
--- linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.h	Mon Jan 21 17:22:32 2002
+++ linux/drivers/net/pcmcia/wavelan_cs.h	Wed Dec 31 16:00:00 1969
@@ -1,825 +0,0 @@
-/*
- *	Wavelan Pcmcia driver
- *
- *		Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- *
- * This file contain all definition and declarations necessary for the
- * wavelan pcmcia driver. This file is a private header, so it should
- * be included only on wavelan_cs.c !!!
- */
-
-#ifndef WAVELAN_CS_H
-#define WAVELAN_CS_H
-
-/************************** DOCUMENTATION **************************/
-/*
- * This driver provide a Linux interface to the Wavelan Pcmcia hardware
- * The Wavelan is a product of Lucent (http://www.wavelan.com/).
- * This division was formerly part of NCR and then AT&T.
- * Wavelan are also distributed by DEC (RoamAbout DS)...
- *
- * To know how to use this driver, read the PCMCIA HOWTO.
- * If you want to exploit the many other fonctionalities, look comments
- * in the code...
- *
- * This driver is the result of the effort of many peoples (see below).
- */
-
-/* ------------------------ SPECIFIC NOTES ------------------------ */
-/*
- * Web page
- * --------
- *	I try to maintain a web page with the Wireless LAN Howto at :
- *	    http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
- *
- * SMP
- * ---
- *	We now are SMP compliant (I eventually fixed the remaining bugs).
- *	The driver has been tested on a dual P6-150 and survived my usual
- *	set of torture tests.
- *	Anyway, I spent enough time chasing interrupt re-entrancy during
- *	errors or reconfigure, and I designed the locked/unlocked sections
- *	of the driver with great care, and with the recent addition of
- *	the spinlock (thanks to the new API), we should be quite close to
- *	the truth.
- *	The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
- *	but better safe than sorry (especially at 2 Mb/s ;-).
- *
- *	I have also looked into disabling only our interrupt on the card
- *	(via HACR) instead of all interrupts in the processor (via cli),
- *	so that other driver are not impacted, and it look like it's
- *	possible, but it's very tricky to do right (full of races). As
- *	the gain would be mostly for SMP systems, it can wait...
- *
- * Debugging and options
- * ---------------------
- *	You will find below a set of '#define" allowing a very fine control
- *	on the driver behaviour and the debug messages printed.
- *	The main options are :
- *	o WAVELAN_ROAMING, for the experimental roaming support.
- *	o SET_PSA_CRC, to have your card correctly recognised by
- *	  an access point and the Point-to-Point diagnostic tool.
- *	o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
- *	  (otherwise we always start afresh with some defaults)
- *
- * wavelan_cs.o is darn too big
- * -------------------------
- *	That's true ! There is a very simple way to reduce the driver
- *	object by 33% (yes !). Comment out the following line :
- *		#include <linux/wireless.h>
- *	Other compile options can also reduce the size of it...
- *
- * MAC address and hardware detection :
- * ----------------------------------
- *	The detection code of the wavelan chech that the first 3
- *	octets of the MAC address fit the company code. This type of
- *	detection work well for AT&T cards (because the AT&T code is
- *	hardcoded in wavelan.h), but of course will fail for other
- *	manufacturer.
- *
- *	If you are sure that your card is derived from the wavelan,
- *	here is the way to configure it :
- *	1) Get your MAC address
- *		a) With your card utilities (wfreqsel, instconf, ...)
- *		b) With the driver :
- *			o compile the kernel with DEBUG_CONFIG_INFO enabled
- *			o Boot and look the card messages
- *	2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
- *	3) Compile & verify
- *	4) Send me the MAC code - I will include it in the next version...
- *
- */
-
-/* --------------------- WIRELESS EXTENSIONS --------------------- */
-/*
- * This driver is the first one to support "wireless extensions".
- * This set of extensions provide you some way to control the wireless
- * caracteristics of the hardware in a standard way and support for
- * applications for taking advantage of it (like Mobile IP).
- *
- * You will need to enable the CONFIG_NET_RADIO define in the kernel
- * configuration to enable the wireless extensions (this is the one
- * giving access to the radio network device choice).
- *
- * It might also be a good idea as well to fetch the wireless tools to
- * configure the device and play a bit.
- */
-
-/* ---------------------------- FILES ---------------------------- */
-/*
- * wavelan_cs.c :	The actual code for the driver - C functions
- *
- * wavelan_cs.h :	Private header : local types / vars for the driver
- *
- * wavelan.h :		Description of the hardware interface & structs
- *
- * i82593.h :		Description if the Ethernet controller
- */
-
-/* --------------------------- HISTORY --------------------------- */
-/*
- * The history of the Wavelan drivers is as complicated as history of
- * the Wavelan itself (NCR -> AT&T -> Lucent).
- *
- * All started with Anders Klemets <klemets@paul.rutgers.edu>,
- * writting a Wavelan ISA driver for the MACH microkernel. Girish
- * Welling <welling@paul.rutgers.edu> had also worked on it.
- * Keith Moore modify this for the Pcmcia hardware.
- * 
- * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
- * and add specific Pcmcia support (there is currently no equivalent
- * of the PCMCIA package under BSD...).
- *
- * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
- *
- * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
- *
- * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
- * (with help of the BSDI PCMCIA driver) for PCMCIA.
- * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
- * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
- * correctly 2.00 cards (2.4 GHz with frequency selection).
- * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
- * Pcmcia package (+ bug corrections).
- *
- * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
- * patchs to the Pcmcia driver. After, I added code in the ISA driver
- * for Wireless Extensions and full support of frequency selection
- * cards. Now, I'm doing the same to the Pcmcia driver + some
- * reorganisation.
- * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
- * much needed informations on the Wavelan hardware.
- */
-
-/* By the way : for the copyright & legal stuff :
- * Almost everybody wrote code under GNU or BSD license (or alike),
- * and want that their original copyright remain somewhere in the
- * code (for myself, I go with the GPL).
- * Nobody want to take responsibility for anything, except the fame...
- */
-
-/* --------------------------- CREDITS --------------------------- */
-/*
- * Credits:
- *    Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
- *	Loeke Brederveld of Lucent for providing extremely useful
- *	information about WaveLAN PCMCIA hardware
- *
- *    This driver is based upon several other drivers, in particular:
- *	David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
- *	Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
- *	Anders Klemets' PCMCIA WaveLAN adapter driver
- *	Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
- *
- * Additional Credits:
- *
- *    This software was originally developed under Linux 1.2.3
- *	(Slackware 2.0 distribution).
- *    And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+)
- *	with an HP OmniBook 4000 and then a 5500.
- *
- *    It is based on other device drivers and information either written
- *    or supplied by:
- *	James Ashton (jaa101@syseng.anu.edu.au),
- *	Ajay Bakre (bakre@paul.rutgers.edu),
- *	Donald Becker (becker@super.org),
- *	Jim Binkley <jrb@cs.pdx.edu>,
- *	Loeke Brederveld <lbrederv@wavelan.com>,
- *	Allan Creighton (allanc@cs.su.oz.au),
- *	Brent Elphick <belphick@uwaterloo.ca>,
- *	Joe Finney <joe@comp.lancs.ac.uk>,
- *	Matthew Geier (matthew@cs.su.oz.au),
- *	Remo di Giovanni (remo@cs.su.oz.au),
- *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
- *	David Hinds <dahinds@users.sourceforge.net>,
- *	Jan Hoogendoorn (c/o marteijn@lucent.com),
- *      Bruce Janson <bruce@cs.usyd.edu.au>,
- *	Anthony D. Joseph <adj@lcs.mit.edu>,
- *	Anders Klemets (klemets@paul.rutgers.edu),
- *	Yunzhou Li <yunzhou@strat.iol.unh.edu>,
- *	Marc Meertens (mmeertens@lucent.com),
- *	Keith Moore,
- *	Robert Morris (rtm@das.harvard.edu),
- *	Ian Parkin (ian@cs.su.oz.au),
- *	John Rosenberg (johnr@cs.su.oz.au),
- *	George Rossi (george@phm.gov.au),
- *	Arthur Scott (arthur@cs.su.oz.au),
- *	Stanislav Sinyagin <stas@isf.ru>
- *	Peter Storey,
- *	Jean Tourrilhes <jt@hpl.hp.com>,
- *	Girish Welling (welling@paul.rutgers.edu)
- *	Clark Woodworth <clark@hiway1.exit109.com>
- *	Yongguang Zhang <ygz@isl.hrl.hac.com>...
- */
-
-/* ------------------------- IMPROVEMENTS ------------------------- */
-/*
- * I proudly present :
- *
- * Changes made in 2.8.22 :
- * ----------------------
- *	- improved wv_set_multicast_list
- *	- catch spurious interrupt
- *	- correct release of the device
- *
- * Changes mades in release :
- * ------------------------
- *	- Reorganisation of the code, function name change
- *	- Creation of private header (wavelan_cs.h)
- *	- Reorganised debug messages
- *	- More comments, history, ...
- *	- Configure earlier (in "insert" instead of "open")
- *        and do things only once
- *	- mmc_init : configure the PSA if not done
- *	- mmc_init : 2.00 detection better code for 2.00 init
- *	- better info at startup
- *	- Correct a HUGE bug (volatile & uncalibrated busy loop)
- *	  in wv_82593_cmd => config speedup
- *	- Stop receiving & power down on close (and power up on open)
- *	  use "ifconfig down" & "ifconfig up ; route add -net ..."
- *	- Send packets : add watchdog instead of pooling
- *	- Receive : check frame wrap around & try to recover some frames
- *	- wavelan_set_multicast_list : avoid reset
- *	- add wireless extensions (ioctl & get_wireless_stats)
- *	  get/set nwid/frequency on fly, info for /proc/net/wireless
- *	- Suppress useless stuff from lp (net_local), but add link
- *	- More inlines
- *	- Lot of others minor details & cleanups
- *
- * Changes made in second release :
- * ------------------------------
- *	- Optimise wv_85893_reconfig stuff, fix potential problems
- *	- Change error values for ioctl
- *	- Non blocking wv_ru_stop() + call wv_reset() in case of problems
- *	- Remove development printk from wavelan_watchdog()
- *	- Remove of the watchdog to wavelan_close instead of wavelan_release
- *	  fix potential problems...
- *	- Start debugging suspend stuff (but it's still a bit weird)
- *	- Debug & optimize dump header/packet in Rx & Tx (debug)
- *	- Use "readb" and "writeb" to be kernel 2.1 compliant
- *	- Better handling of bogus interrupts
- *	- Wireless extension : SETSPY and GETSPY
- *	- Remove old stuff (stats - for those needing it, just ask me...)
- *	- Make wireless extensions optional
- *
- * Changes made in third release :
- * -----------------------------
- *	- cleanups & typos
- *	- modif wireless ext (spy -> only one pointer)
- *	- new private ioctl to set/get quality & level threshold
- *	- Init : correct default value of level threshold for pcmcia
- *	- kill watchdog in hw_reset
- *	- more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
- *	- Add message level (debug stuff in /var/adm/debug & errors not
- *	  displayed at console and still in /var/adm/messages)
- *
- * Changes made in fourth release :
- * ------------------------------
- *	- multicast support (yes !) thanks to Yongguang Zhang.
- *
- * Changes made in fifth release (2.9.0) :
- * -------------------------------------
- *	- Revisited multicast code (it was mostly wrong).
- *	- protect code in wv_82593_reconfig with dev->tbusy (oups !)
- *
- * Changes made in sixth release (2.9.1a) :
- * --------------------------------------
- *	- Change the detection code for multi manufacturer code support
- *	- Correct bug (hang kernel) in init when we were "rejecting" a card 
- *
- * Changes made in seventh release (2.9.1b) :
- * ----------------------------------------
- *	- Update to wireless extensions changes
- *	- Silly bug in card initial configuration (psa_conf_status)
- *
- * Changes made in eigth release :
- * -----------------------------
- *	- Small bug in debug code (probably not the last one...)
- *	- 1.2.13 support (thanks to Clark Woodworth)
- *
- * Changes made for release in 2.9.2b :
- * ----------------------------------
- *	- Level threshold is now a standard wireless extension (version 4 !)
- *	- modules parameters types for kernel > 2.1.17
- *	- updated man page
- *	- Others cleanup from David Hinds
- *
- * Changes made for release in 2.9.5 :
- * ---------------------------------
- *	- byte count stats (courtesy of David Hinds)
- *	- Remove dev_tint stuff (courtesy of David Hinds)
- *	- Others cleanup from David Hinds
- *	- Encryption setting from Brent Elphick (thanks a lot !)
- *	- 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
- *
- * Changes made for release in 2.9.6 :
- * ---------------------------------
- *	- fix bug : no longuer disable watchdog in case of bogus interrupt
- *	- increase timeout in config code for picky hardware
- *	- mask unused bits in status (Wireless Extensions)
- *
- * Changes integrated by Justin Seger <jseger@MIT.EDU> & David Hinds :
- * -----------------------------------------------------------------
- *	- Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
- *	- PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
- *	- Better initialisation of the i82593 controller
- *	  from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
- *
- * Changes made for release in 3.0.10 :
- * ----------------------------------
- *	- Fix eject "hang" of the driver under 2.2.X :
- *		o create wv_flush_stale_links()
- *		o Rename wavelan_release to wv_pcmcia_release & move up
- *		o move unregister_netdev to wavelan_detach()
- *		o wavelan_release() no longer call wavelan_detach()
- *		o Suppress "release" timer
- *		o Other cleanups & fixes
- *	- New MAC address in the probe
- *	- Reorg PSA_CRC code (endian neutral & cleaner)
- *	- Correct initialisation of the i82593 from Lucent manual
- *	- Put back the watchdog, with larger timeout
- *	- TRANSMIT_NO_CRC is a "normal" error, so recover from it
- *	  from Derrick J Brashear <shadow@dementia.org>
- *	- Better handling of TX and RX normal failure conditions
- *	- #ifdef out all the roaming code
- *	- Add ESSID & "AP current address" ioctl stubs
- *	- General cleanup of the code
- *
- * Changes made for release in 3.0.13 :
- * ----------------------------------
- *	- Re-enable compilation of roaming code by default, but with
- *	  do_roaming = 0
- *	- Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
- *	  at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
- *	- Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
- *
- * Changes made for release in 3.0.15 :
- * ----------------------------------
- *	- Change e-mail and web page addresses
- *	- Watchdog timer is now correctly expressed in HZ, not in jiffies
- *	- Add channel number to the list of frequencies in range
- *	- Add the (short) list of bit-rates in range
- *	- Developp a new sensitivity... (sens.value & sens.fixed)
- *
- * Changes made for release in 3.1.2 :
- * ---------------------------------
- *	- Fix check for root permission (break instead of exit)
- *	- New nwid & encoding setting (Wireless Extension 9)
- *
- * Changes made for release in 3.1.12 :
- * ----------------------------------
- *	- reworked wv_82593_cmd to avoid using the IRQ handler and doing
- *	  ugly things with interrupts.
- *	- Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog
- *	- Update to new network API (softnet - 2.3.43) :
- *		o replace dev->tbusy (David + me)
- *		o replace dev->tstart (David + me)
- *		o remove dev->interrupt (David)
- *		o add SMP locking via spinlock in splxx (me)
- *		o add spinlock in interrupt handler (me)
- *		o use kernel watchdog instead of ours (me)
- *		o verify that all the changes make sense and work (me)
- *	- Re-sync kernel/pcmcia versions (not much actually)
- *	- A few other cleanups (David & me)...
- *
- * Changes made for release in 3.1.22 :
- * ----------------------------------
- *	- Check that SMP works, remove annoying log message
- *
- * Changes made for release in 3.1.24 :
- * ----------------------------------
- *	- Fix unfrequent card lockup when watchdog was reseting the hardware :
- *		o control first busy loop in wv_82593_cmd()
- *		o Extend spinlock protection in wv_hw_config()
- *
- * Wishes & dreams:
- * ----------------
- *	- Cleanup and integrate the roaming code
- *	  (std debug, set DomainID, decay avg and co...)
- */
-
-/***************************** INCLUDES *****************************/
-
-/* Linux headers that we need */
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/fcntl.h>
-
-#ifdef CONFIG_NET_PCMCIA_RADIO
-#include <linux/wireless.h>		/* Wireless extensions */
-#endif
-
-/* Pcmcia headers that we need */
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/version.h>
-
-/* Wavelan declarations */
-#include "i82593.h"	/* Definitions for the Intel chip */
-
-#include "wavelan.h"	/* Others bits of the hardware */
-
-/************************** DRIVER OPTIONS **************************/
-/*
- * `#define' or `#undef' the following constant to change the behaviour
- * of the driver...
- */
-#define WAVELAN_ROAMING		/* Include experimental roaming code */
-#undef WAVELAN_ROAMING_EXT	/* Enable roaming wireless extensions */
-#undef SET_PSA_CRC		/* Set the CRC in PSA (slower) */
-#define USE_PSA_CONFIG		/* Use info from the PSA */
-#undef STRUCT_CHECK		/* Verify padding of structures */
-#undef EEPROM_IS_PROTECTED	/* Doesn't seem to be necessary */
-#define MULTICAST_AVOID		/* Avoid extra multicast (I'm sceptical) */
-#undef SET_MAC_ADDRESS		/* Experimental */
-
-#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
-/* Warning : these stuff will slow down the driver... */
-#define WIRELESS_SPY		/* Enable spying addresses */
-#undef HISTOGRAM		/* Enable histogram of sig level... */
-#endif
-
-/****************************** DEBUG ******************************/
-
-#undef DEBUG_MODULE_TRACE	/* Module insertion/removal */
-#undef DEBUG_CALLBACK_TRACE	/* Calls made by Linux */
-#undef DEBUG_INTERRUPT_TRACE	/* Calls to handler */
-#undef DEBUG_INTERRUPT_INFO	/* type of interrupt & so on */
-#define DEBUG_INTERRUPT_ERROR	/* problems */
-#undef DEBUG_CONFIG_TRACE	/* Trace the config functions */
-#undef DEBUG_CONFIG_INFO	/* What's going on... */
-#define DEBUG_CONFIG_ERRORS	/* Errors on configuration */
-#undef DEBUG_TX_TRACE		/* Transmission calls */
-#undef DEBUG_TX_INFO		/* Header of the transmitted packet */
-#undef DEBUG_TX_FAIL		/* Normal failure conditions */
-#define DEBUG_TX_ERROR		/* Unexpected conditions */
-#undef DEBUG_RX_TRACE		/* Transmission calls */
-#undef DEBUG_RX_INFO		/* Header of the transmitted packet */
-#undef DEBUG_RX_FAIL		/* Normal failure conditions */
-#define DEBUG_RX_ERROR		/* Unexpected conditions */
-#undef DEBUG_PACKET_DUMP	32	/* Dump packet on the screen */
-#undef DEBUG_IOCTL_TRACE	/* Misc call by Linux */
-#undef DEBUG_IOCTL_INFO		/* Various debug info */
-#define DEBUG_IOCTL_ERROR	/* What's going wrong */
-#define DEBUG_BASIC_SHOW	/* Show basic startup info */
-#undef DEBUG_VERSION_SHOW	/* Print version info */
-#undef DEBUG_PSA_SHOW		/* Dump psa to screen */
-#undef DEBUG_MMC_SHOW		/* Dump mmc to screen */
-#undef DEBUG_SHOW_UNUSED	/* Show also unused fields */
-#undef DEBUG_I82593_SHOW	/* Show i82593 status */
-#undef DEBUG_DEVICE_SHOW	/* Show device parameters */
-
-/************************ CONSTANTS & MACROS ************************/
-
-#ifdef DEBUG_VERSION_SHOW
-static const char *version = "wavelan_cs.c : v23 (SMP + wireless extensions) 20/12/00\n";
-#endif
-
-/* Watchdog temporisation */
-#define	WATCHDOG_JIFFIES	(256*HZ/100)
-
-/* Fix a bug in some old wireless extension definitions */
-#ifndef IW_ESSID_MAX_SIZE
-#define IW_ESSID_MAX_SIZE	32
-#endif
-
-/* ------------------------ PRIVATE IOCTL ------------------------ */
-
-/* Wireless Extension Backward compatibility - Jean II
- * If the new wireless device private ioctl range is not defined,
- * default to standard device private ioctl range */
-#ifndef SIOCIWFIRSTPRIV
-#define SIOCIWFIRSTPRIV	SIOCDEVPRIVATE
-#endif /* SIOCIWFIRSTPRIV */
-
-#define SIOCSIPQTHR	SIOCIWFIRSTPRIV		/* Set quality threshold */
-#define SIOCGIPQTHR	SIOCIWFIRSTPRIV + 1	/* Get quality threshold */
-#define SIOCSIPROAM     SIOCIWFIRSTPRIV + 2	/* Set roaming state */
-#define SIOCGIPROAM     SIOCIWFIRSTPRIV + 3	/* Get roaming state */
-
-#define SIOCSIPHISTO	SIOCIWFIRSTPRIV + 6	/* Set histogram ranges */
-#define SIOCGIPHISTO	SIOCIWFIRSTPRIV + 7	/* Get histogram values */
-
-/*************************** WaveLAN Roaming  **************************/
-#ifdef WAVELAN_ROAMING		/* Conditional compile, see above in options */
-
-#define WAVELAN_ROAMING_DEBUG	 0	/* 1 = Trace of handover decisions */
-					/* 2 = Info on each beacon rcvd... */
-#define MAX_WAVEPOINTS		7	/* Max visible at one time */
-#define WAVEPOINT_HISTORY	5	/* SNR sample history slow search */
-#define WAVEPOINT_FAST_HISTORY	2	/* SNR sample history fast search */
-#define SEARCH_THRESH_LOW	10	/* SNR to enter cell search */
-#define SEARCH_THRESH_HIGH	13	/* SNR to leave cell search */
-#define WAVELAN_ROAMING_DELTA	1	/* Hysteresis value (+/- SNR) */
-#define CELL_TIMEOUT		2*HZ	/* in jiffies */
-
-#define FAST_CELL_SEARCH	1	/* Boolean values... */
-#define NWID_PROMISC		1	/* for code clarity. */
-
-typedef struct wavepoint_beacon
-{
-  unsigned char		dsap,		/* Unused */
-			ssap,		/* Unused */
-			ctrl,		/* Unused */
-			O,U,I,		/* Unused */
-			spec_id1,	/* Unused */
-			spec_id2,	/* Unused */
-			pdu_type,	/* Unused */
-			seq;		/* WavePoint beacon sequence number */
-  unsigned short	domain_id,	/* WavePoint Domain ID */
-			nwid;		/* WavePoint NWID */
-} wavepoint_beacon;
-
-typedef struct wavepoint_history
-{
-  unsigned short	nwid;		/* WavePoint's NWID */
-  int			average_slow;	/* SNR running average */
-  int			average_fast;	/* SNR running average */
-  unsigned char	  sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */
-  unsigned char		qualptr;	/* Index into ringbuffer */
-  unsigned char		last_seq;	/* Last seq. no seen for WavePoint */
-  struct wavepoint_history *next;	/* Next WavePoint in table */
-  struct wavepoint_history *prev;	/* Previous WavePoint in table */
-  unsigned long		last_seen;	/* Time of last beacon recvd, jiffies */
-} wavepoint_history;
-
-struct wavepoint_table
-{
-  wavepoint_history	*head;		/* Start of ringbuffer */
-  int			num_wavepoints;	/* No. of WavePoints visible */
-  unsigned char		locked;		/* Table lock */
-};
-
-#endif	/* WAVELAN_ROAMING */
-
-/****************************** TYPES ******************************/
-
-/* Shortcuts */
-typedef struct net_device	device;
-typedef struct net_device_stats	en_stats;
-typedef struct iw_statistics	iw_stats;
-typedef struct iw_quality	iw_qual;
-typedef struct iw_freq		iw_freq;
-typedef struct net_local	net_local;
-typedef struct timer_list	timer_list;
-
-/* Basic types */
-typedef u_char		mac_addr[WAVELAN_ADDR_SIZE];	/* Hardware address */
-
-/*
- * Static specific data for the interface.
- *
- * For each network interface, Linux keep data in two structure. "device"
- * keep the generic data (same format for everybody) and "net_local" keep
- * the additional specific data.
- * Note that some of this specific data is in fact generic (en_stats, for
- * example).
- */
-struct net_local
-{
-  dev_node_t 	node;		/* ???? What is this stuff ???? */
-  device *	dev;		/* Reverse link... */
-  spinlock_t	spinlock;	/* Serialize access to the hardware (SMP) */
-  dev_link_t *	link;		/* pcmcia structure */
-  en_stats	stats;		/* Ethernet interface statistics */
-  int		nresets;	/* Number of hw resets */
-  u_char	configured;	/* If it is configured */
-  u_char	reconfig_82593;	/* Need to reconfigure the controller */
-  u_char	promiscuous;	/* Promiscuous mode */
-  u_char	allmulticast;	/* All Multicast mode */
-  int		mc_count;	/* Number of multicast addresses */
-
-  int   	stop;		/* Current i82593 Stop Hit Register */
-  int   	rfp;		/* Last DMA machine receive pointer */
-  int		overrunning;	/* Receiver overrun flag */
-
-#ifdef WIRELESS_EXT
-  iw_stats	wstats;		/* Wireless specific stats */
-#endif
-
-#ifdef WIRELESS_SPY
-  int		spy_number;		/* Number of addresses to spy */
-  mac_addr	spy_address[IW_MAX_SPY];	/* The addresses to spy */
-  iw_qual	spy_stat[IW_MAX_SPY];		/* Statistics gathered */
-#endif	/* WIRELESS_SPY */
-#ifdef HISTOGRAM
-  int		his_number;		/* Number of intervals */
-  u_char	his_range[16];		/* Boundaries of interval ]n-1; n] */
-  u_long	his_sum[16];		/* Sum in interval */
-#endif	/* HISTOGRAM */
-#ifdef WAVELAN_ROAMING
-  u_long	domain_id;	/* Domain ID we lock on for roaming */
-  int		filter_domains;	/* Check Domain ID of beacon found */
- struct wavepoint_table	wavepoint_table;	/* Table of visible WavePoints*/
-  wavepoint_history *	curr_point;		/* Current wavepoint */
-  int			cell_search;		/* Searching for new cell? */
-  struct timer_list	cell_timer;		/* Garbage collection */
-#endif	/* WAVELAN_ROAMING */
-};
-
-/**************************** PROTOTYPES ****************************/
-
-#ifdef WAVELAN_ROAMING
-/* ---------------------- ROAMING SUBROUTINES -----------------------*/
-
-wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
-wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
-void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
-void wl_cell_expiry(unsigned long data);
-wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
-void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
-void wv_nwid_filter(unsigned char mode, net_local *lp);
-void wv_roam_init(struct net_device *dev);
-void wv_roam_cleanup(struct net_device *dev);
-#endif	/* WAVELAN_ROAMING */
-
-/* ----------------------- MISC SUBROUTINES ------------------------ */
-static inline void
-	wv_splhi(net_local *,		/* Disable interrupts */
-		 unsigned long *);	/* flags */
-static inline void
-	wv_splx(net_local *,		/* ReEnable interrupts */
-		unsigned long *);	/* flags */
-static void
-	cs_error(client_handle_t,	/* Report error to cardmgr */
-		 int,
-		 int);
-/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
-static inline u_char		/* data */
-	hasr_read(u_long);	/* Read the host interface : base address */
-static inline void
-	hacr_write(u_long,	/* Write to host interface : base address */
-		   u_char),	/* data */
-	hacr_write_slow(u_long,
-		   u_char);
-static void
-	psa_read(device *,	/* Read the Parameter Storage Area */
-		 int,		/* offset in PSA */
-		 u_char *,	/* buffer to fill */
-		 int),		/* size to read */
-	psa_write(device *,	/* Write to the PSA */
-		  int,		/* Offset in psa */
-		  u_char *,	/* Buffer in memory */
-		  int);		/* Length of buffer */
-static inline void
-	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control */
-		u_short,
-		u_char),
-	mmc_write(u_long,	/* Write n bytes to the MMC */
-		  u_char,
-		  u_char *,
-		  int);
-static inline u_char		/* Read 1 byte from the MMC */
-	mmc_in(u_long,
-	       u_short);
-static inline void
-	mmc_read(u_long,	/* Read n bytes from the MMC */
-		 u_char,
-		 u_char *,
-		 int),
-	fee_wait(u_long,	/* Wait for frequency EEprom : base address */
-		 int,		/* Base delay to wait for */
-		 int);		/* Number of time to wait */
-static void
-	fee_read(u_long,	/* Read the frequency EEprom : base address */
-		 u_short,	/* destination offset */
-		 u_short *,	/* data buffer */
-		 int);		/* number of registers */
-/* ---------------------- I82593 SUBROUTINES ----------------------- */
-static int
-	wv_82593_cmd(device *,	/* synchronously send a command to i82593 */ 
-		     char *,
-		     int,
-		     int);
-static inline int
-	wv_diag(device *);	/* Diagnostique the i82593 */
-static int
-	read_ringbuf(device *,	/* Read a receive buffer */
-		     int,
-		     char *,
-		     int);
-static inline void
-	wv_82593_reconfig(device *);	/* Reconfigure the controller */
-/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
-static inline void
-	wv_init_info(device *);	/* display startup info */
-/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
-static en_stats	*
-	wavelan_get_stats(device *);	/* Give stats /proc/net/dev */
-/* ----------------------- PACKET RECEPTION ----------------------- */
-static inline int
-	wv_start_of_frame(device *,	/* Seek beggining of current frame */
-			  int,	/* end of frame */
-			  int);	/* start of buffer */
-static inline void
-	wv_packet_read(device *,	/* Read a packet from a frame */
-		       int,
-		       int),
-	wv_packet_rcv(device *);	/* Read all packets waiting */
-/* --------------------- PACKET TRANSMISSION --------------------- */
-static inline void
-	wv_packet_write(device *,	/* Write a packet to the Tx buffer */
-			void *,
-			short);
-static int
-	wavelan_packet_xmit(struct sk_buff *,	/* Send a packet */
-			    device *);
-/* -------------------- HARDWARE CONFIGURATION -------------------- */
-static inline int
-	wv_mmc_init(device *);	/* Initialize the modem */
-static int
-	wv_ru_stop(device *),	/* Stop the i82593 receiver unit */
-	wv_ru_start(device *);	/* Start the i82593 receiver unit */
-static int
-	wv_82593_config(device *);	/* Configure the i82593 */
-static inline int
-	wv_pcmcia_reset(device *);	/* Reset the pcmcia interface */
-static int
-	wv_hw_config(device *);	/* Reset & configure the whole hardware */
-static inline void
-	wv_hw_reset(device *);	/* Same, + start receiver unit */
-static inline int
-	wv_pcmcia_config(dev_link_t *);	/* Configure the pcmcia interface */
-static void
-	wv_pcmcia_release(u_long),	/* Remove a device */
-	wv_flush_stale_links(void);	/* "detach" all possible devices */
-/* ---------------------- INTERRUPT HANDLING ---------------------- */
-static void
-	wavelan_interrupt(int,	/* Interrupt handler */
-			  void *,
-			  struct pt_regs *);
-static void
-	wavelan_watchdog(device *);	/* Transmission watchdog */
-/* ------------------- CONFIGURATION CALLBACKS ------------------- */
-static int
-	wavelan_open(device *),		/* Open the device */
-	wavelan_close(device *);	/* Close the device */
-static dev_link_t *
-	wavelan_attach(void);		/* Create a new device */
-static void
-	wavelan_detach(dev_link_t *);	/* Destroy a removed device */
-static int
-	wavelan_event(event_t,		/* Manage pcmcia events */
-		      int,
-		      event_callback_args_t *);
-
-/**************************** VARIABLES ****************************/
-
-static dev_info_t dev_info = "wavelan_cs";
-static dev_link_t *dev_list = NULL;	/* Linked list of devices */
-
-/*
- * Parameters that can be set with 'insmod'
- * The exact syntax is 'insmod wavelan_cs.o <var>=<value>'
- */
-
-/* Bit map of interrupts to choose from */
-/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
-static int	irq_mask = 0xdeb8;
-static int 	irq_list[4] = { -1 };
-
-/* Shared memory speed, in ns */
-static int	mem_speed = 0;
-
-/* New module interface */
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-MODULE_PARM(mem_speed, "i");
-
-#ifdef WAVELAN_ROAMING		/* Conditional compile, see above in options */
-/* Enable roaming mode ? No ! Please keep this to 0 */
-static int	do_roaming = 0;
-MODULE_PARM(do_roaming, "i");
-#endif	/* WAVELAN_ROAMING */
-
-MODULE_LICENSE("GPL");
-
-#endif	/* WAVELAN_CS_H */
-
diff -urN linux-2.5.3-pre2/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- linux-2.5.3-pre2/drivers/net/wavelan.c	Wed Jan  9 14:54:38 2002
+++ linux/drivers/net/wavelan.c	Wed Dec 31 16:00:00 1969
@@ -1,4342 +0,0 @@
-/*
- *	WaveLAN ISA driver
- *
- *		Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- * Original copyright follows (also see the end of this file).
- * See wavelan.p.h for details.
- *
- *
- *
- * AT&T GIS (nee NCR) WaveLAN card:
- *	An Ethernet-like radio transceiver
- *	controlled by an Intel 82586 coprocessor.
- */
-
-#include "wavelan.p.h"		/* Private header */
-
-/************************* MISC SUBROUTINES **************************/
-/*
- * Subroutines which won't fit in one of the following category
- * (WaveLAN modem or i82586)
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for disabling interrupts and locking the driver.
- * (note : inline, so optimised away)
- */
-static inline void wv_splhi(net_local *		lp,
-			    unsigned long *	pflags)
-{
-	spin_lock_irqsave(&lp->spinlock, *pflags);
-	/* Note : above does the cli(); itself */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for re-enabling interrupts and un-locking the driver.
- */
-static inline void wv_splx(net_local *		lp,
-			   unsigned long *	pflags)
-{
-	spin_unlock_irqrestore(&lp->spinlock, *pflags);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Translate irq number to PSA irq parameter
- */
-static u8 wv_irq_to_psa(int irq)
-{
-	if (irq < 0 || irq >= NELS(irqvals))
-		return 0;
-
-	return irqvals[irq];
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Translate PSA irq parameter to irq number 
- */
-static int __init wv_psa_to_irq(u8 irqval)
-{
-	int irq;
-
-	for (irq = 0; irq < NELS(irqvals); irq++)
-		if (irqvals[irq] == irqval)
-			return irq;
-
-	return -1;
-}
-
-#ifdef STRUCT_CHECK
-/*------------------------------------------------------------------*/
-/*
- * Sanity routine to verify the sizes of the various WaveLAN interface
- * structures.
- */
-static char *wv_struct_check(void)
-{
-#define	SC(t,s,n)	if (sizeof(t) != s) return(n);
-
-	SC(psa_t, PSA_SIZE, "psa_t");
-	SC(mmw_t, MMW_SIZE, "mmw_t");
-	SC(mmr_t, MMR_SIZE, "mmr_t");
-	SC(ha_t, HA_SIZE, "ha_t");
-
-#undef	SC
-
-	return ((char *) NULL);
-}				/* wv_struct_check */
-#endif				/* STRUCT_CHECK */
-
-/********************* HOST ADAPTER SUBROUTINES *********************/
-/*
- * Useful subroutines to manage the WaveLAN ISA interface
- *
- * One major difference with the PCMCIA hardware (except the port mapping)
- * is that we have to keep the state of the Host Control Register
- * because of the interrupt enable & bus size flags.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read from card's Host Adaptor Status Register.
- */
-static inline u16 hasr_read(unsigned long ioaddr)
-{
-	return (inw(HASR(ioaddr)));
-}				/* hasr_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register.
- */
-static inline void hacr_write(unsigned long ioaddr, u16 hacr)
-{
-	outw(hacr, HACR(ioaddr));
-}				/* hacr_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register. Include a delay for
- * those times when it is needed.
- */
-static inline void hacr_write_slow(unsigned long ioaddr, u16 hacr)
-{
-	hacr_write(ioaddr, hacr);
-	/* delay might only be needed sometimes */
-	mdelay(1);
-}				/* hacr_write_slow */
-
-/*------------------------------------------------------------------*/
-/*
- * Set the channel attention bit.
- */
-static inline void set_chan_attn(unsigned long ioaddr, u16 hacr)
-{
-	hacr_write(ioaddr, hacr | HACR_CA);
-}				/* set_chan_attn */
-
-/*------------------------------------------------------------------*/
-/*
- * Reset, and then set host adaptor into default mode.
- */
-static inline void wv_hacr_reset(unsigned long ioaddr)
-{
-	hacr_write_slow(ioaddr, HACR_RESET);
-	hacr_write(ioaddr, HACR_DEFAULT);
-}				/* wv_hacr_reset */
-
-/*------------------------------------------------------------------*/
-/*
- * Set the I/O transfer over the ISA bus to 8-bit mode
- */
-static inline void wv_16_off(unsigned long ioaddr, u16 hacr)
-{
-	hacr &= ~HACR_16BITS;
-	hacr_write(ioaddr, hacr);
-}				/* wv_16_off */
-
-/*------------------------------------------------------------------*/
-/*
- * Set the I/O transfer over the ISA bus to 8-bit mode
- */
-static inline void wv_16_on(unsigned long ioaddr, u16 hacr)
-{
-	hacr |= HACR_16BITS;
-	hacr_write(ioaddr, hacr);
-}				/* wv_16_on */
-
-/*------------------------------------------------------------------*/
-/*
- * Disable interrupts on the WaveLAN hardware.
- * (called by wv_82586_stop())
- */
-static inline void wv_ints_off(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	
-	lp->hacr &= ~HACR_INTRON;
-	hacr_write(ioaddr, lp->hacr);
-}				/* wv_ints_off */
-
-/*------------------------------------------------------------------*/
-/*
- * Enable interrupts on the WaveLAN hardware.
- * (called by wv_hw_reset())
- */
-static inline void wv_ints_on(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-
-	lp->hacr |= HACR_INTRON;
-	hacr_write(ioaddr, lp->hacr);
-}				/* wv_ints_on */
-
-/******************* MODEM MANAGEMENT SUBROUTINES *******************/
-/*
- * Useful subroutines to manage the modem of the WaveLAN
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read the Parameter Storage Area from the WaveLAN card's memory
- */
-/*
- * Read bytes from the PSA.
- */
-static void psa_read(unsigned long ioaddr, u16 hacr, int o,	/* offset in PSA */
-		     u8 * b,	/* buffer to fill */
-		     int n)
-{				/* size to read */
-	wv_16_off(ioaddr, hacr);
-
-	while (n-- > 0) {
-		outw(o, PIOR2(ioaddr));
-		o++;
-		*b++ = inb(PIOP2(ioaddr));
-	}
-
-	wv_16_on(ioaddr, hacr);
-}				/* psa_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write the Parameter Storage Area to the WaveLAN card's memory.
- */
-static void psa_write(unsigned long ioaddr, u16 hacr, int o,	/* Offset in PSA */
-		      u8 * b,	/* Buffer in memory */
-		      int n)
-{				/* Length of buffer */
-	int count = 0;
-
-	wv_16_off(ioaddr, hacr);
-
-	while (n-- > 0) {
-		outw(o, PIOR2(ioaddr));
-		o++;
-
-		outb(*b, PIOP2(ioaddr));
-		b++;
-
-		/* Wait for the memory to finish its write cycle */
-		count = 0;
-		while ((count++ < 100) &&
-		       (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1);
-	}
-
-	wv_16_on(ioaddr, hacr);
-}				/* psa_write */
-
-#ifdef SET_PSA_CRC
-/*------------------------------------------------------------------*/
-/*
- * Calculate the PSA CRC
- * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
- * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA)
- *
- * The Windows drivers don't use the CRC, but the AP and the PtP tool
- * depend on it.
- */
-static inline u16 psa_crc(u8 * psa,	/* The PSA */
-			      int size)
-{				/* Number of short for CRC */
-	int byte_cnt;		/* Loop on the PSA */
-	u16 crc_bytes = 0;	/* Data in the PSA */
-	int bit_cnt;		/* Loop on the bits of the short */
-
-	for (byte_cnt = 0; byte_cnt < size; byte_cnt++) {
-		crc_bytes ^= psa[byte_cnt];	/* Its an xor */
-
-		for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) {
-			if (crc_bytes & 0x0001)
-				crc_bytes = (crc_bytes >> 1) ^ 0xA001;
-			else
-				crc_bytes >>= 1;
-		}
-	}
-
-	return crc_bytes;
-}				/* psa_crc */
-#endif				/* SET_PSA_CRC */
-
-/*------------------------------------------------------------------*/
-/*
- * update the checksum field in the Wavelan's PSA
- */
-static void update_psa_checksum(device * dev, unsigned long ioaddr, u16 hacr)
-{
-#ifdef SET_PSA_CRC
-	psa_t psa;
-	u16 crc;
-
-	/* read the parameter storage area */
-	psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
-
-	/* update the checksum */
-	crc = psa_crc((unsigned char *) &psa,
-		      sizeof(psa) - sizeof(psa.psa_crc[0]) -
-		      sizeof(psa.psa_crc[1])
-		      - sizeof(psa.psa_crc_status));
-
-	psa.psa_crc[0] = crc & 0xFF;
-	psa.psa_crc[1] = (crc & 0xFF00) >> 8;
-
-	/* Write it ! */
-	psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa,
-		  (unsigned char *) &psa.psa_crc, 2);
-
-#ifdef DEBUG_IOCTL_INFO
-	printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
-	       dev->name, psa.psa_crc[0], psa.psa_crc[1]);
-
-	/* Check again (luxury !) */
-	crc = psa_crc((unsigned char *) &psa,
-		      sizeof(psa) - sizeof(psa.psa_crc_status));
-
-	if (crc != 0)
-		printk(KERN_WARNING
-		       "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n",
-		       dev->name);
-#endif				/* DEBUG_IOCTL_INFO */
-#endif				/* SET_PSA_CRC */
-}				/* update_psa_checksum */
-
-/*------------------------------------------------------------------*/
-/*
- * Write 1 byte to the MMC.
- */
-static inline void mmc_out(unsigned long ioaddr, u16 o, u8 d)
-{
-	/* Wait for MMC to go idle */
-	while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
-
-	outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr));
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to write bytes to the Modem Management Controller.
- * We start at the end because it is the way it should be!
- */
-static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n)
-{
-	o += n;
-	b += n;
-
-	while (n-- > 0)
-		mmc_out(ioaddr, --o, *(--b));
-}				/* mmc_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Read a byte from the MMC.
- * Optimised version for 1 byte, avoid using memory.
- */
-static inline u8 mmc_in(unsigned long ioaddr, u16 o)
-{
-	while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
-	outw(o << 1, MMCR(ioaddr));
-
-	while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
-	return (u8) (inw(MMCR(ioaddr)) >> 8);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read bytes from the Modem Management Controller.
- * The implementation is complicated by a lack of address lines,
- * which prevents decoding of the low-order bit.
- * (code has just been moved in the above function)
- * We start at the end because it is the way it should be!
- */
-static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n)
-{
-	o += n;
-	b += n;
-
-	while (n-- > 0)
-		*(--b) = mmc_in(ioaddr, --o);
-}				/* mmc_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the type of encryption available.
- */
-static inline int mmc_encr(unsigned long ioaddr)
-{				/* I/O port of the card */
-	int temp;
-
-	temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
-	if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
-		return 0;
-	else
-		return temp;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wait for the frequency EEPROM to complete a command.
- * I hope this one will be optimally inlined.
- */
-static inline void fee_wait(unsigned long ioaddr,	/* I/O port of the card */
-			    int delay,	/* Base delay to wait for */
-			    int number)
-{				/* Number of time to wait */
-	int count = 0;		/* Wait only a limited time */
-
-	while ((count++ < number) &&
-	       (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
-		MMR_FEE_STATUS_BUSY)) udelay(delay);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the Frequency EEPROM (frequency select cards).
- */
-static void fee_read(unsigned long ioaddr,	/* I/O port of the card */
-		     u16 o,	/* destination offset */
-		     u16 * b,	/* data buffer */
-		     int n)
-{				/* number of registers */
-	b += n;			/* Position at the end of the area */
-
-	/* Write the address */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
-
-	/* Loop on all buffer */
-	while (n-- > 0) {
-		/* Write the read command */
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
-			MMW_FEE_CTRL_READ);
-
-		/* Wait until EEPROM is ready (should be quick). */
-		fee_wait(ioaddr, 10, 100);
-
-		/* Read the value. */
-		*--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
-			mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
-	}
-}
-
-#ifdef WIRELESS_EXT		/* if the wireless extension exists in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes from the Frequency EEPROM (frequency select cards).
- * This is a bit complicated, because the frequency EEPROM has to
- * be unprotected and the write enabled.
- * Jean II
- */
-static void fee_write(unsigned long ioaddr,	/* I/O port of the card */
-		      u16 o,	/* destination offset */
-		      u16 * b,	/* data buffer */
-		      int n)
-{				/* number of registers */
-	b += n;			/* Position at the end of the area. */
-
-#ifdef EEPROM_IS_PROTECTED	/* disabled */
-#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
-	/* Ask to read the protected register */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
-
-	fee_wait(ioaddr, 10, 100);
-
-	/* Read the protected register. */
-	printk("Protected 2:  %02X-%02X\n",
-	       mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
-	       mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
-#endif				/* DOESNT_SEEM_TO_WORK */
-
-	/* Enable protected register. */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
-
-	fee_wait(ioaddr, 10, 100);
-
-	/* Unprotect area. */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
-	/* or use: */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
-#endif				/* DOESNT_SEEM_TO_WORK */
-
-	fee_wait(ioaddr, 10, 100);
-#endif				/* EEPROM_IS_PROTECTED */
-
-	/* Write enable. */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
-
-	fee_wait(ioaddr, 10, 100);
-
-	/* Write the EEPROM address. */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
-
-	/* Loop on all buffer */
-	while (n-- > 0) {
-		/* Write the value. */
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
-
-		/* Write the write command. */
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
-			MMW_FEE_CTRL_WRITE);
-
-		/* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */
-		mdelay(10);
-		fee_wait(ioaddr, 10, 100);
-	}
-
-	/* Write disable. */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
-
-	fee_wait(ioaddr, 10, 100);
-
-#ifdef EEPROM_IS_PROTECTED	/* disabled */
-	/* Reprotect EEPROM. */
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
-	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-
-	fee_wait(ioaddr, 10, 100);
-#endif				/* EEPROM_IS_PROTECTED */
-}
-#endif				/* WIRELESS_EXT */
-
-/************************ I82586 SUBROUTINES *************************/
-/*
- * Useful subroutines to manage the Ethernet controller
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the on-board RAM.
- * Why does inlining this function make it fail?
- */
-static /*inline */ void obram_read(unsigned long ioaddr,
-				   u16 o, u8 * b, int n)
-{
-	outw(o, PIOR1(ioaddr));
-	insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes to the on-board RAM.
- */
-static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n)
-{
-	outw(o, PIOR1(ioaddr));
-	outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Acknowledge the reading of the status issued by the i82586.
- */
-static void wv_ack(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	u16 scb_cs;
-	int i;
-
-	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
-		   (unsigned char *) &scb_cs, sizeof(scb_cs));
-	scb_cs &= SCB_ST_INT;
-
-	if (scb_cs == 0)
-		return;
-
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
-		    (unsigned char *) &scb_cs, sizeof(scb_cs));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	for (i = 1000; i > 0; i--) {
-		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
-			   (unsigned char *) &scb_cs, sizeof(scb_cs));
-		if (scb_cs == 0)
-			break;
-
-		udelay(10);
-	}
-	udelay(100);
-
-#ifdef DEBUG_CONFIG_ERROR
-	if (i <= 0)
-		printk(KERN_INFO
-		       "%s: wv_ack(): board not accepting command.\n",
-		       dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Set channel attention bit and busy wait until command has
- * completed, then acknowledge completion of the command.
- */
-static inline int wv_synchronous_cmd(device * dev, const char *str)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	u16 scb_cmd;
-	ach_t cb;
-	int i;
-
-	scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
-		    (unsigned char *) &scb_cmd, sizeof(scb_cmd));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	for (i = 1000; i > 0; i--) {
-		obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb,
-			   sizeof(cb));
-		if (cb.ac_status & AC_SFLD_C)
-			break;
-
-		udelay(10);
-	}
-	udelay(100);
-
-	if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
-		       dev->name, str, cb.ac_status);
-#endif
-#ifdef DEBUG_I82586_SHOW
-		wv_scb_show(ioaddr);
-#endif
-		return -1;
-	}
-
-	/* Ack the status */
-	wv_ack(dev);
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Configuration commands completion interrupt.
- * Check if done, and if OK.
- */
-static inline int
-wv_config_complete(device * dev, unsigned long ioaddr, net_local * lp)
-{
-	unsigned short mcs_addr;
-	unsigned short status;
-	int ret;
-
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
-#endif
-
-	mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
-	    + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
-
-	/* Read the status of the last command (set mc list). */
-	obram_read(ioaddr, acoff(mcs_addr, ac_status),
-		   (unsigned char *) &status, sizeof(status));
-
-	/* If not completed -> exit */
-	if ((status & AC_SFLD_C) == 0)
-		ret = 0;	/* Not ready to be scrapped */
-	else {
-#ifdef DEBUG_CONFIG_ERROR
-		unsigned short cfg_addr;
-		unsigned short ias_addr;
-
-		/* Check mc_config command */
-		if ((status & AC_SFLD_OK) != AC_SFLD_OK)
-			printk(KERN_INFO
-			       "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
-			       dev->name, status);
-
-		/* check ia-config command */
-		ias_addr = mcs_addr - sizeof(ac_ias_t);
-		obram_read(ioaddr, acoff(ias_addr, ac_status),
-			   (unsigned char *) &status, sizeof(status));
-		if ((status & AC_SFLD_OK) != AC_SFLD_OK)
-			printk(KERN_INFO
-			       "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n",
-			       dev->name, status);
-
-		/* Check config command. */
-		cfg_addr = ias_addr - sizeof(ac_cfg_t);
-		obram_read(ioaddr, acoff(cfg_addr, ac_status),
-			   (unsigned char *) &status, sizeof(status));
-		if ((status & AC_SFLD_OK) != AC_SFLD_OK)
-			printk(KERN_INFO
-			       "%s: wv_config_complete(): configure failed; status = 0x%x\n",
-			       dev->name, status);
-#endif	/* DEBUG_CONFIG_ERROR */
-
-		ret = 1;	/* Ready to be scrapped */
-	}
-
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name,
-	       ret);
-#endif
-	return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Command completion interrupt.
- * Reclaim as many freed tx buffers as we can.
- * (called in wavelan_interrupt()).
- * Note : the spinlock is already grabbed for us.
- */
-static int wv_complete(device * dev, unsigned long ioaddr, net_local * lp)
-{
-	int nreaped = 0;
-
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
-#endif
-
-	/* Loop on all the transmit buffers */
-	while (lp->tx_first_in_use != I82586NULL) {
-		unsigned short tx_status;
-
-		/* Read the first transmit buffer */
-		obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status),
-			   (unsigned char *) &tx_status,
-			   sizeof(tx_status));
-
-		/* If not completed -> exit */
-		if ((tx_status & AC_SFLD_C) == 0)
-			break;
-
-		/* Hack for reconfiguration */
-		if (tx_status == 0xFFFF)
-			if (!wv_config_complete(dev, ioaddr, lp))
-				break;	/* Not completed */
-
-		/* We now remove this buffer */
-		nreaped++;
-		--lp->tx_n_in_use;
-
-/*
-if (lp->tx_n_in_use > 0)
-	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
-*/
-
-		/* Was it the last one? */
-		if (lp->tx_n_in_use <= 0)
-			lp->tx_first_in_use = I82586NULL;
-		else {
-			/* Next one in the chain */
-			lp->tx_first_in_use += TXBLOCKZ;
-			if (lp->tx_first_in_use >=
-			    OFFSET_CU +
-			    NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -=
-				    NTXBLOCKS * TXBLOCKZ;
-		}
-
-		/* Hack for reconfiguration */
-		if (tx_status == 0xFFFF)
-			continue;
-
-		/* Now, check status of the finished command */
-		if (tx_status & AC_SFLD_OK) {
-			int ncollisions;
-
-			lp->stats.tx_packets++;
-			ncollisions = tx_status & AC_SFLD_MAXCOL;
-			lp->stats.collisions += ncollisions;
-#ifdef DEBUG_TX_INFO
-			if (ncollisions > 0)
-				printk(KERN_DEBUG
-				       "%s: wv_complete(): tx completed after %d collisions.\n",
-				       dev->name, ncollisions);
-#endif
-		} else {
-			lp->stats.tx_errors++;
-			if (tx_status & AC_SFLD_S10) {
-				lp->stats.tx_carrier_errors++;
-#ifdef DEBUG_TX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_complete(): tx error: no CS.\n",
-				       dev->name);
-#endif
-			}
-			if (tx_status & AC_SFLD_S9) {
-				lp->stats.tx_carrier_errors++;
-#ifdef DEBUG_TX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_complete(): tx error: lost CTS.\n",
-				       dev->name);
-#endif
-			}
-			if (tx_status & AC_SFLD_S8) {
-				lp->stats.tx_fifo_errors++;
-#ifdef DEBUG_TX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_complete(): tx error: slow DMA.\n",
-				       dev->name);
-#endif
-			}
-			if (tx_status & AC_SFLD_S6) {
-				lp->stats.tx_heartbeat_errors++;
-#ifdef DEBUG_TX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_complete(): tx error: heart beat.\n",
-				       dev->name);
-#endif
-			}
-			if (tx_status & AC_SFLD_S5) {
-				lp->stats.tx_aborted_errors++;
-#ifdef DEBUG_TX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_complete(): tx error: too many collisions.\n",
-				       dev->name);
-#endif
-			}
-		}
-
-#ifdef DEBUG_TX_INFO
-		printk(KERN_DEBUG
-		       "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
-		       dev->name, tx_status);
-#endif
-	}
-
-#ifdef DEBUG_INTERRUPT_INFO
-	if (nreaped > 1)
-		printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n",
-		       dev->name, nreaped);
-#endif
-
-	/*
-	 * Inform upper layers.
-	 */
-	if (lp->tx_n_in_use < NTXBLOCKS - 1) {
-		netif_wake_queue(dev);
-	}
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
-#endif
-	return nreaped;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Reconfigure the i82586, or at least ask for it.
- * Because wv_82586_config uses a transmission buffer, we must do it
- * when we are sure that there is one left, so we do it now
- * or in wavelan_packet_xmit() (I can't find any better place,
- * wavelan_interrupt is not an option), so you may experience
- * delays sometimes.
- */
-static inline void wv_82586_reconfig(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long flags;
-
-	/* Arm the flag, will be cleard in wv_82586_config() */
-	lp->reconfig_82586 = 1;
-
-	/* Check if we can do it now ! */
-	if((netif_running(dev)) && !(netif_queue_stopped(dev))) {
-		wv_splhi(lp, &flags);
-		/* May fail */
-		wv_82586_config(dev);
-		wv_splx(lp, &flags);
-	}
-	else {
-#ifdef DEBUG_CONFIG_INFO
-		printk(KERN_DEBUG
-		       "%s: wv_82586_reconfig(): delayed (state = %lX)\n",
-			       dev->name, dev->state);
-#endif
-	}
-}
-
-/********************* DEBUG & INFO SUBROUTINES *********************/
-/*
- * This routine is used in the code to show information for debugging.
- * Most of the time, it dumps the contents of hardware structures.
- */
-
-#ifdef DEBUG_PSA_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted contents of the Parameter Storage Area.
- */
-static void wv_psa_show(psa_t * p)
-{
-	printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n");
-	printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
-	       p->psa_io_base_addr_1,
-	       p->psa_io_base_addr_2,
-	       p->psa_io_base_addr_3, p->psa_io_base_addr_4);
-	printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
-	       p->psa_rem_boot_addr_1,
-	       p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3);
-	printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
-	printk("psa_int_req_no: %d\n", p->psa_int_req_no);
-#ifdef DEBUG_SHOW_UNUSED
-	printk(KERN_DEBUG
-	       "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
-	       p->psa_unused0[0], p->psa_unused0[1], p->psa_unused0[2],
-	       p->psa_unused0[3], p->psa_unused0[4], p->psa_unused0[5],
-	       p->psa_unused0[6]);
-#endif				/* DEBUG_SHOW_UNUSED */
-	printk(KERN_DEBUG
-	       "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       p->psa_univ_mac_addr[0], p->psa_univ_mac_addr[1],
-	       p->psa_univ_mac_addr[2], p->psa_univ_mac_addr[3],
-	       p->psa_univ_mac_addr[4], p->psa_univ_mac_addr[5]);
-	printk(KERN_DEBUG
-	       "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       p->psa_local_mac_addr[0], p->psa_local_mac_addr[1],
-	       p->psa_local_mac_addr[2], p->psa_local_mac_addr[3],
-	       p->psa_local_mac_addr[4], p->psa_local_mac_addr[5]);
-	printk(KERN_DEBUG "psa_univ_local_sel: %d, ",
-	       p->psa_univ_local_sel);
-	printk("psa_comp_number: %d, ", p->psa_comp_number);
-	printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
-	printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
-	       p->psa_feature_select);
-	printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
-	printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
-	printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
-	printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0],
-	       p->psa_nwid[1]);
-	printk("psa_nwid_select: %d\n", p->psa_nwid_select);
-	printk(KERN_DEBUG "psa_encryption_select: %d, ",
-	       p->psa_encryption_select);
-	printk
-	    ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
-	     p->psa_encryption_key[0], p->psa_encryption_key[1],
-	     p->psa_encryption_key[2], p->psa_encryption_key[3],
-	     p->psa_encryption_key[4], p->psa_encryption_key[5],
-	     p->psa_encryption_key[6], p->psa_encryption_key[7]);
-	printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
-	printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
-	       p->psa_call_code[0]);
-	printk
-	    ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
-	     p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2],
-	     p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5],
-	     p->psa_call_code[6], p->psa_call_code[7]);
-#ifdef DEBUG_SHOW_UNUSED
-	printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
-	       p->psa_reserved[0],
-	       p->psa_reserved[1], p->psa_reserved[2], p->psa_reserved[3]);
-#endif				/* DEBUG_SHOW_UNUSED */
-	printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
-	printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
-	printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
-}				/* wv_psa_show */
-#endif				/* DEBUG_PSA_SHOW */
-
-#ifdef DEBUG_MMC_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the Modem Management Controller.
- * This function needs to be completed.
- */
-static void wv_mmc_show(device * dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	net_local *lp = (net_local *) dev->priv;
-	mmr_t m;
-
-	/* Basic check */
-	if (hasr_read(ioaddr) & HASR_NO_CLK) {
-		printk(KERN_WARNING
-		       "%s: wv_mmc_show: modem not connected\n",
-		       dev->name);
-		return;
-	}
-
-	/* Read the mmc */
-	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
-	mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m));
-	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
-
-#ifdef WIRELESS_EXT		/* if wireless extension exists in the kernel */
-	/* Don't forget to update statistics */
-	lp->wstats.discard.nwid +=
-	    (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-#endif				/* WIRELESS_EXT */
-
-	printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n");
-#ifdef DEBUG_SHOW_UNUSED
-	printk(KERN_DEBUG
-	       "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
-	       m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2],
-	       m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5],
-	       m.mmr_unused0[6], m.mmr_unused0[7]);
-#endif				/* DEBUG_SHOW_UNUSED */
-	printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n",
-	       m.mmr_des_avail, m.mmr_des_status);
-#ifdef DEBUG_SHOW_UNUSED
-	printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
-	       m.mmr_unused1[0],
-	       m.mmr_unused1[1],
-	       m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]);
-#endif				/* DEBUG_SHOW_UNUSED */
-	printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
-	       m.mmr_dce_status,
-	       (m.
-		mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ?
-	       "energy detected," : "",
-	       (m.
-		mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
-	       "loop test indicated," : "",
-	       (m.
-		mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ?
-	       "transmitter on," : "",
-	       (m.
-		mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
-	       "jabber timer expired," : "");
-	printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id);
-#ifdef DEBUG_SHOW_UNUSED
-	printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
-	       m.mmr_unused2[0], m.mmr_unused2[1]);
-#endif				/* DEBUG_SHOW_UNUSED */
-	printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
-	       (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
-	       (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
-	printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
-	       m.mmr_thr_pre_set & MMR_THR_PRE_SET,
-	       (m.
-		mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" :
-	       "below");
-	printk(KERN_DEBUG "signal_lvl: %d [%s], ",
-	       m.mmr_signal_lvl & MMR_SIGNAL_LVL,
-	       (m.
-		mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" :
-	       "no new msg");
-	printk("silence_lvl: %d [%s], ",
-	       m.mmr_silence_lvl & MMR_SILENCE_LVL,
-	       (m.
-		mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" :
-	       "no new update");
-	printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
-	       (m.
-		mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" :
-	       "Antenna 0");
-#ifdef DEBUG_SHOW_UNUSED
-	printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
-#endif				/* DEBUG_SHOW_UNUSED */
-}				/* wv_mmc_show */
-#endif				/* DEBUG_MMC_SHOW */
-
-#ifdef DEBUG_I82586_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the last block of the i82586 memory.
- */
-static void wv_scb_show(unsigned long ioaddr)
-{
-	scb_t scb;
-
-	obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
-		   sizeof(scb));
-
-	printk(KERN_DEBUG "##### WaveLAN system control block: #####\n");
-
-	printk(KERN_DEBUG "status: ");
-	printk("stat 0x%x[%s%s%s%s] ",
-	       (scb.
-		scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA |
-			      SCB_ST_RNR)) >> 12,
-	       (scb.
-		scb_status & SCB_ST_CX) ? "command completion interrupt," :
-	       "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
-	       (scb.
-		scb_status & SCB_ST_CNA) ? "command unit not active," : "",
-	       (scb.
-		scb_status & SCB_ST_RNR) ? "receiving unit not ready," :
-	       "");
-	printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8,
-	       ((scb.scb_status & SCB_ST_CUS) ==
-		SCB_ST_CUS_IDLE) ? "idle" : "",
-	       ((scb.scb_status & SCB_ST_CUS) ==
-		SCB_ST_CUS_SUSP) ? "suspended" : "",
-	       ((scb.scb_status & SCB_ST_CUS) ==
-		SCB_ST_CUS_ACTV) ? "active" : "");
-	printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4,
-	       ((scb.scb_status & SCB_ST_RUS) ==
-		SCB_ST_RUS_IDLE) ? "idle" : "",
-	       ((scb.scb_status & SCB_ST_RUS) ==
-		SCB_ST_RUS_SUSP) ? "suspended" : "",
-	       ((scb.scb_status & SCB_ST_RUS) ==
-		SCB_ST_RUS_NRES) ? "no resources" : "",
-	       ((scb.scb_status & SCB_ST_RUS) ==
-		SCB_ST_RUS_RDY) ? "ready" : "");
-
-	printk(KERN_DEBUG "command: ");
-	printk("ack 0x%x[%s%s%s%s] ",
-	       (scb.
-		scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR |
-			       SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
-	       (scb.
-		scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
-	       (scb.
-		scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
-	       (scb.
-		scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
-	       (scb.
-		scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : "");
-	printk("cuc 0x%x[%s%s%s%s%s] ",
-	       (scb.scb_command & SCB_CMD_CUC) >> 8,
-	       ((scb.scb_command & SCB_CMD_CUC) ==
-		SCB_CMD_CUC_NOP) ? "nop" : "",
-	       ((scb.scb_command & SCB_CMD_CUC) ==
-		SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
-	       ((scb.scb_command & SCB_CMD_CUC) ==
-		SCB_CMD_CUC_RES) ? "resume execution" : "",
-	       ((scb.scb_command & SCB_CMD_CUC) ==
-		SCB_CMD_CUC_SUS) ? "suspend execution" : "",
-	       ((scb.scb_command & SCB_CMD_CUC) ==
-		SCB_CMD_CUC_ABT) ? "abort execution" : "");
-	printk("ruc 0x%x[%s%s%s%s%s]\n",
-	       (scb.scb_command & SCB_CMD_RUC) >> 4,
-	       ((scb.scb_command & SCB_CMD_RUC) ==
-		SCB_CMD_RUC_NOP) ? "nop" : "",
-	       ((scb.scb_command & SCB_CMD_RUC) ==
-		SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
-	       ((scb.scb_command & SCB_CMD_RUC) ==
-		SCB_CMD_RUC_RES) ? "resume reception" : "",
-	       ((scb.scb_command & SCB_CMD_RUC) ==
-		SCB_CMD_RUC_SUS) ? "suspend reception" : "",
-	       ((scb.scb_command & SCB_CMD_RUC) ==
-		SCB_CMD_RUC_ABT) ? "abort reception" : "");
-
-	printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset);
-	printk("rfa_offset 0x%x\n", scb.scb_rfa_offset);
-
-	printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs);
-	printk("alnerrs %d ", scb.scb_alnerrs);
-	printk("rscerrs %d ", scb.scb_rscerrs);
-	printk("ovrnerrs %d\n", scb.scb_ovrnerrs);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the i82586's receive unit.
- */
-static void wv_ru_show(device * dev)
-{
-	/* net_local *lp = (net_local *) dev->priv; */
-
-	printk(KERN_DEBUG
-	       "##### WaveLAN i82586 receiver unit status: #####\n");
-	printk(KERN_DEBUG "ru:");
-	/*
-	 * Not implemented yet
-	 */
-	printk("\n");
-}				/* wv_ru_show */
-
-/*------------------------------------------------------------------*/
-/*
- * Display info about one control block of the i82586 memory.
- */
-static void wv_cu_show_one(device * dev, net_local * lp, int i, u16 p)
-{
-	unsigned long ioaddr;
-	ac_tx_t actx;
-
-	ioaddr = dev->base_addr;
-
-	printk("%d: 0x%x:", i, p);
-
-	obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx));
-	printk(" status=0x%x,", actx.tx_h.ac_status);
-	printk(" command=0x%x,", actx.tx_h.ac_command);
-
-	/*
-	   {
-	   tbd_t      tbd;
-
-	   obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
-	   printk(" tbd_status=0x%x,", tbd.tbd_status);
-	   }
-	 */
-
-	printk("|");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print status of the command unit of the i82586.
- */
-static void wv_cu_show(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned int i;
-	u16 p;
-
-	printk(KERN_DEBUG
-	       "##### WaveLAN i82586 command unit status: #####\n");
-
-	printk(KERN_DEBUG);
-	for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) {
-		wv_cu_show_one(dev, lp, i, p);
-
-		p += TXBLOCKZ;
-		if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-			p -= NTXBLOCKS * TXBLOCKZ;
-	}
-	printk("\n");
-}
-#endif				/* DEBUG_I82586_SHOW */
-
-#ifdef DEBUG_DEVICE_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver.
- */
-static void wv_dev_show(device * dev)
-{
-	printk(KERN_DEBUG "dev:");
-	printk(" state=%lX,", dev->state);
-	printk(" trans_start=%ld,", dev->trans_start);
-	printk(" flags=0x%x,", dev->flags);
-	printk("\n");
-}				/* wv_dev_show */
-
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver's
- * private information.
- */
-static void wv_local_show(device * dev)
-{
-	net_local *lp;
-
-	lp = (net_local *) dev->priv;
-
-	printk(KERN_DEBUG "local:");
-	printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
-	printk(" hacr=0x%x,", lp->hacr);
-	printk(" rx_head=0x%x,", lp->rx_head);
-	printk(" rx_last=0x%x,", lp->rx_last);
-	printk(" tx_first_free=0x%x,", lp->tx_first_free);
-	printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
-	printk("\n");
-}				/* wv_local_show */
-#endif				/* DEBUG_DEVICE_SHOW */
-
-#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
-/*------------------------------------------------------------------*/
-/*
- * Dump packet header (and content if necessary) on the screen
- */
-static inline void wv_packet_info(u8 * p,	/* Packet to dump */
-				  int length,	/* Length of the packet */
-				  char *msg1,	/* Name of the device */
-				  char *msg2)
-{				/* Name of the function */
-	int i;
-	int maxi;
-
-	printk(KERN_DEBUG
-	       "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
-	       msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
-	printk(KERN_DEBUG
-	       "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
-	       msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12],
-	       p[13]);
-
-#ifdef DEBUG_PACKET_DUMP
-
-	printk(KERN_DEBUG "data=\"");
-
-	if ((maxi = length) > DEBUG_PACKET_DUMP)
-		maxi = DEBUG_PACKET_DUMP;
-	for (i = 14; i < maxi; i++)
-		if (p[i] >= ' ' && p[i] <= '~')
-			printk(" %c", p[i]);
-		else
-			printk("%02X", p[i]);
-	if (maxi < length)
-		printk("..");
-	printk("\"\n");
-	printk(KERN_DEBUG "\n");
-#endif				/* DEBUG_PACKET_DUMP */
-}
-#endif				/* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
-
-/*------------------------------------------------------------------*/
-/*
- * This is the information which is displayed by the driver at startup.
- * There are lots of flags for configuring it to your liking.
- */
-static inline void wv_init_info(device * dev)
-{
-	short ioaddr = dev->base_addr;
-	net_local *lp = (net_local *) dev->priv;
-	psa_t psa;
-	int i;
-
-	/* Read the parameter storage area */
-	psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
-
-#ifdef DEBUG_PSA_SHOW
-	wv_psa_show(&psa);
-#endif
-#ifdef DEBUG_MMC_SHOW
-	wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82586_SHOW
-	wv_cu_show(dev);
-#endif
-
-#ifdef DEBUG_BASIC_SHOW
-	/* Now, let's go for the basic stuff. */
-	printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr);
-	for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-		printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
-	printk(", IRQ %d", dev->irq);
-
-	/* Print current network ID. */
-	if (psa.psa_nwid_select)
-		printk(", nwid 0x%02X-%02X", psa.psa_nwid[0],
-		       psa.psa_nwid[1]);
-	else
-		printk(", nwid off");
-
-	/* If 2.00 card */
-	if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
-	      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
-		unsigned short freq;
-
-		/* Ask the EEPROM to read the frequency from the first area. */
-		fee_read(ioaddr, 0x00, &freq, 1);
-
-		/* Print frequency */
-		printk(", 2.00, %ld", (freq >> 6) + 2400L);
-
-		/* Hack! */
-		if (freq & 0x20)
-			printk(".5");
-	} else {
-		printk(", PC");
-		switch (psa.psa_comp_number) {
-		case PSA_COMP_PC_AT_915:
-		case PSA_COMP_PC_AT_2400:
-			printk("-AT");
-			break;
-		case PSA_COMP_PC_MC_915:
-		case PSA_COMP_PC_MC_2400:
-			printk("-MC");
-			break;
-		case PSA_COMP_PCMCIA_915:
-			printk("MCIA");
-			break;
-		default:
-			printk("?");
-		}
-		printk(", ");
-		switch (psa.psa_subband) {
-		case PSA_SUBBAND_915:
-			printk("915");
-			break;
-		case PSA_SUBBAND_2425:
-			printk("2425");
-			break;
-		case PSA_SUBBAND_2460:
-			printk("2460");
-			break;
-		case PSA_SUBBAND_2484:
-			printk("2484");
-			break;
-		case PSA_SUBBAND_2430_5:
-			printk("2430.5");
-			break;
-		default:
-			printk("?");
-		}
-	}
-
-	printk(" MHz\n");
-#endif				/* DEBUG_BASIC_SHOW */
-
-#ifdef DEBUG_VERSION_SHOW
-	/* Print version information */
-	printk(KERN_NOTICE "%s", version);
-#endif
-}				/* wv_init_info */
-
-/********************* IOCTL, STATS & RECONFIG *********************/
-/*
- * We found here routines that are called by Linux on different
- * occasions after the configuration and not for transmitting data
- * These may be called when the user use ifconfig, /proc/net/dev
- * or wireless extensions
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the current Ethernet statistics. This may be called with the
- * card open or closed.
- * Used when the user read /proc/net/dev
- */
-static en_stats *wavelan_get_stats(device * dev)
-{
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
-#endif
-
-	return (&((net_local *) dev->priv)->stats);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Set or clear the multicast filter for this adaptor.
- * num_addrs == -1	Promiscuous mode, receive all packets
- * num_addrs == 0	Normal mode, clear multicast list
- * num_addrs > 0	Multicast mode, receive normal and MC packets,
- *			and do best-effort filtering.
- */
-static void wavelan_set_multicast_list(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n",
-	       dev->name);
-#endif
-
-#ifdef DEBUG_IOCTL_INFO
-	printk(KERN_DEBUG
-	       "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
-	       dev->name, dev->flags, dev->mc_count);
-#endif
-
-	/* Are we asking for promiscuous mode,
-	 * or all multicast addresses (we don't have that!)
-	 * or too many multicast addresses for the hardware filter? */
-	if ((dev->flags & IFF_PROMISC) ||
-	    (dev->flags & IFF_ALLMULTI) ||
-	    (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) {
-		/*
-		 * Enable promiscuous mode: receive all packets.
-		 */
-		if (!lp->promiscuous) {
-			lp->promiscuous = 1;
-			lp->mc_count = 0;
-
-			wv_82586_reconfig(dev);
-
-			/* Tell the kernel that we are doing a really bad job. */
-			dev->flags |= IFF_PROMISC;
-		}
-	} else
-		/* Are there multicast addresses to send? */
-	if (dev->mc_list != (struct dev_mc_list *) NULL) {
-		/*
-		 * Disable promiscuous mode, but receive all packets
-		 * in multicast list
-		 */
-#ifdef MULTICAST_AVOID
-		if (lp->promiscuous || (dev->mc_count != lp->mc_count))
-#endif
-		{
-			lp->promiscuous = 0;
-			lp->mc_count = dev->mc_count;
-
-			wv_82586_reconfig(dev);
-		}
-	} else {
-		/*
-		 * Switch to normal mode: disable promiscuous mode and 
-		 * clear the multicast list.
-		 */
-		if (lp->promiscuous || lp->mc_count == 0) {
-			lp->promiscuous = 0;
-			lp->mc_count = 0;
-
-			wv_82586_reconfig(dev);
-		}
-	}
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n",
-	       dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This function doesn't exist.
- * (Note : it was a nice way to test the reconfigure stuff...)
- */
-#ifdef SET_MAC_ADDRESS
-static int wavelan_set_mac_address(device * dev, void *addr)
-{
-	struct sockaddr *mac = addr;
-
-	/* Copy the address. */
-	memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
-
-	/* Reconfigure the beast. */
-	wv_82586_reconfig(dev);
-
-	return 0;
-}
-#endif				/* SET_MAC_ADDRESS */
-
-#ifdef WIRELESS_EXT		/* if wireless extensions exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Frequency setting (for hardware capable of it)
- * It's a bit complicated and you don't really want to look into it.
- * (called in wavelan_ioctl)
- */
-static inline int wv_set_frequency(unsigned long ioaddr,	/* I/O port of the card */
-				   iw_freq * frequency)
-{
-	const int BAND_NUM = 10;	/* Number of bands */
-	long freq = 0L;		/* offset to 2.4 GHz in .5 MHz */
-#ifdef DEBUG_IOCTL_INFO
-	int i;
-#endif
-
-	/* Setting by frequency */
-	/* Theoretically, you may set any frequency between
-	 * the two limits with a 0.5 MHz precision. In practice,
-	 * I don't want you to have trouble with local regulations.
-	 */
-	if ((frequency->e == 1) &&
-	    (frequency->m >= (int) 2.412e8)
-	    && (frequency->m <= (int) 2.487e8)) {
-		freq = ((frequency->m / 10000) - 24000L) / 5;
-	}
-
-	/* Setting by channel (same as wfreqsel) */
-	/* Warning: each channel is 22 MHz wide, so some of the channels
-	 * will interfere. */
-	if ((frequency->e == 0) && (frequency->m < BAND_NUM)) {
-		/* Get frequency offset. */
-		freq = channel_bands[frequency->m] >> 1;
-	}
-
-	/* Verify that the frequency is allowed. */
-	if (freq != 0L) {
-		u16 table[10];	/* Authorized frequency table */
-
-		/* Read the frequency table. */
-		fee_read(ioaddr, 0x71, table, 10);
-
-#ifdef DEBUG_IOCTL_INFO
-		printk(KERN_DEBUG "Frequency table: ");
-		for (i = 0; i < 10; i++) {
-			printk(" %04X", table[i]);
-		}
-		printk("\n");
-#endif
-
-		/* Look in the table to see whether the frequency is allowed. */
-		if (!(table[9 - ((freq - 24) / 16)] &
-		      (1 << ((freq - 24) % 16)))) return -EINVAL;	/* not allowed */
-	} else
-		return -EINVAL;
-
-	/* if we get a usable frequency */
-	if (freq != 0L) {
-		unsigned short area[16];
-		unsigned short dac[2];
-		unsigned short area_verify[16];
-		unsigned short dac_verify[2];
-		/* Corresponding gain (in the power adjust value table)
-		 * See AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8
-		 * and WCIN062D.DOC, page 6.2.9. */
-		unsigned short power_limit[] = { 40, 80, 120, 160, 0 };
-		int power_band = 0;	/* Selected band */
-		unsigned short power_adjust;	/* Correct value */
-
-		/* Search for the gain. */
-		power_band = 0;
-		while ((freq > power_limit[power_band]) &&
-		       (power_limit[++power_band] != 0));
-
-		/* Read the first area. */
-		fee_read(ioaddr, 0x00, area, 16);
-
-		/* Read the DAC. */
-		fee_read(ioaddr, 0x60, dac, 2);
-
-		/* Read the new power adjust value. */
-		fee_read(ioaddr, 0x6B - (power_band >> 1), &power_adjust,
-			 1);
-		if (power_band & 0x1)
-			power_adjust >>= 8;
-		else
-			power_adjust &= 0xFF;
-
-#ifdef DEBUG_IOCTL_INFO
-		printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
-		for (i = 0; i < 16; i++) {
-			printk(" %04X", area[i]);
-		}
-		printk("\n");
-
-		printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
-		       dac[0], dac[1]);
-#endif
-
-		/* Frequency offset (for info only) */
-		area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
-
-		/* Receiver Principle main divider coefficient */
-		area[3] = (freq >> 1) + 2400L - 352L;
-		area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
-		/* Transmitter Main divider coefficient */
-		area[13] = (freq >> 1) + 2400L;
-		area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
-		/* Other parts of the area are flags, bit streams or unused. */
-
-		/* Set the value in the DAC. */
-		dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
-		dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
-
-		/* Write the first area. */
-		fee_write(ioaddr, 0x00, area, 16);
-
-		/* Write the DAC. */
-		fee_write(ioaddr, 0x60, dac, 2);
-
-		/* We now should verify here that the writing of the EEPROM went OK. */
-
-		/* Reread the first area. */
-		fee_read(ioaddr, 0x00, area_verify, 16);
-
-		/* Reread the DAC. */
-		fee_read(ioaddr, 0x60, dac_verify, 2);
-
-		/* Compare. */
-		if (memcmp(area, area_verify, 16 * 2) ||
-		    memcmp(dac, dac_verify, 2 * 2)) {
-#ifdef DEBUG_IOCTL_ERROR
-			printk(KERN_INFO
-			       "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n");
-#endif
-			return -EOPNOTSUPP;
-		}
-
-		/* We must download the frequency parameters to the
-		 * synthesizers (from the EEPROM - area 1)
-		 * Note: as the EEPROM is automatically decremented, we set the end
-		 * if the area... */
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
-			MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
-		/* Wait until the download is finished. */
-		fee_wait(ioaddr, 100, 100);
-
-		/* We must now download the power adjust value (gain) to
-		 * the synthesizers (from the EEPROM - area 7 - DAC). */
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
-		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
-			MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
-		/* Wait for the download to finish. */
-		fee_wait(ioaddr, 100, 100);
-
-#ifdef DEBUG_IOCTL_INFO
-		/* Verification of what we have done */
-
-		printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
-		for (i = 0; i < 16; i++) {
-			printk(" %04X", area_verify[i]);
-		}
-		printk("\n");
-
-		printk(KERN_DEBUG "WaveLAN EEPROM DAC:  %04X %04X\n",
-		       dac_verify[0], dac_verify[1]);
-#endif
-
-		return 0;
-	} else
-		return -EINVAL;	/* Bah, never get there... */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Give the list of available frequencies.
- */
-static inline int wv_frequency_list(unsigned long ioaddr,	/* I/O port of the card */
-				    iw_freq * list,	/* List of frequencies to fill */
-				    int max)
-{				/* Maximum number of frequencies */
-	u16 table[10];	/* Authorized frequency table */
-	long freq = 0L;		/* offset to 2.4 GHz in .5 MHz + 12 MHz */
-	int i;			/* index in the table */
-	int c = 0;		/* Channel number */
-
-	/* Read the frequency table. */
-	fee_read(ioaddr, 0x71 /* frequency table */ , table, 10);
-
-	/* Check all frequencies. */
-	i = 0;
-	for (freq = 0; freq < 150; freq++)
-		/* Look in the table if the frequency is allowed */
-		if (table[9 - (freq / 16)] & (1 << (freq % 16))) {
-			/* Compute approximate channel number */
-			while ((((channel_bands[c] >> 1) - 24) < freq) &&
-			       (c < NELS(channel_bands)))
-				c++;
-			list[i].i = c;	/* Set the list index */
-
-			/* put in the list */
-			list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
-			list[i++].e = 1;
-
-			/* Check number. */
-			if (i >= max)
-				return (i);
-		}
-
-	return (i);
-}
-
-#ifdef WIRELESS_SPY
-/*------------------------------------------------------------------*/
-/*
- * Gather wireless spy statistics:  for each packet, compare the source
- * address with our list, and if they match, get the statistics.
- * Sorry, but this function really needs the wireless extensions.
- */
-static inline void wl_spy_gather(device * dev, u8 * mac,	/* MAC address */
-				 u8 * stats)
-{				/* Statistics to gather */
-	net_local *lp = (net_local *) dev->priv;
-	int i;
-
-	/* Check all addresses. */
-	for (i = 0; i < lp->spy_number; i++)
-		/* If match */
-		if (!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE)) {
-			/* Update statistics */
-			lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
-			lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
-			lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
-			lp->spy_stat[i].updated = 0x7;
-		}
-}
-#endif				/* WIRELESS_SPY */
-
-#ifdef HISTOGRAM
-/*------------------------------------------------------------------*/
-/*
- * This function calculates a histogram of the signal level.
- * As the noise is quite constant, it's like doing it on the SNR.
- * We have defined a set of interval (lp->his_range), and each time
- * the level goes in that interval, we increment the count (lp->his_sum).
- * With this histogram you may detect if one WaveLAN is really weak,
- * or you may also calculate the mean and standard deviation of the level.
- */
-static inline void wl_his_gather(device * dev, u8 * stats)
-{				/* Statistics to gather */
-	net_local *lp = (net_local *) dev->priv;
-	u8 level = stats[0] & MMR_SIGNAL_LVL;
-	int i;
-
-	/* Find the correct interval. */
-	i = 0;
-	while ((i < (lp->his_number - 1))
-	       && (level >= lp->his_range[i++]));
-
-	/* Increment interval counter. */
-	(lp->his_sum[i])++;
-}
-#endif				/* HISTOGRAM */
-
-/*------------------------------------------------------------------*/
-/*
- * Perform ioctl for configuration and information.
- * It is here that the wireless extensions are treated (iwconfig).
- */
-static int wavelan_ioctl(struct net_device *dev,	/* device on which the ioctl is applied */
-			 struct ifreq *rq,	/* data passed */
-			 int cmd)
-{				/* ioctl number */
-	unsigned long ioaddr = dev->base_addr;
-	net_local *lp = (net_local *) dev->priv;	/* lp is not unused */
-	struct iwreq *wrq = (struct iwreq *) rq;
-	psa_t psa;
-	mm_t m;
-	unsigned long flags;
-	int ret = 0;
-	int err = 0;
-
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name,
-	       cmd);
-#endif
-
-	/* Disable interrupts and save flags. */
-	wv_splhi(lp, &flags);
-	
-	/* Look what is the request */
-	switch (cmd) {
-		/* --------------- WIRELESS EXTENSIONS --------------- */
-
-	case SIOCGIWNAME:
-		strcpy(wrq->u.name, "WaveLAN");
-		break;
-
-	case SIOCSIWNWID:
-		/* Set NWID in WaveLAN. */
-		if (!wrq->u.nwid.disabled) {
-			/* Set NWID in psa */
-			psa.psa_nwid[0] =
-			    (wrq->u.nwid.value & 0xFF00) >> 8;
-			psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
-			psa.psa_nwid_select = 0x01;
-			psa_write(ioaddr, lp->hacr,
-				  (char *) psa.psa_nwid - (char *) &psa,
-				  (unsigned char *) psa.psa_nwid, 3);
-
-			/* Set NWID in mmc. */
-			m.w.mmw_netw_id_l = psa.psa_nwid[1];
-			m.w.mmw_netw_id_h = psa.psa_nwid[0];
-			mmc_write(ioaddr,
-				  (char *) &m.w.mmw_netw_id_l -
-				  (char *) &m,
-				  (unsigned char *) &m.w.mmw_netw_id_l, 2);
-			mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
-		} else {
-			/* Disable NWID in the psa. */
-			psa.psa_nwid_select = 0x00;
-			psa_write(ioaddr, lp->hacr,
-				  (char *) &psa.psa_nwid_select -
-				  (char *) &psa,
-				  (unsigned char *) &psa.psa_nwid_select,
-				  1);
-
-			/* Disable NWID in the mmc (no filtering). */
-			mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel),
-				MMW_LOOPT_SEL_DIS_NWID);
-		}
-		/* update the Wavelan checksum */
-		update_psa_checksum(dev, ioaddr, lp->hacr);
-		break;
-
-	case SIOCGIWNWID:
-		/* Read the NWID. */
-		psa_read(ioaddr, lp->hacr,
-			 (char *) psa.psa_nwid - (char *) &psa,
-			 (unsigned char *) psa.psa_nwid, 3);
-		wrq->u.nwid.value =
-		    (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
-		wrq->u.nwid.disabled = !(psa.psa_nwid_select);
-		wrq->u.nwid.fixed = 1;	/* Superfluous */
-		break;
-
-	case SIOCSIWFREQ:
-		/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
-		if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
-		      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
-			ret = wv_set_frequency(ioaddr, &(wrq->u.freq));
-		else
-			ret = -EOPNOTSUPP;
-		break;
-
-	case SIOCGIWFREQ:
-		/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable).
-		 * Does it work for everybody, especially old cards? */
-		if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
-		      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
-			unsigned short freq;
-
-			/* Ask the EEPROM to read the frequency from the first area. */
-			fee_read(ioaddr, 0x00, &freq, 1);
-			wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
-			wrq->u.freq.e = 1;
-		} else {
-			psa_read(ioaddr, lp->hacr,
-				 (char *) &psa.psa_subband - (char *) &psa,
-				 (unsigned char *) &psa.psa_subband, 1);
-
-			if (psa.psa_subband <= 4) {
-				wrq->u.freq.m =
-				    fixed_bands[psa.psa_subband];
-				wrq->u.freq.e = (psa.psa_subband != 0);
-			} else
-				ret = -EOPNOTSUPP;
-		}
-		break;
-
-	case SIOCSIWSENS:
-		/* Set the level threshold. */
-		/* We should complain loudly if wrq->u.sens.fixed = 0, because we
-		 * can't set auto mode... */
-		psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
-		psa_write(ioaddr, lp->hacr,
-			  (char *) &psa.psa_thr_pre_set - (char *) &psa,
-			  (unsigned char *) &psa.psa_thr_pre_set, 1);
-		/* update the Wavelan checksum */
-		update_psa_checksum(dev, ioaddr, lp->hacr);
-		mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set),
-			psa.psa_thr_pre_set);
-		break;
-
-	case SIOCGIWSENS:
-		/* Read the level threshold. */
-		psa_read(ioaddr, lp->hacr,
-			 (char *) &psa.psa_thr_pre_set - (char *) &psa,
-			 (unsigned char *) &psa.psa_thr_pre_set, 1);
-		wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
-		wrq->u.sens.fixed = 1;
-		break;
-
-	case SIOCSIWENCODE:
-		/* Set encryption key */
-		if (!mmc_encr(ioaddr)) {
-			ret = -EOPNOTSUPP;
-			break;
-		}
-
-		/* Basic checking... */
-		if (wrq->u.encoding.pointer != (caddr_t) 0) {
-			/* Check the size of the key */
-			if (wrq->u.encoding.length != 8) {
-				ret = -EINVAL;
-				break;
-			}
-
-			/* Copy the key in the driver */
-			wv_splx(lp, &flags);
-			err = copy_from_user(psa.psa_encryption_key,
-					     wrq->u.encoding.pointer,
-					     wrq->u.encoding.length);
-			wv_splhi(lp, &flags);
-			if (err) {
-				ret = -EFAULT;
-				break;
-			}
-
-			psa.psa_encryption_select = 1;
-			psa_write(ioaddr, lp->hacr,
-				  (char *) &psa.psa_encryption_select -
-				  (char *) &psa,
-				  (unsigned char *) &psa.
-				  psa_encryption_select, 8 + 1);
-
-			mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
-				MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
-			mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
-				  (unsigned char *) &psa.
-				  psa_encryption_key, 8);
-		}
-
-		if (wrq->u.encoding.flags & IW_ENCODE_DISABLED) {	/* disable encryption */
-			psa.psa_encryption_select = 0;
-			psa_write(ioaddr, lp->hacr,
-				  (char *) &psa.psa_encryption_select -
-				  (char *) &psa,
-				  (unsigned char *) &psa.
-				  psa_encryption_select, 1);
-
-			mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
-		}
-		/* update the Wavelan checksum */
-		update_psa_checksum(dev, ioaddr, lp->hacr);
-		break;
-
-	case SIOCGIWENCODE:
-		/* Read the encryption key */
-		if (!mmc_encr(ioaddr)) {
-			ret = -EOPNOTSUPP;
-			break;
-		}
-
-		/* only super-user can see encryption key */
-		if (!capable(CAP_NET_ADMIN)) {
-			ret = -EPERM;
-			break;
-		}
-
-		/* Basic checking... */
-		if (wrq->u.encoding.pointer != (caddr_t) 0) {
-			/* Verify the user buffer */
-			ret =
-			    verify_area(VERIFY_WRITE,
-					wrq->u.encoding.pointer, 8);
-			if (ret)
-				break;
-
-			psa_read(ioaddr, lp->hacr,
-				 (char *) &psa.psa_encryption_select -
-				 (char *) &psa,
-				 (unsigned char *) &psa.
-				 psa_encryption_select, 1 + 8);
-
-			/* encryption is enabled ? */
-			if (psa.psa_encryption_select)
-				wrq->u.encoding.flags = IW_ENCODE_ENABLED;
-			else
-				wrq->u.encoding.flags = IW_ENCODE_DISABLED;
-			wrq->u.encoding.flags |= mmc_encr(ioaddr);
-
-			/* Copy the key to the user buffer */
-			wrq->u.encoding.length = 8;
-			wv_splx(lp, &flags);
-			if (copy_to_user(wrq->u.encoding.pointer,
-					 psa.psa_encryption_key, 8))
-				ret = -EFAULT;
-			wv_splhi(lp, &flags);
-		}
-		break;
-
-	case SIOCGIWRANGE:
-		/* basic checking */
-		if (wrq->u.data.pointer != (caddr_t) 0) {
-			struct iw_range range;
-
-			/* Set the length (very important for backward
-			 * compatibility) */
-			wrq->u.data.length = sizeof(struct iw_range);
-
-			/* Set all the info we don't care or don't know
-			 * about to zero */
-			memset(&range, 0, sizeof(range));
-
-			/* Set the Wireless Extension versions */
-			range.we_version_compiled = WIRELESS_EXT;
-			range.we_version_source = 9;
-
-			/* Set information in the range struct.  */
-			range.throughput = 1.6 * 1000 * 1000;	/* don't argue on this ! */
-			range.min_nwid = 0x0000;
-			range.max_nwid = 0xFFFF;
-
-			/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
-			if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
-			      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
-				range.num_channels = 10;
-				range.num_frequency =
-				    wv_frequency_list(ioaddr, range.freq,
-						      IW_MAX_FREQUENCIES);
-			} else
-				range.num_channels = range.num_frequency =
-				    0;
-
-			range.sensitivity = 0x3F;
-			range.max_qual.qual = MMR_SGNL_QUAL;
-			range.max_qual.level = MMR_SIGNAL_LVL;
-			range.max_qual.noise = MMR_SILENCE_LVL;
-			range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
-			/* Need to get better values for those two */
-			range.avg_qual.level = 30;
-			range.avg_qual.noise = 8;
-
-			range.num_bitrates = 1;
-			range.bitrate[0] = 2000000;	/* 2 Mb/s */
-
-			/* Encryption supported ? */
-			if (mmc_encr(ioaddr)) {
-				range.encoding_size[0] = 8;	/* DES = 64 bits key */
-				range.num_encoding_sizes = 1;
-				range.max_encoding_tokens = 1;	/* Only one key possible */
-			} else {
-				range.num_encoding_sizes = 0;
-				range.max_encoding_tokens = 0;
-			}
-
-			/* Copy structure to the user buffer. */
-			wv_splx(lp, &flags);
-			if (copy_to_user(wrq->u.data.pointer,
-					 &range,
-					 sizeof(struct iw_range)))
-				ret = -EFAULT;
-			wv_splhi(lp, &flags);
-		}
-		break;
-
-	case SIOCGIWPRIV:
-		/* Basic checking */
-		if (wrq->u.data.pointer != (caddr_t) 0) {
-			struct iw_priv_args priv[] = {
-				/* { cmd,
-				     set_args,
-				     get_args,
-				     name } */
-				{ SIOCSIPQTHR,
-				  IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
-				  0,
-				  "setqualthr" },
-				{ SIOCGIPQTHR,
-				  0,
-				  IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
-				  "getqualthr" },
-				{ SIOCSIPHISTO,
-				  IW_PRIV_TYPE_BYTE | 16,
-				  0,
-				  "sethisto" },
-				{ SIOCGIPHISTO,
-				  0,
-				  IW_PRIV_TYPE_INT | 16,
-				 "gethisto" },
-			};
-
-			/* Set the number of available ioctls. */
-			wrq->u.data.length = 4;
-
-			/* Copy structure to the user buffer. */
-			wv_splx(lp, &flags);
-			if (copy_to_user(wrq->u.data.pointer,
-					      (u8 *) priv,
-					      sizeof(priv)))
-				ret = -EFAULT;
-			wv_splhi(lp, &flags);
-		}
-		break;
-
-#ifdef WIRELESS_SPY
-	case SIOCSIWSPY:
-		/* Set the spy list */
-
-		/* Check the number of addresses. */
-		if (wrq->u.data.length > IW_MAX_SPY) {
-			ret = -E2BIG;
-			break;
-		}
-		lp->spy_number = wrq->u.data.length;
-
-		/* Are there are addresses to copy? */
-		if (lp->spy_number > 0) {
-			struct sockaddr address[IW_MAX_SPY];
-			int i;
-
-			/* Copy addresses to the driver. */
-			wv_splx(lp, &flags);
-			err = copy_from_user(address,
-					     wrq->u.data.pointer,
-					     sizeof(struct sockaddr)
-					     * lp->spy_number);
-			wv_splhi(lp, &flags);
-			if (err) {
-				ret = -EFAULT;
-				break;
-			}
-
-			/* Copy addresses to the lp structure. */
-			for (i = 0; i < lp->spy_number; i++) {
-				memcpy(lp->spy_address[i],
-				       address[i].sa_data,
-				       WAVELAN_ADDR_SIZE);
-			}
-
-			/* Reset structure. */
-			memset(lp->spy_stat, 0x00,
-			       sizeof(iw_qual) * IW_MAX_SPY);
-
-#ifdef DEBUG_IOCTL_INFO
-			printk(KERN_DEBUG
-			       "SetSpy:  set of new addresses is: \n");
-			for (i = 0; i < wrq->u.data.length; i++)
-				printk(KERN_DEBUG
-				       "%02X:%02X:%02X:%02X:%02X:%02X \n",
-				       lp->spy_address[i][0],
-				       lp->spy_address[i][1],
-				       lp->spy_address[i][2],
-				       lp->spy_address[i][3],
-				       lp->spy_address[i][4],
-				       lp->spy_address[i][5]);
-#endif				/* DEBUG_IOCTL_INFO */
-		}
-
-		break;
-
-	case SIOCGIWSPY:
-		/* Get the spy list and spy stats. */
-
-		/* Set the number of addresses */
-		wrq->u.data.length = lp->spy_number;
-
-		/* Does the user want to have the addresses back? */
-		if ((lp->spy_number > 0)
-		    && (wrq->u.data.pointer != (caddr_t) 0)) {
-			struct sockaddr address[IW_MAX_SPY];
-			int i;
-
-			/* Copy addresses from the lp structure. */
-			for (i = 0; i < lp->spy_number; i++) {
-				memcpy(address[i].sa_data,
-				       lp->spy_address[i],
-				       WAVELAN_ADDR_SIZE);
-				address[i].sa_family = AF_UNIX;
-			}
-
-			/* Copy addresses to the user buffer. */
-			wv_splx(lp, &flags);
-			err = copy_to_user(wrq->u.data.pointer,
-					   address,
-					   sizeof(struct sockaddr)
-					   * lp->spy_number);
-
-			/* Copy stats to the user buffer (just after). */
-			err |= copy_to_user(wrq->u.data.pointer
-					    + (sizeof(struct sockaddr)
-					       * lp->spy_number),
-					    lp->spy_stat,
-					    sizeof(iw_qual) * lp->spy_number);
-			wv_splhi(lp, &flags);
-			if (err) {
-				ret = -EFAULT;
-				break;
-			}
-
-			/* Reset updated flags. */
-			for (i = 0; i < lp->spy_number; i++)
-				lp->spy_stat[i].updated = 0x0;
-		}
-		/* if(pointer != NULL) */
-		break;
-#endif				/* WIRELESS_SPY */
-
-		/* ------------------ PRIVATE IOCTL ------------------ */
-
-	case SIOCSIPQTHR:
-		if (!capable(CAP_NET_ADMIN)) {
-			ret = -EPERM;
-			break;
-		}
-		psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
-		psa_write(ioaddr, lp->hacr,
-			  (char *) &psa.psa_quality_thr - (char *) &psa,
-			  (unsigned char *) &psa.psa_quality_thr, 1);
-		/* update the Wavelan checksum */
-		update_psa_checksum(dev, ioaddr, lp->hacr);
-		mmc_out(ioaddr, mmwoff(0, mmw_quality_thr),
-			psa.psa_quality_thr);
-		break;
-
-	case SIOCGIPQTHR:
-		psa_read(ioaddr, lp->hacr,
-			 (char *) &psa.psa_quality_thr - (char *) &psa,
-			 (unsigned char *) &psa.psa_quality_thr, 1);
-		*(wrq->u.name) = psa.psa_quality_thr & 0x0F;
-		break;
-
-#ifdef HISTOGRAM
-	case SIOCSIPHISTO:
-		/* Verify that the user is root. */
-		if (!capable(CAP_NET_ADMIN)) {
-			ret = -EPERM;
-			break;
-		}
-
-		/* Check the number of intervals. */
-		if (wrq->u.data.length > 16) {
-			ret = -E2BIG;
-			break;
-		}
-		lp->his_number = wrq->u.data.length;
-
-		/* Are there addresses to copy? */
-		if (lp->his_number > 0) {
-			/* Copy interval ranges to the driver */
-			wv_splx(lp, &flags);
-			err = copy_from_user(lp->his_range,
-					     wrq->u.data.pointer,
-					     sizeof(char) * lp->his_number);
-			wv_splhi(lp, &flags);
-			if (err) {
-				ret = -EFAULT;
-				break;
-			}
-
-			/* Reset structure. */
-			memset(lp->his_sum, 0x00, sizeof(long) * 16);
-		}
-		break;
-
-	case SIOCGIPHISTO:
-		/* Set the number of intervals. */
-		wrq->u.data.length = lp->his_number;
-
-		/* Give back the distribution statistics */
-		if ((lp->his_number > 0)
-		    && (wrq->u.data.pointer != (caddr_t) 0)) {
-			/* Copy data to the user buffer. */
-			wv_splx(lp, &flags);
-			if (copy_to_user(wrq->u.data.pointer,
-					 lp->his_sum,
-					 sizeof(long) * lp->his_number);
-				ret = -EFAULT;
-			wv_splhi(lp, &flags);
-
-		}		/* if(pointer != NULL) */
-		break;
-#endif				/* HISTOGRAM */
-
-		/* ------------------- OTHER IOCTL ------------------- */
-
-	default:
-		ret = -EOPNOTSUPP;
-	}	/* switch (cmd) */
-
-	/* Enable interrupts and restore flags. */
-	wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
-#endif
-	return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Get wireless statistics.
- * Called by /proc/net/wireless
- */
-static iw_stats *wavelan_get_wireless_stats(device * dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	net_local *lp = (net_local *) dev->priv;
-	mmr_t m;
-	iw_stats *wstats;
-	unsigned long flags;
-
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n",
-	       dev->name);
-#endif
-
-	/* Check */
-	if (lp == (net_local *) NULL)
-		return (iw_stats *) NULL;
-	
-	/* Disable interrupts and save flags. */
-	wv_splhi(lp, &flags);
-	
-	wstats = &lp->wstats;
-
-	/* Get data from the mmc. */
-	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
-
-	mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
-	mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l,
-		 2);
-	mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set,
-		 4);
-
-	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
-
-	/* Copy data to wireless stuff. */
-	wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
-	wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
-	wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
-	wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
-	wstats->qual.updated = (((m. mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) 
-			| ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) 
-			| ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
-	wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-	wstats->discard.code = 0L;
-	wstats->discard.misc = 0L;
-
-	/* Enable interrupts and restore flags. */
-	wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n",
-	       dev->name);
-#endif
-	return &lp->wstats;
-}
-#endif				/* WIRELESS_EXT */
-
-/************************* PACKET RECEPTION *************************/
-/*
- * This part deals with receiving the packets.
- * The interrupt handler gets an interrupt when a packet has been
- * successfully received and calls this part.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does the actual copying of data (including the Ethernet
- * header structure) from the WaveLAN card to an sk_buff chain that
- * will be passed up to the network interface layer. NOTE: we
- * currently don't handle trailer protocols (neither does the rest of
- * the network interface), so if that is needed, it will (at least in
- * part) be added here.  The contents of the receive ring buffer are
- * copied to a message chain that is then passed to the kernel.
- *
- * Note: if any errors occur, the packet is "dropped on the floor".
- * (called by wv_packet_rcv())
- */
-static inline void
-wv_packet_read(device * dev, u16 buf_off, int sksize)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	struct sk_buff *skb;
-
-#ifdef DEBUG_RX_TRACE
-	printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
-	       dev->name, buf_off, sksize);
-#endif
-
-	/* Allocate buffer for the data */
-	if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) {
-#ifdef DEBUG_RX_ERROR
-		printk(KERN_INFO
-		       "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
-		       dev->name, sksize);
-#endif
-		lp->stats.rx_dropped++;
-		return;
-	}
-
-	skb->dev = dev;
-
-	/* Copy the packet to the buffer. */
-	obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
-	skb->protocol = eth_type_trans(skb, dev);
-
-#ifdef DEBUG_RX_INFO
-	wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
-#endif				/* DEBUG_RX_INFO */
-
-	/* Statistics-gathering and associated stuff.
-	 * It seem a bit messy with all the define, but it's really simple... */
-#if defined(WIRELESS_SPY) || defined(HISTOGRAM)
-	if (
-#ifdef WIRELESS_SPY
-		   (lp->spy_number > 0) ||
-#endif				/* WIRELESS_SPY */
-#ifdef HISTOGRAM
-		   (lp->his_number > 0) ||
-#endif				/* HISTOGRAM */
-		   0) {
-		u8 stats[3];	/* signal level, noise level, signal quality */
-
-		/* Read signal level, silence level and signal quality bytes. */
-		/* Note: in the PCMCIA hardware, these are part of the frame.  It seems
-		 * that for the ISA hardware, it's nowhere to be found in the frame,
-		 * so I'm obliged to do this (it has a side effect on /proc/net/wireless).
-		 * Any ideas?
-		 */
-		mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
-		mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
-		mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
-
-#ifdef DEBUG_RX_INFO
-		printk(KERN_DEBUG
-		       "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
-		       dev->name, stats[0] & 0x3F, stats[1] & 0x3F,
-		       stats[2] & 0x0F);
-#endif
-
-		/* Spying stuff */
-#ifdef WIRELESS_SPY
-		wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE,
-			      stats);
-#endif				/* WIRELESS_SPY */
-#ifdef HISTOGRAM
-		wl_his_gather(dev, stats);
-#endif				/* HISTOGRAM */
-	}
-#endif				/* defined(WIRELESS_SPY) || defined(HISTOGRAM) */
-
-	/*
-	 * Hand the packet to the network module.
-	 */
-	netif_rx(skb);
-
-	/* Keep statistics up to date */
-	dev->last_rx = jiffies;
-	lp->stats.rx_packets++;
-	lp->stats.rx_bytes += sksize;
-
-#ifdef DEBUG_RX_TRACE
-	printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Transfer as many packets as we can
- * from the device RAM.
- * (called in wavelan_interrupt()).
- * Note : the spinlock is already grabbed for us.
- */
-static inline void wv_receive(device * dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	net_local *lp = (net_local *) dev->priv;
-	fd_t fd;
-	rbd_t rbd;
-	int nreaped = 0;
-
-#ifdef DEBUG_RX_TRACE
-	printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name);
-#endif
-
-	/* Loop on each received packet. */
-	for (;;) {
-		obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd,
-			   sizeof(fd));
-
-		/* Note about the status :
-		 * It start up to be 0 (the value we set). Then, when the RU
-		 * grab the buffer to prepare for reception, it sets the
-		 * FD_STATUS_B flag. When the RU has finished receiving the
-		 * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate
-		 * completion and set the other flags to indicate the eventual
-		 * errors. FD_STATUS_OK indicates that the reception was OK.
-		 */
-
-		/* If the current frame is not complete, we have reached the end. */
-		if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
-			break;	/* This is how we exit the loop. */
-
-		nreaped++;
-
-		/* Check whether frame was correctly received. */
-		if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) {
-			/* Does the frame contain a pointer to the data?  Let's check. */
-			if (fd.fd_rbd_offset != I82586NULL) {
-				/* Read the receive buffer descriptor */
-				obram_read(ioaddr, fd.fd_rbd_offset,
-					   (unsigned char *) &rbd,
-					   sizeof(rbd));
-
-#ifdef DEBUG_RX_ERROR
-				if ((rbd.rbd_status & RBD_STATUS_EOF) !=
-				    RBD_STATUS_EOF) printk(KERN_INFO
-							   "%s: wv_receive(): missing EOF flag.\n",
-							   dev->name);
-
-				if ((rbd.rbd_status & RBD_STATUS_F) !=
-				    RBD_STATUS_F) printk(KERN_INFO
-							 "%s: wv_receive(): missing F flag.\n",
-							 dev->name);
-#endif				/* DEBUG_RX_ERROR */
-
-				/* Read the packet and transmit to Linux */
-				wv_packet_read(dev, rbd.rbd_bufl,
-					       rbd.
-					       rbd_status &
-					       RBD_STATUS_ACNT);
-			}
-#ifdef DEBUG_RX_ERROR
-			else	/* if frame has no data */
-				printk(KERN_INFO
-				       "%s: wv_receive(): frame has no data.\n",
-				       dev->name);
-#endif
-		} else {	/* If reception was no successful */
-
-			lp->stats.rx_errors++;
-
-#ifdef DEBUG_RX_INFO
-			printk(KERN_DEBUG
-			       "%s: wv_receive(): frame not received successfully (%X).\n",
-			       dev->name, fd.fd_status);
-#endif
-
-#ifdef DEBUG_RX_ERROR
-			if ((fd.fd_status & FD_STATUS_S6) != 0)
-				printk(KERN_INFO
-				       "%s: wv_receive(): no EOF flag.\n",
-				       dev->name);
-#endif
-
-			if ((fd.fd_status & FD_STATUS_S7) != 0) {
-				lp->stats.rx_length_errors++;
-#ifdef DEBUG_RX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_receive(): frame too short.\n",
-				       dev->name);
-#endif
-			}
-
-			if ((fd.fd_status & FD_STATUS_S8) != 0) {
-				lp->stats.rx_over_errors++;
-#ifdef DEBUG_RX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_receive(): rx DMA overrun.\n",
-				       dev->name);
-#endif
-			}
-
-			if ((fd.fd_status & FD_STATUS_S9) != 0) {
-				lp->stats.rx_fifo_errors++;
-#ifdef DEBUG_RX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_receive(): ran out of resources.\n",
-				       dev->name);
-#endif
-			}
-
-			if ((fd.fd_status & FD_STATUS_S10) != 0) {
-				lp->stats.rx_frame_errors++;
-#ifdef DEBUG_RX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_receive(): alignment error.\n",
-				       dev->name);
-#endif
-			}
-
-			if ((fd.fd_status & FD_STATUS_S11) != 0) {
-				lp->stats.rx_crc_errors++;
-#ifdef DEBUG_RX_FAIL
-				printk(KERN_DEBUG
-				       "%s: wv_receive(): CRC error.\n",
-				       dev->name);
-#endif
-			}
-		}
-
-		fd.fd_status = 0;
-		obram_write(ioaddr, fdoff(lp->rx_head, fd_status),
-			    (unsigned char *) &fd.fd_status,
-			    sizeof(fd.fd_status));
-
-		fd.fd_command = FD_COMMAND_EL;
-		obram_write(ioaddr, fdoff(lp->rx_head, fd_command),
-			    (unsigned char *) &fd.fd_command,
-			    sizeof(fd.fd_command));
-
-		fd.fd_command = 0;
-		obram_write(ioaddr, fdoff(lp->rx_last, fd_command),
-			    (unsigned char *) &fd.fd_command,
-			    sizeof(fd.fd_command));
-
-		lp->rx_last = lp->rx_head;
-		lp->rx_head = fd.fd_link_offset;
-	}			/* for(;;) -> loop on all frames */
-
-#ifdef DEBUG_RX_INFO
-	if (nreaped > 1)
-		printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n",
-		       dev->name, nreaped);
-#endif
-#ifdef DEBUG_RX_TRACE
-	printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name);
-#endif
-}
-
-/*********************** PACKET TRANSMISSION ***********************/
-/*
- * This part deals with sending packets through the WaveLAN.
- *
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine fills in the appropriate registers and memory
- * locations on the WaveLAN card and starts the card off on
- * the transmit.
- *
- * The principle:
- * Each block contains a transmit command, a NOP command,
- * a transmit block descriptor and a buffer.
- * The CU read the transmit block which point to the tbd,
- * read the tbd and the content of the buffer.
- * When it has finish with it, it goes to the next command
- * which in our case is the NOP. The NOP points on itself,
- * so the CU stop here.
- * When we add the next block, we modify the previous nop
- * to make it point on the new tx command.
- * Simple, isn't it ?
- *
- * (called in wavelan_packet_xmit())
- */
-static inline int wv_packet_write(device * dev, void *buf, short length)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	unsigned short txblock;
-	unsigned short txpred;
-	unsigned short tx_addr;
-	unsigned short nop_addr;
-	unsigned short tbd_addr;
-	unsigned short buf_addr;
-	ac_tx_t tx;
-	ac_nop_t nop;
-	tbd_t tbd;
-	int clen = length;
-	unsigned long flags;
-
-#ifdef DEBUG_TX_TRACE
-	printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name,
-	       length);
-#endif
-
-	/* Do we need some padding? */
-	if (clen < ETH_ZLEN)
-		clen = ETH_ZLEN;
-
-	wv_splhi(lp, &flags);
-
-	/* Check nothing bad has happened */
-	if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
-#ifdef DEBUG_TX_ERROR
-		printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n",
-		       dev->name);
-#endif
-		wv_splx(lp, &flags);
-		return 1;
-	}
-
-	/* Calculate addresses of next block and previous block. */
-	txblock = lp->tx_first_free;
-	txpred = txblock - TXBLOCKZ;
-	if (txpred < OFFSET_CU)
-		txpred += NTXBLOCKS * TXBLOCKZ;
-	lp->tx_first_free += TXBLOCKZ;
-	if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-		lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
-
-	lp->tx_n_in_use++;
-
-	/* Calculate addresses of the different parts of the block. */
-	tx_addr = txblock;
-	nop_addr = tx_addr + sizeof(tx);
-	tbd_addr = nop_addr + sizeof(nop);
-	buf_addr = tbd_addr + sizeof(tbd);
-
-	/*
-	 * Transmit command
-	 */
-	tx.tx_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
-		    (unsigned char *) &tx.tx_h.ac_status,
-		    sizeof(tx.tx_h.ac_status));
-
-	/*
-	 * NOP command
-	 */
-	nop.nop_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
-		    (unsigned char *) &nop.nop_h.ac_status,
-		    sizeof(nop.nop_h.ac_status));
-	nop.nop_h.ac_link = nop_addr;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
-		    (unsigned char *) &nop.nop_h.ac_link,
-		    sizeof(nop.nop_h.ac_link));
-
-	/*
-	 * Transmit buffer descriptor
-	 */
-	tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
-	tbd.tbd_next_bd_offset = I82586NULL;
-	tbd.tbd_bufl = buf_addr;
-	tbd.tbd_bufh = 0;
-	obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd));
-
-	/*
-	 * Data
-	 */
-	obram_write(ioaddr, buf_addr, buf, length);
-
-	/*
-	 * Overwrite the predecessor NOP link
-	 * so that it points to this txblock.
-	 */
-	nop_addr = txpred + sizeof(tx);
-	nop.nop_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
-		    (unsigned char *) &nop.nop_h.ac_status,
-		    sizeof(nop.nop_h.ac_status));
-	nop.nop_h.ac_link = txblock;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
-		    (unsigned char *) &nop.nop_h.ac_link,
-		    sizeof(nop.nop_h.ac_link));
-
-	/* Keep stats up to date. */
-	lp->stats.tx_bytes += length;
-
-	if (lp->tx_first_in_use == I82586NULL)
-		lp->tx_first_in_use = txblock;
-
-	if (lp->tx_n_in_use < NTXBLOCKS - 1)
-		netif_wake_queue(dev);
-
-	wv_splx(lp, &flags);
-	
-#ifdef DEBUG_TX_INFO
-	wv_packet_info((u8 *) buf, length, dev->name,
-		       "wv_packet_write");
-#endif				/* DEBUG_TX_INFO */
-
-#ifdef DEBUG_TX_TRACE
-	printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
-#endif
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called when we want to send a packet (NET3 callback)
- * In this routine, we check if the harware is ready to accept
- * the packet.  We also prevent reentrance.  Then we call the function
- * to send the packet.
- */
-static int wavelan_packet_xmit(struct sk_buff *skb, device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long flags;
-
-#ifdef DEBUG_TX_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
-	       (unsigned) skb);
-#endif
-
-	/*
-	 * Block a timer-based transmit from overlapping.
-	 * In other words, prevent reentering this routine.
-	 */
-	netif_stop_queue(dev);
-
-	/* If somebody has asked to reconfigure the controller, 
-	 * we can do it now.
-	 */
-	if (lp->reconfig_82586) {
-		wv_splhi(lp, &flags);
-		wv_82586_config(dev);
-		wv_splx(lp, &flags);
-		/* Check that we can continue */
-		if (lp->tx_n_in_use == (NTXBLOCKS - 1))
-			return 1;
-	}
-#ifdef DEBUG_TX_ERROR
-	if (skb->next)
-		printk(KERN_INFO "skb has next\n");
-#endif
-
-	/* Write packet on the card */
-	if(wv_packet_write(dev, skb->data, skb->len))
-		return 1;	/* We failed */
-
-	dev_kfree_skb(skb);
-
-#ifdef DEBUG_TX_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*********************** HARDWARE CONFIGURATION ***********************/
-/*
- * This part does the real job of starting and configuring the hardware.
- */
-
-/*--------------------------------------------------------------------*/
-/*
- * Routine to initialize the Modem Management Controller.
- * (called by wv_hw_reset())
- */
-static inline int wv_mmc_init(device * dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	net_local *lp = (net_local *) dev->priv;
-	psa_t psa;
-	mmw_t m;
-	int configured;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
-#endif
-
-	/* Read the parameter storage area. */
-	psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
-
-#ifdef USE_PSA_CONFIG
-	configured = psa.psa_conf_status & 1;
-#else
-	configured = 0;
-#endif
-
-	/* Is the PSA is not configured */
-	if (!configured) {
-		/* User will be able to configure NWID later (with iwconfig). */
-		psa.psa_nwid[0] = 0;
-		psa.psa_nwid[1] = 0;
-
-		/* no NWID checking since NWID is not set */
-		psa.psa_nwid_select = 0;
-
-		/* Disable encryption */
-		psa.psa_encryption_select = 0;
-
-		/* Set to standard values:
-		 * 0x04 for AT,
-		 * 0x01 for MCA,
-		 * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
-		 */
-		if (psa.psa_comp_number & 1)
-			psa.psa_thr_pre_set = 0x01;
-		else
-			psa.psa_thr_pre_set = 0x04;
-		psa.psa_quality_thr = 0x03;
-
-		/* It is configured */
-		psa.psa_conf_status |= 1;
-
-#ifdef USE_PSA_CONFIG
-		/* Write the psa. */
-		psa_write(ioaddr, lp->hacr,
-			  (char *) psa.psa_nwid - (char *) &psa,
-			  (unsigned char *) psa.psa_nwid, 4);
-		psa_write(ioaddr, lp->hacr,
-			  (char *) &psa.psa_thr_pre_set - (char *) &psa,
-			  (unsigned char *) &psa.psa_thr_pre_set, 1);
-		psa_write(ioaddr, lp->hacr,
-			  (char *) &psa.psa_quality_thr - (char *) &psa,
-			  (unsigned char *) &psa.psa_quality_thr, 1);
-		psa_write(ioaddr, lp->hacr,
-			  (char *) &psa.psa_conf_status - (char *) &psa,
-			  (unsigned char *) &psa.psa_conf_status, 1);
-		/* update the Wavelan checksum */
-		update_psa_checksum(dev, ioaddr, lp->hacr);
-#endif
-	}
-
-	/* Zero the mmc structure. */
-	memset(&m, 0x00, sizeof(m));
-
-	/* Copy PSA info to the mmc. */
-	m.mmw_netw_id_l = psa.psa_nwid[1];
-	m.mmw_netw_id_h = psa.psa_nwid[0];
-
-	if (psa.psa_nwid_select & 1)
-		m.mmw_loopt_sel = 0x00;
-	else
-		m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
-
-	memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,
-	       sizeof(m.mmw_encr_key));
-
-	if (psa.psa_encryption_select)
-		m.mmw_encr_enable =
-		    MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
-	else
-		m.mmw_encr_enable = 0;
-
-	m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
-	m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
-
-	/*
-	 * Set default modem control parameters.
-	 * See NCR document 407-0024326 Rev. A.
-	 */
-	m.mmw_jabber_enable = 0x01;
-	m.mmw_freeze = 0;
-	m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
-	m.mmw_ifs = 0x20;
-	m.mmw_mod_delay = 0x04;
-	m.mmw_jam_time = 0x38;
-
-	m.mmw_des_io_invert = 0;
-	m.mmw_decay_prm = 0;
-	m.mmw_decay_updat_prm = 0;
-
-	/* Write all info to MMC. */
-	mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m));
-
-	/* The following code starts the modem of the 2.00 frequency
-	 * selectable cards at power on.  It's not strictly needed for the
-	 * following boots.
-	 * The original patch was by Joe Finney for the PCMCIA driver, but
-	 * I've cleaned it up a bit and added documentation.
-	 * Thanks to Loeke Brederveld from Lucent for the info.
-	 */
-
-	/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
-	 * Does it work for everybody, especially old cards? */
-	/* Note: WFREQSEL verifies that it is able to read a sensible
-	 * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID
-	 * is 0xA (Xilinx version) or 0xB (Ariadne version).
-	 * My test is more crude but does work. */
-	if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
-	      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
-		/* We must download the frequency parameters to the
-		 * synthesizers (from the EEPROM - area 1)
-		 * Note: as the EEPROM is automatically decremented, we set the end
-		 * if the area... */
-		m.mmw_fee_addr = 0x0F;
-		m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
-		mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
-			  (unsigned char *) &m.mmw_fee_ctrl, 2);
-
-		/* Wait until the download is finished. */
-		fee_wait(ioaddr, 100, 100);
-
-#ifdef DEBUG_CONFIG_INFO
-		/* The frequency was in the last word downloaded. */
-		mmc_read(ioaddr, (char *) &m.mmw_fee_data_l - (char *) &m,
-			 (unsigned char *) &m.mmw_fee_data_l, 2);
-
-		/* Print some info for the user. */
-		printk(KERN_DEBUG
-		       "%s: WaveLAN 2.00 recognised (frequency select).  Current frequency = %ld\n",
-		       dev->name,
-		       ((m.
-			 mmw_fee_data_h << 4) | (m.mmw_fee_data_l >> 4)) *
-		       5 / 2 + 24000L);
-#endif
-
-		/* We must now download the power adjust value (gain) to
-		 * the synthesizers (from the EEPROM - area 7 - DAC). */
-		m.mmw_fee_addr = 0x61;
-		m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
-		mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
-			  (unsigned char *) &m.mmw_fee_ctrl, 2);
-
-		/* Wait until the download is finished. */
-	}
-	/* if 2.00 card */
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Construct the fd and rbd structures.
- * Start the receive unit.
- * (called by wv_hw_reset())
- */
-static inline int wv_ru_start(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	u16 scb_cs;
-	fd_t fd;
-	rbd_t rbd;
-	u16 rx;
-	u16 rx_next;
-	int i;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
-#endif
-
-	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
-		   (unsigned char *) &scb_cs, sizeof(scb_cs));
-	if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
-		return 0;
-
-	lp->rx_head = OFFSET_RU;
-
-	for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) {
-		rx_next =
-		    (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
-
-		fd.fd_status = 0;
-		fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
-		fd.fd_link_offset = rx_next;
-		fd.fd_rbd_offset = rx + sizeof(fd);
-		obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd));
-
-		rbd.rbd_status = 0;
-		rbd.rbd_next_rbd_offset = I82586NULL;
-		rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
-		rbd.rbd_bufh = 0;
-		rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
-		obram_write(ioaddr, rx + sizeof(fd),
-			    (unsigned char *) &rbd, sizeof(rbd));
-
-		lp->rx_last = rx;
-	}
-
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset),
-		    (unsigned char *) &lp->rx_head, sizeof(lp->rx_head));
-
-	scb_cs = SCB_CMD_RUC_GO;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
-		    (unsigned char *) &scb_cs, sizeof(scb_cs));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	for (i = 1000; i > 0; i--) {
-		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
-			   (unsigned char *) &scb_cs, sizeof(scb_cs));
-		if (scb_cs == 0)
-			break;
-
-		udelay(10);
-	}
-
-	if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_ru_start(): board not accepting command.\n",
-		       dev->name);
-#endif
-		return -1;
-	}
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Initialise the transmit blocks.
- * Start the command unit executing the NOP
- * self-loop of the first transmit block.
- *
- * Here we create the list of send buffers used to transmit packets
- * between the PC and the command unit. For each buffer, we create a
- * buffer descriptor (pointing on the buffer), a transmit command
- * (pointing to the buffer descriptor) and a NOP command.
- * The transmit command is linked to the NOP, and the NOP to itself.
- * When we will have finished executing the transmit command, we will
- * then loop on the NOP. By releasing the NOP link to a new command,
- * we may send another buffer.
- *
- * (called by wv_hw_reset())
- */
-static inline int wv_cu_start(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	int i;
-	u16 txblock;
-	u16 first_nop;
-	u16 scb_cs;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name);
-#endif
-
-	lp->tx_first_free = OFFSET_CU;
-	lp->tx_first_in_use = I82586NULL;
-
-	for (i = 0, txblock = OFFSET_CU;
-	     i < NTXBLOCKS; i++, txblock += TXBLOCKZ) {
-		ac_tx_t tx;
-		ac_nop_t nop;
-		tbd_t tbd;
-		unsigned short tx_addr;
-		unsigned short nop_addr;
-		unsigned short tbd_addr;
-		unsigned short buf_addr;
-
-		tx_addr = txblock;
-		nop_addr = tx_addr + sizeof(tx);
-		tbd_addr = nop_addr + sizeof(nop);
-		buf_addr = tbd_addr + sizeof(tbd);
-
-		tx.tx_h.ac_status = 0;
-		tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
-		tx.tx_h.ac_link = nop_addr;
-		tx.tx_tbd_offset = tbd_addr;
-		obram_write(ioaddr, tx_addr, (unsigned char *) &tx,
-			    sizeof(tx));
-
-		nop.nop_h.ac_status = 0;
-		nop.nop_h.ac_command = acmd_nop;
-		nop.nop_h.ac_link = nop_addr;
-		obram_write(ioaddr, nop_addr, (unsigned char *) &nop,
-			    sizeof(nop));
-
-		tbd.tbd_status = TBD_STATUS_EOF;
-		tbd.tbd_next_bd_offset = I82586NULL;
-		tbd.tbd_bufl = buf_addr;
-		tbd.tbd_bufh = 0;
-		obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd,
-			    sizeof(tbd));
-	}
-
-	first_nop =
-	    OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset),
-		    (unsigned char *) &first_nop, sizeof(first_nop));
-
-	scb_cs = SCB_CMD_CUC_GO;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
-		    (unsigned char *) &scb_cs, sizeof(scb_cs));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	for (i = 1000; i > 0; i--) {
-		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
-			   (unsigned char *) &scb_cs, sizeof(scb_cs));
-		if (scb_cs == 0)
-			break;
-
-		udelay(10);
-	}
-
-	if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_cu_start(): board not accepting command.\n",
-		       dev->name);
-#endif
-		return -1;
-	}
-
-	lp->tx_n_in_use = 0;
-	netif_start_queue(dev);
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a standard configuration of the WaveLAN 
- * controller (i82586).
- *
- * It initialises the scp, iscp and scb structure
- * The first two are just pointers to the next.
- * The last one is used for basic configuration and for basic
- * communication (interrupt status).
- *
- * (called by wv_hw_reset())
- */
-static inline int wv_82586_start(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	scp_t scp;		/* system configuration pointer */
-	iscp_t iscp;		/* intermediate scp */
-	scb_t scb;		/* system control block */
-	ach_t cb;		/* Action command header */
-	u8 zeroes[512];
-	int i;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name);
-#endif
-
-	/*
-	 * Clear the onboard RAM.
-	 */
-	memset(&zeroes[0], 0x00, sizeof(zeroes));
-	for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
-		obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
-
-	/*
-	 * Construct the command unit structures:
-	 * scp, iscp, scb, cb.
-	 */
-	memset(&scp, 0x00, sizeof(scp));
-	scp.scp_sysbus = SCP_SY_16BBUS;
-	scp.scp_iscpl = OFFSET_ISCP;
-	obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp,
-		    sizeof(scp));
-
-	memset(&iscp, 0x00, sizeof(iscp));
-	iscp.iscp_busy = 1;
-	iscp.iscp_offset = OFFSET_SCB;
-	obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
-		    sizeof(iscp));
-
-	/* Our first command is to reset the i82586. */
-	memset(&scb, 0x00, sizeof(scb));
-	scb.scb_command = SCB_CMD_RESET;
-	scb.scb_cbl_offset = OFFSET_CU;
-	scb.scb_rfa_offset = OFFSET_RU;
-	obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
-		    sizeof(scb));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	/* Wait for command to finish. */
-	for (i = 1000; i > 0; i--) {
-		obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
-			   sizeof(iscp));
-
-		if (iscp.iscp_busy == (unsigned short) 0)
-			break;
-
-		udelay(10);
-	}
-
-	if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wv_82586_start(): iscp_busy timeout.\n",
-		       dev->name);
-#endif
-		return -1;
-	}
-
-	/* Check command completion. */
-	for (i = 15; i > 0; i--) {
-		obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
-			   sizeof(scb));
-
-		if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
-			break;
-
-		udelay(10);
-	}
-
-	if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n",
-		       dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
-#endif
-		return -1;
-	}
-
-	wv_ack(dev);
-
-	/* Set the action command header. */
-	memset(&cb, 0x00, sizeof(cb));
-	cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
-	cb.ac_link = OFFSET_CU;
-	obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
-
-	if (wv_synchronous_cmd(dev, "diag()") == -1)
-		return -1;
-
-	obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
-	if (cb.ac_status & AC_SFLD_FAIL) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wv_82586_start(): i82586 Self Test failed.\n",
-		       dev->name);
-#endif
-		return -1;
-	}
-#ifdef DEBUG_I82586_SHOW
-	wv_scb_show(ioaddr);
-#endif
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a standard configuration of the WaveLAN
- * controller (i82586).
- *
- * This routine is a violent hack. We use the first free transmit block
- * to make our configuration. In the buffer area, we create the three
- * configuration commands (linked). We make the previous NOP point to
- * the beginning of the buffer instead of the tx command. After, we go
- * as usual to the NOP command.
- * Note that only the last command (mc_set) will generate an interrupt.
- *
- * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit())
- */
-static void wv_82586_config(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	unsigned short txblock;
-	unsigned short txpred;
-	unsigned short tx_addr;
-	unsigned short nop_addr;
-	unsigned short tbd_addr;
-	unsigned short cfg_addr;
-	unsigned short ias_addr;
-	unsigned short mcs_addr;
-	ac_tx_t tx;
-	ac_nop_t nop;
-	ac_cfg_t cfg;		/* Configure action */
-	ac_ias_t ias;		/* IA-setup action */
-	ac_mcs_t mcs;		/* Multicast setup */
-	struct dev_mc_list *dmi;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name);
-#endif
-
-	/* Check nothing bad has happened */
-	if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n",
-		       dev->name);
-#endif
-		return;
-	}
-
-	/* Calculate addresses of next block and previous block. */
-	txblock = lp->tx_first_free;
-	txpred = txblock - TXBLOCKZ;
-	if (txpred < OFFSET_CU)
-		txpred += NTXBLOCKS * TXBLOCKZ;
-	lp->tx_first_free += TXBLOCKZ;
-	if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-		lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
-
-	lp->tx_n_in_use++;
-
-	/* Calculate addresses of the different parts of the block. */
-	tx_addr = txblock;
-	nop_addr = tx_addr + sizeof(tx);
-	tbd_addr = nop_addr + sizeof(nop);
-	cfg_addr = tbd_addr + sizeof(tbd_t);	/* beginning of the buffer */
-	ias_addr = cfg_addr + sizeof(cfg);
-	mcs_addr = ias_addr + sizeof(ias);
-
-	/*
-	 * Transmit command
-	 */
-	tx.tx_h.ac_status = 0xFFFF;	/* Fake completion value */
-	obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
-		    (unsigned char *) &tx.tx_h.ac_status,
-		    sizeof(tx.tx_h.ac_status));
-
-	/*
-	 * NOP command
-	 */
-	nop.nop_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
-		    (unsigned char *) &nop.nop_h.ac_status,
-		    sizeof(nop.nop_h.ac_status));
-	nop.nop_h.ac_link = nop_addr;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
-		    (unsigned char *) &nop.nop_h.ac_link,
-		    sizeof(nop.nop_h.ac_link));
-
-	/* Create a configure action. */
-	memset(&cfg, 0x00, sizeof(cfg));
-
-	/*
-	 * For Linux we invert AC_CFG_ALOC() so as to conform
-	 * to the way that net packets reach us from above.
-	 * (See also ac_tx_t.)
-	 *
-	 * Updated from Wavelan Manual WCIN085B
-	 */
-	cfg.cfg_byte_cnt =
-	    AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
-	cfg.cfg_fifolim = AC_CFG_FIFOLIM(4);
-	cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0);
-	cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
-	    AC_CFG_ILPBCK(0) |
-	    AC_CFG_PRELEN(AC_CFG_PLEN_2) |
-	    AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
-	cfg.cfg_byte10 = AC_CFG_BOFMET(1) |
-	    AC_CFG_ACR(6) | AC_CFG_LINPRIO(0);
-	cfg.cfg_ifs = 0x20;
-	cfg.cfg_slotl = 0x0C;
-	cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0);
-	cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
-	    AC_CFG_BTSTF(0) |
-	    AC_CFG_CRC16(0) |
-	    AC_CFG_NCRC(0) |
-	    AC_CFG_TNCRS(1) |
-	    AC_CFG_MANCH(0) |
-	    AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous);
-	cfg.cfg_byte15 = AC_CFG_ICDS(0) |
-	    AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0);
-/*
-  cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
-*/
-	cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
-
-	cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure);
-	cfg.cfg_h.ac_link = ias_addr;
-	obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg));
-
-	/* Set up the MAC address */
-	memset(&ias, 0x00, sizeof(ias));
-	ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup);
-	ias.ias_h.ac_link = mcs_addr;
-	memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0],
-	       sizeof(ias.ias_addr));
-	obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias));
-
-	/* Initialize adapter's Ethernet multicast addresses */
-	memset(&mcs, 0x00, sizeof(mcs));
-	mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup);
-	mcs.mcs_h.ac_link = nop_addr;
-	mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count;
-	obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs));
-
-	/* Any address to set? */
-	if (lp->mc_count) {
-		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
-			outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr,
-			      WAVELAN_ADDR_SIZE >> 1);
-
-#ifdef DEBUG_CONFIG_INFO
-		printk(KERN_DEBUG
-		       "%s: wv_82586_config(): set %d multicast addresses:\n",
-		       dev->name, lp->mc_count);
-		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
-			printk(KERN_DEBUG
-			       " %02x:%02x:%02x:%02x:%02x:%02x\n",
-			       dmi->dmi_addr[0], dmi->dmi_addr[1],
-			       dmi->dmi_addr[2], dmi->dmi_addr[3],
-			       dmi->dmi_addr[4], dmi->dmi_addr[5]);
-#endif
-	}
-
-	/*
-	 * Overwrite the predecessor NOP link
-	 * so that it points to the configure action.
-	 */
-	nop_addr = txpred + sizeof(tx);
-	nop.nop_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
-		    (unsigned char *) &nop.nop_h.ac_status,
-		    sizeof(nop.nop_h.ac_status));
-	nop.nop_h.ac_link = cfg_addr;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
-		    (unsigned char *) &nop.nop_h.ac_link,
-		    sizeof(nop.nop_h.ac_link));
-
-	/* Job done, clear the flag */
-	lp->reconfig_82586 = 0;
-
-	if (lp->tx_first_in_use == I82586NULL)
-		lp->tx_first_in_use = txblock;
-
-	if (lp->tx_n_in_use == (NTXBLOCKS - 1))
-		netif_stop_queue(dev);
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine, called by wavelan_close(), gracefully stops the 
- * WaveLAN controller (i82586).
- * (called by wavelan_close())
- */
-static inline void wv_82586_stop(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-	u16 scb_cmd;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
-#endif
-
-	/* Suspend both command unit and receive unit. */
-	scb_cmd =
-	    (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC &
-					       SCB_CMD_RUC_SUS);
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
-		    (unsigned char *) &scb_cmd, sizeof(scb_cmd));
-	set_chan_attn(ioaddr, lp->hacr);
-
-	/* No more interrupts */
-	wv_ints_off(dev);
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Totally reset the WaveLAN and restart it.
- * Performs the following actions:
- *	1. A power reset (reset DMA)
- *	2. Initialize the radio modem (using wv_mmc_init)
- *	3. Reset & Configure LAN controller (using wv_82586_start)
- *	4. Start the LAN controller's command unit
- *	5. Start the LAN controller's receive unit
- * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open())
- */
-static int wv_hw_reset(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long ioaddr = dev->base_addr;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
-	       (unsigned int) dev);
-#endif
-
-	/* Increase the number of resets done. */
-	lp->nresets++;
-
-	wv_hacr_reset(ioaddr);
-	lp->hacr = HACR_DEFAULT;
-
-	if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0))
-		return -1;
-
-	/* Enable the card to send interrupts. */
-	wv_ints_on(dev);
-
-	/* Start card functions */
-	if (wv_cu_start(dev) < 0)
-		return -1;
-
-	/* Setup the controller and parameters */
-	wv_82586_config(dev);
-
-	/* Finish configuration with the receive unit */
-	if (wv_ru_start(dev) < 0)
-		return -1;
-
-#ifdef DEBUG_CONFIG_TRACE
-	printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Check if there is a WaveLAN at the specific base address.
- * As a side effect, this reads the MAC address.
- * (called in wavelan_probe() and init_module())
- */
-static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac)
-{
-	int i;			/* Loop counter */
-
-	/* Check if the base address if available. */
-	if (check_region(ioaddr, sizeof(ha_t)))
-		return -EADDRINUSE;	/* ioaddr already used */
-
-	/* Reset host interface */
-	wv_hacr_reset(ioaddr);
-
-	/* Read the MAC address from the parameter storage area. */
-	psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr),
-		 mac, 6);
-
-	/*
-	 * Check the first three octets of the address for the manufacturer's code.
-	 * Note: if this can't find your WaveLAN card, you've got a
-	 * non-NCR/AT&T/Lucent ISA card.  See wavelan.p.h for detail on
-	 * how to configure your card.
-	 */
-	for (i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
-		if ((mac[0] == MAC_ADDRESSES[i][0]) &&
-		    (mac[1] == MAC_ADDRESSES[i][1]) &&
-		    (mac[2] == MAC_ADDRESSES[i][2]))
-			return 0;
-
-#ifdef DEBUG_CONFIG_INFO
-	printk(KERN_WARNING
-	       "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n",
-	       ioaddr, mac[0], mac[1], mac[2]);
-#endif
-	return -ENODEV;
-}
-
-/************************ INTERRUPT HANDLING ************************/
-
-/*
- * This function is the interrupt handler for the WaveLAN card. This
- * routine will be called whenever: 
- */
-static void wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	device *dev;
-	unsigned long ioaddr;
-	net_local *lp;
-	u16 hasr;
-	u16 status;
-	u16 ack_cmd;
-
-	dev = dev_id;
-
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
-#endif
-
-	lp = (net_local *) dev->priv;
-	ioaddr = dev->base_addr;
-
-#ifdef DEBUG_INTERRUPT_INFO
-	/* Check state of our spinlock */
-	if(spin_is_locked(&lp->spinlock))
-		printk(KERN_DEBUG
-		       "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
-		       dev->name);
-#endif
-
-	/* Prevent reentrancy. We need to do that because we may have
-	 * multiple interrupt handler running concurrently.
-	 * It is safe because wv_splhi() disables interrupts before acquiring
-	 * the spinlock. */
-	spin_lock(&lp->spinlock);
-
-	/* Check modem interrupt */
-	if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) {
-		u8 dce_status;
-
-		/*
-		 * Interrupt from the modem management controller.
-		 * This will clear it -- ignored for now.
-		 */
-		mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status,
-			 sizeof(dce_status));
-#ifdef DEBUG_INTERRUPT_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n",
-		       dev->name, dce_status);
-#endif
-	}
-
-	/* Check if not controller interrupt */
-	if ((hasr & HASR_82586_INTR) == 0) {
-#ifdef DEBUG_INTERRUPT_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_interrupt(): interrupt not coming from i82586\n",
-		       dev->name);
-#endif
-		spin_unlock (&lp->spinlock);
-		return;
-	}
-
-	/* Read interrupt data. */
-	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
-		   (unsigned char *) &status, sizeof(status));
-
-	/*
-	 * Acknowledge the interrupt(s).
-	 */
-	ack_cmd = status & SCB_ST_INT;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
-		    (unsigned char *) &ack_cmd, sizeof(ack_cmd));
-	set_chan_attn(ioaddr, lp->hacr);
-
-#ifdef DEBUG_INTERRUPT_INFO
-	printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n",
-	       dev->name, status);
-#endif
-
-	/* Command completed. */
-	if ((status & SCB_ST_CX) == SCB_ST_CX) {
-#ifdef DEBUG_INTERRUPT_INFO
-		printk(KERN_DEBUG
-		       "%s: wavelan_interrupt(): command completed.\n",
-		       dev->name);
-#endif
-		wv_complete(dev, ioaddr, lp);
-	}
-
-	/* Frame received. */
-	if ((status & SCB_ST_FR) == SCB_ST_FR) {
-#ifdef DEBUG_INTERRUPT_INFO
-		printk(KERN_DEBUG
-		       "%s: wavelan_interrupt(): received packet.\n",
-		       dev->name);
-#endif
-		wv_receive(dev);
-	}
-
-	/* Check the state of the command unit. */
-	if (((status & SCB_ST_CNA) == SCB_ST_CNA) ||
-	    (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) &&
-	     (netif_running(dev)))) {
-#ifdef DEBUG_INTERRUPT_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_interrupt(): CU inactive -- restarting\n",
-		       dev->name);
-#endif
-		wv_hw_reset(dev);
-	}
-
-	/* Check the state of the command unit. */
-	if (((status & SCB_ST_RNR) == SCB_ST_RNR) ||
-	    (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) &&
-	     (netif_running(dev)))) {
-#ifdef DEBUG_INTERRUPT_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_interrupt(): RU not ready -- restarting\n",
-		       dev->name);
-#endif
-		wv_hw_reset(dev);
-	}
-
-	/* Release spinlock */
-	spin_unlock (&lp->spinlock);
-
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Watchdog: when we start a transmission, a timer is set for us in the
- * kernel.  If the transmission completes, this timer is disabled. If
- * the timer expires, we are called and we try to unlock the hardware.
- */
-static void wavelan_watchdog(device *	dev)
-{
-	net_local *	lp = (net_local *)dev->priv;
-	u_long		ioaddr = dev->base_addr;
-	unsigned long	flags;
-	unsigned int	nreaped;
-
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
-#endif
-
-#ifdef DEBUG_INTERRUPT_ERROR
-	printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
-	       dev->name);
-#endif
-
-	/* Check that we came here for something */
-	if (lp->tx_n_in_use <= 0) {
-		return;
-	}
-
-	wv_splhi(lp, &flags);
-
-	/* Try to see if some buffers are not free (in case we missed
-	 * an interrupt */
-	nreaped = wv_complete(dev, ioaddr, lp);
-
-#ifdef DEBUG_INTERRUPT_INFO
-	printk(KERN_DEBUG
-	       "%s: wavelan_watchdog(): %d reaped, %d remain.\n",
-	       dev->name, nreaped, lp->tx_n_in_use);
-#endif
-
-#ifdef DEBUG_PSA_SHOW
-	{
-		psa_t psa;
-		psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-		wv_psa_show(&psa);
-	}
-#endif
-#ifdef DEBUG_MMC_SHOW
-	wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82586_SHOW
-	wv_cu_show(dev);
-#endif
-
-	/* If no buffer has been freed */
-	if (nreaped == 0) {
-#ifdef DEBUG_INTERRUPT_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_watchdog(): cleanup failed, trying reset\n",
-		       dev->name);
-#endif
-		wv_hw_reset(dev);
-	}
-
-	/* At this point, we should have some free Tx buffer ;-) */
-	if (lp->tx_n_in_use < NTXBLOCKS - 1)
-		netif_wake_queue(dev);
-
-	wv_splx(lp, &flags);
-	
-#ifdef DEBUG_INTERRUPT_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
-#endif
-}
-
-/********************* CONFIGURATION CALLBACKS *********************/
-/*
- * Here are the functions called by the Linux networking code (NET3)
- * for initialization, configuration and deinstallations of the 
- * WaveLAN ISA hardware.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Configure and start up the WaveLAN PCMCIA adaptor.
- * Called by NET3 when it "opens" the device.
- */
-static int wavelan_open(device * dev)
-{
-	net_local *	lp = (net_local *)dev->priv;
-	unsigned long	flags;
-
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
-	       (unsigned int) dev);
-#endif
-
-	/* Check irq */
-	if (dev->irq == 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n",
-		       dev->name);
-#endif
-		return -ENXIO;
-	}
-
-	if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) 
-	{
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n",
-		       dev->name);
-#endif
-		return -EAGAIN;
-	}
-
-	wv_splhi(lp, &flags);
-	
-	if (wv_hw_reset(dev) != -1) {
-		netif_start_queue(dev);
-	} else {
-		free_irq(dev->irq, dev);
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_open(): impossible to start the card\n",
-		       dev->name);
-#endif
-		wv_splx(lp, &flags);
-		return -EAGAIN;
-	}
-	wv_splx(lp, &flags);
-	
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Shut down the WaveLAN ISA card.
- * Called by NET3 when it "closes" the device.
- */
-static int wavelan_close(device * dev)
-{
-	net_local *lp = (net_local *) dev->priv;
-	unsigned long flags;
-
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
-	       (unsigned int) dev);
-#endif
-
-	netif_stop_queue(dev);
-
-	/*
-	 * Flush the Tx and disable Rx.
-	 */
-	wv_splhi(lp, &flags);
-	wv_82586_stop(dev);
-	wv_splx(lp, &flags);
-
-	free_irq(dev->irq, dev);
-
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Probe an I/O address, and if the WaveLAN is there configure the
- * device structure
- * (called by wavelan_probe() and via init_module()).
- */
-static int __init wavelan_config(device * dev)
-{
-	unsigned long ioaddr = dev->base_addr;
-	u8 irq_mask;
-	int irq;
-	net_local *lp;
-
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n",
-	       dev->name, (unsigned int) dev, ioaddr);
-#endif
-
-	/* Check IRQ argument on command line. */
-	if (dev->irq != 0) {
-		irq_mask = wv_irq_to_psa(dev->irq);
-
-		if (irq_mask == 0) {
-#ifdef DEBUG_CONFIG_ERROR
-			printk(KERN_WARNING
-			       "%s: wavelan_config(): invalid IRQ %d ignored.\n",
-			       dev->name, dev->irq);
-#endif
-			dev->irq = 0;
-		} else {
-#ifdef DEBUG_CONFIG_INFO
-			printk(KERN_DEBUG
-			       "%s: wavelan_config(): changing IRQ to %d\n",
-			       dev->name, dev->irq);
-#endif
-			psa_write(ioaddr, HACR_DEFAULT,
-				  psaoff(0, psa_int_req_no), &irq_mask, 1);
-			/* update the Wavelan checksum */
-			update_psa_checksum(dev, ioaddr, HACR_DEFAULT);
-			wv_hacr_reset(ioaddr);
-		}
-	}
-
-	psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no),
-		 &irq_mask, 1);
-	if ((irq = wv_psa_to_irq(irq_mask)) == -1) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_INFO
-		       "%s: wavelan_config(): could not wavelan_map_irq(%d).\n",
-		       dev->name, irq_mask);
-#endif
-		return -EAGAIN;
-	}
-
-	dev->irq = irq;
-
-	if (!request_region(ioaddr, sizeof(ha_t), "wavelan"))
-		return -EBUSY;
-
-	dev->mem_start = 0x0000;
-	dev->mem_end = 0x0000;
-	dev->if_port = 0;
-
-	/* Initialize device structures */
-	dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
-	if (dev->priv == NULL) {
-		release_region(ioaddr, sizeof(ha_t));
-		return -ENOMEM;
-	}
-	memset(dev->priv, 0x00, sizeof(net_local));
-	lp = (net_local *) dev->priv;
-
-	/* Back link to the device structure. */
-	lp->dev = dev;
-	/* Add the device at the beginning of the linked list. */
-	lp->next = wavelan_list;
-	wavelan_list = lp;
-
-	lp->hacr = HACR_DEFAULT;
-
-	/* Multicast stuff */
-	lp->promiscuous = 0;
-	lp->mc_count = 0;
-
-	/* Init spinlock */
-	spin_lock_init(&lp->spinlock);
-
-	/*
-	 * Fill in the fields of the device structure
-	 * with generic Ethernet values.
-	 */
-	ether_setup(dev);
-
-	SET_MODULE_OWNER(dev);
-	dev->open = wavelan_open;
-	dev->stop = wavelan_close;
-	dev->hard_start_xmit = wavelan_packet_xmit;
-	dev->get_stats = wavelan_get_stats;
-	dev->set_multicast_list = &wavelan_set_multicast_list;
-        dev->tx_timeout		= &wavelan_watchdog;
-        dev->watchdog_timeo	= WATCHDOG_JIFFIES;
-#ifdef SET_MAC_ADDRESS
-	dev->set_mac_address = &wavelan_set_mac_address;
-#endif				/* SET_MAC_ADDRESS */
-
-#ifdef WIRELESS_EXT		/* if wireless extension exists in the kernel */
-	dev->do_ioctl = wavelan_ioctl;
-	dev->get_wireless_stats = wavelan_get_wireless_stats;
-#endif
-
-	dev->mtu = WAVELAN_MTU;
-
-	/* Display nice information. */
-	wv_init_info(dev);
-
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name);
-#endif
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Check for a network adaptor of this type.  Return '0' iff one 
- * exists.  There seem to be different interpretations of
- * the initial value of dev->base_addr.
- * We follow the example in drivers/net/ne.c.
- * (called in "Space.c")
- */
-int __init wavelan_probe(device * dev)
-{
-	short base_addr;
-	mac_addr mac;		/* MAC address (check existence of WaveLAN) */
-	int i;
-	int r;
-
-#ifdef DEBUG_CALLBACK_TRACE
-	printk(KERN_DEBUG
-	       "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n",
-	       dev->name, (unsigned int) dev,
-	       (unsigned int) dev->base_addr);
-#endif
-
-#ifdef	STRUCT_CHECK
-	if (wv_struct_check() != (char *) NULL) {
-		printk(KERN_WARNING
-		       "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n",
-		       dev->name, wv_struct_check());
-		return -ENODEV;
-	}
-#endif				/* STRUCT_CHECK */
-
-	/* Check the value of the command line parameter for base address. */
-	base_addr = dev->base_addr;
-
-	/* Don't probe at all. */
-	if (base_addr < 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_WARNING
-		       "%s: wavelan_probe(): invalid base address\n",
-		       dev->name);
-#endif
-		return -ENXIO;
-	}
-
-	/* Check a single specified location. */
-	if (base_addr > 0x100) {
-		/* Check if there is something at this base address */
-		if ((r = wv_check_ioaddr(base_addr, mac)) == 0) {
-			memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
-			r = wavelan_config(dev);
-		}
-#ifdef DEBUG_CONFIG_INFO
-		if (r != 0)
-			printk(KERN_DEBUG
-			       "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n",
-			       dev->name, base_addr);
-#endif
-
-#ifdef DEBUG_CALLBACK_TRACE
-		printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
-#endif
-		return r;
-	}
-
-	/* Scan all possible addresses of the WaveLAN hardware. */
-	for (i = 0; i < NELS(iobase); i++) {
-		/* Check whether there is something at this base address. */
-		if (wv_check_ioaddr(iobase[i], mac) == 0) {
-			dev->base_addr = iobase[i];	/* Copy base address. */
-			memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
-			if (wavelan_config(dev) == 0) {
-#ifdef DEBUG_CALLBACK_TRACE
-				printk(KERN_DEBUG
-				       "%s: <-wavelan_probe()\n",
-				       dev->name);
-#endif
-				return 0;
-			}
-		}
-	}
-
-	/* We may have touched base_addr.  Another driver may not like it. */
-	dev->base_addr = base_addr;
-
-#ifdef DEBUG_CONFIG_INFO
-	printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n",
-	       dev->name);
-#endif
-
-	return -ENODEV;
-}
-
-/****************************** MODULE ******************************/
-/*
- * Module entry point: insertion and removal
- */
-
-#ifdef	MODULE
-/*------------------------------------------------------------------*/
-/*
- * Insertion of the module
- * I'm now quite proud of the multi-device support.
- */
-int init_module(void)
-{
-	mac_addr mac;		/* MAC address (check WaveLAN existence) */
-	int ret = -EIO;		/* Return error if no cards found */
-	int i;
-
-#ifdef DEBUG_MODULE_TRACE
-	printk(KERN_DEBUG "-> init_module()\n");
-#endif
-
-	/* If probing is asked */
-	if (io[0] == 0) {
-#ifdef DEBUG_CONFIG_ERROR
-		printk(KERN_WARNING
-		       "WaveLAN init_module(): doing device probing (bad !)\n");
-		printk(KERN_WARNING
-		       "Specify base addresses while loading module to correct the problem\n");
-#endif
-
-		/* Copy the basic set of address to be probed. */
-		for (i = 0; i < NELS(iobase); i++)
-			io[i] = iobase[i];
-	}
-
-
-	/* Loop on all possible base addresses. */
-	i = -1;
-	while ((io[++i] != 0) && (i < NELS(io))) {
-		/* Check if there is something at this base address. */
-		if (wv_check_ioaddr(io[i], mac) == 0) {
-			device *dev;
-
-			/* Create device and set basic arguments. */
-			dev =
-			    kmalloc(sizeof(struct net_device), GFP_KERNEL);
-			if (dev == NULL) {
-				ret = -ENOMEM;
-				break;
-			}
-			memset(dev, 0x00, sizeof(struct net_device));
-			memcpy(dev->name, name[i], IFNAMSIZ);	/* Copy name */
-			dev->base_addr = io[i];
-			dev->irq = irq[i];
-			dev->init = &wavelan_config;
-			memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
-
-			/* Try to create the device. */
-			if (register_netdev(dev) != 0) {
-				/* Deallocate everything. */
-				/* Note: if dev->priv is mallocated, there is no way to fail. */
-				kfree(dev);
-			} else {
-				/* If at least one device OK, we do not fail */
-				ret = 0;
-			}
-		}		/* if there is something at the address */
-	}			/* Loop on all addresses. */
-
-#ifdef DEBUG_CONFIG_ERROR
-	if (wavelan_list == (net_local *) NULL)
-		printk(KERN_WARNING
-		       "WaveLAN init_module(): no device found\n");
-#endif
-
-#ifdef DEBUG_MODULE_TRACE
-	printk(KERN_DEBUG "<- init_module()\n");
-#endif
-	return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Removal of the module
- */
-void cleanup_module(void)
-{
-#ifdef DEBUG_MODULE_TRACE
-	printk(KERN_DEBUG "-> cleanup_module()\n");
-#endif
-
-	/* Loop on all devices and release them. */
-	while (wavelan_list != (net_local *) NULL) {
-		device *dev = wavelan_list->dev;
-
-#ifdef DEBUG_CONFIG_INFO
-		printk(KERN_DEBUG
-		       "%s: cleanup_module(): removing device at 0x%x\n",
-		       dev->name, (unsigned int) dev);
-#endif
-
-		/* Release the ioport region. */
-		release_region(dev->base_addr, sizeof(ha_t));
-
-		/* Definitely remove the device. */
-		unregister_netdev(dev);
-
-		/* Unlink the device. */
-		wavelan_list = wavelan_list->next;
-
-		/* Free pieces. */
-		kfree(dev->priv);
-		kfree(dev);
-	}
-
-#ifdef DEBUG_MODULE_TRACE
-	printk(KERN_DEBUG "<- cleanup_module()\n");
-#endif
-}
-#endif				/* MODULE */
-MODULE_LICENSE("GPL");
-
-/*
- * This software may only be used and distributed
- * according to the terms of the GNU General Public License.
- *
- * This software was developed as a component of the
- * Linux operating system.
- * It is based on other device drivers and information
- * either written or supplied by:
- *	Ajay Bakre (bakre@paul.rutgers.edu),
- *	Donald Becker (becker@scyld.com),
- *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
- *	Anders Klemets (klemets@it.kth.se),
- *	Vladimir V. Kolpakov (w@stier.koenig.ru),
- *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
- *	Pauline Middelink (middelin@polyware.iaf.nl),
- *	Robert Morris (rtm@das.harvard.edu),
- *	Jean Tourrilhes (jt@hplb.hpl.hp.com),
- *	Girish Welling (welling@paul.rutgers.edu),
- *
- * Thanks go also to:
- *	James Ashton (jaa101@syseng.anu.edu.au),
- *	Alan Cox (alan@redhat.com),
- *	Allan Creighton (allanc@cs.usyd.edu.au),
- *	Matthew Geier (matthew@cs.usyd.edu.au),
- *	Remo di Giovanni (remo@cs.usyd.edu.au),
- *	Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
- *	Vipul Gupta (vgupta@cs.binghamton.edu),
- *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
- *	Tim Nicholson (tim@cs.usyd.edu.au),
- *	Ian Parkin (ian@cs.usyd.edu.au),
- *	John Rosenberg (johnr@cs.usyd.edu.au),
- *	George Rossi (george@phm.gov.au),
- *	Arthur Scott (arthur@cs.usyd.edu.au),
- *	Peter Storey,
- * for their assistance and advice.
- *
- * Please send bug reports, updates, comments to:
- *
- * Bruce Janson                                    Email:  bruce@cs.usyd.edu.au
- * Basser Department of Computer Science           Phone:  +61-2-9351-3423
- * University of Sydney, N.S.W., 2006, AUSTRALIA   Fax:    +61-2-9351-3838
- */
diff -urN linux-2.5.3-pre2/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- linux-2.5.3-pre2/drivers/net/wavelan.h	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/wavelan.h	Wed Dec 31 16:00:00 1969
@@ -1,370 +0,0 @@
-/*
- *	WaveLAN ISA driver
- *
- *		Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- * Original copyright follows. See wavelan.p.h for details.
- *
- * This file contains the declarations for the WaveLAN hardware. Note that
- * the WaveLAN ISA includes a i82586 controller (see definitions in
- * file i82586.h).
- *
- * The main difference between the ISA hardware and the PCMCIA one is
- * the Ethernet controller (i82586 instead of i82593).
- * The i82586 allows multiple transmit buffers.  The PSA needs to be accessed
- * through the host interface.
- */
-
-#ifndef _WAVELAN_H
-#define	_WAVELAN_H
-
-/************************** MAGIC NUMBERS ***************************/
-
-/* Detection of the WaveLAN card is done by reading the MAC
- * address from the card and checking it.  If you have a non-AT&T
- * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
- * you might need to modify this part to accommodate your hardware.
- */
-static const char	MAC_ADDRESSES[][3] =
-{
-  { 0x08, 0x00, 0x0E },		/* AT&T WaveLAN (standard) & DEC RoamAbout */
-  { 0x08, 0x00, 0x6A },		/* AT&T WaveLAN (alternate) */
-  { 0x00, 0x00, 0xE1 },		/* Hitachi Wavelan */
-  { 0x00, 0x60, 0x1D }		/* Lucent Wavelan (another one) */
-  /* Add your card here and send me the patch! */
-};
-
-#define WAVELAN_ADDR_SIZE	6	/* Size of a MAC address */
-
-#define WAVELAN_MTU		1500	/* Maximum size of WaveLAN packet */
-
-#define	MAXDATAZ		(WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
-
-/*
- * Constants used to convert channels to frequencies
- */
-
-/* Frequency available in the 2.0 modem, in units of 250 kHz
- * (as read in the offset register of the dac area).
- * Used to map channel numbers used by `wfreqsel' to frequencies
- */
-static const short	channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
-				    0xD0, 0xF0, 0xF8, 0x150 };
-
-/* Frequencies of the 1.0 modem (fixed frequencies).
- * Use to map the PSA `subband' to a frequency
- * Note : all frequencies apart from the first one need to be multiplied by 10
- */
-static const int	fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
-
-
-
-/*************************** PC INTERFACE ****************************/
-
-/*
- * Host Adaptor structure.
- * (base is board port address).
- */
-typedef union hacs_u	hacs_u;
-union hacs_u
-{
-	unsigned short	hu_command;		/* Command register */
-#define		HACR_RESET		0x0001	/* Reset board */
-#define		HACR_CA			0x0002	/* Set Channel Attention for 82586 */
-#define		HACR_16BITS		0x0004	/* 16-bit operation (0 => 8bits) */
-#define		HACR_OUT0		0x0008	/* General purpose output pin 0 */
-						/* not used - must be 1 */
-#define		HACR_OUT1		0x0010	/* General purpose output pin 1 */
-						/* not used - must be 1 */
-#define		HACR_82586_INT_ENABLE	0x0020	/* Enable 82586 interrupts */
-#define		HACR_MMC_INT_ENABLE	0x0040	/* Enable MMC interrupts */
-#define		HACR_INTR_CLR_ENABLE	0x0080	/* Enable interrupt status read/clear */
-	unsigned short	hu_status;		/* Status Register */
-#define		HASR_82586_INTR		0x0001	/* Interrupt request from 82586 */
-#define		HASR_MMC_INTR		0x0002	/* Interrupt request from MMC */
-#define		HASR_MMC_BUSY		0x0004	/* MMC busy indication */
-#define		HASR_PSA_BUSY		0x0008	/* LAN parameter storage area busy */
-};
-
-typedef struct ha_t	ha_t;
-struct ha_t
-{
-	hacs_u		ha_cs;		/* Command and status registers */
-#define 		ha_command	ha_cs.hu_command
-#define 		ha_status	ha_cs.hu_status
-	unsigned short	ha_mmcr;	/* Modem Management Ctrl Register */
-	unsigned short	ha_pior0;	/* Program I/O Address Register Port 0 */
-	unsigned short	ha_piop0;	/* Program I/O Port 0 */
-	unsigned short	ha_pior1;	/* Program I/O Address Register Port 1 */
-	unsigned short	ha_piop1;	/* Program I/O Port 1 */
-	unsigned short	ha_pior2;	/* Program I/O Address Register Port 2 */
-	unsigned short	ha_piop2;	/* Program I/O Port 2 */
-};
-
-#define HA_SIZE		16
-
-#define	hoff(p,f) 	(unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
-#define	HACR(p)		hoff(p, ha_command)
-#define	HASR(p)		hoff(p, ha_status)
-#define	MMCR(p)		hoff(p, ha_mmcr)
-#define	PIOR0(p)	hoff(p, ha_pior0)
-#define	PIOP0(p)	hoff(p, ha_piop0)
-#define	PIOR1(p)	hoff(p, ha_pior1)
-#define	PIOP1(p)	hoff(p, ha_piop1)
-#define	PIOR2(p)	hoff(p, ha_pior2)
-#define	PIOP2(p)	hoff(p, ha_piop2)
-
-/*
- * Program I/O Mode Register values.
- */
-#define STATIC_PIO		0	/* Mode 1: static mode */
-					/* RAM access ??? */
-#define AUTOINCR_PIO		1	/* Mode 2: auto increment mode */
-					/* RAM access ??? */
-#define AUTODECR_PIO		2	/* Mode 3: auto decrement mode */
-					/* RAM access ??? */
-#define PARAM_ACCESS_PIO	3	/* Mode 4: LAN parameter access mode */
-					/* Parameter access. */
-#define PIO_MASK		3	/* register mask */
-#define PIOM(cmd,piono)		((u_short)cmd << 10 << (piono * 2))
-
-#define	HACR_DEFAULT		(HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
-#define	HACR_INTRON		(HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
-
-/************************** MEMORY LAYOUT **************************/
-
-/*
- * Onboard 64 k RAM layout.
- * (Offsets from 0x0000.)
- */
-#define OFFSET_RU		0x0000		/* 75% memory */
-#define OFFSET_CU		0xC000		/* 25% memory */
-#define OFFSET_SCB		(OFFSET_ISCP - sizeof(scb_t))
-#define OFFSET_ISCP		(OFFSET_SCP - sizeof(iscp_t))
-#define OFFSET_SCP		I82586_SCP_ADDR
-
-#define	RXBLOCKZ		(sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
-#define	TXBLOCKZ		(sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
-
-#define	NRXBLOCKS		((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
-#define	NTXBLOCKS		((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
-
-/********************** PARAMETER STORAGE AREA **********************/
-
-/*
- * Parameter Storage Area (PSA).
- */
-typedef struct psa_t	psa_t;
-struct psa_t
-{
-  unsigned char	psa_io_base_addr_1;	/* [0x00] Base address 1 ??? */
-  unsigned char	psa_io_base_addr_2;	/* [0x01] Base address 2 */
-  unsigned char	psa_io_base_addr_3;	/* [0x02] Base address 3 */
-  unsigned char	psa_io_base_addr_4;	/* [0x03] Base address 4 */
-  unsigned char	psa_rem_boot_addr_1;	/* [0x04] Remote Boot Address 1 */
-  unsigned char	psa_rem_boot_addr_2;	/* [0x05] Remote Boot Address 2 */
-  unsigned char	psa_rem_boot_addr_3;	/* [0x06] Remote Boot Address 3 */
-  unsigned char	psa_holi_params;	/* [0x07] HOst Lan Interface (HOLI) Parameters */
-  unsigned char	psa_int_req_no;		/* [0x08] Interrupt Request Line */
-  unsigned char	psa_unused0[7];		/* [0x09-0x0F] unused */
-
-  unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x10-0x15] Universal (factory) MAC Address */
-  unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x16-1B] Local MAC Address */
-  unsigned char	psa_univ_local_sel;	/* [0x1C] Universal Local Selection */
-#define		PSA_UNIVERSAL	0		/* Universal (factory) */
-#define		PSA_LOCAL	1		/* Local */
-  unsigned char	psa_comp_number;	/* [0x1D] Compatibility Number:	 */
-#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz 	 */
-#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	 */
-#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	 */
-#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz 	 */
-#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz or 2.0 */
-  unsigned char	psa_thr_pre_set;	/* [0x1E] Modem Threshold Preset */
-  unsigned char	psa_feature_select;	/* [0x1F] Call code required (1=on) */
-#define		PSA_FEATURE_CALL_CODE	0x01 	/* Call code required (Japan) */
-  unsigned char	psa_subband;		/* [0x20] Subband	  */
-#define		PSA_SUBBAND_915		0	/* 915 MHz or 2.0 */
-#define		PSA_SUBBAND_2425	1	/* 2425 MHz	  */
-#define		PSA_SUBBAND_2460	2	/* 2460 MHz	  */
-#define		PSA_SUBBAND_2484	3	/* 2484 MHz	  */
-#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	  */
-  unsigned char	psa_quality_thr;	/* [0x21] Modem Quality Threshold */
-  unsigned char	psa_mod_delay;		/* [0x22] Modem Delay (?) (reserved) */
-  unsigned char	psa_nwid[2];		/* [0x23-0x24] Network ID */
-  unsigned char	psa_nwid_select;	/* [0x25] Network ID Select On/Off */
-  unsigned char	psa_encryption_select;	/* [0x26] Encryption On/Off */
-  unsigned char	psa_encryption_key[8];	/* [0x27-0x2E] Encryption Key */
-  unsigned char	psa_databus_width;	/* [0x2F] AT bus width select 8/16 */
-  unsigned char	psa_call_code[8];	/* [0x30-0x37] (Japan) Call Code */
-  unsigned char	psa_nwid_prefix[2];	/* [0x38-0x39] Roaming domain */
-  unsigned char	psa_reserved[2];	/* [0x3A-0x3B] Reserved - fixed 00 */
-  unsigned char	psa_conf_status;	/* [0x3C] Conf Status, bit 0=1:config*/
-  unsigned char	psa_crc[2];		/* [0x3D] CRC-16 over PSA */
-  unsigned char	psa_crc_status;		/* [0x3F] CRC Valid Flag */
-};
-
-#define	PSA_SIZE	64
-
-/* Calculate offset of a field in the above structure.
- * Warning:  only even addresses are used. */
-#define	psaoff(p,f) 	((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
-
-/******************** MODEM MANAGEMENT INTERFACE ********************/
-
-/*
- * Modem Management Controller (MMC) write structure.
- */
-typedef struct mmw_t	mmw_t;
-struct mmw_t
-{
-  unsigned char	mmw_encr_key[8];	/* encryption key */
-  unsigned char	mmw_encr_enable;	/* Enable or disable encryption. */
-#define	MMW_ENCR_ENABLE_MODE	0x02	/* mode of security option */
-#define	MMW_ENCR_ENABLE_EN	0x01	/* Enable security option. */
-  unsigned char	mmw_unused0[1];		/* unused */
-  unsigned char	mmw_des_io_invert;	/* encryption option */
-#define	MMW_DES_IO_INVERT_RES	0x0F	/* reserved */
-#define	MMW_DES_IO_INVERT_CTRL	0xF0	/* control (?) (set to 0) */
-  unsigned char	mmw_unused1[5];		/* unused */
-  unsigned char	mmw_loopt_sel;		/* looptest selection */
-#define	MMW_LOOPT_SEL_DIS_NWID	0x40	/* Disable NWID filtering. */
-#define	MMW_LOOPT_SEL_INT	0x20	/* Activate Attention Request. */
-#define	MMW_LOOPT_SEL_LS	0x10	/* looptest, no collision avoidance */
-#define MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */
-#define	MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */
-#define	MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */
-#define	MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */
-  unsigned char	mmw_jabber_enable;	/* jabber timer enable */
-  /* Abort transmissions > 200 ms */
-  unsigned char	mmw_freeze;		/* freeze or unfreeze signal level */
-  /* 0 : signal level & qual updated for every new message, 1 : frozen */
-  unsigned char	mmw_anten_sel;		/* antenna selection */
-#define MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */
-#define	MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algo. enable */
-  unsigned char	mmw_ifs;		/* inter frame spacing */
-  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
-  unsigned char	mmw_mod_delay;	 	/* modem delay (synchro) */
-  unsigned char	mmw_jam_time;		/* jamming time (after collision) */
-  unsigned char	mmw_unused2[1];		/* unused */
-  unsigned char	mmw_thr_pre_set;	/* level threshold preset */
-  /* Discard all packet with signal < this value (4) */
-  unsigned char	mmw_decay_prm;		/* decay parameters */
-  unsigned char	mmw_decay_updat_prm;	/* decay update parameters */
-  unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
-  /* Discard all packet with quality < this value (3) */
-  unsigned char	mmw_netw_id_l;		/* NWID low order byte */
-  unsigned char	mmw_netw_id_h;		/* NWID high order byte */
-  /* Network ID or Domain : create virtual net on the air */
-
-  /* 2.0 Hardware extension - frequency selection support */
-  unsigned char	mmw_mode_select;	/* for analog tests (set to 0) */
-  unsigned char	mmw_unused3[1];		/* unused */
-  unsigned char	mmw_fee_ctrl;		/* frequency EEPROM control */
-#define	MMW_FEE_CTRL_PRE	0x10	/* Enable protected instructions. */
-#define	MMW_FEE_CTRL_DWLD	0x08	/* Download EEPROM to mmc. */
-#define	MMW_FEE_CTRL_CMD	0x07	/* EEPROM commands:  */
-#define	MMW_FEE_CTRL_READ	0x06	/* Read */
-#define	MMW_FEE_CTRL_WREN	0x04	/* Write enable */
-#define	MMW_FEE_CTRL_WRITE	0x05	/* Write data to address. */
-#define	MMW_FEE_CTRL_WRALL	0x04	/* Write data to all addresses. */
-#define	MMW_FEE_CTRL_WDS	0x04	/* Write disable */
-#define	MMW_FEE_CTRL_PRREAD	0x16	/* Read addr from protect register */
-#define	MMW_FEE_CTRL_PREN	0x14	/* Protect register enable */
-#define	MMW_FEE_CTRL_PRCLEAR	0x17	/* Unprotect all registers. */
-#define	MMW_FEE_CTRL_PRWRITE	0x15	/* Write address in protect register */
-#define	MMW_FEE_CTRL_PRDS	0x14	/* Protect register disable */
-  /* Never issue the PRDS command:  it's irreversible! */
-
-  unsigned char	mmw_fee_addr;		/* EEPROM address */
-#define	MMW_FEE_ADDR_CHANNEL	0xF0	/* Select the channel. */
-#define	MMW_FEE_ADDR_OFFSET	0x0F	/* Offset in channel data */
-#define	MMW_FEE_ADDR_EN		0xC0	/* FEE_CTRL enable operations */
-#define	MMW_FEE_ADDR_DS		0x00	/* FEE_CTRL disable operations */
-#define	MMW_FEE_ADDR_ALL	0x40	/* FEE_CTRL all operations */
-#define	MMW_FEE_ADDR_CLEAR	0xFF	/* FEE_CTRL clear operations */
-
-  unsigned char	mmw_fee_data_l;		/* Write data to EEPROM. */
-  unsigned char	mmw_fee_data_h;		/* high octet */
-  unsigned char	mmw_ext_ant;		/* Setting for external antenna */
-#define	MMW_EXT_ANT_EXTANT	0x01	/* Select external antenna */
-#define	MMW_EXT_ANT_POL		0x02	/* Polarity of the antenna */
-#define	MMW_EXT_ANT_INTERNAL	0x00	/* Internal antenna */
-#define	MMW_EXT_ANT_EXTERNAL	0x03	/* External antenna */
-#define	MMW_EXT_ANT_IQ_TEST	0x1C	/* IQ test pattern (set to 0) */
-};
-
-#define	MMW_SIZE	37
-
-#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
-
-/*
- * Modem Management Controller (MMC) read structure.
- */
-typedef struct mmr_t	mmr_t;
-struct mmr_t
-{
-  unsigned char	mmr_unused0[8];		/* unused */
-  unsigned char	mmr_des_status;		/* encryption status */
-  unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */
-#define	MMR_DES_AVAIL_DES	0x55		/* DES available */
-#define	MMR_DES_AVAIL_AES	0x33		/* AES (AT&T) available */
-  unsigned char	mmr_des_io_invert;	/* des I/O invert register */
-  unsigned char	mmr_unused1[5];		/* unused */
-  unsigned char	mmr_dce_status;		/* DCE status */
-#define	MMR_DCE_STATUS_RX_BUSY		0x01	/* receiver busy */
-#define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
-#define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */
-#define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
-#define MMR_DCE_STATUS			0x0F	/* mask to get the bits */
-  unsigned char	mmr_dsp_id;		/* DSP ID (AA = Daedalus rev A) */
-  unsigned char	mmr_unused2[2];		/* unused */
-  unsigned char	mmr_correct_nwid_l;	/* # of correct NWIDs rxd (low) */
-  unsigned char	mmr_correct_nwid_h;	/* # of correct NWIDs rxd (high) */
-  /* Warning:  read high-order octet first! */
-  unsigned char	mmr_wrong_nwid_l;	/* # of wrong NWIDs rxd (low) */
-  unsigned char	mmr_wrong_nwid_h;	/* # of wrong NWIDs rxd (high) */
-  unsigned char	mmr_thr_pre_set;	/* level threshold preset */
-#define	MMR_THR_PRE_SET		0x3F		/* level threshold preset */
-#define	MMR_THR_PRE_SET_CUR	0x80		/* Current signal above it */
-  unsigned char	mmr_signal_lvl;		/* signal level */
-#define	MMR_SIGNAL_LVL		0x3F		/* signal level */
-#define	MMR_SIGNAL_LVL_VALID	0x80		/* Updated since last read */
-  unsigned char	mmr_silence_lvl;	/* silence level (noise) */
-#define	MMR_SILENCE_LVL		0x3F		/* silence level */
-#define	MMR_SILENCE_LVL_VALID	0x80		/* Updated since last read */
-  unsigned char	mmr_sgnl_qual;		/* signal quality */
-#define	MMR_SGNL_QUAL		0x0F		/* signal quality */
-#define	MMR_SGNL_QUAL_ANT	0x80		/* current antenna used */
-  unsigned char	mmr_netw_id_l;		/* NWID low order byte (?) */
-  unsigned char	mmr_unused3[3];		/* unused */
-
-  /* 2.0 Hardware extension - frequency selection support */
-  unsigned char	mmr_fee_status;		/* Status of frequency EEPROM */
-#define	MMR_FEE_STATUS_ID	0xF0		/* Modem revision ID */
-#define	MMR_FEE_STATUS_DWLD	0x08		/* Download in progress */
-#define	MMR_FEE_STATUS_BUSY	0x04		/* EEPROM busy */
-  unsigned char	mmr_unused4[1];		/* unused */
-  unsigned char	mmr_fee_data_l;		/* Read data from EEPROM (low) */
-  unsigned char	mmr_fee_data_h;		/* Read data from EEPROM (high) */
-};
-
-#define	MMR_SIZE	36
-
-#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
-
-/* Make the two above structures one */
-typedef union mm_t
-{
-  struct mmw_t	w;	/* Write to the mmc */
-  struct mmr_t	r;	/* Read from the mmc */
-} mm_t;
-
-#endif /* _WAVELAN_H */
-
-/*
- * This software may only be used and distributed
- * according to the terms of the GNU General Public License.
- *
- * For more details, see wavelan.c.
- */
diff -urN linux-2.5.3-pre2/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- linux-2.5.3-pre2/drivers/net/wavelan.p.h	Fri Oct 12 14:21:18 2001
+++ linux/drivers/net/wavelan.p.h	Wed Dec 31 16:00:00 1969
@@ -1,713 +0,0 @@
-/*
- *	WaveLAN ISA driver
- *
- *		Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- *
- * This file contains all definitions and declarations necessary for the
- * WaveLAN ISA driver.  This file is a private header, so it should
- * be included only in wavelan.c!
- */
-
-#ifndef WAVELAN_P_H
-#define WAVELAN_P_H
-
-/************************** DOCUMENTATION ***************************/
-/*
- * This driver provides a Linux interface to the WaveLAN ISA hardware.
- * The WaveLAN is a product of Lucent (http://www.wavelan.com/).
- * This division was formerly part of NCR and then AT&T.
- * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean.
- *
- * To learn how to use this driver, read the NET3 HOWTO.
- * If you want to exploit the many other functionalities, read the comments
- * in the code.
- *
- * This driver is the result of the effort of many people (see below).
- */
-
-/* ------------------------ SPECIFIC NOTES ------------------------ */
-/*
- * Web page
- * --------
- *	I try to maintain a web page with the Wireless LAN Howto at :
- *	    http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
- *
- * SMP
- * ---
- *	We now are SMP compliant (I eventually fixed the remaining bugs).
- *	The driver has been tested on a dual P6-150 and survived my usual
- *	set of torture tests.
- *	Anyway, I spent enough time chasing interrupt re-entrancy during
- *	errors or reconfigure, and I designed the locked/unlocked sections
- *	of the driver with great care, and with the recent addition of
- *	the spinlock (thanks to the new API), we should be quite close to
- *	the truth.
- *	The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
- *	but better safe than sorry (especially at 2 Mb/s ;-).
- *
- *	I have also looked into disabling only our interrupt on the card
- *	(via HACR) instead of all interrupts in the processor (via cli),
- *	so that other driver are not impacted, and it look like it's
- *	possible, but it's very tricky to do right (full of races). As
- *	the gain would be mostly for SMP systems, it can wait...
- *
- * Debugging and options
- * ---------------------
- *	You will find below a set of '#define" allowing a very fine control
- *	on the driver behaviour and the debug messages printed.
- *	The main options are :
- *	o SET_PSA_CRC, to have your card correctly recognised by
- *	  an access point and the Point-to-Point diagnostic tool.
- *	o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
- *	  (otherwise we always start afresh with some defaults)
- *
- * wavelan.o is too darned big
- * ---------------------------
- *	That's true!  There is a very simple way to reduce the driver
- *	object by 33%!  Comment out the following line:
- *		#include <linux/wireless.h>
- *	Other compile options can also reduce the size of it...
- *
- * MAC address and hardware detection:
- * -----------------------------------
- *	The detection code for the WaveLAN checks that the first three
- *	octets of the MAC address fit the company code.  This type of
- *	detection works well for AT&T cards (because the AT&T code is
- *	hardcoded in wavelan.h), but of course will fail for other
- *	manufacturers.
- *
- *	If you are sure that your card is derived from the WaveLAN,
- *	here is the way to configure it:
- *	1) Get your MAC address
- *		a) With your card utilities (wfreqsel, instconf, etc.)
- *		b) With the driver:
- *			o compile the kernel with DEBUG_CONFIG_INFO enabled
- *			o Boot and look the card messages
- *	2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
- *	3) Compile and verify
- *	4) Send me the MAC code.  I will include it in the next version.
- *
- */
-
-/* --------------------- WIRELESS EXTENSIONS --------------------- */
-/*
- * This driver is the first to support "wireless extensions".
- * This set of extensions provides a standard way to control the wireless
- * characteristics of the hardware.  Applications such as mobile IP may
- * take advantage of it.
- *
- * You will need to enable the CONFIG_NET_RADIO define in the kernel
- * configuration to enable the wireless extensions (this is the one
- * giving access to the radio network device choice).
- *
- * It might also be a good idea as well to fetch the wireless tools to
- * configure the device and play a bit.
- */
-
-/* ---------------------------- FILES ---------------------------- */
-/*
- * wavelan.c:		actual code for the driver:  C functions
- *
- * wavelan.p.h:		private header:  local types and variables for driver
- *
- * wavelan.h:		description of the hardware interface and structs
- *
- * i82586.h:		description of the Ethernet controller
- */
-
-/* --------------------------- HISTORY --------------------------- */
-/*
- * This is based on information in the drivers' headers. It may not be
- * accurate, and I guarantee only my best effort.
- *
- * The history of the WaveLAN drivers is as complicated as the history of
- * the WaveLAN itself (NCR -> AT&T -> Lucent).
- *
- * It all started with Anders Klemets <klemets@paul.rutgers.edu>
- * writing a WaveLAN ISA driver for the Mach microkernel.  Girish
- * Welling <welling@paul.rutgers.edu> had also worked on it.
- * Keith Moore modified this for the PCMCIA hardware.
- * 
- * Robert Morris <rtm@das.harvard.edu> ported these two drivers to BSDI
- * and added specific PCMCIA support (there is currently no equivalent
- * of the PCMCIA package under BSD).
- *
- * Jim Binkley <jrb@cs.pdx.edu> ported both BSDI drivers to FreeBSD.
- *
- * Bruce Janson <bruce@cs.usyd.edu.au> ported the BSDI ISA driver to Linux.
- *
- * Anthony D. Joseph <adj@lcs.mit.edu> started to modify Bruce's driver
- * (with help of the BSDI PCMCIA driver) for PCMCIA.
- * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished this work.
- * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
- * 2.00 cards correctly (2.4 GHz with frequency selection).
- * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
- * PCMCIA package (and bug corrections).
- *
- * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
- * patches to the PCMCIA driver.  Later, I added code in the ISA driver
- * for Wireless Extensions and full support of frequency selection
- * cards.  Then, I did the same to the PCMCIA driver, and did some
- * reorganisation.  Finally, I came back to the ISA driver to
- * upgrade it at the same level as the PCMCIA one and reorganise
- * the code.
- * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
- * much needed information on the WaveLAN hardware.
- */
-
-/* The original copyrights and literature mention others' names and
- * credits.  I don't know what their part in this development was.
- */
-
-/* By the way, for the copyright and legal stuff:
- * almost everybody wrote code under the GNU or BSD license (or similar),
- * and want their original copyright to remain somewhere in the
- * code (for myself, I go with the GPL).
- * Nobody wants to take responsibility for anything, except the fame.
- */
-
-/* --------------------------- CREDITS --------------------------- */
-/*
- * This software was developed as a component of the
- * Linux operating system.
- * It is based on other device drivers and information
- * either written or supplied by:
- *	Ajay Bakre <bakre@paul.rutgers.edu>,
- *	Donald Becker <becker@cesdis.gsfc.nasa.gov>,
- *	Loeke Brederveld <Loeke.Brederveld@Utrecht.NCR.com>,
- *	Brent Elphick <belphick@uwaterloo.ca>,
- *	Anders Klemets <klemets@it.kth.se>,
- *	Vladimir V. Kolpakov <w@stier.koenig.ru>,
- *	Marc Meertens <Marc.Meertens@Utrecht.NCR.com>,
- *	Pauline Middelink <middelin@polyware.iaf.nl>,
- *	Robert Morris <rtm@das.harvard.edu>,
- *	Jean Tourrilhes <jt@hpl.hp.com>,
- *	Girish Welling <welling@paul.rutgers.edu>,
- *	Clark Woodworth <clark@hiway1.exit109.com>
- *	Yongguang Zhang <ygz@isl.hrl.hac.com>
- *
- * Thanks go also to:
- *	James Ashton <jaa101@syseng.anu.edu.au>,
- *	Alan Cox <alan@redhat.com>,
- *	Allan Creighton <allanc@cs.usyd.edu.au>,
- *	Matthew Geier <matthew@cs.usyd.edu.au>,
- *	Remo di Giovanni <remo@cs.usyd.edu.au>,
- *	Eckhard Grah <grah@wrcs1.urz.uni-wuppertal.de>,
- *	Vipul Gupta <vgupta@cs.binghamton.edu>,
- *	Mark Hagan <mhagan@wtcpost.daytonoh.NCR.COM>,
- *	Tim Nicholson <tim@cs.usyd.edu.au>,
- *	Ian Parkin <ian@cs.usyd.edu.au>,
- *	John Rosenberg <johnr@cs.usyd.edu.au>,
- *	George Rossi <george@phm.gov.au>,
- *	Arthur Scott <arthur@cs.usyd.edu.au>,
- *	Stanislav Sinyagin <stas@isf.ru>
- *	and Peter Storey for their assistance and advice.
- *
- * Additional Credits:
- *
- *	My development has been done initially under Debian 1.1 (Linux 2.0.x)
- *	and now	under Debian 2.2, initially with an HP Vectra XP/60, and now
- *	an HP Vectra XP/90.
- *
- */
-
-/* ------------------------- IMPROVEMENTS ------------------------- */
-/*
- * I proudly present:
- *
- * Changes made in first pre-release:
- * ----------------------------------
- *	- reorganisation of the code, function name change
- *	- creation of private header (wavelan.p.h)
- *	- reorganised debug messages
- *	- more comments, history, etc.
- *	- mmc_init:  configure the PSA if not done
- *	- mmc_init:  correct default value of level threshold for PCMCIA
- *	- mmc_init:  2.00 detection better code for 2.00 initialization
- *	- better info at startup
- *	- IRQ setting (note:  this setting is permanent)
- *	- watchdog:  change strategy (and solve module removal problems)
- *	- add wireless extensions (ioctl and get_wireless_stats)
- *	  get/set nwid/frequency on fly, info for /proc/net/wireless
- *	- more wireless extensions:  SETSPY and GETSPY
- *	- make wireless extensions optional
- *	- private ioctl to set/get quality and level threshold, histogram
- *	- remove /proc/net/wavelan
- *	- suppress useless stuff from lp (net_local)
- *	- kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
- *	- add message level (debug stuff in /var/adm/debug and errors not
- *	  displayed at console and still in /var/adm/messages)
- *	- multi device support
- *	- start fixing the probe (init code)
- *	- more inlines
- *	- man page
- *	- many other minor details and cleanups
- *
- * Changes made in second pre-release:
- * -----------------------------------
- *	- clean up init code (probe and module init)
- *	- better multiple device support (module)
- *	- name assignment (module)
- *
- * Changes made in third pre-release:
- * ----------------------------------
- *	- be more conservative on timers
- *	- preliminary support for multicast (I still lack some details)
- *
- * Changes made in fourth pre-release:
- * -----------------------------------
- *	- multicast (revisited and finished)
- *	- avoid reset in set_multicast_list (a really big hack)
- *	  if somebody could apply this code for other i82586 based drivers
- *	- share onboard memory 75% RU and 25% CU (instead of 50/50)
- *
- * Changes made for release in 2.1.15:
- * -----------------------------------
- *	- change the detection code for multi manufacturer code support
- *
- * Changes made for release in 2.1.17:
- * -----------------------------------
- *	- update to wireless extensions changes
- *	- silly bug in card initial configuration (psa_conf_status)
- *
- * Changes made for release in 2.1.27 & 2.0.30:
- * --------------------------------------------
- *	- small bug in debug code (probably not the last one...)
- *	- remove extern keyword for wavelan_probe()
- *	- level threshold is now a standard wireless extension (version 4 !)
- *	- modules parameters types (new module interface)
- *
- * Changes made for release in 2.1.36:
- * -----------------------------------
- *	- byte count stats (courtesy of David Hinds)
- *	- remove dev_tint stuff (courtesy of David Hinds)
- *	- encryption setting from Brent Elphick (thanks a lot!)
- *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
- *
- * Other changes (not by me) :
- * -------------------------
- *	- Spelling and gramar "rectification".
- *
- * Changes made for release in 2.0.37 & 2.2.2 :
- * ------------------------------------------
- *	- Correct status in /proc/net/wireless
- *	- Set PSA CRC to make PtP diagnostic tool happy (Bob Gray)
- *	- Module init code don't fail if we found at least one card in
- *	  the address list (Karlis Peisenieks)
- *	- Missing parenthesis (Christopher Peterson)
- *	- Correct i82586 configuration parameters
- *	- Encryption initialisation bug (Robert McCormack)
- *	- New mac addresses detected in the probe
- *	- Increase watchdog for busy environments
- *
- * Changes made for release in 2.0.38 & 2.2.7 :
- * ------------------------------------------
- *	- Correct the reception logic to better report errors and avoid
- *	  sending bogus packet up the stack
- *	- Delay RU config to avoid corrupting first received packet
- *	- Change config completion code (to actually check something)
- *	- Avoid reading out of bound in skbuf to transmit
- *	- Rectify a lot of (useless) debugging code
- *	- Change the way to `#ifdef SET_PSA_CRC'
- *
- * Changes made for release in 2.2.11 & 2.3.13 :
- * -------------------------------------------
- *	- Change e-mail and web page addresses
- *	- Watchdog timer is now correctly expressed in HZ, not in jiffies
- *	- Add channel number to the list of frequencies in range
- *	- Add the (short) list of bit-rates in range
- *	- Developp a new sensitivity... (sens.value & sens.fixed)
- *
- * Changes made for release in 2.2.14 & 2.3.23 :
- * -------------------------------------------
- *	- Fix check for root permission (break instead of exit)
- *	- New nwid & encoding setting (Wireless Extension 9)
- *
- * Changes made for release in 2.3.49 :
- * ----------------------------------
- *	- Indentation reformating (Alan)
- *	- Update to new network API (softnet - 2.3.43) :
- *		o replace dev->tbusy (Alan)
- *		o replace dev->tstart (Alan)
- *		o remove dev->interrupt (Alan)
- *		o add SMP locking via spinlock in splxx (me)
- *		o add spinlock in interrupt handler (me)
- *		o use kernel watchdog instead of ours (me)
- *		o increase watchdog timeout (kernel is more sensitive) (me)
- *		o verify that all the changes make sense and work (me)
- *	- Fixup a potential gotcha when reconfiguring and thighten a bit
- *		the interactions with Tx queue.
- *
- * Changes made for release in 2.4.0 :
- * ---------------------------------
- *	- Fix spinlock stupid bugs that I left in. The driver is now SMP
- *		compliant and doesn't lockup at startup.
- *
- * Wishes & dreams:
- * ----------------
- *	- roaming (see Pcmcia driver)
- */
-
-/***************************** INCLUDES *****************************/
-
-#include	<linux/module.h>
-
-#include	<linux/kernel.h>
-#include	<linux/sched.h>
-#include	<linux/types.h>
-#include	<linux/fcntl.h>
-#include	<linux/interrupt.h>
-#include	<linux/stat.h>
-#include	<linux/ptrace.h>
-#include	<linux/ioport.h>
-#include	<linux/in.h>
-#include	<linux/string.h>
-#include	<linux/delay.h>
-#include	<asm/system.h>
-#include	<asm/bitops.h>
-#include	<asm/io.h>
-#include	<asm/dma.h>
-#include	<asm/uaccess.h>
-#include	<linux/errno.h>
-#include	<linux/netdevice.h>
-#include	<linux/etherdevice.h>
-#include	<linux/skbuff.h>
-#include	<linux/slab.h>
-#include	<linux/timer.h>
-#include	<linux/init.h>
-
-#include <linux/wireless.h>		/* Wireless extensions */
-
-/* WaveLAN declarations */
-#include	"i82586.h"
-#include	"wavelan.h"
-
-/************************** DRIVER OPTIONS **************************/
-/*
- * `#define' or `#undef' the following constant to change the behaviour
- * of the driver...
- */
-#undef SET_PSA_CRC		/* Calculate and set the CRC on PSA (slower) */
-#define USE_PSA_CONFIG		/* Use info from the PSA. */
-#undef STRUCT_CHECK		/* Verify padding of structures. */
-#undef EEPROM_IS_PROTECTED	/* doesn't seem to be necessary */
-#define MULTICAST_AVOID		/* Avoid extra multicast (I'm sceptical). */
-#undef SET_MAC_ADDRESS		/* Experimental */
-
-#ifdef WIRELESS_EXT	/* If wireless extensions exist in the kernel */
-/* Warning:  this stuff will slow down the driver. */
-#define WIRELESS_SPY		/* Enable spying addresses. */
-#undef HISTOGRAM		/* Enable histogram of signal level. */
-#endif
-
-/****************************** DEBUG ******************************/
-
-#undef DEBUG_MODULE_TRACE	/* module insertion/removal */
-#undef DEBUG_CALLBACK_TRACE	/* calls made by Linux */
-#undef DEBUG_INTERRUPT_TRACE	/* calls to handler */
-#undef DEBUG_INTERRUPT_INFO	/* type of interrupt and so on */
-#define DEBUG_INTERRUPT_ERROR	/* problems */
-#undef DEBUG_CONFIG_TRACE	/* Trace the config functions. */
-#undef DEBUG_CONFIG_INFO	/* what's going on */
-#define DEBUG_CONFIG_ERROR	/* errors on configuration */
-#undef DEBUG_TX_TRACE		/* transmission calls */
-#undef DEBUG_TX_INFO		/* header of the transmitted packet */
-#undef DEBUG_TX_FAIL		/* Normal failure conditions */
-#define DEBUG_TX_ERROR		/* Unexpected conditions */
-#undef DEBUG_RX_TRACE		/* transmission calls */
-#undef DEBUG_RX_INFO		/* header of the received packet */
-#undef DEBUG_RX_FAIL		/* Normal failure conditions */
-#define DEBUG_RX_ERROR		/* Unexpected conditions */
-
-#undef DEBUG_PACKET_DUMP	/* Dump packet on the screen if defined to 32. */
-#undef DEBUG_IOCTL_TRACE	/* misc. call by Linux */
-#undef DEBUG_IOCTL_INFO		/* various debugging info */
-#define DEBUG_IOCTL_ERROR	/* what's going wrong */
-#define DEBUG_BASIC_SHOW	/* Show basic startup info. */
-#undef DEBUG_VERSION_SHOW	/* Print version info. */
-#undef DEBUG_PSA_SHOW		/* Dump PSA to screen. */
-#undef DEBUG_MMC_SHOW		/* Dump mmc to screen. */
-#undef DEBUG_SHOW_UNUSED	/* Show unused fields too. */
-#undef DEBUG_I82586_SHOW	/* Show i82586 status. */
-#undef DEBUG_DEVICE_SHOW	/* Show device parameters. */
-
-/************************ CONSTANTS & MACROS ************************/
-
-#ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n";
-#endif
-
-/* Watchdog temporisation */
-#define	WATCHDOG_JIFFIES	(512*HZ/100)
-
-/* Macro to get the number of elements in an array */
-#define	NELS(a)				(sizeof(a) / sizeof(a[0]))
-
-/* ------------------------ PRIVATE IOCTL ------------------------ */
-
-#define SIOCSIPQTHR	SIOCIWFIRSTPRIV		/* Set quality threshold */
-#define SIOCGIPQTHR	SIOCIWFIRSTPRIV + 1	/* Get quality threshold */
-#define SIOCSIPLTHR	SIOCIWFIRSTPRIV + 2	/* Set level threshold */
-#define SIOCGIPLTHR	SIOCIWFIRSTPRIV + 3	/* Get level threshold */
-
-#define SIOCSIPHISTO	SIOCIWFIRSTPRIV + 6	/* Set histogram ranges */
-#define SIOCGIPHISTO	SIOCIWFIRSTPRIV + 7	/* Get histogram values */
-
-/****************************** TYPES ******************************/
-
-/* Shortcuts */
-typedef struct net_device		device;
-typedef struct net_device_stats	en_stats;
-typedef struct iw_statistics	iw_stats;
-typedef struct iw_quality	iw_qual;
-typedef struct iw_freq		iw_freq;
-typedef struct net_local	net_local;
-typedef struct timer_list	timer_list;
-
-/* Basic types */
-typedef u_char		mac_addr[WAVELAN_ADDR_SIZE];	/* Hardware address */
-
-/*
- * Static specific data for the interface.
- *
- * For each network interface, Linux keeps data in two structures:  "device"
- * keeps the generic data (same format for everybody) and "net_local" keeps
- * additional specific data.
- * Note that some of this specific data is in fact generic (en_stats, for
- * example).
- */
-struct net_local
-{
-  net_local *	next;		/* linked list of the devices */
-  device *	dev;		/* reverse link */
-  spinlock_t	spinlock;	/* Serialize access to the hardware (SMP) */
-  en_stats	stats;		/* Ethernet interface statistics */
-  int		nresets;	/* number of hardware resets */
-  u_char	reconfig_82586;	/* We need to reconfigure the controller. */
-  u_char	promiscuous;	/* promiscuous mode */
-  int		mc_count;	/* number of multicast addresses */
-  u_short	hacr;		/* current host interface state */
-
-  int		tx_n_in_use;
-  u_short	rx_head;
-  u_short	rx_last;
-  u_short	tx_first_free;
-  u_short	tx_first_in_use;
-
-#ifdef WIRELESS_EXT
-  iw_stats	wstats;		/* Wireless-specific statistics */
-#endif
-
-#ifdef WIRELESS_SPY
-  int		spy_number;			/* number of addresses to spy */
-  mac_addr	spy_address[IW_MAX_SPY];	/* the addresses to spy */
-  iw_qual	spy_stat[IW_MAX_SPY];		/* statistics gathered */
-#endif	/* WIRELESS_SPY */
-
-#ifdef HISTOGRAM
-  int		his_number;		/* number of intervals */
-  u_char	his_range[16];		/* boundaries of interval ]n-1; n] */
-  u_long	his_sum[16];		/* sum in interval */
-#endif	/* HISTOGRAM */
-};
-
-/**************************** PROTOTYPES ****************************/
-
-/* ----------------------- MISC. SUBROUTINES ------------------------ */
-static inline void
-	wv_splhi(net_local *,		/* Disable interrupts, lock driver */
-		 unsigned long *);	/* flags */
-static inline void
-	wv_splx(net_local *,		/* Enable interrupts, unlock driver */
-		unsigned long *);	/* flags */
-static u_char
-	wv_irq_to_psa(int);
-static int
-	wv_psa_to_irq(u_char);
-/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
-static inline u_short		/* data */
-	hasr_read(u_long);	/* Read the host interface:  base address */
-static inline void
-	hacr_write(u_long,	/* Write to host interface:  base address */
-		   u_short),	/* data */
-	hacr_write_slow(u_long,
-		   u_short),
-	set_chan_attn(u_long,	/* ioaddr */
-		      u_short),	/* hacr   */
-	wv_hacr_reset(u_long),	/* ioaddr */
-	wv_16_off(u_long,	/* ioaddr */
-		  u_short),	/* hacr   */
-	wv_16_on(u_long,	/* ioaddr */
-		 u_short),	/* hacr   */
-	wv_ints_off(device *),
-	wv_ints_on(device *);
-/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
-static void
-	psa_read(u_long,	/* Read the Parameter Storage Area. */
-		 u_short,	/* hacr */
-		 int,		/* offset in PSA */
-		 u_char *,	/* buffer to fill */
-		 int),		/* size to read */
-	psa_write(u_long, 	/* Write to the PSA. */
-		  u_short,	/* hacr */
-		  int,		/* offset in PSA */
-		  u_char *,	/* buffer in memory */
-		  int);		/* length of buffer */
-static inline void
-	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control. */
-		u_short,
-		u_char),
-	mmc_write(u_long,	/* Write n bytes to the MMC. */
-		  u_char,
-		  u_char *,
-		  int);
-static inline u_char		/* Read 1 byte from the MMC. */
-	mmc_in(u_long,
-	       u_short);
-static inline void
-	mmc_read(u_long,	/* Read n bytes from the MMC. */
-		 u_char,
-		 u_char *,
-		 int),
-	fee_wait(u_long,	/* Wait for frequency EEPROM:  base address */
-		 int,		/* base delay to wait for */
-		 int);		/* time to wait */
-static void
-	fee_read(u_long,	/* Read the frequency EEPROM:  base address */
-		 u_short,	/* destination offset */
-		 u_short *,	/* data buffer */
-		 int);		/* number of registers */
-/* ---------------------- I82586 SUBROUTINES ----------------------- */
-static /*inline*/ void
-	obram_read(u_long,	/* ioaddr */
-		   u_short,	/* o */
-		   u_char *,	/* b */
-		   int);	/* n */
-static inline void
-	obram_write(u_long,	/* ioaddr */
-		    u_short,	/* o */
-		    u_char *,	/* b */
-		    int);	/* n */
-static void
-	wv_ack(device *);
-static inline int
-	wv_synchronous_cmd(device *,
-			   const char *),
-	wv_config_complete(device *,
-			   u_long,
-			   net_local *);
-static int
-	wv_complete(device *,
-		    u_long,
-		    net_local *);
-static inline void
-	wv_82586_reconfig(device *);
-/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
-#ifdef DEBUG_I82586_SHOW
-static void
-	wv_scb_show(unsigned short);
-#endif
-static inline void
-	wv_init_info(device *);	/* display startup info */
-/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
-static en_stats	*
-	wavelan_get_stats(device *);	/* Give stats /proc/net/dev */
-static void
-	wavelan_set_multicast_list(device *);
-/* ----------------------- PACKET RECEPTION ----------------------- */
-static inline void
-	wv_packet_read(device *,	/* Read a packet from a frame. */
-		       u_short,
-		       int),
-	wv_receive(device *);	/* Read all packets waiting. */
-/* --------------------- PACKET TRANSMISSION --------------------- */
-static inline int
-	wv_packet_write(device *,	/* Write a packet to the Tx buffer. */
-			void *,
-			short);
-static int
-	wavelan_packet_xmit(struct sk_buff *,	/* Send a packet. */
-			    device *);
-/* -------------------- HARDWARE CONFIGURATION -------------------- */
-static inline int
-	wv_mmc_init(device *),		/* Initialize the modem. */
-	wv_ru_start(device *),		/* Start the i82586 receiver unit. */
-	wv_cu_start(device *),		/* Start the i82586 command unit. */
-	wv_82586_start(device *);	/* Start the i82586. */
-static void
-	wv_82586_config(device *);	/* Configure the i82586. */
-static inline void
-	wv_82586_stop(device *);
-static int
-	wv_hw_reset(device *),		/* Reset the WaveLAN hardware. */
-	wv_check_ioaddr(u_long,		/* ioaddr */
-			u_char *);	/* mac address (read) */
-/* ---------------------- INTERRUPT HANDLING ---------------------- */
-static void
-	wavelan_interrupt(int,		/* interrupt handler */
-			  void *,
-			  struct pt_regs *);
-static void
-	wavelan_watchdog(device *);	/* transmission watchdog */
-/* ------------------- CONFIGURATION CALLBACKS ------------------- */
-static int
-	wavelan_open(device *),		/* Open the device. */
-	wavelan_close(device *),	/* Close the device. */
-	wavelan_config(device *);	/* Configure one device. */
-extern int
-	wavelan_probe(device *);	/* See Space.c. */
-
-/**************************** VARIABLES ****************************/
-
-/*
- * This is the root of the linked list of WaveLAN drivers
- * It is use to verify that we don't reuse the same base address
- * for two different drivers and to clean up when removing the module.
- */
-static net_local *	wavelan_list	= (net_local *) NULL;
-
-/*
- * This table is used to translate the PSA value to IRQ number
- * and vice versa.
- */
-static u_char	irqvals[]	=
-{
-	   0,    0,    0, 0x01,
-	0x02, 0x04,    0, 0x08,
-	   0,    0, 0x10, 0x20,
-	0x40,    0,    0, 0x80,
-};
-
-/*
- * Table of the available I/O addresses (base addresses) for WaveLAN
- */
-static unsigned short	iobase[]	=
-{
-#if	0
-  /* Leave out 0x3C0 for now -- seems to clash with some video
-   * controllers.
-   * Leave out the others too -- we will always use 0x390 and leave
-   * 0x300 for the Ethernet device.
-   * Jean II:  0x3E0 is fine as well.
-   */
-  0x300, 0x390, 0x3E0, 0x3C0
-#endif	/* 0 */
-  0x390, 0x3E0
-};
-
-#ifdef	MODULE
-/* Parameters set by insmod */
-static int	io[4];
-static int	irq[4];
-static char	name[4][IFNAMSIZ];
-MODULE_PARM(io, "1-4i");
-MODULE_PARM(irq, "1-4i");
-MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ));
-MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required");
-MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)");
-MODULE_PARM_DESC(name, "WaveLAN interface neme(s)");
-#endif	/* MODULE */
-
-#endif	/* WAVELAN_P_H */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/Config.in linux/drivers/net/wireless/Config.in
--- linux-2.5.3-pre2/drivers/net/wireless/Config.in	Tue Oct  9 15:13:03 2001
+++ linux/drivers/net/wireless/Config.in	Mon Jan 21 17:23:23 2002
@@ -2,6 +2,12 @@
 # Wireless LAN device configuration
 #
 
+comment 'Wireless ISA/PCI cards support' 
+
+# Good old obsolete Wavelan.
+tristate '  AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support' CONFIG_WAVELAN
+
+# 802.11b cards
 if [ "$CONFIG_ISA" = "y" -o "$CONFIG_PCI" = "y" ]; then
    tristate '  Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards' CONFIG_AIRO
 fi
@@ -18,8 +24,13 @@
 
 # If Pcmcia is compiled in, offer Pcmcia cards...
 if [ "$CONFIG_PCMCIA" != "n" ]; then
-   comment 'Wireless Pcmcia cards support' 
+   comment 'Wireless Pcmcia/Cardbus cards support' 
+
+# Obsolete cards
+   dep_tristate '  Xircom Netwave AirSurfer Pcmcia wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
+   dep_tristate '  AT&T/Lucent old Wavelan Pcmcia wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
 
+# 802.11b cards
    dep_tristate '  Hermes PCMCIA card support' CONFIG_PCMCIA_HERMES $CONFIG_HERMES
    tristate '  Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards' CONFIG_AIRO_CS
 fi
diff -urN linux-2.5.3-pre2/drivers/net/wireless/Makefile linux/drivers/net/wireless/Makefile
--- linux-2.5.3-pre2/drivers/net/wireless/Makefile	Tue Oct  9 15:13:03 2001
+++ linux/drivers/net/wireless/Makefile	Mon Jan 21 17:23:23 2002
@@ -14,6 +14,11 @@
 # Things that need to export symbols
 export-objs	:= airo.o orinoco.o hermes.o
 
+# Obsolete cards
+obj-$(CONFIG_WAVELAN)		+= wavelan.o
+obj-$(CONFIG_PCMCIA_NETWAVE)	+= netwave_cs.o
+obj-$(CONFIG_PCMCIA_WAVELAN)	+= wavelan_cs.o
+
 obj-$(CONFIG_HERMES)		+= orinoco.o hermes.o
 obj-$(CONFIG_PCMCIA_HERMES)	+= orinoco_cs.o
 obj-$(CONFIG_APPLE_AIRPORT)	+= airport.o
diff -urN linux-2.5.3-pre2/drivers/net/wireless/i82586.h linux/drivers/net/wireless/i82586.h
--- linux-2.5.3-pre2/drivers/net/wireless/i82586.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/i82586.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,413 @@
+/*
+ * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
+ *
+ * See:
+ *	Intel Microcommunications 1991
+ *	p1-1 to p1-37
+ *	Intel order No. 231658
+ *	ISBN 1-55512-119-5
+ *
+ *     Unfortunately, the above chapter mentions neither
+ * the System Configuration Pointer (SCP) nor the
+ * Intermediate System Configuration Pointer (ISCP),
+ * so we probably need to look elsewhere for the
+ * whole story -- some recommend the "Intel LAN
+ * Components manual" but I have neither a copy
+ * nor a full reference.  But "elsewhere" may be
+ * in the same publication...
+ *     The description of a later device, the
+ * "82596CA High-Performance 32-Bit Local Area Network
+ * Coprocessor", (ibid. p1-38 to p1-109) does mention
+ * the SCP and ISCP and also has an i82586 compatibility
+ * mode.  Even more useful is "AP-235 An 82586 Data Link
+ * Driver" (ibid. p1-337 to p1-417).
+ */
+
+#define	I82586_MEMZ	(64 * 1024)
+
+#define	I82586_SCP_ADDR	(I82586_MEMZ - sizeof(scp_t))
+
+#define	ADDR_LEN	6
+#define	I82586NULL	0xFFFF
+
+#define	toff(t,p,f) 	(unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * System Configuration Pointer (SCP).
+ */
+typedef struct scp_t	scp_t;
+struct scp_t
+{
+	unsigned short	scp_sysbus;	/* 82586 bus width:	*/
+#define		SCP_SY_16BBUS	(0x0 << 0)	/* 16 bits */
+#define		SCP_SY_8BBUS	(0x1 << 0)	/*  8 bits. */
+	unsigned short	scp_junk[2];	/* Unused */
+	unsigned short	scp_iscpl;	/* lower 16 bits of ISCP_ADDR */
+	unsigned short	scp_iscph;	/* upper 16 bits of ISCP_ADDR */
+};
+
+/*
+ * Intermediate System Configuration Pointer (ISCP).
+ */
+typedef struct iscp_t	iscp_t;
+struct iscp_t
+{
+	unsigned short	iscp_busy;	/* set by CPU before first CA,	*/
+					/* cleared by 82586 after read.	*/
+	unsigned short	iscp_offset;	/* offset of SCB		*/
+	unsigned short	iscp_basel;	/* base of SCB			*/
+	unsigned short	iscp_baseh;	/*  "				*/
+};
+
+/*
+ * System Control Block (SCB).
+ *	The 82586 writes its status to scb_status and then
+ *	raises an interrupt to alert the CPU.
+ *	The CPU writes a command to scb_command and
+ *	then issues a Channel Attention (CA) to alert the 82586.
+ */
+typedef struct scb_t	scb_t;
+struct scb_t
+{
+	unsigned short	scb_status;	/* Status of 82586		*/
+#define		SCB_ST_INT	(0xF << 12)	/* Some of:		*/
+#define		SCB_ST_CX	(0x1 << 15)	/* Cmd completed	*/
+#define		SCB_ST_FR	(0x1 << 14)	/* Frame received	*/
+#define		SCB_ST_CNA	(0x1 << 13)	/* Cmd unit not active	*/
+#define		SCB_ST_RNR	(0x1 << 12)	/* Rcv unit not ready	*/
+#define		SCB_ST_JUNK0	(0x1 << 11)	/* 0			*/
+#define		SCB_ST_CUS	(0x7 <<  8)	/* Cmd unit status	*/
+#define			SCB_ST_CUS_IDLE	(0 << 8)	/* Idle		*/
+#define			SCB_ST_CUS_SUSP	(1 << 8)	/* Suspended	*/
+#define			SCB_ST_CUS_ACTV	(2 << 8)	/* Active	*/
+#define		SCB_ST_JUNK1	(0x1 <<  7)	/* 0			*/
+#define		SCB_ST_RUS	(0x7 <<  4)	/* Rcv unit status	*/
+#define			SCB_ST_RUS_IDLE	(0 << 4)	/* Idle		*/
+#define			SCB_ST_RUS_SUSP	(1 << 4)	/* Suspended	*/
+#define			SCB_ST_RUS_NRES	(2 << 4)	/* No resources	*/
+#define			SCB_ST_RUS_RDY	(4 << 4)	/* Ready	*/
+	unsigned short	scb_command;	/* Next command			*/
+#define		SCB_CMD_ACK_CX	(0x1 << 15)	/* Ack cmd completion	*/
+#define		SCB_CMD_ACK_FR	(0x1 << 14)	/* Ack frame received	*/
+#define		SCB_CMD_ACK_CNA	(0x1 << 13)	/* Ack CU not active	*/
+#define		SCB_CMD_ACK_RNR	(0x1 << 12)	/* Ack RU not ready	*/
+#define		SCB_CMD_JUNKX	(0x1 << 11)	/* Unused		*/
+#define		SCB_CMD_CUC	(0x7 <<  8)	/* Command Unit command	*/
+#define			SCB_CMD_CUC_NOP	(0 << 8)	/* Nop		*/
+#define			SCB_CMD_CUC_GO	(1 << 8)	/* Start cbl_offset */
+#define			SCB_CMD_CUC_RES	(2 << 8)	/* Resume execution */
+#define			SCB_CMD_CUC_SUS	(3 << 8)	/* Suspend   "	*/
+#define			SCB_CMD_CUC_ABT	(4 << 8)	/* Abort     "	*/
+#define		SCB_CMD_RESET	(0x1 <<  7)	/* Reset chip (hardware) */
+#define		SCB_CMD_RUC	(0x7 <<  4)	/* Receive Unit command	*/
+#define			SCB_CMD_RUC_NOP	(0 << 4)	/* Nop		*/
+#define			SCB_CMD_RUC_GO	(1 << 4)	/* Start rfa_offset */
+#define			SCB_CMD_RUC_RES	(2 << 4)	/* Resume reception */
+#define			SCB_CMD_RUC_SUS	(3 << 4)	/* Suspend   "	*/
+#define			SCB_CMD_RUC_ABT	(4 << 4)	/* Abort     "	*/
+	unsigned short	scb_cbl_offset;	/* Offset of first command unit	*/
+					/* Action Command		*/
+	unsigned short	scb_rfa_offset;	/* Offset of first Receive	*/
+					/* Frame Descriptor in the	*/
+					/* Receive Frame Area		*/
+	unsigned short	scb_crcerrs;	/* Properly aligned frames	*/
+					/* received with a CRC error	*/
+	unsigned short	scb_alnerrs;	/* Misaligned frames received	*/
+					/* with a CRC error		*/
+	unsigned short	scb_rscerrs;	/* Frames lost due to no space	*/
+	unsigned short	scb_ovrnerrs;	/* Frames lost due to slow bus	*/
+};
+
+#define	scboff(p,f) 	toff(scb_t, p, f)
+
+/*
+ * The eight Action Commands.
+ */
+typedef enum acmd_e	acmd_e;
+enum acmd_e
+{
+	acmd_nop	= 0,	/* Do nothing				*/
+	acmd_ia_setup	= 1,	/* Load an (ethernet) address into the	*/
+				/* 82586				*/
+	acmd_configure	= 2,	/* Update the 82586 operating parameters */
+	acmd_mc_setup	= 3,	/* Load a list of (ethernet) multicast	*/
+				/* addresses into the 82586		*/
+	acmd_transmit	= 4,	/* Transmit a frame			*/
+	acmd_tdr	= 5,	/* Perform a Time Domain Reflectometer	*/
+				/* test on the serial link		*/
+	acmd_dump	= 6,	/* Copy 82586 registers to memory	*/
+	acmd_diagnose	= 7,	/* Run an internal self test		*/
+};
+
+/*
+ * Generic Action Command header.
+ */
+typedef struct ach_t	ach_t;
+struct ach_t
+{
+	unsigned short	ac_status;		/* Command status:	*/
+#define		AC_SFLD_C	(0x1 << 15)	/* Command completed	*/
+#define		AC_SFLD_B	(0x1 << 14)	/* Busy executing	*/
+#define		AC_SFLD_OK	(0x1 << 13)	/* Completed error free	*/
+#define		AC_SFLD_A	(0x1 << 12)	/* Command aborted	*/
+#define		AC_SFLD_FAIL	(0x1 << 11)	/* Selftest failed	*/
+#define		AC_SFLD_S10	(0x1 << 10)	/* No carrier sense	*/
+						/* during transmission	*/
+#define		AC_SFLD_S9	(0x1 <<  9)	/* Tx unsuccessful:	*/
+						/* (stopped) lost CTS	*/
+#define		AC_SFLD_S8	(0x1 <<  8)	/* Tx unsuccessful:	*/
+						/* (stopped) slow DMA	*/
+#define		AC_SFLD_S7	(0x1 <<  7)	/* Tx deferred:		*/
+						/* other link traffic	*/
+#define		AC_SFLD_S6	(0x1 <<  6)	/* Heart Beat: collision */
+						/* detect after last tx	*/
+#define		AC_SFLD_S5	(0x1 <<  5)	/* Tx stopped:		*/
+						/* excessive collisions	*/
+#define		AC_SFLD_MAXCOL	(0xF <<  0)	/* Collision count  	*/
+	unsigned short	ac_command;		/* Command specifier:	*/
+#define		AC_CFLD_EL	(0x1 << 15)	/* End of command list	*/
+#define		AC_CFLD_S	(0x1 << 14)	/* Suspend on completion */
+#define		AC_CFLD_I	(0x1 << 13)	/* Interrupt on completion */
+#define		AC_CFLD_CMD	(0x7 <<  0)	/* acmd_e		*/
+	unsigned short	ac_link;		/* Next Action Command	*/
+};
+
+#define	acoff(p,f) 	toff(ach_t, p, f)
+
+/*
+ * The Nop Action Command.
+ */
+typedef struct ac_nop_t	ac_nop_t;
+struct ac_nop_t
+{
+	ach_t	nop_h;
+};
+
+/*
+ * The IA-Setup Action Command.
+ */
+typedef struct ac_ias_t	ac_ias_t;
+struct ac_ias_t
+{
+	ach_t		ias_h;
+	unsigned char	ias_addr[ADDR_LEN]; /* The (ethernet) address	*/
+};
+
+/*
+ * The Configure Action Command.
+ */
+typedef struct ac_cfg_t	ac_cfg_t;
+struct ac_cfg_t
+{
+	ach_t		cfg_h;
+	unsigned char	cfg_byte_cnt;	/* Size foll data: 4-12	*/
+#define	AC_CFG_BYTE_CNT(v)	(((v) & 0xF) << 0)
+	unsigned char	cfg_fifolim;	/* FIFO threshold	*/
+#define	AC_CFG_FIFOLIM(v)	(((v) & 0xF) << 0)
+	unsigned char	cfg_byte8;
+#define	AC_CFG_SAV_BF(v) 	(((v) & 0x1) << 7)	/* Save rxd bad frames	*/
+#define	AC_CFG_SRDY(v) 		(((v) & 0x1) << 6)	/* SRDY/ARDY pin means	*/
+							/* external sync.	*/
+	unsigned char	cfg_byte9;
+#define	AC_CFG_ELPBCK(v)	(((v) & 0x1) << 7)	/* External loopback	*/
+#define	AC_CFG_ILPBCK(v)	(((v) & 0x1) << 6)	/* Internal loopback	*/
+#define	AC_CFG_PRELEN(v)	(((v) & 0x3) << 4)	/* Preamble length	*/
+#define		AC_CFG_PLEN_2		0		/*  2 bytes	*/
+#define		AC_CFG_PLEN_4		1		/*  4 bytes	*/
+#define		AC_CFG_PLEN_8		2		/*  8 bytes	*/
+#define		AC_CFG_PLEN_16		3		/* 16 bytes	*/
+#define	AC_CFG_ALOC(v)		(((v) & 0x1) << 3)	/* Addr/len data is	*/
+							/* explicit in buffers	*/
+#define	AC_CFG_ADDRLEN(v)	(((v) & 0x7) << 0)	/* Bytes per address	*/
+	unsigned char	cfg_byte10;
+#define	AC_CFG_BOFMET(v)	(((v) & 0x1) << 7)	/* Use alternate expo.	*/
+							/* backoff method	*/
+#define	AC_CFG_ACR(v)		(((v) & 0x7) << 4)	/* Accelerated cont. res. */
+#define	AC_CFG_LINPRIO(v)	(((v) & 0x7) << 0)	/* Linear priority	*/
+	unsigned char	cfg_ifs;	/* Interframe spacing		*/
+	unsigned char	cfg_slotl;	/* Slot time (low byte)		*/
+	unsigned char	cfg_byte13;
+#define	AC_CFG_RETRYNUM(v)	(((v) & 0xF) << 4)	/* Max. collision retry	*/
+#define	AC_CFG_SLTTMHI(v)	(((v) & 0x7) << 0)	/* Slot time (high bits) */
+	unsigned char	cfg_byte14;
+#define	AC_CFG_FLGPAD(v)	(((v) & 0x1) << 7)	/* Pad with HDLC flags	*/
+#define	AC_CFG_BTSTF(v)		(((v) & 0x1) << 6)	/* Do HDLC bitstuffing	*/
+#define	AC_CFG_CRC16(v)		(((v) & 0x1) << 5)	/* 16 bit CCITT CRC	*/
+#define	AC_CFG_NCRC(v)		(((v) & 0x1) << 4)	/* Insert no CRC	*/
+#define	AC_CFG_TNCRS(v)		(((v) & 0x1) << 3)	/* Tx even if no carrier */
+#define	AC_CFG_MANCH(v)		(((v) & 0x1) << 2)	/* Manchester coding	*/
+#define	AC_CFG_BCDIS(v)		(((v) & 0x1) << 1)	/* Disable broadcast	*/
+#define	AC_CFG_PRM(v)		(((v) & 0x1) << 0)	/* Promiscuous mode	*/
+	unsigned char	cfg_byte15;
+#define	AC_CFG_ICDS(v)		(((v) & 0x1) << 7)	/* Internal collision	*/
+							/* detect source	*/
+#define	AC_CFG_CDTF(v)		(((v) & 0x7) << 4)	/* Collision detect	*/
+							/* filter in bit times	*/
+#define	AC_CFG_ICSS(v)		(((v) & 0x1) << 3)	/* Internal carrier	*/
+							/* sense source		*/
+#define	AC_CFG_CSTF(v)		(((v) & 0x7) << 0)	/* Carrier sense	*/
+							/* filter in bit times	*/
+	unsigned short	cfg_min_frm_len;
+#define	AC_CFG_MNFRM(v)		(((v) & 0xFF) << 0)	/* Min. bytes/frame (<= 255) */
+};
+
+/*
+ * The MC-Setup Action Command.
+ */
+typedef struct ac_mcs_t	ac_mcs_t;
+struct ac_mcs_t
+{
+	ach_t		mcs_h;
+	unsigned short	mcs_cnt;	/* No. of bytes of MC addresses	*/
+#if 0
+	unsigned char	mcs_data[ADDR_LEN]; /* The first MC address ..	*/
+	...
+#endif
+};
+
+#define I82586_MAX_MULTICAST_ADDRESSES	128	/* Hardware hashed filter */
+
+/*
+ * The Transmit Action Command.
+ */
+typedef struct ac_tx_t	ac_tx_t;
+struct ac_tx_t
+{
+	ach_t		tx_h;
+	unsigned short	tx_tbd_offset;	/* Address of list of buffers.	*/
+#if	0
+Linux packets are passed down with the destination MAC address
+and length/type field already prepended to the data,
+so we do not need to insert it.  Consistent with this
+we must also set the AC_CFG_ALOC(..) flag during the
+ac_cfg_t action command.
+	unsigned char	tx_addr[ADDR_LEN]; /* The frame dest. address	*/
+	unsigned short	tx_length;	/* The frame length		*/
+#endif	/* 0 */
+};
+
+/*
+ * The Time Domain Reflectometer Action Command.
+ */
+typedef struct ac_tdr_t	ac_tdr_t;
+struct ac_tdr_t
+{
+	ach_t		tdr_h;
+	unsigned short	tdr_result;	/* Result.	*/
+#define		AC_TDR_LNK_OK	(0x1 << 15)	/* No link problem	*/
+#define		AC_TDR_XCVR_PRB	(0x1 << 14)	/* Txcvr cable problem	*/
+#define		AC_TDR_ET_OPN	(0x1 << 13)	/* Open on the link	*/
+#define		AC_TDR_ET_SRT	(0x1 << 12)	/* Short on the link	*/
+#define		AC_TDR_TIME	(0x7FF << 0)	/* Distance to problem	*/
+						/* site	in transmit	*/
+						/* clock cycles		*/
+};
+
+/*
+ * The Dump Action Command.
+ */
+typedef struct ac_dmp_t	ac_dmp_t;
+struct ac_dmp_t
+{
+	ach_t		dmp_h;
+	unsigned short	dmp_offset;	/* Result.	*/
+};
+
+/*
+ * Size of the result of the dump command.
+ */
+#define	DUMPBYTES	170
+
+/*
+ * The Diagnose Action Command.
+ */
+typedef struct ac_dgn_t	ac_dgn_t;
+struct ac_dgn_t
+{
+	ach_t		dgn_h;
+};
+
+/*
+ * Transmit Buffer Descriptor (TBD).
+ */
+typedef struct tbd_t	tbd_t;
+struct tbd_t
+{
+	unsigned short	tbd_status;		/* Written by the CPU	*/
+#define		TBD_STATUS_EOF	(0x1 << 15)	/* This TBD is the	*/
+						/* last for this frame	*/
+#define		TBD_STATUS_ACNT	(0x3FFF << 0)	/* Actual count of data	*/
+						/* bytes in this buffer	*/
+	unsigned short	tbd_next_bd_offset;	/* Next in list		*/
+	unsigned short	tbd_bufl;		/* Buffer address (low)	*/
+	unsigned short	tbd_bufh;		/*    "	     "	(high)	*/
+};
+
+/*
+ * Receive Buffer Descriptor (RBD).
+ */
+typedef struct rbd_t	rbd_t;
+struct rbd_t
+{
+	unsigned short	rbd_status;		/* Written by the 82586	*/
+#define		RBD_STATUS_EOF	(0x1 << 15)	/* This RBD is the	*/
+						/* last for this frame	*/
+#define		RBD_STATUS_F	(0x1 << 14)	/* ACNT field is valid	*/
+#define		RBD_STATUS_ACNT	(0x3FFF << 0)	/* Actual no. of data	*/
+						/* bytes in this buffer	*/
+	unsigned short	rbd_next_rbd_offset;	/* Next rbd in list	*/
+	unsigned short	rbd_bufl;		/* Data pointer (low)	*/
+	unsigned short	rbd_bufh;		/*  "	   "    (high)	*/
+	unsigned short	rbd_el_size;		/* EL+Data buf. size	*/
+#define		RBD_EL	(0x1 << 15)		/* This BD is the	*/
+						/* last in the list	*/
+#define		RBD_SIZE (0x3FFF << 0)		/* No. of bytes the	*/
+						/* buffer can hold	*/
+};
+
+#define	rbdoff(p,f) 	toff(rbd_t, p, f)
+
+/*
+ * Frame Descriptor (FD).
+ */
+typedef struct fd_t	fd_t;
+struct fd_t
+{
+	unsigned short	fd_status;		/* Written by the 82586	*/
+#define		FD_STATUS_C	(0x1 << 15)	/* Completed storing frame */
+#define		FD_STATUS_B	(0x1 << 14)	/* FD was consumed by RU */
+#define		FD_STATUS_OK	(0x1 << 13)	/* Frame rxd successfully */
+#define		FD_STATUS_S11	(0x1 << 11)	/* CRC error		*/
+#define		FD_STATUS_S10	(0x1 << 10)	/* Alignment error	*/
+#define		FD_STATUS_S9	(0x1 <<  9)	/* Ran out of resources	*/
+#define		FD_STATUS_S8	(0x1 <<  8)	/* Rx DMA overrun	*/
+#define		FD_STATUS_S7	(0x1 <<  7)	/* Frame too short	*/
+#define		FD_STATUS_S6	(0x1 <<  6)	/* No EOF flag		*/
+	unsigned short	fd_command;		/* Command		*/
+#define		FD_COMMAND_EL	(0x1 << 15)	/* Last FD in list	*/
+#define		FD_COMMAND_S	(0x1 << 14)	/* Suspend RU after rx	*/
+	unsigned short	fd_link_offset;		/* Next FD		*/
+	unsigned short	fd_rbd_offset;		/* First RBD (data)	*/
+						/* Prepared by CPU,	*/
+						/* updated by 82586	*/
+#if	0
+I think the rest is unused since we
+have set AC_CFG_ALOC(..).  However, just
+in case, we leave the space.
+#endif	/* 0 */
+	unsigned char	fd_dest[ADDR_LEN];	/* Destination address	*/
+						/* Written by 82586	*/
+	unsigned char	fd_src[ADDR_LEN];	/* Source address	*/
+						/* Written by 82586	*/
+	unsigned short	fd_length;		/* Frame length or type	*/
+						/* Written by 82586	*/
+};
+
+#define	fdoff(p,f) 	toff(fd_t, p, f)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU General Public License.
+ *
+ * For more details, see wavelan.c.
+ */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/i82593.h linux/drivers/net/wireless/i82593.h
--- linux-2.5.3-pre2/drivers/net/wireless/i82593.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/i82593.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,224 @@
+/*
+ * Definitions for Intel 82593 CSMA/CD Core LAN Controller
+ * The definitions are taken from the 1992 users manual with Intel
+ * order number 297125-001.
+ *
+ * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
+ *
+ * Copyright 1994, Anders Klemets <klemets@it.kth.se>
+ *
+ * This software may be freely distributed for noncommercial purposes
+ * as long as this notice is retained.
+ * 
+ * HISTORY
+ * i82593.h,v
+ * Revision 1.1  1996/07/17 15:23:12  root
+ * Initial revision
+ *
+ * Revision 1.3  1995/04/05  15:13:58  adj
+ * Initial alpha release
+ *
+ * Revision 1.2  1994/06/16  23:57:31  klemets
+ * Mirrored all the fields in the configuration block.
+ *
+ * Revision 1.1  1994/06/02  20:25:34  klemets
+ * Initial revision
+ *
+ *
+ */
+#ifndef	_I82593_H
+#define	_I82593_H
+
+/* Intel 82593 CSMA/CD Core LAN Controller */
+
+/* Port 0 Command Register definitions */
+
+/* Execution operations */
+#define OP0_NOP			0	/* CHNL = 0 */
+#define OP0_SWIT_TO_PORT_1	0	/* CHNL = 1 */
+#define OP0_IA_SETUP		1
+#define OP0_CONFIGURE		2
+#define OP0_MC_SETUP		3
+#define OP0_TRANSMIT		4
+#define OP0_TDR			5
+#define OP0_DUMP		6
+#define OP0_DIAGNOSE		7
+#define OP0_TRANSMIT_NO_CRC	9
+#define OP0_RETRANSMIT		12
+#define OP0_ABORT		13
+/* Reception operations */
+#define OP0_RCV_ENABLE		8
+#define OP0_RCV_DISABLE		10
+#define OP0_STOP_RCV		11
+/* Status pointer control operations */
+#define OP0_FIX_PTR		15	/* CHNL = 1 */
+#define OP0_RLS_PTR		15	/* CHNL = 0 */
+#define OP0_RESET		14
+
+#define CR0_CHNL		(1 << 4)	/* 0=Channel 0, 1=Channel 1 */
+#define CR0_STATUS_0		0x00
+#define CR0_STATUS_1		0x20
+#define CR0_STATUS_2		0x40
+#define CR0_STATUS_3		0x60
+#define CR0_INT_ACK		(1 << 7)	/* 0=No ack, 1=acknowledge */
+
+/* Port 0 Status Register definitions */
+
+#define SR0_NO_RESULT		0		/* dummy */
+#define SR0_EVENT_MASK		0x0f
+#define SR0_IA_SETUP_DONE	1
+#define SR0_CONFIGURE_DONE	2
+#define SR0_MC_SETUP_DONE	3
+#define SR0_TRANSMIT_DONE	4
+#define SR0_TDR_DONE		5
+#define SR0_DUMP_DONE		6
+#define SR0_DIAGNOSE_PASSED	7
+#define SR0_TRANSMIT_NO_CRC_DONE 9
+#define SR0_RETRANSMIT_DONE	12
+#define SR0_EXECUTION_ABORTED	13
+#define SR0_END_OF_FRAME	8
+#define SR0_RECEPTION_ABORTED	10
+#define SR0_DIAGNOSE_FAILED	15
+#define SR0_STOP_REG_HIT	11
+
+#define SR0_CHNL		(1 << 4)
+#define SR0_EXECUTION		(1 << 5)
+#define SR0_RECEPTION		(1 << 6)
+#define SR0_INTERRUPT		(1 << 7)
+#define SR0_BOTH_RX_TX		(SR0_EXECUTION | SR0_RECEPTION)
+
+#define SR3_EXEC_STATE_MASK	0x03
+#define SR3_EXEC_IDLE		0
+#define SR3_TX_ABORT_IN_PROGRESS 1
+#define SR3_EXEC_ACTIVE		2
+#define SR3_ABORT_IN_PROGRESS	3
+#define SR3_EXEC_CHNL		(1 << 2)
+#define SR3_STP_ON_NO_RSRC	(1 << 3)
+#define SR3_RCVING_NO_RSRC	(1 << 4)
+#define SR3_RCV_STATE_MASK	0x60
+#define SR3_RCV_IDLE		0x00
+#define SR3_RCV_READY		0x20
+#define SR3_RCV_ACTIVE		0x40
+#define SR3_RCV_STOP_IN_PROG	0x60
+#define SR3_RCV_CHNL		(1 << 7)
+
+/* Port 1 Command Register definitions */
+
+#define OP1_NOP			0
+#define OP1_SWIT_TO_PORT_0	1
+#define OP1_INT_DISABLE		2
+#define OP1_INT_ENABLE		3
+#define OP1_SET_TS		5
+#define OP1_RST_TS		7
+#define OP1_POWER_DOWN		8
+#define OP1_RESET_RING_MNGMT	11
+#define OP1_RESET		14
+#define OP1_SEL_RST		15
+
+#define CR1_STATUS_4		0x00
+#define CR1_STATUS_5		0x20
+#define CR1_STATUS_6		0x40
+#define CR1_STOP_REG_UPDATE	(1 << 7)
+
+/* Receive frame status bits */
+
+#define	RX_RCLD			(1 << 0)
+#define RX_IA_MATCH		(1 << 1)
+#define	RX_NO_AD_MATCH		(1 << 2)
+#define RX_NO_SFD		(1 << 3)
+#define RX_SRT_FRM		(1 << 7)
+#define RX_OVRRUN		(1 << 8)
+#define RX_ALG_ERR		(1 << 10)
+#define RX_CRC_ERR		(1 << 11)
+#define RX_LEN_ERR		(1 << 12)
+#define RX_RCV_OK		(1 << 13)
+#define RX_TYP_LEN		(1 << 15)
+
+/* Transmit status bits */
+
+#define TX_NCOL_MASK		0x0f
+#define TX_FRTL			(1 << 4)
+#define TX_MAX_COL		(1 << 5)
+#define TX_HRT_BEAT		(1 << 6)
+#define TX_DEFER		(1 << 7)
+#define TX_UND_RUN		(1 << 8)
+#define TX_LOST_CTS		(1 << 9)
+#define TX_LOST_CRS		(1 << 10)
+#define TX_LTCOL		(1 << 11)
+#define TX_OK			(1 << 13)
+#define TX_COLL			(1 << 15)
+
+struct i82593_conf_block {
+  u_char fifo_limit : 4,
+  	 forgnesi   : 1,
+  	 fifo_32    : 1,
+  	 d6mod      : 1,
+  	 throttle_enb : 1;
+  u_char throttle   : 6,
+	 cntrxint   : 1,
+	 contin	    : 1;
+  u_char addr_len   : 3,
+  	 acloc 	    : 1,
+ 	 preamb_len : 2,
+  	 loopback   : 2;
+  u_char lin_prio   : 3,
+	 tbofstop   : 1,
+	 exp_prio   : 3,
+	 bof_met    : 1;
+  u_char	    : 4,
+	 ifrm_spc   : 4;
+  u_char	    : 5,
+	 slottim_low : 3;
+  u_char slottim_hi : 3,
+		    : 1,
+	 max_retr   : 4;
+  u_char prmisc     : 1,
+	 bc_dis     : 1,
+  		    : 1,
+	 crs_1	    : 1,
+	 nocrc_ins  : 1,
+	 crc_1632   : 1,
+  	 	    : 1,
+  	 crs_cdt    : 1;
+  u_char cs_filter  : 3,
+	 crs_src    : 1,
+	 cd_filter  : 3,
+		    : 1;
+  u_char	    : 2,
+  	 min_fr_len : 6;
+  u_char lng_typ    : 1,
+	 lng_fld    : 1,
+	 rxcrc_xf   : 1,
+	 artx	    : 1,
+	 sarec	    : 1,
+	 tx_jabber  : 1,	/* why is this called max_len in the manual? */
+	 hash_1	    : 1,
+  	 lbpkpol    : 1;
+  u_char	    : 6,
+  	 fdx	    : 1,
+  	  	    : 1;
+  u_char dummy_6    : 6,	/* supposed to be ones */
+  	 mult_ia    : 1,
+  	 dis_bof    : 1;
+  u_char dummy_1    : 1,	/* supposed to be one */
+	 tx_ifs_retrig : 2,
+	 mc_all     : 1,
+	 rcv_mon    : 2,
+	 frag_acpt  : 1,
+  	 tstrttrs   : 1;
+  u_char fretx	    : 1,
+	 runt_eop   : 1,
+	 hw_sw_pin  : 1,
+	 big_endn   : 1,
+	 syncrqs    : 1,
+	 sttlen     : 1,
+	 tx_eop     : 1,
+  	 rx_eop	    : 1;
+  u_char rbuf_size  : 5,
+	 rcvstop    : 1,
+  	 	    : 2;
+};
+
+#define I82593_MAX_MULTICAST_ADDRESSES	128	/* Hardware hashed filter */
+
+#endif /* _I82593_H */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/netwave_cs.c linux/drivers/net/wireless/netwave_cs.c
--- linux-2.5.3-pre2/drivers/net/wireless/netwave_cs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/netwave_cs.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,1600 @@
+/*********************************************************************
+ *                
+ * Filename:      netwave_cs.c
+ * Version:       0.4.1
+ * Description:   Netwave AirSurfer Wireless LAN PC Card driver
+ * Status:        Experimental.
+ * Authors:       John Markus Bjørndalen <johnm@cs.uit.no>
+ *                Dag Brattli <dagb@cs.uit.no>
+ *                David Hinds <dahinds@users.sourceforge.net>
+ * Created at:    A long time ago!
+ * Modified at:   Mon Nov 10 11:54:37 1997
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1997 University of Tromsø, Norway
+ *
+ * Revision History:
+ *
+ *   08-Nov-97 15:14:47   John Markus Bjørndalen <johnm@cs.uit.no>
+ *    - Fixed some bugs in netwave_rx and cleaned it up a bit. 
+ *      (One of the bugs would have destroyed packets when receiving
+ *      multiple packets per interrupt). 
+ *    - Cleaned up parts of newave_hw_xmit. 
+ *    - A few general cleanups. 
+ *   24-Oct-97 13:17:36   Dag Brattli <dagb@cs.uit.no>
+ *    - Fixed netwave_rx receive function (got updated docs)
+ *   Others:
+ *    - Changed name from xircnw to netwave, take a look at 
+ *      http://www.netwave-wireless.com
+ *    - Some reorganizing of the code
+ *    - Removed possible race condition between interrupt handler and transmit
+ *      function
+ *    - Started to add wireless extensions, but still needs some coding
+ *    - Added watchdog for better handling of transmission timeouts 
+ *      (hopefully this works better)
+ ********************************************************************/
+
+/* To have statistics (just packets sent) define this */
+#undef NETWAVE_STATS
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+#define NETWAVE_REGOFF         0x8000
+/* The Netwave IO registers, offsets to iobase */
+#define NETWAVE_REG_COR        0x0
+#define NETWAVE_REG_CCSR       0x2
+#define NETWAVE_REG_ASR        0x4
+#define NETWAVE_REG_IMR        0xa
+#define NETWAVE_REG_PMR        0xc
+#define NETWAVE_REG_IOLOW      0x6
+#define NETWAVE_REG_IOHI       0x7
+#define NETWAVE_REG_IOCONTROL  0x8
+#define NETWAVE_REG_DATA       0xf
+/* The Netwave Extended IO registers, offsets to RamBase */
+#define NETWAVE_EREG_ASCC      0x114
+#define NETWAVE_EREG_RSER      0x120
+#define NETWAVE_EREG_RSERW     0x124
+#define NETWAVE_EREG_TSER      0x130
+#define NETWAVE_EREG_TSERW     0x134
+#define NETWAVE_EREG_CB        0x100
+#define NETWAVE_EREG_SPCQ      0x154
+#define NETWAVE_EREG_SPU       0x155
+#define NETWAVE_EREG_LIF       0x14e
+#define NETWAVE_EREG_ISPLQ     0x156
+#define NETWAVE_EREG_HHC       0x158
+#define NETWAVE_EREG_NI        0x16e
+#define NETWAVE_EREG_MHS       0x16b
+#define NETWAVE_EREG_TDP       0x140
+#define NETWAVE_EREG_RDP       0x150
+#define NETWAVE_EREG_PA        0x160
+#define NETWAVE_EREG_EC        0x180
+#define NETWAVE_EREG_CRBP      0x17a
+#define NETWAVE_EREG_ARW       0x166
+
+/*
+ * Commands used in the extended command buffer
+ * NETWAVE_EREG_CB (0x100-0x10F) 
+ */
+#define NETWAVE_CMD_NOP        0x00
+#define NETWAVE_CMD_SRC        0x01
+#define NETWAVE_CMD_STC        0x02
+#define NETWAVE_CMD_AMA        0x03
+#define NETWAVE_CMD_DMA        0x04
+#define NETWAVE_CMD_SAMA       0x05
+#define NETWAVE_CMD_ER         0x06
+#define NETWAVE_CMD_DR         0x07
+#define NETWAVE_CMD_TL         0x08
+#define NETWAVE_CMD_SRP        0x09
+#define NETWAVE_CMD_SSK        0x0a
+#define NETWAVE_CMD_SMD        0x0b
+#define NETWAVE_CMD_SAPD       0x0c
+#define NETWAVE_CMD_SSS        0x11
+/* End of Command marker */
+#define NETWAVE_CMD_EOC        0x00
+
+/* ASR register bits */
+#define NETWAVE_ASR_RXRDY   0x80
+#define NETWAVE_ASR_TXBA    0x01
+
+#define TX_TIMEOUT		((32*HZ)/100)
+
+static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */
+static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */
+
+static const unsigned int corConfIENA   = 0x01; /* Interrupt enable */
+static const unsigned int corConfLVLREQ = 0x40; /* Keep high */
+
+static const unsigned int rxConfRxEna  = 0x80; /* Receive Enable */
+static const unsigned int rxConfMAC    = 0x20; /* MAC host receive mode*/ 
+static const unsigned int rxConfPro    = 0x10; /* Promiscuous */
+static const unsigned int rxConfAMP    = 0x08; /* Accept Multicast Packets */
+static const unsigned int rxConfBcast  = 0x04; /* Accept Broadcast Packets */
+
+static const unsigned int txConfTxEna  = 0x80; /* Transmit Enable */
+static const unsigned int txConfMAC    = 0x20; /* Host sends MAC mode */
+static const unsigned int txConfEUD    = 0x10; /* Enable Uni-Data packets */
+static const unsigned int txConfKey    = 0x02; /* Scramble data packets */
+static const unsigned int txConfLoop   = 0x01; /* Loopback mode */
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#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 =
+"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+static dev_info_t dev_info = "netwave_cs";
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Choose the domain, default is 0x100 */
+static u_int  domain = 0x100;
+
+/* Scramble key, range from 0x0 to 0xffff.  
+ * 0x0 is no scrambling. 
+ */
+static u_int  scramble_key = 0x0;
+
+/* Shared memory speed, in ns. The documentation states that 
+ * the card should not be read faster than every 400ns. 
+ * This timing should be provided by the HBA. If it becomes a 
+ * problem, try setting mem_speed to 400. 
+ */
+static int mem_speed;
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(domain, "i");
+MODULE_PARM(scramble_key, "i");
+MODULE_PARM(mem_speed, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+/* PCMCIA (Card Services) related functions */
+static void netwave_release(u_long arg);     /* Card removal */
+static int  netwave_event(event_t event, int priority, 
+					      event_callback_args_t *args);
+static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card 
+													   insertion */
+static dev_link_t *netwave_attach(void);     /* Create instance */
+static void netwave_detach(dev_link_t *);    /* Destroy instance */
+static void netwave_flush_stale_links(void);	     /* Destroy all staled instances */
+
+/* Hardware configuration */
+static void netwave_doreset(ioaddr_t iobase, u_char* ramBase);
+static void netwave_reset(struct net_device *dev);
+
+/* Misc device stuff */
+static int netwave_open(struct net_device *dev);  /* Open the device */
+static int netwave_close(struct net_device *dev); /* Close the device */
+static int netwave_config(struct net_device *dev, struct ifmap *map);
+
+/* Packet transmission and Packet reception */
+static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev);
+static int netwave_rx( struct net_device *dev);
+
+/* Interrupt routines */
+static void netwave_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void netwave_watchdog(struct net_device *);
+
+/* Statistics */
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *netwave_get_stats(struct net_device *dev);
+
+/* Wireless extensions */
+#ifdef WIRELESS_EXT
+static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev);
+#endif
+static int netwave_ioctl(struct net_device *, struct ifreq *, int);
+
+static void set_multicast_list(struct net_device *dev);
+
+/*
+   A linked list of "instances" of the skeleton device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+static dev_link_t *dev_list;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally can't be allocated dynamically.
+*/
+
+/* Wireless Extension Backward compatibility - Jean II
+ * If the new wireless device private ioctl range is not defined,
+ * default to standard device private ioctl range */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV	SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+#define SIOCGIPSNAP	SIOCIWFIRSTPRIV		/* Site Survey Snapshot */
+/*#define SIOCGIPQTHR	SIOCIWFIRSTPRIV + 1*/
+
+#define MAX_ESA 10
+
+typedef struct net_addr {
+    u_char addr48[6];
+} net_addr;
+
+struct site_survey {
+    u_short length;
+    u_char  struct_revision;
+    u_char  roaming_state;
+	
+    u_char  sp_existsFlag;
+    u_char  sp_link_quality;
+    u_char  sp_max_link_quality;
+    u_char  linkQualityGoodFairBoundary;
+    u_char  linkQualityFairPoorBoundary;
+    u_char  sp_utilization;
+    u_char  sp_goodness;
+    u_char  sp_hotheadcount;
+    u_char  roaming_condition;
+	
+    net_addr sp;
+    u_char   numAPs;
+    net_addr nearByAccessPoints[MAX_ESA];
+};	
+   
+typedef struct netwave_private {
+    dev_link_t link;
+    struct net_device      dev;
+    dev_node_t node;
+    u_char     *ramBase;
+    int        timeoutCounter;
+    int        lastExec;
+    struct timer_list      watchdog;	/* To avoid blocking state */
+    struct site_survey     nss;
+    struct net_device_stats stats;
+#ifdef WIRELESS_EXT
+    struct iw_statistics   iw_stats;    /* Wireless stats */
+#endif
+} netwave_private;
+
+#ifdef NETWAVE_STATS
+static struct net_device_stats *netwave_get_stats(struct net_device *dev);
+#endif
+
+/*
+ * The Netwave card is little-endian, so won't work for big endian
+ * systems.
+ */
+static inline unsigned short get_uint16(u_char* staddr) 
+{
+    return readw(staddr); /* Return only 16 bits */
+}
+
+static inline short get_int16(u_char* staddr)
+{
+    return readw(staddr);
+}
+
+/**************************************************************************/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/* 
+ * Wait until the WOC (Write Operation Complete) bit in the 
+ * ASR (Adapter Status Register) is asserted. 
+ * This should have aborted if it takes too long time. 
+ */
+static inline void wait_WOC(unsigned int iobase)
+{
+    /* Spin lock */
+    while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ; 
+}
+
+#ifdef WIRELESS_EXT
+static void netwave_snapshot(netwave_private *priv, u_char *ramBase, 
+			     ioaddr_t iobase) { 
+    u_short resultBuffer;
+
+    /* if time since last snapshot is > 1 sec. (100 jiffies?)  then take 
+     * new snapshot, else return cached data. This is the recommended rate.  
+     */
+    if ( jiffies - priv->lastExec > 100) { 
+	/* Take site survey  snapshot */ 
+	/*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies -
+	  priv->lastExec); */
+	wait_WOC(iobase); 
+	writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0); 
+	writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); 
+	wait_WOC(iobase); 
+
+	/* Get result and copy to cach */ 
+	resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP); 
+	copy_from_pc( &priv->nss, ramBase+resultBuffer, 
+		      sizeof(struct site_survey)); 
+    } 
+}
+#endif
+
+#ifdef WIRELESS_EXT
+/*
+ * Function netwave_get_wireless_stats (dev)
+ *
+ *    Wireless extensions statistics
+ *
+ */
+static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev)
+{	
+    unsigned long flags;
+    ioaddr_t iobase = dev->base_addr;
+    netwave_private *priv = (netwave_private *) dev->priv;
+    u_char *ramBase = priv->ramBase;
+    struct iw_statistics* wstats;
+	
+    wstats = &priv->iw_stats;
+
+    save_flags(flags);
+    cli();
+	
+    netwave_snapshot( priv, ramBase, iobase);
+
+    wstats->status = priv->nss.roaming_state;
+    wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ); 
+    wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ);
+    wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f;
+    wstats->discard.nwid = 0L;
+    wstats->discard.code = 0L;
+    wstats->discard.misc = 0L;
+
+    restore_flags(flags);
+    
+    return &priv->iw_stats;
+}
+#endif
+
+/*
+ * Function netwave_attach (void)
+ *
+ *     Creates an "instance" of the driver, allocating local data 
+ *     structures for one device.  The device is registered with Card 
+ *     Services.
+ *
+ *     The dev_link structure is initialized, but we don't actually
+ *     configure the card at this point -- we wait until we receive a
+ *     card insertion event.
+ */
+static dev_link_t *netwave_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    netwave_private *priv;
+    int i, ret;
+    
+    DEBUG(0, "netwave_attach()\n");
+    
+    /* Perform some cleanup */
+    netwave_flush_stale_links();
+
+    /* Initialize the dev_link_t structure */
+    priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+    if (!priv) return NULL;
+    memset(priv, 0, sizeof(*priv));
+    link = &priv->link; dev = &priv->dev;
+    link->priv = dev->priv = priv;
+    link->release.function = &netwave_release;
+    link->release.data = (u_long)link;
+	
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+    /* link->io.NumPorts2 = 16; 
+       link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */
+    link->io.IOAddrLines = 5;
+    
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    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->irq.Handler = &netwave_interrupt;
+    
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Netwave specific entries in the device structure */
+    dev->hard_start_xmit = &netwave_start_xmit;
+    dev->set_config = &netwave_config;
+    dev->get_stats  = &netwave_get_stats;
+    dev->set_multicast_list = &set_multicast_list;
+    /* wireless extensions */
+#ifdef WIRELESS_EXT
+    dev->get_wireless_stats = &netwave_get_wireless_stats;
+#endif
+    dev->do_ioctl = &netwave_ioctl;
+
+    dev->tx_timeout = &netwave_watchdog;
+    dev->watchdog_timeo = TX_TIMEOUT;
+
+    ether_setup(dev);
+    dev->open = &netwave_open;
+    dev->stop = &netwave_close;
+    link->irq.Instance = dev;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &netwave_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	netwave_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* netwave_attach */
+
+/*
+ * Function netwave_detach (link)
+ *
+ *    This deletes a driver "instance".  The device is de-registered
+ *    with Card Services.  If it has been released, all local data
+ *    structures are freed.  Otherwise, the structures will be freed
+ *    when the device is released.
+ */
+static void netwave_detach(dev_link_t *link)
+{
+    netwave_private *priv = link->priv;
+    dev_link_t **linkp;
+
+    DEBUG(0, "netwave_detach(0x%p)\n", link);
+  
+    /*
+	  If the device is currently configured and active, we won't
+	  actually delete it yet.  Instead, it is marked so that when
+	  the release() function is called, that will trigger a proper
+	  detach().
+	*/
+    del_timer(&link->release);
+    if (link->state & DEV_CONFIG) {
+	netwave_release((u_long) link);
+	if (link->state & DEV_STALE_CONFIG) {
+	    DEBUG(1, "netwave_cs: detach postponed, '%s' still "
+		  "locked\n", link->dev->dev_name);
+	    link->state |= DEV_STALE_LINK;
+	    return;
+	}
+    }
+	
+    /* Break the link with Card Services */
+    if (link->handle)
+	CardServices(DeregisterClient, link->handle);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+      {
+	DEBUG(1, "netwave_cs: detach fail, '%s' not in list\n",
+	      link->dev->dev_name);
+	return;
+      }
+
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->dev)
+	unregister_netdev(&priv->dev);
+    kfree(priv);
+    
+} /* netwave_detach */
+
+/*
+ * Function netwave_flush_stale_links (void)
+ *
+ *    This deletes all driver "instances" that need to be deleted.
+ *    Sometimes, netwave_detach can't be performed following a call from
+ *    cardmgr (device still open) and the device is put in a STALE_LINK
+ *    state.
+ *    This function is in charge of making the cleanup...
+ */
+static void netwave_flush_stale_links(void)
+{
+    dev_link_t *	link;		/* Current node in linked list */
+    dev_link_t *	next;		/* Next node in linked list */
+
+    DEBUG(1, "netwave_flush_stale_links(0x%p)\n", dev_list);
+
+    /* Go through the list */
+    for (link = dev_list; link; link = next) {
+        next = link->next;
+        /* Check if in need of being removed */
+        if(link->state & DEV_STALE_LINK)
+	    netwave_detach(link);
+    }
+} /* netwave_flush_stale_links */
+
+/*
+ * Function netwave_ioctl (dev, rq, cmd)
+ *
+ *     Perform ioctl : config & info stuff
+ *     This is the stuff that are treated the wireless extensions (iwconfig)
+ *
+ */
+static int netwave_ioctl(struct net_device *dev, /* ioctl device */
+			 struct ifreq *rq,	 /* Data passed */
+			 int	cmd)	     /* Ioctl number */
+{
+    unsigned long flags;
+    int			ret = 0;
+#ifdef WIRELESS_EXT
+    ioaddr_t iobase = dev->base_addr;
+    netwave_private *priv = (netwave_private *) dev->priv;
+    u_char *ramBase = priv->ramBase;
+    struct iwreq *wrq = (struct iwreq *) rq;
+#endif
+	
+    DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
+	
+    /* Disable interrupts & save flags */
+    save_flags(flags);
+    cli();
+
+    /* Look what is the request */
+    switch(cmd) {
+	/* --------------- WIRELESS EXTENSIONS --------------- */
+#ifdef WIRELESS_EXT
+    case SIOCGIWNAME:
+	/* Get name */
+	strcpy(wrq->u.name, "Netwave");
+	break;
+    case SIOCSIWNWID:
+	/* Set domain */
+#if WIRELESS_EXT > 8
+	if(!wrq->u.nwid.disabled) {
+	    domain = wrq->u.nwid.value;
+#else	/* WIRELESS_EXT > 8 */
+	if(wrq->u.nwid.on) {
+	    domain = wrq->u.nwid.nwid;
+#endif	/* WIRELESS_EXT > 8 */
+	    printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", 
+		    (domain >> 8) & 0x01, domain & 0xff);
+	    wait_WOC(iobase);
+	    writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
+	    writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+	    writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2);
+	    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+	} break;
+    case SIOCGIWNWID:
+	/* Read domain*/
+#if WIRELESS_EXT > 8
+	wrq->u.nwid.value = domain;
+	wrq->u.nwid.disabled = 0;
+	wrq->u.nwid.fixed = 1;
+#else	/* WIRELESS_EXT > 8 */
+	wrq->u.nwid.nwid = domain;
+	wrq->u.nwid.on = 1;
+#endif	/* WIRELESS_EXT > 8 */
+	break;
+#if WIRELESS_EXT > 8	/* Note : The API did change... */
+    case SIOCGIWENCODE:
+	/* Get scramble key */
+	if(wrq->u.encoding.pointer != (caddr_t) 0)
+	  {
+	    char	key[2];
+	    key[1] = scramble_key & 0xff;
+	    key[0] = (scramble_key>>8) & 0xff;
+	    wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+	    wrq->u.encoding.length = 2;
+	    if(copy_to_user(wrq->u.encoding.pointer, key, 2))
+	      ret = -EFAULT;
+	  }
+	break;
+    case SIOCSIWENCODE:
+	/* Set  scramble key */
+	if(wrq->u.encoding.pointer != (caddr_t) 0)
+	  {
+	    char	key[2];
+	    if(copy_from_user(key, wrq->u.encoding.pointer, 2))
+	      {
+		ret = -EFAULT;
+		break;
+	      }
+	    scramble_key = (key[0] << 8) | key[1];
+	    wait_WOC(iobase);
+	    writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+	    writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+	    writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+	    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+	  }
+	break;
+    case SIOCGIWMODE:
+      /* Mode of operation */
+	if(domain & 0x100)
+	  wrq->u.mode = IW_MODE_INFRA;
+	else
+	  wrq->u.mode = IW_MODE_ADHOC;
+      break;
+#else /* WIRELESS_EXT > 8 */
+    case SIOCGIWENCODE:
+	/* Get scramble key */
+	wrq->u.encoding.code = scramble_key;
+	wrq->u.encoding.method = 1;
+	break;
+    case SIOCSIWENCODE:
+	/* Set  scramble key */
+	scramble_key = wrq->u.encoding.code;
+	wait_WOC(iobase);
+	writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+	writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+	writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+	writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+	break;
+#endif /* WIRELESS_EXT > 8 */
+   case SIOCGIWRANGE:
+       /* Basic checking... */
+       if(wrq->u.data.pointer != (caddr_t) 0) {
+	   struct iw_range	range;
+		   
+	   /* Set the length (very important for backward compatibility) */
+	   wrq->u.data.length = sizeof(struct iw_range);
+
+	   /* Set all the info we don't care or don't know about to zero */
+	   memset(&range, 0, sizeof(range));
+
+#if WIRELESS_EXT > 10
+	   /* Set the Wireless Extension versions */
+	   range.we_version_compiled = WIRELESS_EXT;
+	   range.we_version_source = 9;	/* Nothing for us in v10 and v11 */
+#endif /* WIRELESS_EXT > 10 */
+		   
+	   /* Set information in the range struct */
+	   range.throughput = 450 * 1000;	/* don't argue on this ! */
+	   range.min_nwid = 0x0000;
+	   range.max_nwid = 0x01FF;
+
+	   range.num_channels = range.num_frequency = 0;
+		   
+	   range.sensitivity = 0x3F;
+	   range.max_qual.qual = 255;
+	   range.max_qual.level = 255;
+	   range.max_qual.noise = 0;
+		   
+#if WIRELESS_EXT > 7
+	   range.num_bitrates = 1;
+	   range.bitrate[0] = 1000000;	/* 1 Mb/s */
+#endif /* WIRELESS_EXT > 7 */
+
+#if WIRELESS_EXT > 8
+	   range.encoding_size[0] = 2;		/* 16 bits scrambling */
+	   range.num_encoding_sizes = 1;
+	   range.max_encoding_tokens = 1;	/* Only one key possible */
+#endif /* WIRELESS_EXT > 8 */
+
+	   /* Copy structure to the user buffer */
+	   if(copy_to_user(wrq->u.data.pointer, &range,
+			sizeof(struct iw_range)))
+	     ret = -EFAULT;
+       }
+       break;
+    case SIOCGIWPRIV:
+	/* Basic checking... */
+	if(wrq->u.data.pointer != (caddr_t) 0) {
+	    struct iw_priv_args	priv[] =
+	    {	/* cmd,		set_args,	get_args,	name */
+		{ SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, 
+		  sizeof(struct site_survey), 
+		  "getsitesurvey" },
+	    };
+			
+	    /* Set the number of ioctl available */
+	    wrq->u.data.length = 1;
+			
+	    /* Copy structure to the user buffer */
+	    if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+			 sizeof(priv)))
+	      ret = -EFAULT;
+	} 
+	break;
+    case SIOCGIPSNAP:
+	if(wrq->u.data.pointer != (caddr_t) 0) {
+	    /* Take snapshot of environment */
+	    netwave_snapshot( priv, ramBase, iobase);
+	    wrq->u.data.length = priv->nss.length;
+	    /* Copy structure to the user buffer */
+	    if(copy_to_user(wrq->u.data.pointer, 
+			 (u_char *) &priv->nss,
+			 sizeof( struct site_survey)))
+	      {
+		printk(KERN_DEBUG "Bad buffer!\n");
+		break;
+	      }
+
+	    priv->lastExec = jiffies;
+	}
+	break;
+#endif
+    default:
+	ret = -EOPNOTSUPP;
+    }
+	
+    /* ReEnable interrupts & restore flags */
+    restore_flags(flags);
+    
+    return ret;
+}
+
+/*
+ * Function netwave_pcmcia_config (link)
+ *
+ *     netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION 
+ *     event is received, to configure the PCMCIA socket, and to make the
+ *     device available to the system. 
+ *
+ */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void netwave_pcmcia_config(dev_link_t *link) {
+    client_handle_t handle = link->handle;
+    netwave_private *priv = link->priv;
+    struct net_device *dev = &priv->dev;
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, j, last_ret, last_fn;
+    u_char buf[64];
+    win_req_t req;
+    memreq_t mem;
+    u_char *ramBase = NULL;
+
+    DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
+
+    /*
+      This reads the card's CONFIG tuple to find its configuration
+      registers.
+    */
+    tuple.Attributes = 0;
+    tuple.TupleData = (cisdata_t *) buf;
+    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;
+    link->conf.Present = parse.config.rmask[0];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /*
+     *  Try allocating IO ports.  This tries a few fixed addresses.
+     *  If you want, you can also read the card's config table to
+     *  pick addresses -- see the serial driver for an example.
+     */
+    for (i = j = 0x0; j < 0x400; j += 0x20) {
+	link->io.BasePort1 = j ^ 0x300;
+	i = CardServices(RequestIO, link->handle, &link->io);
+	if (i == CS_SUCCESS) break;
+    }
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, RequestIO, i);
+	goto failed;
+    }
+
+    /*
+     *  Now allocate an interrupt line.  Note that this does not
+     *  actually assign a handler to the interrupt.
+     */
+    CS_CHECK(RequestIRQ, handle, &link->irq);
+
+    /*
+     *  This actually configures the PCMCIA socket -- setting up
+     *  the I/O windows and the interrupt mapping.
+     */
+    CS_CHECK(RequestConfiguration, handle, &link->conf);
+
+    /*
+     *  Allocate a 32K memory window.  Note that the dev_link_t
+     *  structure provides space for one window handle -- if your
+     *  device needs several windows, you'll need to keep track of
+     *  the handles in your private data structure, link->priv.
+     */
+    DEBUG(1, "Setting mem speed of %d\n", mem_speed);
+
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+    req.Base = 0; req.Size = 0x8000;
+    req.AccessSpeed = mem_speed;
+    link->win = (window_handle_t)link->handle;
+    CS_CHECK(RequestWindow, &link->win, &req);
+    mem.CardOffset = 0x20000; mem.Page = 0; 
+    CS_CHECK(MapMemPage, link->win, &mem);
+
+    /* Store base address of the common window frame */
+    ramBase = ioremap(req.Base, 0x8000);
+    ((netwave_private*)dev->priv)->ramBase = ramBase;
+
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+    if (register_netdev(dev) != 0) {
+	printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
+	goto failed;
+    }
+
+    strcpy(priv->node.dev_name, dev->name);
+    link->dev = &priv->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    /* Reset card before reading physical address */
+    netwave_doreset(dev->base_addr, ramBase);
+
+    /* Read the ethernet address and fill in the Netwave registers. */
+    for (i = 0; i < 6; i++) 
+	dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i);
+
+    printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx id "
+	   "%c%c, hw_addr ", dev->name, dev->base_addr, dev->irq,
+	   (u_long) ramBase, (int) readb(ramBase+NETWAVE_EREG_NI),
+	   (int) readb(ramBase+NETWAVE_EREG_NI+1));
+    for (i = 0; i < 6; i++)
+	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+    /* get revision words */
+    printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", 
+	   get_uint16(ramBase + NETWAVE_EREG_ARW),
+	   get_uint16(ramBase + NETWAVE_EREG_ARW+2));
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    netwave_release((u_long)link);
+} /* netwave_pcmcia_config */
+
+/*
+ * Function netwave_release (arg)
+ *
+ *    After a card is removed, netwave_release() will unregister the net
+ *    device, and release the PCMCIA configuration.  If the device is
+ *    still open, this will be postponed until it is closed.
+ */
+static void netwave_release(u_long arg) {
+    dev_link_t *link = (dev_link_t *)arg;
+    netwave_private *priv = link->priv;
+
+    DEBUG(0, "netwave_release(0x%p)\n", link);
+
+    /*
+      If the device is currently in use, we won't release until it
+      is actually closed.
+      */
+    if (link->open) {
+	printk(KERN_DEBUG "netwave_cs: release postponed, '%s' still open\n",
+	       link->dev->dev_name);
+	link->state |= DEV_STALE_CONFIG;
+	return;
+    }
+
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win) {
+	iounmap(priv->ramBase);
+	CardServices(ReleaseWindow, link->win);
+    }
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+    link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
+
+} /* netwave_release */
+
+/*
+ * Function netwave_event (event, priority, args)
+ *
+ *    The card status event handler.  Mostly, this schedules other
+ *    stuff to run after an event is received.  A CARD_REMOVAL event
+ *    also sets some flags to discourage the net drivers from trying
+ *    to talk to the card any more.
+ *
+ *    When a CARD_REMOVAL event is received, we immediately set a flag
+ *    to block future accesses to this device.  All the functions that
+ *    actually access the device should check this flag to make sure
+ *    the card is still present.
+ *
+ */
+static int netwave_event(event_t event, int priority,
+			 event_callback_args_t *args) {
+    dev_link_t *link = args->client_data;
+    netwave_private *priv = link->priv;
+    struct net_device *dev = &priv->dev;
+	
+    DEBUG(1, "netwave_event(0x%06x)\n", event);
+  
+    switch (event) {
+    case CS_EVENT_REGISTRATION_COMPLETE:
+	DEBUG(0, "netwave_cs: registration complete\n");
+	break;
+
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG) {
+	    netif_device_detach(dev);
+	    mod_timer(&link->release, jiffies + HZ/20);
+	}
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	netwave_pcmcia_config( link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open)
+		netif_device_detach(dev);
+	    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);
+	    if (link->open) {
+		netwave_reset(dev);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* netwave_event */
+
+/*
+ * Function netwave_doreset (ioBase, ramBase)
+ *
+ *    Proper hardware reset of the card.
+ */
+static void netwave_doreset(ioaddr_t ioBase, u_char* ramBase) {
+    /* Reset card */
+    wait_WOC(ioBase);
+    outb(0x80, ioBase + NETWAVE_REG_PMR);
+    writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */
+    outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */
+}
+
+/*
+ * Function netwave_reset (dev)
+ *
+ *    Reset and restore all of the netwave registers 
+ */
+static void netwave_reset(struct net_device *dev) {
+    /* u_char state; */
+    netwave_private *priv = (netwave_private*) dev->priv;
+    u_char *ramBase = priv->ramBase;
+    ioaddr_t iobase = dev->base_addr;
+
+    DEBUG(0, "netwave_reset: Done with hardware reset\n");
+
+    priv->timeoutCounter = 0;
+
+    /* Reset card */
+    netwave_doreset(iobase, ramBase);
+    printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n");
+	
+    /* Write a NOP to check the card */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+	
+    /* Set receive conf */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+    
+    /* Set transmit conf */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+    
+    /* Now set the MU Domain */
+    printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff);
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+    writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+	
+    /* Set scramble key */
+    printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key);
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+    writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+
+    /* Enable interrupts, bit 4 high to keep unused
+     * source from interrupting us, bit 2 high to 
+     * set interrupt enable, 567 to enable TxDN, 
+     * RxErr and RxRdy
+     */
+    wait_WOC(iobase);
+    outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR);
+
+    /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36
+     * waitWOC
+     * skriv 80 til d000:3688
+     * sjekk om det ble 80
+     */
+    
+    /* Enable Receiver */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+	
+    /* Set the IENA bit in COR */
+    wait_WOC(iobase);
+    outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR);
+}
+
+/*
+ * Function netwave_config (dev, map)
+ *
+ *    Configure device, this work is done by netwave_pcmcia_config when a
+ *    card is inserted
+ */
+static int netwave_config(struct net_device *dev, struct ifmap *map) {
+    return 0; 
+}
+
+/*
+ * Function netwave_hw_xmit (data, len, dev)    
+ */
+static int netwave_hw_xmit(unsigned char* data, int len,
+			   struct net_device* dev) {
+    unsigned long flags;
+    unsigned int TxFreeList,
+	         curBuff,
+	         MaxData, 
+                 DataOffset;
+    int tmpcount; 
+	
+    netwave_private *priv = (netwave_private *) dev->priv;
+    u_char* ramBase = priv->ramBase;
+    ioaddr_t iobase = dev->base_addr;
+
+    /* Disable interrupts & save flags */
+    save_flags(flags);
+    cli();
+
+    /* Check if there are transmit buffers available */
+    wait_WOC(iobase);
+    if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) {
+	/* No buffers available */
+	printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n",
+	       dev->name);
+	restore_flags(flags);
+	return 1;
+    }
+
+    priv->stats.tx_bytes += len;
+
+    DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
+	  readb(ramBase + NETWAVE_EREG_SPCQ),
+	  readb(ramBase + NETWAVE_EREG_SPU),
+	  readb(ramBase + NETWAVE_EREG_LIF),
+	  readb(ramBase + NETWAVE_EREG_ISPLQ));
+
+    /* Now try to insert it into the adapters free memory */
+    wait_WOC(iobase);
+    TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP);
+    MaxData    = get_uint16(ramBase + NETWAVE_EREG_TDP+2);
+    DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4);
+	
+    DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n",
+	  TxFreeList, MaxData, DataOffset);
+
+    /* Copy packet to the adapter fragment buffers */
+    curBuff = TxFreeList; 
+    tmpcount = 0; 
+    while (tmpcount < len) {
+	int tmplen = len - tmpcount; 
+	copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount, 
+		   (tmplen < MaxData) ? tmplen : MaxData);
+	tmpcount += MaxData;
+			
+	/* Advance to next buffer */
+	curBuff = get_uint16(ramBase + curBuff);
+    }
+    
+    /* Now issue transmit list */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+    writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+
+    restore_flags( flags);
+    return 0;
+}
+
+static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+	/* This flag indicate that the hardware can't perform a transmission.
+	 * Theoritically, NET3 check it before sending a packet to the driver,
+	 * but in fact it never do that and pool continuously.
+	 * As the watchdog will abort too long transmissions, we are quite safe...
+	 */
+
+    netif_stop_queue(dev);
+
+    {
+	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	unsigned char* buf = skb->data;
+	
+	if (netwave_hw_xmit( buf, length, dev) == 1) {
+	    /* Some error, let's make them call us another time? */
+	    netif_start_queue(dev);
+	}
+	dev->trans_start = jiffies;
+    }
+    dev_kfree_skb(skb);
+    
+    return 0;
+} /* netwave_start_xmit */
+
+/*
+ * Function netwave_interrupt (irq, dev_id, regs)
+ *
+ *    This function is the interrupt handler for the Netwave card. This
+ *    routine will be called whenever: 
+ *	  1. A packet is received.
+ *	  2. A packet has successfully been transferred and the unit is
+ *	     ready to transmit another packet.
+ *	  3. A command has completed execution.
+ */
+static void netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs) {
+    ioaddr_t iobase;
+    u_char *ramBase;
+    struct net_device *dev = (struct net_device *)dev_id;
+    struct netwave_private *priv = dev->priv;
+    dev_link_t *link = &priv->link;
+    int i;
+    
+    if (!netif_device_present(dev))
+	return;
+    
+    iobase = dev->base_addr;
+    ramBase = priv->ramBase;
+	
+    /* Now find what caused the interrupt, check while interrupts ready */
+    for (i = 0; i < 10; i++) {
+	u_char status;
+		
+	wait_WOC(iobase);	
+	if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02))
+	    break; /* None of the interrupt sources asserted */
+	
+        status = inb(iobase + NETWAVE_REG_ASR);
+		
+	if (!DEV_OK(link)) {
+	    DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x "
+		  "from removed or suspended card!\n", status);
+	    break;
+	}
+		
+	/* RxRdy */
+	if (status & 0x80) {
+	    netwave_rx(dev);
+	    /* wait_WOC(iobase); */
+	    /* RxRdy cannot be reset directly by the host */
+	}
+	/* RxErr */
+	if (status & 0x40) {
+	    u_char rser;
+			
+	    rser = readb(ramBase + NETWAVE_EREG_RSER);			
+	    
+	    if (rser & 0x04) {
+		++priv->stats.rx_dropped; 
+		++priv->stats.rx_crc_errors;
+	    }
+	    if (rser & 0x02)
+		++priv->stats.rx_frame_errors;
+			
+	    /* Clear the RxErr bit in RSER. RSER+4 is the
+	     * write part. Also clear the RxCRC (0x04) and 
+	     * RxBig (0x02) bits if present */
+	    wait_WOC(iobase);
+	    writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4);
+
+	    /* Write bit 6 high to ASCC to clear RxErr in ASR,
+	     * WOC must be set first! 
+	     */
+	    wait_WOC(iobase);
+	    writeb(0x40, ramBase + NETWAVE_EREG_ASCC);
+
+	    /* Remember to count up priv->stats on error packets */
+	    ++priv->stats.rx_errors;
+	}
+	/* TxDN */
+	if (status & 0x20) {
+	    int txStatus;
+
+	    txStatus = readb(ramBase + NETWAVE_EREG_TSER);
+	    DEBUG(3, "Transmit done. TSER = %x id %x\n", 
+		  txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1));
+	    
+	    if (txStatus & 0x20) {
+		/* Transmitting was okay, clear bits */
+		wait_WOC(iobase);
+		writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4);
+		++priv->stats.tx_packets;
+	    }
+			
+	    if (txStatus & 0xd0) {
+		if (txStatus & 0x80) {
+		    ++priv->stats.collisions; /* Because of /proc/net/dev*/
+		    /* ++priv->stats.tx_aborted_errors; */
+		    /* printk("Collision. %ld\n", jiffies - dev->trans_start); */
+		}
+		if (txStatus & 0x40) 
+		    ++priv->stats.tx_carrier_errors;
+		/* 0x80 TxGU Transmit giveup - nine times and no luck
+		 * 0x40 TxNOAP No access point. Discarded packet.
+		 * 0x10 TxErr Transmit error. Always set when 
+		 *      TxGU and TxNOAP is set. (Those are the only ones
+		 *      to set TxErr).
+		 */
+		DEBUG(3, "netwave_interrupt: TxDN with error status %x\n", 
+		      txStatus);
+		
+		/* Clear out TxGU, TxNOAP, TxErr and TxTrys */
+		wait_WOC(iobase);
+		writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4);
+		++priv->stats.tx_errors;
+	    }
+	    DEBUG(3, "New status is TSER %x ASR %x\n",
+		  readb(ramBase + NETWAVE_EREG_TSER),
+		  inb(iobase + NETWAVE_REG_ASR));
+
+	    netif_wake_queue(dev);
+	}
+	/* TxBA, this would trigger on all error packets received */
+	/* if (status & 0x01) {
+	   DEBUG(4, "Transmit buffers available, %x\n", status);
+	   }
+	   */
+    }
+} /* netwave_interrupt */
+
+/*
+ * Function netwave_watchdog (a)
+ *
+ *    Watchdog : when we start a transmission, we set a timer in the
+ *    kernel.  If the transmission complete, this timer is disabled. If
+ *    it expire, we reset the card.
+ *
+ */
+static void netwave_watchdog(struct net_device *dev) {
+
+    DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
+    netwave_reset(dev);
+    dev->trans_start = jiffies;
+    netif_wake_queue(dev);
+} /* netwave_watchdog */
+
+static struct net_device_stats *netwave_get_stats(struct net_device *dev) {
+    netwave_private *priv = (netwave_private*)dev->priv;
+
+    update_stats(dev);
+
+    DEBUG(2, "netwave: SPCQ %x SPU %x LIF %x ISPLQ %x MHS %x rxtx %x"
+	  " %x tx %x %x %x %x\n", 
+	  readb(priv->ramBase + NETWAVE_EREG_SPCQ),
+	  readb(priv->ramBase + NETWAVE_EREG_SPU),
+	  readb(priv->ramBase + NETWAVE_EREG_LIF),
+	  readb(priv->ramBase + NETWAVE_EREG_ISPLQ),
+	  readb(priv->ramBase + NETWAVE_EREG_MHS),
+	  readb(priv->ramBase + NETWAVE_EREG_EC + 0xe),
+	  readb(priv->ramBase + NETWAVE_EREG_EC + 0xf),
+	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x18),
+	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x19),
+	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x1a),
+	  readb(priv->ramBase + NETWAVE_EREG_EC + 0x1b));
+
+    return &priv->stats;
+}
+
+static void update_stats(struct net_device *dev) {
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+
+/*     netwave_private *priv = (netwave_private*) dev->priv;
+    priv->stats.rx_packets = readb(priv->ramBase + 0x18e); 
+    priv->stats.tx_packets = readb(priv->ramBase + 0x18f); */
+
+    restore_flags(flags);
+}
+
+static int netwave_rx(struct net_device *dev) {
+    netwave_private *priv = (netwave_private*)(dev->priv);
+    u_char *ramBase = priv->ramBase;
+    ioaddr_t iobase = dev->base_addr;
+    u_char rxStatus;
+    struct sk_buff *skb = NULL;
+    unsigned int curBuffer,
+		rcvList;
+    int rcvLen;
+    int tmpcount = 0;
+    int dataCount, dataOffset;
+    int i;
+    u_char *ptr;
+	
+    DEBUG(3, "xinw_rx: Receiving ... \n");
+
+    /* Receive max 10 packets for now. */
+    for (i = 0; i < 10; i++) {
+	/* Any packets? */
+	wait_WOC(iobase);
+	rxStatus = readb(ramBase + NETWAVE_EREG_RSER);		
+	if ( !( rxStatus & 0x80)) /* No more packets */
+	    break;
+		
+	/* Check if multicast/broadcast or other */
+	/* multicast = (rxStatus & 0x20);  */
+		
+	/* The receive list pointer and length of the packet */
+	wait_WOC(iobase);
+	rcvLen  = get_int16( ramBase + NETWAVE_EREG_RDP);
+	rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2);
+		
+	if (rcvLen < 0) {
+	    printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n", 
+		   rcvLen);
+	    return 0;
+	}
+		
+	skb = dev_alloc_skb(rcvLen+5);
+	if (skb == NULL) {
+	    DEBUG(1, "netwave_rx: Could not allocate an sk_buff of "
+		  "length %d\n", rcvLen);
+	    ++priv->stats.rx_dropped; 
+	    /* Tell the adapter to skip the packet */
+	    wait_WOC(iobase);
+	    writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
+	    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+	    return 0;
+	}
+
+	skb_reserve( skb, 2);  /* Align IP on 16 byte */
+	skb_put( skb, rcvLen);
+	skb->dev = dev;
+
+	/* Copy packet fragments to the skb data area */
+	ptr = (u_char*) skb->data;
+	curBuffer = rcvList;
+	tmpcount = 0; 
+	while ( tmpcount < rcvLen) {
+	    /* Get length and offset of current buffer */
+	    dataCount  = get_uint16( ramBase+curBuffer+2);
+	    dataOffset = get_uint16( ramBase+curBuffer+4);
+		
+	    copy_from_pc( ptr + tmpcount,
+			  ramBase+curBuffer+dataOffset, dataCount);
+
+	    tmpcount += dataCount;
+		
+	    /* Point to next buffer */
+	    curBuffer = get_uint16(ramBase + curBuffer);
+	}
+	
+	skb->protocol = eth_type_trans(skb,dev);
+	/* Queue packet for network layer */
+	netif_rx(skb);
+
+	dev->last_rx = jiffies;
+	priv->stats.rx_packets++;
+	priv->stats.rx_bytes += rcvLen;
+
+	/* Got the packet, tell the adapter to skip it */
+	wait_WOC(iobase);
+	writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
+	writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+	DEBUG(3, "Packet reception ok\n");
+    }
+    return 0;
+}
+
+static int netwave_open(struct net_device *dev) {
+    netwave_private *priv = dev->priv;
+    dev_link_t *link = &priv->link;
+
+    DEBUG(1, "netwave_open: starting.\n");
+    
+    if (!DEV_OK(link))
+	return -ENODEV;
+
+    link->open++;
+    MOD_INC_USE_COUNT;
+
+    netif_start_queue(dev);
+    netwave_reset(dev);
+	
+    return 0;
+}
+
+static int netwave_close(struct net_device *dev) {
+    netwave_private *priv = (netwave_private *)dev->priv;
+    dev_link_t *link = &priv->link;
+
+    DEBUG(1, "netwave_close: finishing.\n");
+
+    link->open--;
+    netif_stop_queue(dev);
+    if (link->state & DEV_STALE_CONFIG)
+	mod_timer(&link->release, jiffies + HZ/20);
+	
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int __init init_netwave_cs(void) {
+    servinfo_t serv;
+
+    DEBUG(0, "%s\n", version);
+
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	printk("netwave_cs: Card Services release does not match!\n");
+	return -1;
+    }
+ 
+    register_pccard_driver(&dev_info, &netwave_attach, &netwave_detach);
+	
+    return 0;
+}
+
+static void __exit exit_netwave_cs(void) {
+    DEBUG(1, "netwave_cs: unloading\n");
+
+    unregister_pccard_driver(&dev_info);
+
+    /* Do some cleanup of the device list */
+    netwave_flush_stale_links();
+    if(dev_list != NULL)	/* Critical situation */
+        printk("netwave_cs: devices remaining when removing module\n");
+}
+
+module_init(init_netwave_cs);
+module_exit(exit_netwave_cs);
+
+/* Set or clear the multicast filter for this adaptor.
+   num_addrs == -1	Promiscuous mode, receive all packets
+   num_addrs == 0	Normal mode, clear multicast list
+   num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+   best-effort filtering.
+ */
+static void set_multicast_list(struct net_device *dev)
+{
+    ioaddr_t iobase = dev->base_addr;
+    u_char* ramBase = ((netwave_private*) dev->priv)->ramBase;
+    u_char  rcvMode = 0;
+   
+#ifdef PCMCIA_DEBUG
+    if (pc_debug > 2) {
+	static int old;
+	if (old != dev->mc_count) {
+	    old = dev->mc_count;
+	    DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+		  dev->name, dev->mc_count);
+	}
+    }
+#endif
+	
+    if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
+	/* Multicast Mode */
+	rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast;
+    } else if (dev->flags & IFF_PROMISC) {
+	/* Promiscous mode */
+	rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast;
+    } else {
+	/* Normal mode */
+	rcvMode = rxConfRxEna + rxConfBcast;
+    }
+	
+    /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/
+    /* Now set receive mode */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+}
+MODULE_LICENSE("GPL");
diff -urN linux-2.5.3-pre2/drivers/net/wireless/todo.txt linux/drivers/net/wireless/todo.txt
--- linux-2.5.3-pre2/drivers/net/wireless/todo.txt	Mon May  7 19:42:14 2001
+++ linux/drivers/net/wireless/todo.txt	Mon Jan 21 17:23:23 2002
@@ -4,21 +4,26 @@
 1) Bring other kernel Wireless LAN drivers here
 	Already done :
 	 o hermes.c/orinoco.c	-> Wavelan IEEE driver + Airport driver
-	Drivers I have control over :
+	 o airo.c/airo_cs.c	-> Ben's Aironet driver
 	 o wavelan.c		-> old Wavelan ISA driver
-	 o wavelan_cs.c		-> old Wavelan Pcmcia driver (warning : header)
+	 o wavelan_cs.c		-> old Wavelan Pcmcia driver
 	 o netwave_cs.c		-> Netwave Pcmcia driver
 	Drivers likely to go :
 	 o ray_cs.c		-> Raytheon/Aviator driver (maintainer MIA)
 	Drivers I have absolutely no control over :
 	 o arlan.c		-> old Aironet Arlan 655 (need to ask Elmer)
 	 o aironet4500_xxx.c	-> Elmer's Aironet driver (need to ask Elmer)
-	 o airo.c/airo_cs.c	-> Ben's Aironet driver (not yet in kernel)
 	 o strip.c		-> Metricom's stuff. Not a wlan. Hum...
 
 	ETA : Kernel 2.5.X
 
 2) Bring new Wireless LAN driver not yet in the kernel there
 	See my web page for details
+
+3) Misc
+	o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete
+	o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete
+	o Use new Probe/module stuff in wavelan.c
+	o New Wireless Extension API (pending)
 
 	Jean II
diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan.c linux/drivers/net/wireless/wavelan.c
--- linux-2.5.3-pre2/drivers/net/wireless/wavelan.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/wavelan.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,4342 @@
+/*
+ *	WaveLAN ISA driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follows (also see the end of this file).
+ * See wavelan.p.h for details.
+ *
+ *
+ *
+ * AT&T GIS (nee NCR) WaveLAN card:
+ *	An Ethernet-like radio transceiver
+ *	controlled by an Intel 82586 coprocessor.
+ */
+
+#include "wavelan.p.h"		/* Private header */
+
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (WaveLAN modem or i82586)
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts and locking the driver.
+ * (note : inline, so optimised away)
+ */
+static inline void wv_splhi(net_local *		lp,
+			    unsigned long *	pflags)
+{
+	spin_lock_irqsave(&lp->spinlock, *pflags);
+	/* Note : above does the cli(); itself */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts and un-locking the driver.
+ */
+static inline void wv_splx(net_local *		lp,
+			   unsigned long *	pflags)
+{
+	spin_unlock_irqrestore(&lp->spinlock, *pflags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate irq number to PSA irq parameter
+ */
+static u8 wv_irq_to_psa(int irq)
+{
+	if (irq < 0 || irq >= NELS(irqvals))
+		return 0;
+
+	return irqvals[irq];
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate PSA irq parameter to irq number 
+ */
+static int __init wv_psa_to_irq(u8 irqval)
+{
+	int irq;
+
+	for (irq = 0; irq < NELS(irqvals); irq++)
+		if (irqvals[irq] == irqval)
+			return irq;
+
+	return -1;
+}
+
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *wv_struct_check(void)
+{
+#define	SC(t,s,n)	if (sizeof(t) != s) return(n);
+
+	SC(psa_t, PSA_SIZE, "psa_t");
+	SC(mmw_t, MMW_SIZE, "mmw_t");
+	SC(mmr_t, MMR_SIZE, "mmr_t");
+	SC(ha_t, HA_SIZE, "ha_t");
+
+#undef	SC
+
+	return ((char *) NULL);
+}				/* wv_struct_check */
+#endif				/* STRUCT_CHECK */
+
+/********************* HOST ADAPTER SUBROUTINES *********************/
+/*
+ * Useful subroutines to manage the WaveLAN ISA interface
+ *
+ * One major difference with the PCMCIA hardware (except the port mapping)
+ * is that we have to keep the state of the Host Control Register
+ * because of the interrupt enable & bus size flags.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u16 hasr_read(unsigned long ioaddr)
+{
+	return (inw(HASR(ioaddr)));
+}				/* hasr_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void hacr_write(unsigned long ioaddr, u16 hacr)
+{
+	outw(hacr, HACR(ioaddr));
+}				/* hacr_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void hacr_write_slow(unsigned long ioaddr, u16 hacr)
+{
+	hacr_write(ioaddr, hacr);
+	/* delay might only be needed sometimes */
+	mdelay(1);
+}				/* hacr_write_slow */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the channel attention bit.
+ */
+static inline void set_chan_attn(unsigned long ioaddr, u16 hacr)
+{
+	hacr_write(ioaddr, hacr | HACR_CA);
+}				/* set_chan_attn */
+
+/*------------------------------------------------------------------*/
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static inline void wv_hacr_reset(unsigned long ioaddr)
+{
+	hacr_write_slow(ioaddr, HACR_RESET);
+	hacr_write(ioaddr, HACR_DEFAULT);
+}				/* wv_hacr_reset */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the I/O transfer over the ISA bus to 8-bit mode
+ */
+static inline void wv_16_off(unsigned long ioaddr, u16 hacr)
+{
+	hacr &= ~HACR_16BITS;
+	hacr_write(ioaddr, hacr);
+}				/* wv_16_off */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the I/O transfer over the ISA bus to 8-bit mode
+ */
+static inline void wv_16_on(unsigned long ioaddr, u16 hacr)
+{
+	hacr |= HACR_16BITS;
+	hacr_write(ioaddr, hacr);
+}				/* wv_16_on */
+
+/*------------------------------------------------------------------*/
+/*
+ * Disable interrupts on the WaveLAN hardware.
+ * (called by wv_82586_stop())
+ */
+static inline void wv_ints_off(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	
+	lp->hacr &= ~HACR_INTRON;
+	hacr_write(ioaddr, lp->hacr);
+}				/* wv_ints_off */
+
+/*------------------------------------------------------------------*/
+/*
+ * Enable interrupts on the WaveLAN hardware.
+ * (called by wv_hw_reset())
+ */
+static inline void wv_ints_on(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+
+	lp->hacr |= HACR_INTRON;
+	hacr_write(ioaddr, lp->hacr);
+}				/* wv_ints_on */
+
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Useful subroutines to manage the modem of the WaveLAN
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+/*
+ * Read bytes from the PSA.
+ */
+static void psa_read(unsigned long ioaddr, u16 hacr, int o,	/* offset in PSA */
+		     u8 * b,	/* buffer to fill */
+		     int n)
+{				/* size to read */
+	wv_16_off(ioaddr, hacr);
+
+	while (n-- > 0) {
+		outw(o, PIOR2(ioaddr));
+		o++;
+		*b++ = inb(PIOP2(ioaddr));
+	}
+
+	wv_16_on(ioaddr, hacr);
+}				/* psa_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write the Parameter Storage Area to the WaveLAN card's memory.
+ */
+static void psa_write(unsigned long ioaddr, u16 hacr, int o,	/* Offset in PSA */
+		      u8 * b,	/* Buffer in memory */
+		      int n)
+{				/* Length of buffer */
+	int count = 0;
+
+	wv_16_off(ioaddr, hacr);
+
+	while (n-- > 0) {
+		outw(o, PIOR2(ioaddr));
+		o++;
+
+		outb(*b, PIOP2(ioaddr));
+		b++;
+
+		/* Wait for the memory to finish its write cycle */
+		count = 0;
+		while ((count++ < 100) &&
+		       (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1);
+	}
+
+	wv_16_on(ioaddr, hacr);
+}				/* psa_write */
+
+#ifdef SET_PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static inline u16 psa_crc(u8 * psa,	/* The PSA */
+			      int size)
+{				/* Number of short for CRC */
+	int byte_cnt;		/* Loop on the PSA */
+	u16 crc_bytes = 0;	/* Data in the PSA */
+	int bit_cnt;		/* Loop on the bits of the short */
+
+	for (byte_cnt = 0; byte_cnt < size; byte_cnt++) {
+		crc_bytes ^= psa[byte_cnt];	/* Its an xor */
+
+		for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) {
+			if (crc_bytes & 0x0001)
+				crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+			else
+				crc_bytes >>= 1;
+		}
+	}
+
+	return crc_bytes;
+}				/* psa_crc */
+#endif				/* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void update_psa_checksum(device * dev, unsigned long ioaddr, u16 hacr)
+{
+#ifdef SET_PSA_CRC
+	psa_t psa;
+	u16 crc;
+
+	/* read the parameter storage area */
+	psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+	/* update the checksum */
+	crc = psa_crc((unsigned char *) &psa,
+		      sizeof(psa) - sizeof(psa.psa_crc[0]) -
+		      sizeof(psa.psa_crc[1])
+		      - sizeof(psa.psa_crc_status));
+
+	psa.psa_crc[0] = crc & 0xFF;
+	psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+	/* Write it ! */
+	psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa,
+		  (unsigned char *) &psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+	printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+	       dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+	/* Check again (luxury !) */
+	crc = psa_crc((unsigned char *) &psa,
+		      sizeof(psa) - sizeof(psa.psa_crc_status));
+
+	if (crc != 0)
+		printk(KERN_WARNING
+		       "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n",
+		       dev->name);
+#endif				/* DEBUG_IOCTL_INFO */
+#endif				/* SET_PSA_CRC */
+}				/* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void mmc_out(unsigned long ioaddr, u16 o, u8 d)
+{
+	/* Wait for MMC to go idle */
+	while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
+
+	outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start at the end because it is the way it should be!
+ */
+static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n)
+{
+	o += n;
+	b += n;
+
+	while (n-- > 0)
+		mmc_out(ioaddr, --o, *(--b));
+}				/* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read a byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory.
+ */
+static inline u8 mmc_in(unsigned long ioaddr, u16 o)
+{
+	while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
+	outw(o << 1, MMCR(ioaddr));
+
+	while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
+	return (u8) (inw(MMCR(ioaddr)) >> 8);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start at the end because it is the way it should be!
+ */
+static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n)
+{
+	o += n;
+	b += n;
+
+	while (n-- > 0)
+		*(--b) = mmc_in(ioaddr, --o);
+}				/* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available.
+ */
+static inline int mmc_encr(unsigned long ioaddr)
+{				/* I/O port of the card */
+	int temp;
+
+	temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
+	if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+		return 0;
+	else
+		return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEPROM to complete a command.
+ * I hope this one will be optimally inlined.
+ */
+static inline void fee_wait(unsigned long ioaddr,	/* I/O port of the card */
+			    int delay,	/* Base delay to wait for */
+			    int number)
+{				/* Number of time to wait */
+	int count = 0;		/* Wait only a limited time */
+
+	while ((count++ < number) &&
+	       (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+		MMR_FEE_STATUS_BUSY)) udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEPROM (frequency select cards).
+ */
+static void fee_read(unsigned long ioaddr,	/* I/O port of the card */
+		     u16 o,	/* destination offset */
+		     u16 * b,	/* data buffer */
+		     int n)
+{				/* number of registers */
+	b += n;			/* Position at the end of the area */
+
+	/* Write the address */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+	/* Loop on all buffer */
+	while (n-- > 0) {
+		/* Write the read command */
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+			MMW_FEE_CTRL_READ);
+
+		/* Wait until EEPROM is ready (should be quick). */
+		fee_wait(ioaddr, 10, 100);
+
+		/* Read the value. */
+		*--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
+			mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+	}
+}
+
+#ifdef WIRELESS_EXT		/* if the wireless extension exists in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEPROM (frequency select cards).
+ * This is a bit complicated, because the frequency EEPROM has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void fee_write(unsigned long ioaddr,	/* I/O port of the card */
+		      u16 o,	/* destination offset */
+		      u16 * b,	/* data buffer */
+		      int n)
+{				/* number of registers */
+	b += n;			/* Position at the end of the area. */
+
+#ifdef EEPROM_IS_PROTECTED	/* disabled */
+#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
+	/* Ask to read the protected register */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+	fee_wait(ioaddr, 10, 100);
+
+	/* Read the protected register. */
+	printk("Protected 2:  %02X-%02X\n",
+	       mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
+	       mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+#endif				/* DOESNT_SEEM_TO_WORK */
+
+	/* Enable protected register. */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+	fee_wait(ioaddr, 10, 100);
+
+	/* Unprotect area. */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
+	/* or use: */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif				/* DOESNT_SEEM_TO_WORK */
+
+	fee_wait(ioaddr, 10, 100);
+#endif				/* EEPROM_IS_PROTECTED */
+
+	/* Write enable. */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+	fee_wait(ioaddr, 10, 100);
+
+	/* Write the EEPROM address. */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+	/* Loop on all buffer */
+	while (n-- > 0) {
+		/* Write the value. */
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+		/* Write the write command. */
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+			MMW_FEE_CTRL_WRITE);
+
+		/* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */
+		mdelay(10);
+		fee_wait(ioaddr, 10, 100);
+	}
+
+	/* Write disable. */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
+
+	fee_wait(ioaddr, 10, 100);
+
+#ifdef EEPROM_IS_PROTECTED	/* disabled */
+	/* Reprotect EEPROM. */
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
+	mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+
+	fee_wait(ioaddr, 10, 100);
+#endif				/* EEPROM_IS_PROTECTED */
+}
+#endif				/* WIRELESS_EXT */
+
+/************************ I82586 SUBROUTINES *************************/
+/*
+ * Useful subroutines to manage the Ethernet controller
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the on-board RAM.
+ * Why does inlining this function make it fail?
+ */
+static /*inline */ void obram_read(unsigned long ioaddr,
+				   u16 o, u8 * b, int n)
+{
+	outw(o, PIOR1(ioaddr));
+	insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes to the on-board RAM.
+ */
+static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n)
+{
+	outw(o, PIOR1(ioaddr));
+	outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Acknowledge the reading of the status issued by the i82586.
+ */
+static void wv_ack(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	u16 scb_cs;
+	int i;
+
+	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+		   (unsigned char *) &scb_cs, sizeof(scb_cs));
+	scb_cs &= SCB_ST_INT;
+
+	if (scb_cs == 0)
+		return;
+
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+		    (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+	set_chan_attn(ioaddr, lp->hacr);
+
+	for (i = 1000; i > 0; i--) {
+		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+			   (unsigned char *) &scb_cs, sizeof(scb_cs));
+		if (scb_cs == 0)
+			break;
+
+		udelay(10);
+	}
+	udelay(100);
+
+#ifdef DEBUG_CONFIG_ERROR
+	if (i <= 0)
+		printk(KERN_INFO
+		       "%s: wv_ack(): board not accepting command.\n",
+		       dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge completion of the command.
+ */
+static inline int wv_synchronous_cmd(device * dev, const char *str)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	u16 scb_cmd;
+	ach_t cb;
+	int i;
+
+	scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+		    (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+
+	set_chan_attn(ioaddr, lp->hacr);
+
+	for (i = 1000; i > 0; i--) {
+		obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb,
+			   sizeof(cb));
+		if (cb.ac_status & AC_SFLD_C)
+			break;
+
+		udelay(10);
+	}
+	udelay(100);
+
+	if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
+		       dev->name, str, cb.ac_status);
+#endif
+#ifdef DEBUG_I82586_SHOW
+		wv_scb_show(ioaddr);
+#endif
+		return -1;
+	}
+
+	/* Ack the status */
+	wv_ack(dev);
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Configuration commands completion interrupt.
+ * Check if done, and if OK.
+ */
+static inline int
+wv_config_complete(device * dev, unsigned long ioaddr, net_local * lp)
+{
+	unsigned short mcs_addr;
+	unsigned short status;
+	int ret;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
+#endif
+
+	mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
+	    + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
+
+	/* Read the status of the last command (set mc list). */
+	obram_read(ioaddr, acoff(mcs_addr, ac_status),
+		   (unsigned char *) &status, sizeof(status));
+
+	/* If not completed -> exit */
+	if ((status & AC_SFLD_C) == 0)
+		ret = 0;	/* Not ready to be scrapped */
+	else {
+#ifdef DEBUG_CONFIG_ERROR
+		unsigned short cfg_addr;
+		unsigned short ias_addr;
+
+		/* Check mc_config command */
+		if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+			printk(KERN_INFO
+			       "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
+			       dev->name, status);
+
+		/* check ia-config command */
+		ias_addr = mcs_addr - sizeof(ac_ias_t);
+		obram_read(ioaddr, acoff(ias_addr, ac_status),
+			   (unsigned char *) &status, sizeof(status));
+		if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+			printk(KERN_INFO
+			       "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n",
+			       dev->name, status);
+
+		/* Check config command. */
+		cfg_addr = ias_addr - sizeof(ac_cfg_t);
+		obram_read(ioaddr, acoff(cfg_addr, ac_status),
+			   (unsigned char *) &status, sizeof(status));
+		if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+			printk(KERN_INFO
+			       "%s: wv_config_complete(): configure failed; status = 0x%x\n",
+			       dev->name, status);
+#endif	/* DEBUG_CONFIG_ERROR */
+
+		ret = 1;	/* Ready to be scrapped */
+	}
+
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name,
+	       ret);
+#endif
+	return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ * (called in wavelan_interrupt()).
+ * Note : the spinlock is already grabbed for us.
+ */
+static int wv_complete(device * dev, unsigned long ioaddr, net_local * lp)
+{
+	int nreaped = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
+#endif
+
+	/* Loop on all the transmit buffers */
+	while (lp->tx_first_in_use != I82586NULL) {
+		unsigned short tx_status;
+
+		/* Read the first transmit buffer */
+		obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status),
+			   (unsigned char *) &tx_status,
+			   sizeof(tx_status));
+
+		/* If not completed -> exit */
+		if ((tx_status & AC_SFLD_C) == 0)
+			break;
+
+		/* Hack for reconfiguration */
+		if (tx_status == 0xFFFF)
+			if (!wv_config_complete(dev, ioaddr, lp))
+				break;	/* Not completed */
+
+		/* We now remove this buffer */
+		nreaped++;
+		--lp->tx_n_in_use;
+
+/*
+if (lp->tx_n_in_use > 0)
+	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+		/* Was it the last one? */
+		if (lp->tx_n_in_use <= 0)
+			lp->tx_first_in_use = I82586NULL;
+		else {
+			/* Next one in the chain */
+			lp->tx_first_in_use += TXBLOCKZ;
+			if (lp->tx_first_in_use >=
+			    OFFSET_CU +
+			    NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -=
+				    NTXBLOCKS * TXBLOCKZ;
+		}
+
+		/* Hack for reconfiguration */
+		if (tx_status == 0xFFFF)
+			continue;
+
+		/* Now, check status of the finished command */
+		if (tx_status & AC_SFLD_OK) {
+			int ncollisions;
+
+			lp->stats.tx_packets++;
+			ncollisions = tx_status & AC_SFLD_MAXCOL;
+			lp->stats.collisions += ncollisions;
+#ifdef DEBUG_TX_INFO
+			if (ncollisions > 0)
+				printk(KERN_DEBUG
+				       "%s: wv_complete(): tx completed after %d collisions.\n",
+				       dev->name, ncollisions);
+#endif
+		} else {
+			lp->stats.tx_errors++;
+			if (tx_status & AC_SFLD_S10) {
+				lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_TX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_complete(): tx error: no CS.\n",
+				       dev->name);
+#endif
+			}
+			if (tx_status & AC_SFLD_S9) {
+				lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_TX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_complete(): tx error: lost CTS.\n",
+				       dev->name);
+#endif
+			}
+			if (tx_status & AC_SFLD_S8) {
+				lp->stats.tx_fifo_errors++;
+#ifdef DEBUG_TX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_complete(): tx error: slow DMA.\n",
+				       dev->name);
+#endif
+			}
+			if (tx_status & AC_SFLD_S6) {
+				lp->stats.tx_heartbeat_errors++;
+#ifdef DEBUG_TX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_complete(): tx error: heart beat.\n",
+				       dev->name);
+#endif
+			}
+			if (tx_status & AC_SFLD_S5) {
+				lp->stats.tx_aborted_errors++;
+#ifdef DEBUG_TX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_complete(): tx error: too many collisions.\n",
+				       dev->name);
+#endif
+			}
+		}
+
+#ifdef DEBUG_TX_INFO
+		printk(KERN_DEBUG
+		       "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
+		       dev->name, tx_status);
+#endif
+	}
+
+#ifdef DEBUG_INTERRUPT_INFO
+	if (nreaped > 1)
+		printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n",
+		       dev->name, nreaped);
+#endif
+
+	/*
+	 * Inform upper layers.
+	 */
+	if (lp->tx_n_in_use < NTXBLOCKS - 1) {
+		netif_wake_queue(dev);
+	}
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
+#endif
+	return nreaped;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82586, or at least ask for it.
+ * Because wv_82586_config uses a transmission buffer, we must do it
+ * when we are sure that there is one left, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option), so you may experience
+ * delays sometimes.
+ */
+static inline void wv_82586_reconfig(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long flags;
+
+	/* Arm the flag, will be cleard in wv_82586_config() */
+	lp->reconfig_82586 = 1;
+
+	/* Check if we can do it now ! */
+	if((netif_running(dev)) && !(netif_queue_stopped(dev))) {
+		wv_splhi(lp, &flags);
+		/* May fail */
+		wv_82586_config(dev);
+		wv_splx(lp, &flags);
+	}
+	else {
+#ifdef DEBUG_CONFIG_INFO
+		printk(KERN_DEBUG
+		       "%s: wv_82586_reconfig(): delayed (state = %lX)\n",
+			       dev->name, dev->state);
+#endif
+	}
+}
+
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routine is used in the code to show information for debugging.
+ * Most of the time, it dumps the contents of hardware structures.
+ */
+
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void wv_psa_show(psa_t * p)
+{
+	printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n");
+	printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+	       p->psa_io_base_addr_1,
+	       p->psa_io_base_addr_2,
+	       p->psa_io_base_addr_3, p->psa_io_base_addr_4);
+	printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+	       p->psa_rem_boot_addr_1,
+	       p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3);
+	printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+	printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+	printk(KERN_DEBUG
+	       "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	       p->psa_unused0[0], p->psa_unused0[1], p->psa_unused0[2],
+	       p->psa_unused0[3], p->psa_unused0[4], p->psa_unused0[5],
+	       p->psa_unused0[6]);
+#endif				/* DEBUG_SHOW_UNUSED */
+	printk(KERN_DEBUG
+	       "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       p->psa_univ_mac_addr[0], p->psa_univ_mac_addr[1],
+	       p->psa_univ_mac_addr[2], p->psa_univ_mac_addr[3],
+	       p->psa_univ_mac_addr[4], p->psa_univ_mac_addr[5]);
+	printk(KERN_DEBUG
+	       "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       p->psa_local_mac_addr[0], p->psa_local_mac_addr[1],
+	       p->psa_local_mac_addr[2], p->psa_local_mac_addr[3],
+	       p->psa_local_mac_addr[4], p->psa_local_mac_addr[5]);
+	printk(KERN_DEBUG "psa_univ_local_sel: %d, ",
+	       p->psa_univ_local_sel);
+	printk("psa_comp_number: %d, ", p->psa_comp_number);
+	printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+	printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+	       p->psa_feature_select);
+	printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+	printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+	printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+	printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0],
+	       p->psa_nwid[1]);
+	printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+	printk(KERN_DEBUG "psa_encryption_select: %d, ",
+	       p->psa_encryption_select);
+	printk
+	    ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	     p->psa_encryption_key[0], p->psa_encryption_key[1],
+	     p->psa_encryption_key[2], p->psa_encryption_key[3],
+	     p->psa_encryption_key[4], p->psa_encryption_key[5],
+	     p->psa_encryption_key[6], p->psa_encryption_key[7]);
+	printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+	printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+	       p->psa_call_code[0]);
+	printk
+	    ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	     p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2],
+	     p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5],
+	     p->psa_call_code[6], p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+	printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+	       p->psa_reserved[0],
+	       p->psa_reserved[1], p->psa_reserved[2], p->psa_reserved[3]);
+#endif				/* DEBUG_SHOW_UNUSED */
+	printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+	printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+	printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+}				/* wv_psa_show */
+#endif				/* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function needs to be completed.
+ */
+static void wv_mmc_show(device * dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	net_local *lp = (net_local *) dev->priv;
+	mmr_t m;
+
+	/* Basic check */
+	if (hasr_read(ioaddr) & HASR_NO_CLK) {
+		printk(KERN_WARNING
+		       "%s: wv_mmc_show: modem not connected\n",
+		       dev->name);
+		return;
+	}
+
+	/* Read the mmc */
+	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+	mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m));
+	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT		/* if wireless extension exists in the kernel */
+	/* Don't forget to update statistics */
+	lp->wstats.discard.nwid +=
+	    (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif				/* WIRELESS_EXT */
+
+	printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+	printk(KERN_DEBUG
+	       "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	       m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2],
+	       m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5],
+	       m.mmr_unused0[6], m.mmr_unused0[7]);
+#endif				/* DEBUG_SHOW_UNUSED */
+	printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n",
+	       m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+	printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+	       m.mmr_unused1[0],
+	       m.mmr_unused1[1],
+	       m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]);
+#endif				/* DEBUG_SHOW_UNUSED */
+	printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+	       m.mmr_dce_status,
+	       (m.
+		mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ?
+	       "energy detected," : "",
+	       (m.
+		mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+	       "loop test indicated," : "",
+	       (m.
+		mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ?
+	       "transmitter on," : "",
+	       (m.
+		mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+	       "jabber timer expired," : "");
+	printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+	printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+	       m.mmr_unused2[0], m.mmr_unused2[1]);
+#endif				/* DEBUG_SHOW_UNUSED */
+	printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+	       (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+	       (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+	printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+	       m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+	       (m.
+		mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" :
+	       "below");
+	printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+	       m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+	       (m.
+		mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" :
+	       "no new msg");
+	printk("silence_lvl: %d [%s], ",
+	       m.mmr_silence_lvl & MMR_SILENCE_LVL,
+	       (m.
+		mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" :
+	       "no new update");
+	printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+	       (m.
+		mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" :
+	       "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+	printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif				/* DEBUG_SHOW_UNUSED */
+}				/* wv_mmc_show */
+#endif				/* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82586_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the last block of the i82586 memory.
+ */
+static void wv_scb_show(unsigned long ioaddr)
+{
+	scb_t scb;
+
+	obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+		   sizeof(scb));
+
+	printk(KERN_DEBUG "##### WaveLAN system control block: #####\n");
+
+	printk(KERN_DEBUG "status: ");
+	printk("stat 0x%x[%s%s%s%s] ",
+	       (scb.
+		scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA |
+			      SCB_ST_RNR)) >> 12,
+	       (scb.
+		scb_status & SCB_ST_CX) ? "command completion interrupt," :
+	       "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+	       (scb.
+		scb_status & SCB_ST_CNA) ? "command unit not active," : "",
+	       (scb.
+		scb_status & SCB_ST_RNR) ? "receiving unit not ready," :
+	       "");
+	printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8,
+	       ((scb.scb_status & SCB_ST_CUS) ==
+		SCB_ST_CUS_IDLE) ? "idle" : "",
+	       ((scb.scb_status & SCB_ST_CUS) ==
+		SCB_ST_CUS_SUSP) ? "suspended" : "",
+	       ((scb.scb_status & SCB_ST_CUS) ==
+		SCB_ST_CUS_ACTV) ? "active" : "");
+	printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4,
+	       ((scb.scb_status & SCB_ST_RUS) ==
+		SCB_ST_RUS_IDLE) ? "idle" : "",
+	       ((scb.scb_status & SCB_ST_RUS) ==
+		SCB_ST_RUS_SUSP) ? "suspended" : "",
+	       ((scb.scb_status & SCB_ST_RUS) ==
+		SCB_ST_RUS_NRES) ? "no resources" : "",
+	       ((scb.scb_status & SCB_ST_RUS) ==
+		SCB_ST_RUS_RDY) ? "ready" : "");
+
+	printk(KERN_DEBUG "command: ");
+	printk("ack 0x%x[%s%s%s%s] ",
+	       (scb.
+		scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR |
+			       SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+	       (scb.
+		scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+	       (scb.
+		scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+	       (scb.
+		scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+	       (scb.
+		scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : "");
+	printk("cuc 0x%x[%s%s%s%s%s] ",
+	       (scb.scb_command & SCB_CMD_CUC) >> 8,
+	       ((scb.scb_command & SCB_CMD_CUC) ==
+		SCB_CMD_CUC_NOP) ? "nop" : "",
+	       ((scb.scb_command & SCB_CMD_CUC) ==
+		SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+	       ((scb.scb_command & SCB_CMD_CUC) ==
+		SCB_CMD_CUC_RES) ? "resume execution" : "",
+	       ((scb.scb_command & SCB_CMD_CUC) ==
+		SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+	       ((scb.scb_command & SCB_CMD_CUC) ==
+		SCB_CMD_CUC_ABT) ? "abort execution" : "");
+	printk("ruc 0x%x[%s%s%s%s%s]\n",
+	       (scb.scb_command & SCB_CMD_RUC) >> 4,
+	       ((scb.scb_command & SCB_CMD_RUC) ==
+		SCB_CMD_RUC_NOP) ? "nop" : "",
+	       ((scb.scb_command & SCB_CMD_RUC) ==
+		SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+	       ((scb.scb_command & SCB_CMD_RUC) ==
+		SCB_CMD_RUC_RES) ? "resume reception" : "",
+	       ((scb.scb_command & SCB_CMD_RUC) ==
+		SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+	       ((scb.scb_command & SCB_CMD_RUC) ==
+		SCB_CMD_RUC_ABT) ? "abort reception" : "");
+
+	printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset);
+	printk("rfa_offset 0x%x\n", scb.scb_rfa_offset);
+
+	printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs);
+	printk("alnerrs %d ", scb.scb_alnerrs);
+	printk("rscerrs %d ", scb.scb_rscerrs);
+	printk("ovrnerrs %d\n", scb.scb_ovrnerrs);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the i82586's receive unit.
+ */
+static void wv_ru_show(device * dev)
+{
+	/* net_local *lp = (net_local *) dev->priv; */
+
+	printk(KERN_DEBUG
+	       "##### WaveLAN i82586 receiver unit status: #####\n");
+	printk(KERN_DEBUG "ru:");
+	/*
+	 * Not implemented yet
+	 */
+	printk("\n");
+}				/* wv_ru_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Display info about one control block of the i82586 memory.
+ */
+static void wv_cu_show_one(device * dev, net_local * lp, int i, u16 p)
+{
+	unsigned long ioaddr;
+	ac_tx_t actx;
+
+	ioaddr = dev->base_addr;
+
+	printk("%d: 0x%x:", i, p);
+
+	obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx));
+	printk(" status=0x%x,", actx.tx_h.ac_status);
+	printk(" command=0x%x,", actx.tx_h.ac_command);
+
+	/*
+	   {
+	   tbd_t      tbd;
+
+	   obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+	   printk(" tbd_status=0x%x,", tbd.tbd_status);
+	   }
+	 */
+
+	printk("|");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print status of the command unit of the i82586.
+ */
+static void wv_cu_show(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned int i;
+	u16 p;
+
+	printk(KERN_DEBUG
+	       "##### WaveLAN i82586 command unit status: #####\n");
+
+	printk(KERN_DEBUG);
+	for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) {
+		wv_cu_show_one(dev, lp, i, p);
+
+		p += TXBLOCKZ;
+		if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+			p -= NTXBLOCKS * TXBLOCKZ;
+	}
+	printk("\n");
+}
+#endif				/* DEBUG_I82586_SHOW */
+
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
+ */
+static void wv_dev_show(device * dev)
+{
+	printk(KERN_DEBUG "dev:");
+	printk(" state=%lX,", dev->state);
+	printk(" trans_start=%ld,", dev->trans_start);
+	printk(" flags=0x%x,", dev->flags);
+	printk("\n");
+}				/* wv_dev_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void wv_local_show(device * dev)
+{
+	net_local *lp;
+
+	lp = (net_local *) dev->priv;
+
+	printk(KERN_DEBUG "local:");
+	printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+	printk(" hacr=0x%x,", lp->hacr);
+	printk(" rx_head=0x%x,", lp->rx_head);
+	printk(" rx_last=0x%x,", lp->rx_last);
+	printk(" tx_first_free=0x%x,", lp->tx_first_free);
+	printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+	printk("\n");
+}				/* wv_local_show */
+#endif				/* DEBUG_DEVICE_SHOW */
+
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void wv_packet_info(u8 * p,	/* Packet to dump */
+				  int length,	/* Length of the packet */
+				  char *msg1,	/* Name of the device */
+				  char *msg2)
+{				/* Name of the function */
+	int i;
+	int maxi;
+
+	printk(KERN_DEBUG
+	       "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+	       msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+	printk(KERN_DEBUG
+	       "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+	       msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12],
+	       p[13]);
+
+#ifdef DEBUG_PACKET_DUMP
+
+	printk(KERN_DEBUG "data=\"");
+
+	if ((maxi = length) > DEBUG_PACKET_DUMP)
+		maxi = DEBUG_PACKET_DUMP;
+	for (i = 14; i < maxi; i++)
+		if (p[i] >= ' ' && p[i] <= '~')
+			printk(" %c", p[i]);
+		else
+			printk("%02X", p[i]);
+	if (maxi < length)
+		printk("..");
+	printk("\"\n");
+	printk(KERN_DEBUG "\n");
+#endif				/* DEBUG_PACKET_DUMP */
+}
+#endif				/* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
+
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup.
+ * There are lots of flags for configuring it to your liking.
+ */
+static inline void wv_init_info(device * dev)
+{
+	short ioaddr = dev->base_addr;
+	net_local *lp = (net_local *) dev->priv;
+	psa_t psa;
+	int i;
+
+	/* Read the parameter storage area */
+	psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+	wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+	wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+	wv_cu_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+	/* Now, let's go for the basic stuff. */
+	printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr);
+	for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+		printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+	printk(", IRQ %d", dev->irq);
+
+	/* Print current network ID. */
+	if (psa.psa_nwid_select)
+		printk(", nwid 0x%02X-%02X", psa.psa_nwid[0],
+		       psa.psa_nwid[1]);
+	else
+		printk(", nwid off");
+
+	/* If 2.00 card */
+	if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+	      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+		unsigned short freq;
+
+		/* Ask the EEPROM to read the frequency from the first area. */
+		fee_read(ioaddr, 0x00, &freq, 1);
+
+		/* Print frequency */
+		printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+		/* Hack! */
+		if (freq & 0x20)
+			printk(".5");
+	} else {
+		printk(", PC");
+		switch (psa.psa_comp_number) {
+		case PSA_COMP_PC_AT_915:
+		case PSA_COMP_PC_AT_2400:
+			printk("-AT");
+			break;
+		case PSA_COMP_PC_MC_915:
+		case PSA_COMP_PC_MC_2400:
+			printk("-MC");
+			break;
+		case PSA_COMP_PCMCIA_915:
+			printk("MCIA");
+			break;
+		default:
+			printk("?");
+		}
+		printk(", ");
+		switch (psa.psa_subband) {
+		case PSA_SUBBAND_915:
+			printk("915");
+			break;
+		case PSA_SUBBAND_2425:
+			printk("2425");
+			break;
+		case PSA_SUBBAND_2460:
+			printk("2460");
+			break;
+		case PSA_SUBBAND_2484:
+			printk("2484");
+			break;
+		case PSA_SUBBAND_2430_5:
+			printk("2430.5");
+			break;
+		default:
+			printk("?");
+		}
+	}
+
+	printk(" MHz\n");
+#endif				/* DEBUG_BASIC_SHOW */
+
+#ifdef DEBUG_VERSION_SHOW
+	/* Print version information */
+	printk(KERN_NOTICE "%s", version);
+#endif
+}				/* wv_init_info */
+
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on different
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the current Ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats *wavelan_get_stats(device * dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
+
+	return (&((net_local *) dev->priv)->stats);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+static void wavelan_set_multicast_list(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n",
+	       dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+	printk(KERN_DEBUG
+	       "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+	       dev->name, dev->flags, dev->mc_count);
+#endif
+
+	/* Are we asking for promiscuous mode,
+	 * or all multicast addresses (we don't have that!)
+	 * or too many multicast addresses for the hardware filter? */
+	if ((dev->flags & IFF_PROMISC) ||
+	    (dev->flags & IFF_ALLMULTI) ||
+	    (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) {
+		/*
+		 * Enable promiscuous mode: receive all packets.
+		 */
+		if (!lp->promiscuous) {
+			lp->promiscuous = 1;
+			lp->mc_count = 0;
+
+			wv_82586_reconfig(dev);
+
+			/* Tell the kernel that we are doing a really bad job. */
+			dev->flags |= IFF_PROMISC;
+		}
+	} else
+		/* Are there multicast addresses to send? */
+	if (dev->mc_list != (struct dev_mc_list *) NULL) {
+		/*
+		 * Disable promiscuous mode, but receive all packets
+		 * in multicast list
+		 */
+#ifdef MULTICAST_AVOID
+		if (lp->promiscuous || (dev->mc_count != lp->mc_count))
+#endif
+		{
+			lp->promiscuous = 0;
+			lp->mc_count = dev->mc_count;
+
+			wv_82586_reconfig(dev);
+		}
+	} else {
+		/*
+		 * Switch to normal mode: disable promiscuous mode and 
+		 * clear the multicast list.
+		 */
+		if (lp->promiscuous || lp->mc_count == 0) {
+			lp->promiscuous = 0;
+			lp->mc_count = 0;
+
+			wv_82586_reconfig(dev);
+		}
+	}
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n",
+	       dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This function doesn't exist.
+ * (Note : it was a nice way to test the reconfigure stuff...)
+ */
+#ifdef SET_MAC_ADDRESS
+static int wavelan_set_mac_address(device * dev, void *addr)
+{
+	struct sockaddr *mac = addr;
+
+	/* Copy the address. */
+	memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
+
+	/* Reconfigure the beast. */
+	wv_82586_reconfig(dev);
+
+	return 0;
+}
+#endif				/* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT		/* if wireless extensions exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware capable of it)
+ * It's a bit complicated and you don't really want to look into it.
+ * (called in wavelan_ioctl)
+ */
+static inline int wv_set_frequency(unsigned long ioaddr,	/* I/O port of the card */
+				   iw_freq * frequency)
+{
+	const int BAND_NUM = 10;	/* Number of bands */
+	long freq = 0L;		/* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+	int i;
+#endif
+
+	/* Setting by frequency */
+	/* Theoretically, you may set any frequency between
+	 * the two limits with a 0.5 MHz precision. In practice,
+	 * I don't want you to have trouble with local regulations.
+	 */
+	if ((frequency->e == 1) &&
+	    (frequency->m >= (int) 2.412e8)
+	    && (frequency->m <= (int) 2.487e8)) {
+		freq = ((frequency->m / 10000) - 24000L) / 5;
+	}
+
+	/* Setting by channel (same as wfreqsel) */
+	/* Warning: each channel is 22 MHz wide, so some of the channels
+	 * will interfere. */
+	if ((frequency->e == 0) && (frequency->m < BAND_NUM)) {
+		/* Get frequency offset. */
+		freq = channel_bands[frequency->m] >> 1;
+	}
+
+	/* Verify that the frequency is allowed. */
+	if (freq != 0L) {
+		u16 table[10];	/* Authorized frequency table */
+
+		/* Read the frequency table. */
+		fee_read(ioaddr, 0x71, table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+		printk(KERN_DEBUG "Frequency table: ");
+		for (i = 0; i < 10; i++) {
+			printk(" %04X", table[i]);
+		}
+		printk("\n");
+#endif
+
+		/* Look in the table to see whether the frequency is allowed. */
+		if (!(table[9 - ((freq - 24) / 16)] &
+		      (1 << ((freq - 24) % 16)))) return -EINVAL;	/* not allowed */
+	} else
+		return -EINVAL;
+
+	/* if we get a usable frequency */
+	if (freq != 0L) {
+		unsigned short area[16];
+		unsigned short dac[2];
+		unsigned short area_verify[16];
+		unsigned short dac_verify[2];
+		/* Corresponding gain (in the power adjust value table)
+		 * See AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8
+		 * and WCIN062D.DOC, page 6.2.9. */
+		unsigned short power_limit[] = { 40, 80, 120, 160, 0 };
+		int power_band = 0;	/* Selected band */
+		unsigned short power_adjust;	/* Correct value */
+
+		/* Search for the gain. */
+		power_band = 0;
+		while ((freq > power_limit[power_band]) &&
+		       (power_limit[++power_band] != 0));
+
+		/* Read the first area. */
+		fee_read(ioaddr, 0x00, area, 16);
+
+		/* Read the DAC. */
+		fee_read(ioaddr, 0x60, dac, 2);
+
+		/* Read the new power adjust value. */
+		fee_read(ioaddr, 0x6B - (power_band >> 1), &power_adjust,
+			 1);
+		if (power_band & 0x1)
+			power_adjust >>= 8;
+		else
+			power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+		printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
+		for (i = 0; i < 16; i++) {
+			printk(" %04X", area[i]);
+		}
+		printk("\n");
+
+		printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
+		       dac[0], dac[1]);
+#endif
+
+		/* Frequency offset (for info only) */
+		area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
+
+		/* Receiver Principle main divider coefficient */
+		area[3] = (freq >> 1) + 2400L - 352L;
+		area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+		/* Transmitter Main divider coefficient */
+		area[13] = (freq >> 1) + 2400L;
+		area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+		/* Other parts of the area are flags, bit streams or unused. */
+
+		/* Set the value in the DAC. */
+		dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+		dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
+
+		/* Write the first area. */
+		fee_write(ioaddr, 0x00, area, 16);
+
+		/* Write the DAC. */
+		fee_write(ioaddr, 0x60, dac, 2);
+
+		/* We now should verify here that the writing of the EEPROM went OK. */
+
+		/* Reread the first area. */
+		fee_read(ioaddr, 0x00, area_verify, 16);
+
+		/* Reread the DAC. */
+		fee_read(ioaddr, 0x60, dac_verify, 2);
+
+		/* Compare. */
+		if (memcmp(area, area_verify, 16 * 2) ||
+		    memcmp(dac, dac_verify, 2 * 2)) {
+#ifdef DEBUG_IOCTL_ERROR
+			printk(KERN_INFO
+			       "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n");
+#endif
+			return -EOPNOTSUPP;
+		}
+
+		/* We must download the frequency parameters to the
+		 * synthesizers (from the EEPROM - area 1)
+		 * Note: as the EEPROM is automatically decremented, we set the end
+		 * if the area... */
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+			MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+		/* Wait until the download is finished. */
+		fee_wait(ioaddr, 100, 100);
+
+		/* We must now download the power adjust value (gain) to
+		 * the synthesizers (from the EEPROM - area 7 - DAC). */
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
+		mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+			MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+		/* Wait for the download to finish. */
+		fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+		/* Verification of what we have done */
+
+		printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
+		for (i = 0; i < 16; i++) {
+			printk(" %04X", area_verify[i]);
+		}
+		printk("\n");
+
+		printk(KERN_DEBUG "WaveLAN EEPROM DAC:  %04X %04X\n",
+		       dac_verify[0], dac_verify[1]);
+#endif
+
+		return 0;
+	} else
+		return -EINVAL;	/* Bah, never get there... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Give the list of available frequencies.
+ */
+static inline int wv_frequency_list(unsigned long ioaddr,	/* I/O port of the card */
+				    iw_freq * list,	/* List of frequencies to fill */
+				    int max)
+{				/* Maximum number of frequencies */
+	u16 table[10];	/* Authorized frequency table */
+	long freq = 0L;		/* offset to 2.4 GHz in .5 MHz + 12 MHz */
+	int i;			/* index in the table */
+	int c = 0;		/* Channel number */
+
+	/* Read the frequency table. */
+	fee_read(ioaddr, 0x71 /* frequency table */ , table, 10);
+
+	/* Check all frequencies. */
+	i = 0;
+	for (freq = 0; freq < 150; freq++)
+		/* Look in the table if the frequency is allowed */
+		if (table[9 - (freq / 16)] & (1 << (freq % 16))) {
+			/* Compute approximate channel number */
+			while ((((channel_bands[c] >> 1) - 24) < freq) &&
+			       (c < NELS(channel_bands)))
+				c++;
+			list[i].i = c;	/* Set the list index */
+
+			/* put in the list */
+			list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
+			list[i++].e = 1;
+
+			/* Check number. */
+			if (i >= max)
+				return (i);
+		}
+
+	return (i);
+}
+
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
+/*
+ * Gather wireless spy statistics:  for each packet, compare the source
+ * address with our list, and if they match, get the statistics.
+ * Sorry, but this function really needs the wireless extensions.
+ */
+static inline void wl_spy_gather(device * dev, u8 * mac,	/* MAC address */
+				 u8 * stats)
+{				/* Statistics to gather */
+	net_local *lp = (net_local *) dev->priv;
+	int i;
+
+	/* Check all addresses. */
+	for (i = 0; i < lp->spy_number; i++)
+		/* If match */
+		if (!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE)) {
+			/* Update statistics */
+			lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+			lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+			lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+			lp->spy_stat[i].updated = 0x7;
+		}
+}
+#endif				/* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculates a histogram of the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one WaveLAN is really weak,
+ * or you may also calculate the mean and standard deviation of the level.
+ */
+static inline void wl_his_gather(device * dev, u8 * stats)
+{				/* Statistics to gather */
+	net_local *lp = (net_local *) dev->priv;
+	u8 level = stats[0] & MMR_SIGNAL_LVL;
+	int i;
+
+	/* Find the correct interval. */
+	i = 0;
+	while ((i < (lp->his_number - 1))
+	       && (level >= lp->his_range[i++]));
+
+	/* Increment interval counter. */
+	(lp->his_sum[i])++;
+}
+#endif				/* HISTOGRAM */
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl for configuration and information.
+ * It is here that the wireless extensions are treated (iwconfig).
+ */
+static int wavelan_ioctl(struct net_device *dev,	/* device on which the ioctl is applied */
+			 struct ifreq *rq,	/* data passed */
+			 int cmd)
+{				/* ioctl number */
+	unsigned long ioaddr = dev->base_addr;
+	net_local *lp = (net_local *) dev->priv;	/* lp is not unused */
+	struct iwreq *wrq = (struct iwreq *) rq;
+	psa_t psa;
+	mm_t m;
+	unsigned long flags;
+	int ret = 0;
+	int err = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name,
+	       cmd);
+#endif
+
+	/* Disable interrupts and save flags. */
+	wv_splhi(lp, &flags);
+	
+	/* Look what is the request */
+	switch (cmd) {
+		/* --------------- WIRELESS EXTENSIONS --------------- */
+
+	case SIOCGIWNAME:
+		strcpy(wrq->u.name, "WaveLAN");
+		break;
+
+	case SIOCSIWNWID:
+		/* Set NWID in WaveLAN. */
+		if (!wrq->u.nwid.disabled) {
+			/* Set NWID in psa */
+			psa.psa_nwid[0] =
+			    (wrq->u.nwid.value & 0xFF00) >> 8;
+			psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
+			psa.psa_nwid_select = 0x01;
+			psa_write(ioaddr, lp->hacr,
+				  (char *) psa.psa_nwid - (char *) &psa,
+				  (unsigned char *) psa.psa_nwid, 3);
+
+			/* Set NWID in mmc. */
+			m.w.mmw_netw_id_l = psa.psa_nwid[1];
+			m.w.mmw_netw_id_h = psa.psa_nwid[0];
+			mmc_write(ioaddr,
+				  (char *) &m.w.mmw_netw_id_l -
+				  (char *) &m,
+				  (unsigned char *) &m.w.mmw_netw_id_l, 2);
+			mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
+		} else {
+			/* Disable NWID in the psa. */
+			psa.psa_nwid_select = 0x00;
+			psa_write(ioaddr, lp->hacr,
+				  (char *) &psa.psa_nwid_select -
+				  (char *) &psa,
+				  (unsigned char *) &psa.psa_nwid_select,
+				  1);
+
+			/* Disable NWID in the mmc (no filtering). */
+			mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel),
+				MMW_LOOPT_SEL_DIS_NWID);
+		}
+		/* update the Wavelan checksum */
+		update_psa_checksum(dev, ioaddr, lp->hacr);
+		break;
+
+	case SIOCGIWNWID:
+		/* Read the NWID. */
+		psa_read(ioaddr, lp->hacr,
+			 (char *) psa.psa_nwid - (char *) &psa,
+			 (unsigned char *) psa.psa_nwid, 3);
+		wrq->u.nwid.value =
+		    (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+		wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+		wrq->u.nwid.fixed = 1;	/* Superfluous */
+		break;
+
+	case SIOCSIWFREQ:
+		/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
+		if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+		      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+			ret = wv_set_frequency(ioaddr, &(wrq->u.freq));
+		else
+			ret = -EOPNOTSUPP;
+		break;
+
+	case SIOCGIWFREQ:
+		/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable).
+		 * Does it work for everybody, especially old cards? */
+		if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+		      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+			unsigned short freq;
+
+			/* Ask the EEPROM to read the frequency from the first area. */
+			fee_read(ioaddr, 0x00, &freq, 1);
+			wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
+			wrq->u.freq.e = 1;
+		} else {
+			psa_read(ioaddr, lp->hacr,
+				 (char *) &psa.psa_subband - (char *) &psa,
+				 (unsigned char *) &psa.psa_subband, 1);
+
+			if (psa.psa_subband <= 4) {
+				wrq->u.freq.m =
+				    fixed_bands[psa.psa_subband];
+				wrq->u.freq.e = (psa.psa_subband != 0);
+			} else
+				ret = -EOPNOTSUPP;
+		}
+		break;
+
+	case SIOCSIWSENS:
+		/* Set the level threshold. */
+		/* We should complain loudly if wrq->u.sens.fixed = 0, because we
+		 * can't set auto mode... */
+		psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
+		psa_write(ioaddr, lp->hacr,
+			  (char *) &psa.psa_thr_pre_set - (char *) &psa,
+			  (unsigned char *) &psa.psa_thr_pre_set, 1);
+		/* update the Wavelan checksum */
+		update_psa_checksum(dev, ioaddr, lp->hacr);
+		mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set),
+			psa.psa_thr_pre_set);
+		break;
+
+	case SIOCGIWSENS:
+		/* Read the level threshold. */
+		psa_read(ioaddr, lp->hacr,
+			 (char *) &psa.psa_thr_pre_set - (char *) &psa,
+			 (unsigned char *) &psa.psa_thr_pre_set, 1);
+		wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
+		wrq->u.sens.fixed = 1;
+		break;
+
+	case SIOCSIWENCODE:
+		/* Set encryption key */
+		if (!mmc_encr(ioaddr)) {
+			ret = -EOPNOTSUPP;
+			break;
+		}
+
+		/* Basic checking... */
+		if (wrq->u.encoding.pointer != (caddr_t) 0) {
+			/* Check the size of the key */
+			if (wrq->u.encoding.length != 8) {
+				ret = -EINVAL;
+				break;
+			}
+
+			/* Copy the key in the driver */
+			wv_splx(lp, &flags);
+			err = copy_from_user(psa.psa_encryption_key,
+					     wrq->u.encoding.pointer,
+					     wrq->u.encoding.length);
+			wv_splhi(lp, &flags);
+			if (err) {
+				ret = -EFAULT;
+				break;
+			}
+
+			psa.psa_encryption_select = 1;
+			psa_write(ioaddr, lp->hacr,
+				  (char *) &psa.psa_encryption_select -
+				  (char *) &psa,
+				  (unsigned char *) &psa.
+				  psa_encryption_select, 8 + 1);
+
+			mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
+				MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+			mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
+				  (unsigned char *) &psa.
+				  psa_encryption_key, 8);
+		}
+
+		if (wrq->u.encoding.flags & IW_ENCODE_DISABLED) {	/* disable encryption */
+			psa.psa_encryption_select = 0;
+			psa_write(ioaddr, lp->hacr,
+				  (char *) &psa.psa_encryption_select -
+				  (char *) &psa,
+				  (unsigned char *) &psa.
+				  psa_encryption_select, 1);
+
+			mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
+		}
+		/* update the Wavelan checksum */
+		update_psa_checksum(dev, ioaddr, lp->hacr);
+		break;
+
+	case SIOCGIWENCODE:
+		/* Read the encryption key */
+		if (!mmc_encr(ioaddr)) {
+			ret = -EOPNOTSUPP;
+			break;
+		}
+
+		/* only super-user can see encryption key */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		/* Basic checking... */
+		if (wrq->u.encoding.pointer != (caddr_t) 0) {
+			/* Verify the user buffer */
+			ret =
+			    verify_area(VERIFY_WRITE,
+					wrq->u.encoding.pointer, 8);
+			if (ret)
+				break;
+
+			psa_read(ioaddr, lp->hacr,
+				 (char *) &psa.psa_encryption_select -
+				 (char *) &psa,
+				 (unsigned char *) &psa.
+				 psa_encryption_select, 1 + 8);
+
+			/* encryption is enabled ? */
+			if (psa.psa_encryption_select)
+				wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+			else
+				wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+			wrq->u.encoding.flags |= mmc_encr(ioaddr);
+
+			/* Copy the key to the user buffer */
+			wrq->u.encoding.length = 8;
+			wv_splx(lp, &flags);
+			if (copy_to_user(wrq->u.encoding.pointer,
+					 psa.psa_encryption_key, 8))
+				ret = -EFAULT;
+			wv_splhi(lp, &flags);
+		}
+		break;
+
+	case SIOCGIWRANGE:
+		/* basic checking */
+		if (wrq->u.data.pointer != (caddr_t) 0) {
+			struct iw_range range;
+
+			/* Set the length (very important for backward
+			 * compatibility) */
+			wrq->u.data.length = sizeof(struct iw_range);
+
+			/* Set all the info we don't care or don't know
+			 * about to zero */
+			memset(&range, 0, sizeof(range));
+
+			/* Set the Wireless Extension versions */
+			range.we_version_compiled = WIRELESS_EXT;
+			range.we_version_source = 9;
+
+			/* Set information in the range struct.  */
+			range.throughput = 1.6 * 1000 * 1000;	/* don't argue on this ! */
+			range.min_nwid = 0x0000;
+			range.max_nwid = 0xFFFF;
+
+			/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
+			if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+			      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+				range.num_channels = 10;
+				range.num_frequency =
+				    wv_frequency_list(ioaddr, range.freq,
+						      IW_MAX_FREQUENCIES);
+			} else
+				range.num_channels = range.num_frequency =
+				    0;
+
+			range.sensitivity = 0x3F;
+			range.max_qual.qual = MMR_SGNL_QUAL;
+			range.max_qual.level = MMR_SIGNAL_LVL;
+			range.max_qual.noise = MMR_SILENCE_LVL;
+			range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
+			/* Need to get better values for those two */
+			range.avg_qual.level = 30;
+			range.avg_qual.noise = 8;
+
+			range.num_bitrates = 1;
+			range.bitrate[0] = 2000000;	/* 2 Mb/s */
+
+			/* Encryption supported ? */
+			if (mmc_encr(ioaddr)) {
+				range.encoding_size[0] = 8;	/* DES = 64 bits key */
+				range.num_encoding_sizes = 1;
+				range.max_encoding_tokens = 1;	/* Only one key possible */
+			} else {
+				range.num_encoding_sizes = 0;
+				range.max_encoding_tokens = 0;
+			}
+
+			/* Copy structure to the user buffer. */
+			wv_splx(lp, &flags);
+			if (copy_to_user(wrq->u.data.pointer,
+					 &range,
+					 sizeof(struct iw_range)))
+				ret = -EFAULT;
+			wv_splhi(lp, &flags);
+		}
+		break;
+
+	case SIOCGIWPRIV:
+		/* Basic checking */
+		if (wrq->u.data.pointer != (caddr_t) 0) {
+			struct iw_priv_args priv[] = {
+				/* { cmd,
+				     set_args,
+				     get_args,
+				     name } */
+				{ SIOCSIPQTHR,
+				  IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+				  0,
+				  "setqualthr" },
+				{ SIOCGIPQTHR,
+				  0,
+				  IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+				  "getqualthr" },
+				{ SIOCSIPHISTO,
+				  IW_PRIV_TYPE_BYTE | 16,
+				  0,
+				  "sethisto" },
+				{ SIOCGIPHISTO,
+				  0,
+				  IW_PRIV_TYPE_INT | 16,
+				 "gethisto" },
+			};
+
+			/* Set the number of available ioctls. */
+			wrq->u.data.length = 4;
+
+			/* Copy structure to the user buffer. */
+			wv_splx(lp, &flags);
+			if (copy_to_user(wrq->u.data.pointer,
+					      (u8 *) priv,
+					      sizeof(priv)))
+				ret = -EFAULT;
+			wv_splhi(lp, &flags);
+		}
+		break;
+
+#ifdef WIRELESS_SPY
+	case SIOCSIWSPY:
+		/* Set the spy list */
+
+		/* Check the number of addresses. */
+		if (wrq->u.data.length > IW_MAX_SPY) {
+			ret = -E2BIG;
+			break;
+		}
+		lp->spy_number = wrq->u.data.length;
+
+		/* Are there are addresses to copy? */
+		if (lp->spy_number > 0) {
+			struct sockaddr address[IW_MAX_SPY];
+			int i;
+
+			/* Copy addresses to the driver. */
+			wv_splx(lp, &flags);
+			err = copy_from_user(address,
+					     wrq->u.data.pointer,
+					     sizeof(struct sockaddr)
+					     * lp->spy_number);
+			wv_splhi(lp, &flags);
+			if (err) {
+				ret = -EFAULT;
+				break;
+			}
+
+			/* Copy addresses to the lp structure. */
+			for (i = 0; i < lp->spy_number; i++) {
+				memcpy(lp->spy_address[i],
+				       address[i].sa_data,
+				       WAVELAN_ADDR_SIZE);
+			}
+
+			/* Reset structure. */
+			memset(lp->spy_stat, 0x00,
+			       sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+			printk(KERN_DEBUG
+			       "SetSpy:  set of new addresses is: \n");
+			for (i = 0; i < wrq->u.data.length; i++)
+				printk(KERN_DEBUG
+				       "%02X:%02X:%02X:%02X:%02X:%02X \n",
+				       lp->spy_address[i][0],
+				       lp->spy_address[i][1],
+				       lp->spy_address[i][2],
+				       lp->spy_address[i][3],
+				       lp->spy_address[i][4],
+				       lp->spy_address[i][5]);
+#endif				/* DEBUG_IOCTL_INFO */
+		}
+
+		break;
+
+	case SIOCGIWSPY:
+		/* Get the spy list and spy stats. */
+
+		/* Set the number of addresses */
+		wrq->u.data.length = lp->spy_number;
+
+		/* Does the user want to have the addresses back? */
+		if ((lp->spy_number > 0)
+		    && (wrq->u.data.pointer != (caddr_t) 0)) {
+			struct sockaddr address[IW_MAX_SPY];
+			int i;
+
+			/* Copy addresses from the lp structure. */
+			for (i = 0; i < lp->spy_number; i++) {
+				memcpy(address[i].sa_data,
+				       lp->spy_address[i],
+				       WAVELAN_ADDR_SIZE);
+				address[i].sa_family = AF_UNIX;
+			}
+
+			/* Copy addresses to the user buffer. */
+			wv_splx(lp, &flags);
+			err = copy_to_user(wrq->u.data.pointer,
+					   address,
+					   sizeof(struct sockaddr)
+					   * lp->spy_number);
+
+			/* Copy stats to the user buffer (just after). */
+			err |= copy_to_user(wrq->u.data.pointer
+					    + (sizeof(struct sockaddr)
+					       * lp->spy_number),
+					    lp->spy_stat,
+					    sizeof(iw_qual) * lp->spy_number);
+			wv_splhi(lp, &flags);
+			if (err) {
+				ret = -EFAULT;
+				break;
+			}
+
+			/* Reset updated flags. */
+			for (i = 0; i < lp->spy_number; i++)
+				lp->spy_stat[i].updated = 0x0;
+		}
+		/* if(pointer != NULL) */
+		break;
+#endif				/* WIRELESS_SPY */
+
+		/* ------------------ PRIVATE IOCTL ------------------ */
+
+	case SIOCSIPQTHR:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+		psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+		psa_write(ioaddr, lp->hacr,
+			  (char *) &psa.psa_quality_thr - (char *) &psa,
+			  (unsigned char *) &psa.psa_quality_thr, 1);
+		/* update the Wavelan checksum */
+		update_psa_checksum(dev, ioaddr, lp->hacr);
+		mmc_out(ioaddr, mmwoff(0, mmw_quality_thr),
+			psa.psa_quality_thr);
+		break;
+
+	case SIOCGIPQTHR:
+		psa_read(ioaddr, lp->hacr,
+			 (char *) &psa.psa_quality_thr - (char *) &psa,
+			 (unsigned char *) &psa.psa_quality_thr, 1);
+		*(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+		break;
+
+#ifdef HISTOGRAM
+	case SIOCSIPHISTO:
+		/* Verify that the user is root. */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		/* Check the number of intervals. */
+		if (wrq->u.data.length > 16) {
+			ret = -E2BIG;
+			break;
+		}
+		lp->his_number = wrq->u.data.length;
+
+		/* Are there addresses to copy? */
+		if (lp->his_number > 0) {
+			/* Copy interval ranges to the driver */
+			wv_splx(lp, &flags);
+			err = copy_from_user(lp->his_range,
+					     wrq->u.data.pointer,
+					     sizeof(char) * lp->his_number);
+			wv_splhi(lp, &flags);
+			if (err) {
+				ret = -EFAULT;
+				break;
+			}
+
+			/* Reset structure. */
+			memset(lp->his_sum, 0x00, sizeof(long) * 16);
+		}
+		break;
+
+	case SIOCGIPHISTO:
+		/* Set the number of intervals. */
+		wrq->u.data.length = lp->his_number;
+
+		/* Give back the distribution statistics */
+		if ((lp->his_number > 0)
+		    && (wrq->u.data.pointer != (caddr_t) 0)) {
+			/* Copy data to the user buffer. */
+			wv_splx(lp, &flags);
+			if (copy_to_user(wrq->u.data.pointer,
+					 lp->his_sum,
+					 sizeof(long) * lp->his_number);
+				ret = -EFAULT;
+			wv_splhi(lp, &flags);
+
+		}		/* if(pointer != NULL) */
+		break;
+#endif				/* HISTOGRAM */
+
+		/* ------------------- OTHER IOCTL ------------------- */
+
+	default:
+		ret = -EOPNOTSUPP;
+	}	/* switch (cmd) */
+
+	/* Enable interrupts and restore flags. */
+	wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+	return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ */
+static iw_stats *wavelan_get_wireless_stats(device * dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	net_local *lp = (net_local *) dev->priv;
+	mmr_t m;
+	iw_stats *wstats;
+	unsigned long flags;
+
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n",
+	       dev->name);
+#endif
+
+	/* Check */
+	if (lp == (net_local *) NULL)
+		return (iw_stats *) NULL;
+	
+	/* Disable interrupts and save flags. */
+	wv_splhi(lp, &flags);
+	
+	wstats = &lp->wstats;
+
+	/* Get data from the mmc. */
+	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+
+	mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+	mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l,
+		 2);
+	mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set,
+		 4);
+
+	mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+	/* Copy data to wireless stuff. */
+	wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
+	wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+	wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+	wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+	wstats->qual.updated = (((m. mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) 
+			| ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) 
+			| ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+	wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+	wstats->discard.code = 0L;
+	wstats->discard.misc = 0L;
+
+	/* Enable interrupts and restore flags. */
+	wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n",
+	       dev->name);
+#endif
+	return &lp->wstats;
+}
+#endif				/* WIRELESS_EXT */
+
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deals with receiving the packets.
+ * The interrupt handler gets an interrupt when a packet has been
+ * successfully received and calls this part.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copying of data (including the Ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: we
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here.  The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor".
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device * dev, u16 buf_off, int sksize)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	struct sk_buff *skb;
+
+#ifdef DEBUG_RX_TRACE
+	printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+	       dev->name, buf_off, sksize);
+#endif
+
+	/* Allocate buffer for the data */
+	if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) {
+#ifdef DEBUG_RX_ERROR
+		printk(KERN_INFO
+		       "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
+		       dev->name, sksize);
+#endif
+		lp->stats.rx_dropped++;
+		return;
+	}
+
+	skb->dev = dev;
+
+	/* Copy the packet to the buffer. */
+	obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
+	skb->protocol = eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+	wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif				/* DEBUG_RX_INFO */
+
+	/* Statistics-gathering and associated stuff.
+	 * It seem a bit messy with all the define, but it's really simple... */
+#if defined(WIRELESS_SPY) || defined(HISTOGRAM)
+	if (
+#ifdef WIRELESS_SPY
+		   (lp->spy_number > 0) ||
+#endif				/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+		   (lp->his_number > 0) ||
+#endif				/* HISTOGRAM */
+		   0) {
+		u8 stats[3];	/* signal level, noise level, signal quality */
+
+		/* Read signal level, silence level and signal quality bytes. */
+		/* Note: in the PCMCIA hardware, these are part of the frame.  It seems
+		 * that for the ISA hardware, it's nowhere to be found in the frame,
+		 * so I'm obliged to do this (it has a side effect on /proc/net/wireless).
+		 * Any ideas?
+		 */
+		mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+		mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
+		mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef DEBUG_RX_INFO
+		printk(KERN_DEBUG
+		       "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+		       dev->name, stats[0] & 0x3F, stats[1] & 0x3F,
+		       stats[2] & 0x0F);
+#endif
+
+		/* Spying stuff */
+#ifdef WIRELESS_SPY
+		wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE,
+			      stats);
+#endif				/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+		wl_his_gather(dev, stats);
+#endif				/* HISTOGRAM */
+	}
+#endif				/* defined(WIRELESS_SPY) || defined(HISTOGRAM) */
+
+	/*
+	 * Hand the packet to the network module.
+	 */
+	netif_rx(skb);
+
+	/* Keep statistics up to date */
+	dev->last_rx = jiffies;
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes += sksize;
+
+#ifdef DEBUG_RX_TRACE
+	printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Transfer as many packets as we can
+ * from the device RAM.
+ * (called in wavelan_interrupt()).
+ * Note : the spinlock is already grabbed for us.
+ */
+static inline void wv_receive(device * dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	net_local *lp = (net_local *) dev->priv;
+	fd_t fd;
+	rbd_t rbd;
+	int nreaped = 0;
+
+#ifdef DEBUG_RX_TRACE
+	printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name);
+#endif
+
+	/* Loop on each received packet. */
+	for (;;) {
+		obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd,
+			   sizeof(fd));
+
+		/* Note about the status :
+		 * It start up to be 0 (the value we set). Then, when the RU
+		 * grab the buffer to prepare for reception, it sets the
+		 * FD_STATUS_B flag. When the RU has finished receiving the
+		 * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate
+		 * completion and set the other flags to indicate the eventual
+		 * errors. FD_STATUS_OK indicates that the reception was OK.
+		 */
+
+		/* If the current frame is not complete, we have reached the end. */
+		if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+			break;	/* This is how we exit the loop. */
+
+		nreaped++;
+
+		/* Check whether frame was correctly received. */
+		if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) {
+			/* Does the frame contain a pointer to the data?  Let's check. */
+			if (fd.fd_rbd_offset != I82586NULL) {
+				/* Read the receive buffer descriptor */
+				obram_read(ioaddr, fd.fd_rbd_offset,
+					   (unsigned char *) &rbd,
+					   sizeof(rbd));
+
+#ifdef DEBUG_RX_ERROR
+				if ((rbd.rbd_status & RBD_STATUS_EOF) !=
+				    RBD_STATUS_EOF) printk(KERN_INFO
+							   "%s: wv_receive(): missing EOF flag.\n",
+							   dev->name);
+
+				if ((rbd.rbd_status & RBD_STATUS_F) !=
+				    RBD_STATUS_F) printk(KERN_INFO
+							 "%s: wv_receive(): missing F flag.\n",
+							 dev->name);
+#endif				/* DEBUG_RX_ERROR */
+
+				/* Read the packet and transmit to Linux */
+				wv_packet_read(dev, rbd.rbd_bufl,
+					       rbd.
+					       rbd_status &
+					       RBD_STATUS_ACNT);
+			}
+#ifdef DEBUG_RX_ERROR
+			else	/* if frame has no data */
+				printk(KERN_INFO
+				       "%s: wv_receive(): frame has no data.\n",
+				       dev->name);
+#endif
+		} else {	/* If reception was no successful */
+
+			lp->stats.rx_errors++;
+
+#ifdef DEBUG_RX_INFO
+			printk(KERN_DEBUG
+			       "%s: wv_receive(): frame not received successfully (%X).\n",
+			       dev->name, fd.fd_status);
+#endif
+
+#ifdef DEBUG_RX_ERROR
+			if ((fd.fd_status & FD_STATUS_S6) != 0)
+				printk(KERN_INFO
+				       "%s: wv_receive(): no EOF flag.\n",
+				       dev->name);
+#endif
+
+			if ((fd.fd_status & FD_STATUS_S7) != 0) {
+				lp->stats.rx_length_errors++;
+#ifdef DEBUG_RX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_receive(): frame too short.\n",
+				       dev->name);
+#endif
+			}
+
+			if ((fd.fd_status & FD_STATUS_S8) != 0) {
+				lp->stats.rx_over_errors++;
+#ifdef DEBUG_RX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_receive(): rx DMA overrun.\n",
+				       dev->name);
+#endif
+			}
+
+			if ((fd.fd_status & FD_STATUS_S9) != 0) {
+				lp->stats.rx_fifo_errors++;
+#ifdef DEBUG_RX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_receive(): ran out of resources.\n",
+				       dev->name);
+#endif
+			}
+
+			if ((fd.fd_status & FD_STATUS_S10) != 0) {
+				lp->stats.rx_frame_errors++;
+#ifdef DEBUG_RX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_receive(): alignment error.\n",
+				       dev->name);
+#endif
+			}
+
+			if ((fd.fd_status & FD_STATUS_S11) != 0) {
+				lp->stats.rx_crc_errors++;
+#ifdef DEBUG_RX_FAIL
+				printk(KERN_DEBUG
+				       "%s: wv_receive(): CRC error.\n",
+				       dev->name);
+#endif
+			}
+		}
+
+		fd.fd_status = 0;
+		obram_write(ioaddr, fdoff(lp->rx_head, fd_status),
+			    (unsigned char *) &fd.fd_status,
+			    sizeof(fd.fd_status));
+
+		fd.fd_command = FD_COMMAND_EL;
+		obram_write(ioaddr, fdoff(lp->rx_head, fd_command),
+			    (unsigned char *) &fd.fd_command,
+			    sizeof(fd.fd_command));
+
+		fd.fd_command = 0;
+		obram_write(ioaddr, fdoff(lp->rx_last, fd_command),
+			    (unsigned char *) &fd.fd_command,
+			    sizeof(fd.fd_command));
+
+		lp->rx_last = lp->rx_head;
+		lp->rx_head = fd.fd_link_offset;
+	}			/* for(;;) -> loop on all frames */
+
+#ifdef DEBUG_RX_INFO
+	if (nreaped > 1)
+		printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n",
+		       dev->name, nreaped);
+#endif
+#ifdef DEBUG_RX_TRACE
+	printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name);
+#endif
+}
+
+/*********************** PACKET TRANSMISSION ***********************/
+/*
+ * This part deals with sending packets through the WaveLAN.
+ *
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ *
+ * The principle:
+ * Each block contains a transmit command, a NOP command,
+ * a transmit block descriptor and a buffer.
+ * The CU read the transmit block which point to the tbd,
+ * read the tbd and the content of the buffer.
+ * When it has finish with it, it goes to the next command
+ * which in our case is the NOP. The NOP points on itself,
+ * so the CU stop here.
+ * When we add the next block, we modify the previous nop
+ * to make it point on the new tx command.
+ * Simple, isn't it ?
+ *
+ * (called in wavelan_packet_xmit())
+ */
+static inline int wv_packet_write(device * dev, void *buf, short length)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	unsigned short txblock;
+	unsigned short txpred;
+	unsigned short tx_addr;
+	unsigned short nop_addr;
+	unsigned short tbd_addr;
+	unsigned short buf_addr;
+	ac_tx_t tx;
+	ac_nop_t nop;
+	tbd_t tbd;
+	int clen = length;
+	unsigned long flags;
+
+#ifdef DEBUG_TX_TRACE
+	printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name,
+	       length);
+#endif
+
+	/* Do we need some padding? */
+	if (clen < ETH_ZLEN)
+		clen = ETH_ZLEN;
+
+	wv_splhi(lp, &flags);
+
+	/* Check nothing bad has happened */
+	if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
+#ifdef DEBUG_TX_ERROR
+		printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n",
+		       dev->name);
+#endif
+		wv_splx(lp, &flags);
+		return 1;
+	}
+
+	/* Calculate addresses of next block and previous block. */
+	txblock = lp->tx_first_free;
+	txpred = txblock - TXBLOCKZ;
+	if (txpred < OFFSET_CU)
+		txpred += NTXBLOCKS * TXBLOCKZ;
+	lp->tx_first_free += TXBLOCKZ;
+	if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+		lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+	lp->tx_n_in_use++;
+
+	/* Calculate addresses of the different parts of the block. */
+	tx_addr = txblock;
+	nop_addr = tx_addr + sizeof(tx);
+	tbd_addr = nop_addr + sizeof(nop);
+	buf_addr = tbd_addr + sizeof(tbd);
+
+	/*
+	 * Transmit command
+	 */
+	tx.tx_h.ac_status = 0;
+	obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+		    (unsigned char *) &tx.tx_h.ac_status,
+		    sizeof(tx.tx_h.ac_status));
+
+	/*
+	 * NOP command
+	 */
+	nop.nop_h.ac_status = 0;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+		    (unsigned char *) &nop.nop_h.ac_status,
+		    sizeof(nop.nop_h.ac_status));
+	nop.nop_h.ac_link = nop_addr;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+		    (unsigned char *) &nop.nop_h.ac_link,
+		    sizeof(nop.nop_h.ac_link));
+
+	/*
+	 * Transmit buffer descriptor
+	 */
+	tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
+	tbd.tbd_next_bd_offset = I82586NULL;
+	tbd.tbd_bufl = buf_addr;
+	tbd.tbd_bufh = 0;
+	obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd));
+
+	/*
+	 * Data
+	 */
+	obram_write(ioaddr, buf_addr, buf, length);
+
+	/*
+	 * Overwrite the predecessor NOP link
+	 * so that it points to this txblock.
+	 */
+	nop_addr = txpred + sizeof(tx);
+	nop.nop_h.ac_status = 0;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+		    (unsigned char *) &nop.nop_h.ac_status,
+		    sizeof(nop.nop_h.ac_status));
+	nop.nop_h.ac_link = txblock;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+		    (unsigned char *) &nop.nop_h.ac_link,
+		    sizeof(nop.nop_h.ac_link));
+
+	/* Keep stats up to date. */
+	lp->stats.tx_bytes += length;
+
+	if (lp->tx_first_in_use == I82586NULL)
+		lp->tx_first_in_use = txblock;
+
+	if (lp->tx_n_in_use < NTXBLOCKS - 1)
+		netif_wake_queue(dev);
+
+	wv_splx(lp, &flags);
+	
+#ifdef DEBUG_TX_INFO
+	wv_packet_info((u8 *) buf, length, dev->name,
+		       "wv_packet_write");
+#endif				/* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+	printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the harware is ready to accept
+ * the packet.  We also prevent reentrance.  Then we call the function
+ * to send the packet.
+ */
+static int wavelan_packet_xmit(struct sk_buff *skb, device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long flags;
+
+#ifdef DEBUG_TX_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+	       (unsigned) skb);
+#endif
+
+	/*
+	 * Block a timer-based transmit from overlapping.
+	 * In other words, prevent reentering this routine.
+	 */
+	netif_stop_queue(dev);
+
+	/* If somebody has asked to reconfigure the controller, 
+	 * we can do it now.
+	 */
+	if (lp->reconfig_82586) {
+		wv_splhi(lp, &flags);
+		wv_82586_config(dev);
+		wv_splx(lp, &flags);
+		/* Check that we can continue */
+		if (lp->tx_n_in_use == (NTXBLOCKS - 1))
+			return 1;
+	}
+#ifdef DEBUG_TX_ERROR
+	if (skb->next)
+		printk(KERN_INFO "skb has next\n");
+#endif
+
+	/* Write packet on the card */
+	if(wv_packet_write(dev, skb->data, skb->len))
+		return 1;	/* We failed */
+
+	dev_kfree_skb(skb);
+
+#ifdef DEBUG_TX_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*********************** HARDWARE CONFIGURATION ***********************/
+/*
+ * This part does the real job of starting and configuring the hardware.
+ */
+
+/*--------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_reset())
+ */
+static inline int wv_mmc_init(device * dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	net_local *lp = (net_local *) dev->priv;
+	psa_t psa;
+	mmw_t m;
+	int configured;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+	/* Read the parameter storage area. */
+	psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef USE_PSA_CONFIG
+	configured = psa.psa_conf_status & 1;
+#else
+	configured = 0;
+#endif
+
+	/* Is the PSA is not configured */
+	if (!configured) {
+		/* User will be able to configure NWID later (with iwconfig). */
+		psa.psa_nwid[0] = 0;
+		psa.psa_nwid[1] = 0;
+
+		/* no NWID checking since NWID is not set */
+		psa.psa_nwid_select = 0;
+
+		/* Disable encryption */
+		psa.psa_encryption_select = 0;
+
+		/* Set to standard values:
+		 * 0x04 for AT,
+		 * 0x01 for MCA,
+		 * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+		 */
+		if (psa.psa_comp_number & 1)
+			psa.psa_thr_pre_set = 0x01;
+		else
+			psa.psa_thr_pre_set = 0x04;
+		psa.psa_quality_thr = 0x03;
+
+		/* It is configured */
+		psa.psa_conf_status |= 1;
+
+#ifdef USE_PSA_CONFIG
+		/* Write the psa. */
+		psa_write(ioaddr, lp->hacr,
+			  (char *) psa.psa_nwid - (char *) &psa,
+			  (unsigned char *) psa.psa_nwid, 4);
+		psa_write(ioaddr, lp->hacr,
+			  (char *) &psa.psa_thr_pre_set - (char *) &psa,
+			  (unsigned char *) &psa.psa_thr_pre_set, 1);
+		psa_write(ioaddr, lp->hacr,
+			  (char *) &psa.psa_quality_thr - (char *) &psa,
+			  (unsigned char *) &psa.psa_quality_thr, 1);
+		psa_write(ioaddr, lp->hacr,
+			  (char *) &psa.psa_conf_status - (char *) &psa,
+			  (unsigned char *) &psa.psa_conf_status, 1);
+		/* update the Wavelan checksum */
+		update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
+	}
+
+	/* Zero the mmc structure. */
+	memset(&m, 0x00, sizeof(m));
+
+	/* Copy PSA info to the mmc. */
+	m.mmw_netw_id_l = psa.psa_nwid[1];
+	m.mmw_netw_id_h = psa.psa_nwid[0];
+
+	if (psa.psa_nwid_select & 1)
+		m.mmw_loopt_sel = 0x00;
+	else
+		m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+	memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,
+	       sizeof(m.mmw_encr_key));
+
+	if (psa.psa_encryption_select)
+		m.mmw_encr_enable =
+		    MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+	else
+		m.mmw_encr_enable = 0;
+
+	m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+	m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+	/*
+	 * Set default modem control parameters.
+	 * See NCR document 407-0024326 Rev. A.
+	 */
+	m.mmw_jabber_enable = 0x01;
+	m.mmw_freeze = 0;
+	m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+	m.mmw_ifs = 0x20;
+	m.mmw_mod_delay = 0x04;
+	m.mmw_jam_time = 0x38;
+
+	m.mmw_des_io_invert = 0;
+	m.mmw_decay_prm = 0;
+	m.mmw_decay_updat_prm = 0;
+
+	/* Write all info to MMC. */
+	mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m));
+
+	/* The following code starts the modem of the 2.00 frequency
+	 * selectable cards at power on.  It's not strictly needed for the
+	 * following boots.
+	 * The original patch was by Joe Finney for the PCMCIA driver, but
+	 * I've cleaned it up a bit and added documentation.
+	 * Thanks to Loeke Brederveld from Lucent for the info.
+	 */
+
+	/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+	 * Does it work for everybody, especially old cards? */
+	/* Note: WFREQSEL verifies that it is able to read a sensible
+	 * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID
+	 * is 0xA (Xilinx version) or 0xB (Ariadne version).
+	 * My test is more crude but does work. */
+	if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+	      (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+		/* We must download the frequency parameters to the
+		 * synthesizers (from the EEPROM - area 1)
+		 * Note: as the EEPROM is automatically decremented, we set the end
+		 * if the area... */
+		m.mmw_fee_addr = 0x0F;
+		m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+		mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
+			  (unsigned char *) &m.mmw_fee_ctrl, 2);
+
+		/* Wait until the download is finished. */
+		fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+		/* The frequency was in the last word downloaded. */
+		mmc_read(ioaddr, (char *) &m.mmw_fee_data_l - (char *) &m,
+			 (unsigned char *) &m.mmw_fee_data_l, 2);
+
+		/* Print some info for the user. */
+		printk(KERN_DEBUG
+		       "%s: WaveLAN 2.00 recognised (frequency select).  Current frequency = %ld\n",
+		       dev->name,
+		       ((m.
+			 mmw_fee_data_h << 4) | (m.mmw_fee_data_l >> 4)) *
+		       5 / 2 + 24000L);
+#endif
+
+		/* We must now download the power adjust value (gain) to
+		 * the synthesizers (from the EEPROM - area 7 - DAC). */
+		m.mmw_fee_addr = 0x61;
+		m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+		mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
+			  (unsigned char *) &m.mmw_fee_ctrl, 2);
+
+		/* Wait until the download is finished. */
+	}
+	/* if 2.00 card */
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ * (called by wv_hw_reset())
+ */
+static inline int wv_ru_start(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	u16 scb_cs;
+	fd_t fd;
+	rbd_t rbd;
+	u16 rx;
+	u16 rx_next;
+	int i;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+		   (unsigned char *) &scb_cs, sizeof(scb_cs));
+	if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+		return 0;
+
+	lp->rx_head = OFFSET_RU;
+
+	for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) {
+		rx_next =
+		    (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+		fd.fd_status = 0;
+		fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+		fd.fd_link_offset = rx_next;
+		fd.fd_rbd_offset = rx + sizeof(fd);
+		obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd));
+
+		rbd.rbd_status = 0;
+		rbd.rbd_next_rbd_offset = I82586NULL;
+		rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+		rbd.rbd_bufh = 0;
+		rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+		obram_write(ioaddr, rx + sizeof(fd),
+			    (unsigned char *) &rbd, sizeof(rbd));
+
+		lp->rx_last = rx;
+	}
+
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset),
+		    (unsigned char *) &lp->rx_head, sizeof(lp->rx_head));
+
+	scb_cs = SCB_CMD_RUC_GO;
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+		    (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+	set_chan_attn(ioaddr, lp->hacr);
+
+	for (i = 1000; i > 0; i--) {
+		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+			   (unsigned char *) &scb_cs, sizeof(scb_cs));
+		if (scb_cs == 0)
+			break;
+
+		udelay(10);
+	}
+
+	if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_ru_start(): board not accepting command.\n",
+		       dev->name);
+#endif
+		return -1;
+	}
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ *
+ * Here we create the list of send buffers used to transmit packets
+ * between the PC and the command unit. For each buffer, we create a
+ * buffer descriptor (pointing on the buffer), a transmit command
+ * (pointing to the buffer descriptor) and a NOP command.
+ * The transmit command is linked to the NOP, and the NOP to itself.
+ * When we will have finished executing the transmit command, we will
+ * then loop on the NOP. By releasing the NOP link to a new command,
+ * we may send another buffer.
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int wv_cu_start(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	int i;
+	u16 txblock;
+	u16 first_nop;
+	u16 scb_cs;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name);
+#endif
+
+	lp->tx_first_free = OFFSET_CU;
+	lp->tx_first_in_use = I82586NULL;
+
+	for (i = 0, txblock = OFFSET_CU;
+	     i < NTXBLOCKS; i++, txblock += TXBLOCKZ) {
+		ac_tx_t tx;
+		ac_nop_t nop;
+		tbd_t tbd;
+		unsigned short tx_addr;
+		unsigned short nop_addr;
+		unsigned short tbd_addr;
+		unsigned short buf_addr;
+
+		tx_addr = txblock;
+		nop_addr = tx_addr + sizeof(tx);
+		tbd_addr = nop_addr + sizeof(nop);
+		buf_addr = tbd_addr + sizeof(tbd);
+
+		tx.tx_h.ac_status = 0;
+		tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+		tx.tx_h.ac_link = nop_addr;
+		tx.tx_tbd_offset = tbd_addr;
+		obram_write(ioaddr, tx_addr, (unsigned char *) &tx,
+			    sizeof(tx));
+
+		nop.nop_h.ac_status = 0;
+		nop.nop_h.ac_command = acmd_nop;
+		nop.nop_h.ac_link = nop_addr;
+		obram_write(ioaddr, nop_addr, (unsigned char *) &nop,
+			    sizeof(nop));
+
+		tbd.tbd_status = TBD_STATUS_EOF;
+		tbd.tbd_next_bd_offset = I82586NULL;
+		tbd.tbd_bufl = buf_addr;
+		tbd.tbd_bufh = 0;
+		obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd,
+			    sizeof(tbd));
+	}
+
+	first_nop =
+	    OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset),
+		    (unsigned char *) &first_nop, sizeof(first_nop));
+
+	scb_cs = SCB_CMD_CUC_GO;
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+		    (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+	set_chan_attn(ioaddr, lp->hacr);
+
+	for (i = 1000; i > 0; i--) {
+		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+			   (unsigned char *) &scb_cs, sizeof(scb_cs));
+		if (scb_cs == 0)
+			break;
+
+		udelay(10);
+	}
+
+	if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_cu_start(): board not accepting command.\n",
+		       dev->name);
+#endif
+		return -1;
+	}
+
+	lp->tx_n_in_use = 0;
+	netif_start_queue(dev);
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard configuration of the WaveLAN 
+ * controller (i82586).
+ *
+ * It initialises the scp, iscp and scb structure
+ * The first two are just pointers to the next.
+ * The last one is used for basic configuration and for basic
+ * communication (interrupt status).
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int wv_82586_start(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	scp_t scp;		/* system configuration pointer */
+	iscp_t iscp;		/* intermediate scp */
+	scb_t scb;		/* system control block */
+	ach_t cb;		/* Action command header */
+	u8 zeroes[512];
+	int i;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name);
+#endif
+
+	/*
+	 * Clear the onboard RAM.
+	 */
+	memset(&zeroes[0], 0x00, sizeof(zeroes));
+	for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+		obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+
+	/*
+	 * Construct the command unit structures:
+	 * scp, iscp, scb, cb.
+	 */
+	memset(&scp, 0x00, sizeof(scp));
+	scp.scp_sysbus = SCP_SY_16BBUS;
+	scp.scp_iscpl = OFFSET_ISCP;
+	obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp,
+		    sizeof(scp));
+
+	memset(&iscp, 0x00, sizeof(iscp));
+	iscp.iscp_busy = 1;
+	iscp.iscp_offset = OFFSET_SCB;
+	obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
+		    sizeof(iscp));
+
+	/* Our first command is to reset the i82586. */
+	memset(&scb, 0x00, sizeof(scb));
+	scb.scb_command = SCB_CMD_RESET;
+	scb.scb_cbl_offset = OFFSET_CU;
+	scb.scb_rfa_offset = OFFSET_RU;
+	obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+		    sizeof(scb));
+
+	set_chan_attn(ioaddr, lp->hacr);
+
+	/* Wait for command to finish. */
+	for (i = 1000; i > 0; i--) {
+		obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
+			   sizeof(iscp));
+
+		if (iscp.iscp_busy == (unsigned short) 0)
+			break;
+
+		udelay(10);
+	}
+
+	if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wv_82586_start(): iscp_busy timeout.\n",
+		       dev->name);
+#endif
+		return -1;
+	}
+
+	/* Check command completion. */
+	for (i = 15; i > 0; i--) {
+		obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+			   sizeof(scb));
+
+		if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+			break;
+
+		udelay(10);
+	}
+
+	if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n",
+		       dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+#endif
+		return -1;
+	}
+
+	wv_ack(dev);
+
+	/* Set the action command header. */
+	memset(&cb, 0x00, sizeof(cb));
+	cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+	cb.ac_link = OFFSET_CU;
+	obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
+
+	if (wv_synchronous_cmd(dev, "diag()") == -1)
+		return -1;
+
+	obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
+	if (cb.ac_status & AC_SFLD_FAIL) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wv_82586_start(): i82586 Self Test failed.\n",
+		       dev->name);
+#endif
+		return -1;
+	}
+#ifdef DEBUG_I82586_SHOW
+	wv_scb_show(ioaddr);
+#endif
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard configuration of the WaveLAN
+ * controller (i82586).
+ *
+ * This routine is a violent hack. We use the first free transmit block
+ * to make our configuration. In the buffer area, we create the three
+ * configuration commands (linked). We make the previous NOP point to
+ * the beginning of the buffer instead of the tx command. After, we go
+ * as usual to the NOP command.
+ * Note that only the last command (mc_set) will generate an interrupt.
+ *
+ * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit())
+ */
+static void wv_82586_config(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	unsigned short txblock;
+	unsigned short txpred;
+	unsigned short tx_addr;
+	unsigned short nop_addr;
+	unsigned short tbd_addr;
+	unsigned short cfg_addr;
+	unsigned short ias_addr;
+	unsigned short mcs_addr;
+	ac_tx_t tx;
+	ac_nop_t nop;
+	ac_cfg_t cfg;		/* Configure action */
+	ac_ias_t ias;		/* IA-setup action */
+	ac_mcs_t mcs;		/* Multicast setup */
+	struct dev_mc_list *dmi;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name);
+#endif
+
+	/* Check nothing bad has happened */
+	if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n",
+		       dev->name);
+#endif
+		return;
+	}
+
+	/* Calculate addresses of next block and previous block. */
+	txblock = lp->tx_first_free;
+	txpred = txblock - TXBLOCKZ;
+	if (txpred < OFFSET_CU)
+		txpred += NTXBLOCKS * TXBLOCKZ;
+	lp->tx_first_free += TXBLOCKZ;
+	if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+		lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+	lp->tx_n_in_use++;
+
+	/* Calculate addresses of the different parts of the block. */
+	tx_addr = txblock;
+	nop_addr = tx_addr + sizeof(tx);
+	tbd_addr = nop_addr + sizeof(nop);
+	cfg_addr = tbd_addr + sizeof(tbd_t);	/* beginning of the buffer */
+	ias_addr = cfg_addr + sizeof(cfg);
+	mcs_addr = ias_addr + sizeof(ias);
+
+	/*
+	 * Transmit command
+	 */
+	tx.tx_h.ac_status = 0xFFFF;	/* Fake completion value */
+	obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+		    (unsigned char *) &tx.tx_h.ac_status,
+		    sizeof(tx.tx_h.ac_status));
+
+	/*
+	 * NOP command
+	 */
+	nop.nop_h.ac_status = 0;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+		    (unsigned char *) &nop.nop_h.ac_status,
+		    sizeof(nop.nop_h.ac_status));
+	nop.nop_h.ac_link = nop_addr;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+		    (unsigned char *) &nop.nop_h.ac_link,
+		    sizeof(nop.nop_h.ac_link));
+
+	/* Create a configure action. */
+	memset(&cfg, 0x00, sizeof(cfg));
+
+	/*
+	 * For Linux we invert AC_CFG_ALOC() so as to conform
+	 * to the way that net packets reach us from above.
+	 * (See also ac_tx_t.)
+	 *
+	 * Updated from Wavelan Manual WCIN085B
+	 */
+	cfg.cfg_byte_cnt =
+	    AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+	cfg.cfg_fifolim = AC_CFG_FIFOLIM(4);
+	cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0);
+	cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+	    AC_CFG_ILPBCK(0) |
+	    AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+	    AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+	cfg.cfg_byte10 = AC_CFG_BOFMET(1) |
+	    AC_CFG_ACR(6) | AC_CFG_LINPRIO(0);
+	cfg.cfg_ifs = 0x20;
+	cfg.cfg_slotl = 0x0C;
+	cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0);
+	cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+	    AC_CFG_BTSTF(0) |
+	    AC_CFG_CRC16(0) |
+	    AC_CFG_NCRC(0) |
+	    AC_CFG_TNCRS(1) |
+	    AC_CFG_MANCH(0) |
+	    AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous);
+	cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+	    AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0);
+/*
+  cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+	cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
+
+	cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure);
+	cfg.cfg_h.ac_link = ias_addr;
+	obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg));
+
+	/* Set up the MAC address */
+	memset(&ias, 0x00, sizeof(ias));
+	ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup);
+	ias.ias_h.ac_link = mcs_addr;
+	memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0],
+	       sizeof(ias.ias_addr));
+	obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias));
+
+	/* Initialize adapter's Ethernet multicast addresses */
+	memset(&mcs, 0x00, sizeof(mcs));
+	mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup);
+	mcs.mcs_h.ac_link = nop_addr;
+	mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count;
+	obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs));
+
+	/* Any address to set? */
+	if (lp->mc_count) {
+		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+			outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr,
+			      WAVELAN_ADDR_SIZE >> 1);
+
+#ifdef DEBUG_CONFIG_INFO
+		printk(KERN_DEBUG
+		       "%s: wv_82586_config(): set %d multicast addresses:\n",
+		       dev->name, lp->mc_count);
+		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+			printk(KERN_DEBUG
+			       " %02x:%02x:%02x:%02x:%02x:%02x\n",
+			       dmi->dmi_addr[0], dmi->dmi_addr[1],
+			       dmi->dmi_addr[2], dmi->dmi_addr[3],
+			       dmi->dmi_addr[4], dmi->dmi_addr[5]);
+#endif
+	}
+
+	/*
+	 * Overwrite the predecessor NOP link
+	 * so that it points to the configure action.
+	 */
+	nop_addr = txpred + sizeof(tx);
+	nop.nop_h.ac_status = 0;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+		    (unsigned char *) &nop.nop_h.ac_status,
+		    sizeof(nop.nop_h.ac_status));
+	nop.nop_h.ac_link = cfg_addr;
+	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+		    (unsigned char *) &nop.nop_h.ac_link,
+		    sizeof(nop.nop_h.ac_link));
+
+	/* Job done, clear the flag */
+	lp->reconfig_82586 = 0;
+
+	if (lp->tx_first_in_use == I82586NULL)
+		lp->tx_first_in_use = txblock;
+
+	if (lp->tx_n_in_use == (NTXBLOCKS - 1))
+		netif_stop_queue(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine, called by wavelan_close(), gracefully stops the 
+ * WaveLAN controller (i82586).
+ * (called by wavelan_close())
+ */
+static inline void wv_82586_stop(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	u16 scb_cmd;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
+#endif
+
+	/* Suspend both command unit and receive unit. */
+	scb_cmd =
+	    (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC &
+					       SCB_CMD_RUC_SUS);
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+		    (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+	set_chan_attn(ioaddr, lp->hacr);
+
+	/* No more interrupts */
+	wv_ints_off(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the WaveLAN and restart it.
+ * Performs the following actions:
+ *	1. A power reset (reset DMA)
+ *	2. Initialize the radio modem (using wv_mmc_init)
+ *	3. Reset & Configure LAN controller (using wv_82586_start)
+ *	4. Start the LAN controller's command unit
+ *	5. Start the LAN controller's receive unit
+ * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open())
+ */
+static int wv_hw_reset(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
+	       (unsigned int) dev);
+#endif
+
+	/* Increase the number of resets done. */
+	lp->nresets++;
+
+	wv_hacr_reset(ioaddr);
+	lp->hacr = HACR_DEFAULT;
+
+	if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0))
+		return -1;
+
+	/* Enable the card to send interrupts. */
+	wv_ints_on(dev);
+
+	/* Start card functions */
+	if (wv_cu_start(dev) < 0)
+		return -1;
+
+	/* Setup the controller and parameters */
+	wv_82586_config(dev);
+
+	/* Finish configuration with the receive unit */
+	if (wv_ru_start(dev) < 0)
+		return -1;
+
+#ifdef DEBUG_CONFIG_TRACE
+	printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Check if there is a WaveLAN at the specific base address.
+ * As a side effect, this reads the MAC address.
+ * (called in wavelan_probe() and init_module())
+ */
+static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac)
+{
+	int i;			/* Loop counter */
+
+	/* Check if the base address if available. */
+	if (check_region(ioaddr, sizeof(ha_t)))
+		return -EADDRINUSE;	/* ioaddr already used */
+
+	/* Reset host interface */
+	wv_hacr_reset(ioaddr);
+
+	/* Read the MAC address from the parameter storage area. */
+	psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr),
+		 mac, 6);
+
+	/*
+	 * Check the first three octets of the address for the manufacturer's code.
+	 * Note: if this can't find your WaveLAN card, you've got a
+	 * non-NCR/AT&T/Lucent ISA card.  See wavelan.p.h for detail on
+	 * how to configure your card.
+	 */
+	for (i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+		if ((mac[0] == MAC_ADDRESSES[i][0]) &&
+		    (mac[1] == MAC_ADDRESSES[i][1]) &&
+		    (mac[2] == MAC_ADDRESSES[i][2]))
+			return 0;
+
+#ifdef DEBUG_CONFIG_INFO
+	printk(KERN_WARNING
+	       "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n",
+	       ioaddr, mac[0], mac[1], mac[2]);
+#endif
+	return -ENODEV;
+}
+
+/************************ INTERRUPT HANDLING ************************/
+
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever: 
+ */
+static void wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	device *dev;
+	unsigned long ioaddr;
+	net_local *lp;
+	u16 hasr;
+	u16 status;
+	u16 ack_cmd;
+
+	dev = dev_id;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+	lp = (net_local *) dev->priv;
+	ioaddr = dev->base_addr;
+
+#ifdef DEBUG_INTERRUPT_INFO
+	/* Check state of our spinlock */
+	if(spin_is_locked(&lp->spinlock))
+		printk(KERN_DEBUG
+		       "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
+		       dev->name);
+#endif
+
+	/* Prevent reentrancy. We need to do that because we may have
+	 * multiple interrupt handler running concurrently.
+	 * It is safe because wv_splhi() disables interrupts before acquiring
+	 * the spinlock. */
+	spin_lock(&lp->spinlock);
+
+	/* Check modem interrupt */
+	if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) {
+		u8 dce_status;
+
+		/*
+		 * Interrupt from the modem management controller.
+		 * This will clear it -- ignored for now.
+		 */
+		mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status,
+			 sizeof(dce_status));
+#ifdef DEBUG_INTERRUPT_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n",
+		       dev->name, dce_status);
+#endif
+	}
+
+	/* Check if not controller interrupt */
+	if ((hasr & HASR_82586_INTR) == 0) {
+#ifdef DEBUG_INTERRUPT_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_interrupt(): interrupt not coming from i82586\n",
+		       dev->name);
+#endif
+		spin_unlock (&lp->spinlock);
+		return;
+	}
+
+	/* Read interrupt data. */
+	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+		   (unsigned char *) &status, sizeof(status));
+
+	/*
+	 * Acknowledge the interrupt(s).
+	 */
+	ack_cmd = status & SCB_ST_INT;
+	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+		    (unsigned char *) &ack_cmd, sizeof(ack_cmd));
+	set_chan_attn(ioaddr, lp->hacr);
+
+#ifdef DEBUG_INTERRUPT_INFO
+	printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n",
+	       dev->name, status);
+#endif
+
+	/* Command completed. */
+	if ((status & SCB_ST_CX) == SCB_ST_CX) {
+#ifdef DEBUG_INTERRUPT_INFO
+		printk(KERN_DEBUG
+		       "%s: wavelan_interrupt(): command completed.\n",
+		       dev->name);
+#endif
+		wv_complete(dev, ioaddr, lp);
+	}
+
+	/* Frame received. */
+	if ((status & SCB_ST_FR) == SCB_ST_FR) {
+#ifdef DEBUG_INTERRUPT_INFO
+		printk(KERN_DEBUG
+		       "%s: wavelan_interrupt(): received packet.\n",
+		       dev->name);
+#endif
+		wv_receive(dev);
+	}
+
+	/* Check the state of the command unit. */
+	if (((status & SCB_ST_CNA) == SCB_ST_CNA) ||
+	    (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) &&
+	     (netif_running(dev)))) {
+#ifdef DEBUG_INTERRUPT_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_interrupt(): CU inactive -- restarting\n",
+		       dev->name);
+#endif
+		wv_hw_reset(dev);
+	}
+
+	/* Check the state of the command unit. */
+	if (((status & SCB_ST_RNR) == SCB_ST_RNR) ||
+	    (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) &&
+	     (netif_running(dev)))) {
+#ifdef DEBUG_INTERRUPT_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_interrupt(): RU not ready -- restarting\n",
+		       dev->name);
+#endif
+		wv_hw_reset(dev);
+	}
+
+	/* Release spinlock */
+	spin_unlock (&lp->spinlock);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog: when we start a transmission, a timer is set for us in the
+ * kernel.  If the transmission completes, this timer is disabled. If
+ * the timer expires, we are called and we try to unlock the hardware.
+ */
+static void wavelan_watchdog(device *	dev)
+{
+	net_local *	lp = (net_local *)dev->priv;
+	u_long		ioaddr = dev->base_addr;
+	unsigned long	flags;
+	unsigned int	nreaped;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+	printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+	       dev->name);
+#endif
+
+	/* Check that we came here for something */
+	if (lp->tx_n_in_use <= 0) {
+		return;
+	}
+
+	wv_splhi(lp, &flags);
+
+	/* Try to see if some buffers are not free (in case we missed
+	 * an interrupt */
+	nreaped = wv_complete(dev, ioaddr, lp);
+
+#ifdef DEBUG_INTERRUPT_INFO
+	printk(KERN_DEBUG
+	       "%s: wavelan_watchdog(): %d reaped, %d remain.\n",
+	       dev->name, nreaped, lp->tx_n_in_use);
+#endif
+
+#ifdef DEBUG_PSA_SHOW
+	{
+		psa_t psa;
+		psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+		wv_psa_show(&psa);
+	}
+#endif
+#ifdef DEBUG_MMC_SHOW
+	wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+	wv_cu_show(dev);
+#endif
+
+	/* If no buffer has been freed */
+	if (nreaped == 0) {
+#ifdef DEBUG_INTERRUPT_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_watchdog(): cleanup failed, trying reset\n",
+		       dev->name);
+#endif
+		wv_hw_reset(dev);
+	}
+
+	/* At this point, we should have some free Tx buffer ;-) */
+	if (lp->tx_n_in_use < NTXBLOCKS - 1)
+		netif_wake_queue(dev);
+
+	wv_splx(lp, &flags);
+	
+#ifdef DEBUG_INTERRUPT_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
+}
+
+/********************* CONFIGURATION CALLBACKS *********************/
+/*
+ * Here are the functions called by the Linux networking code (NET3)
+ * for initialization, configuration and deinstallations of the 
+ * WaveLAN ISA hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "opens" the device.
+ */
+static int wavelan_open(device * dev)
+{
+	net_local *	lp = (net_local *)dev->priv;
+	unsigned long	flags;
+
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+	       (unsigned int) dev);
+#endif
+
+	/* Check irq */
+	if (dev->irq == 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n",
+		       dev->name);
+#endif
+		return -ENXIO;
+	}
+
+	if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) 
+	{
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n",
+		       dev->name);
+#endif
+		return -EAGAIN;
+	}
+
+	wv_splhi(lp, &flags);
+	
+	if (wv_hw_reset(dev) != -1) {
+		netif_start_queue(dev);
+	} else {
+		free_irq(dev->irq, dev);
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_open(): impossible to start the card\n",
+		       dev->name);
+#endif
+		wv_splx(lp, &flags);
+		return -EAGAIN;
+	}
+	wv_splx(lp, &flags);
+	
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Shut down the WaveLAN ISA card.
+ * Called by NET3 when it "closes" the device.
+ */
+static int wavelan_close(device * dev)
+{
+	net_local *lp = (net_local *) dev->priv;
+	unsigned long flags;
+
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+	       (unsigned int) dev);
+#endif
+
+	netif_stop_queue(dev);
+
+	/*
+	 * Flush the Tx and disable Rx.
+	 */
+	wv_splhi(lp, &flags);
+	wv_82586_stop(dev);
+	wv_splx(lp, &flags);
+
+	free_irq(dev->irq, dev);
+
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Probe an I/O address, and if the WaveLAN is there configure the
+ * device structure
+ * (called by wavelan_probe() and via init_module()).
+ */
+static int __init wavelan_config(device * dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	u8 irq_mask;
+	int irq;
+	net_local *lp;
+
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n",
+	       dev->name, (unsigned int) dev, ioaddr);
+#endif
+
+	/* Check IRQ argument on command line. */
+	if (dev->irq != 0) {
+		irq_mask = wv_irq_to_psa(dev->irq);
+
+		if (irq_mask == 0) {
+#ifdef DEBUG_CONFIG_ERROR
+			printk(KERN_WARNING
+			       "%s: wavelan_config(): invalid IRQ %d ignored.\n",
+			       dev->name, dev->irq);
+#endif
+			dev->irq = 0;
+		} else {
+#ifdef DEBUG_CONFIG_INFO
+			printk(KERN_DEBUG
+			       "%s: wavelan_config(): changing IRQ to %d\n",
+			       dev->name, dev->irq);
+#endif
+			psa_write(ioaddr, HACR_DEFAULT,
+				  psaoff(0, psa_int_req_no), &irq_mask, 1);
+			/* update the Wavelan checksum */
+			update_psa_checksum(dev, ioaddr, HACR_DEFAULT);
+			wv_hacr_reset(ioaddr);
+		}
+	}
+
+	psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no),
+		 &irq_mask, 1);
+	if ((irq = wv_psa_to_irq(irq_mask)) == -1) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_INFO
+		       "%s: wavelan_config(): could not wavelan_map_irq(%d).\n",
+		       dev->name, irq_mask);
+#endif
+		return -EAGAIN;
+	}
+
+	dev->irq = irq;
+
+	if (!request_region(ioaddr, sizeof(ha_t), "wavelan"))
+		return -EBUSY;
+
+	dev->mem_start = 0x0000;
+	dev->mem_end = 0x0000;
+	dev->if_port = 0;
+
+	/* Initialize device structures */
+	dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+	if (dev->priv == NULL) {
+		release_region(ioaddr, sizeof(ha_t));
+		return -ENOMEM;
+	}
+	memset(dev->priv, 0x00, sizeof(net_local));
+	lp = (net_local *) dev->priv;
+
+	/* Back link to the device structure. */
+	lp->dev = dev;
+	/* Add the device at the beginning of the linked list. */
+	lp->next = wavelan_list;
+	wavelan_list = lp;
+
+	lp->hacr = HACR_DEFAULT;
+
+	/* Multicast stuff */
+	lp->promiscuous = 0;
+	lp->mc_count = 0;
+
+	/* Init spinlock */
+	spin_lock_init(&lp->spinlock);
+
+	/*
+	 * Fill in the fields of the device structure
+	 * with generic Ethernet values.
+	 */
+	ether_setup(dev);
+
+	SET_MODULE_OWNER(dev);
+	dev->open = wavelan_open;
+	dev->stop = wavelan_close;
+	dev->hard_start_xmit = wavelan_packet_xmit;
+	dev->get_stats = wavelan_get_stats;
+	dev->set_multicast_list = &wavelan_set_multicast_list;
+        dev->tx_timeout		= &wavelan_watchdog;
+        dev->watchdog_timeo	= WATCHDOG_JIFFIES;
+#ifdef SET_MAC_ADDRESS
+	dev->set_mac_address = &wavelan_set_mac_address;
+#endif				/* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT		/* if wireless extension exists in the kernel */
+	dev->do_ioctl = wavelan_ioctl;
+	dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+	dev->mtu = WAVELAN_MTU;
+
+	/* Display nice information. */
+	wv_init_info(dev);
+
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Check for a network adaptor of this type.  Return '0' iff one 
+ * exists.  There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.
+ * (called in "Space.c")
+ */
+int __init wavelan_probe(device * dev)
+{
+	short base_addr;
+	mac_addr mac;		/* MAC address (check existence of WaveLAN) */
+	int i;
+	int r;
+
+#ifdef DEBUG_CALLBACK_TRACE
+	printk(KERN_DEBUG
+	       "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n",
+	       dev->name, (unsigned int) dev,
+	       (unsigned int) dev->base_addr);
+#endif
+
+#ifdef	STRUCT_CHECK
+	if (wv_struct_check() != (char *) NULL) {
+		printk(KERN_WARNING
+		       "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n",
+		       dev->name, wv_struct_check());
+		return -ENODEV;
+	}
+#endif				/* STRUCT_CHECK */
+
+	/* Check the value of the command line parameter for base address. */
+	base_addr = dev->base_addr;
+
+	/* Don't probe at all. */
+	if (base_addr < 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_WARNING
+		       "%s: wavelan_probe(): invalid base address\n",
+		       dev->name);
+#endif
+		return -ENXIO;
+	}
+
+	/* Check a single specified location. */
+	if (base_addr > 0x100) {
+		/* Check if there is something at this base address */
+		if ((r = wv_check_ioaddr(base_addr, mac)) == 0) {
+			memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
+			r = wavelan_config(dev);
+		}
+#ifdef DEBUG_CONFIG_INFO
+		if (r != 0)
+			printk(KERN_DEBUG
+			       "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n",
+			       dev->name, base_addr);
+#endif
+
+#ifdef DEBUG_CALLBACK_TRACE
+		printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
+#endif
+		return r;
+	}
+
+	/* Scan all possible addresses of the WaveLAN hardware. */
+	for (i = 0; i < NELS(iobase); i++) {
+		/* Check whether there is something at this base address. */
+		if (wv_check_ioaddr(iobase[i], mac) == 0) {
+			dev->base_addr = iobase[i];	/* Copy base address. */
+			memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
+			if (wavelan_config(dev) == 0) {
+#ifdef DEBUG_CALLBACK_TRACE
+				printk(KERN_DEBUG
+				       "%s: <-wavelan_probe()\n",
+				       dev->name);
+#endif
+				return 0;
+			}
+		}
+	}
+
+	/* We may have touched base_addr.  Another driver may not like it. */
+	dev->base_addr = base_addr;
+
+#ifdef DEBUG_CONFIG_INFO
+	printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n",
+	       dev->name);
+#endif
+
+	return -ENODEV;
+}
+
+/****************************** MODULE ******************************/
+/*
+ * Module entry point: insertion and removal
+ */
+
+#ifdef	MODULE
+/*------------------------------------------------------------------*/
+/*
+ * Insertion of the module
+ * I'm now quite proud of the multi-device support.
+ */
+int init_module(void)
+{
+	mac_addr mac;		/* MAC address (check WaveLAN existence) */
+	int ret = -EIO;		/* Return error if no cards found */
+	int i;
+
+#ifdef DEBUG_MODULE_TRACE
+	printk(KERN_DEBUG "-> init_module()\n");
+#endif
+
+	/* If probing is asked */
+	if (io[0] == 0) {
+#ifdef DEBUG_CONFIG_ERROR
+		printk(KERN_WARNING
+		       "WaveLAN init_module(): doing device probing (bad !)\n");
+		printk(KERN_WARNING
+		       "Specify base addresses while loading module to correct the problem\n");
+#endif
+
+		/* Copy the basic set of address to be probed. */
+		for (i = 0; i < NELS(iobase); i++)
+			io[i] = iobase[i];
+	}
+
+
+	/* Loop on all possible base addresses. */
+	i = -1;
+	while ((io[++i] != 0) && (i < NELS(io))) {
+		/* Check if there is something at this base address. */
+		if (wv_check_ioaddr(io[i], mac) == 0) {
+			device *dev;
+
+			/* Create device and set basic arguments. */
+			dev =
+			    kmalloc(sizeof(struct net_device), GFP_KERNEL);
+			if (dev == NULL) {
+				ret = -ENOMEM;
+				break;
+			}
+			memset(dev, 0x00, sizeof(struct net_device));
+			memcpy(dev->name, name[i], IFNAMSIZ);	/* Copy name */
+			dev->base_addr = io[i];
+			dev->irq = irq[i];
+			dev->init = &wavelan_config;
+			memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
+
+			/* Try to create the device. */
+			if (register_netdev(dev) != 0) {
+				/* Deallocate everything. */
+				/* Note: if dev->priv is mallocated, there is no way to fail. */
+				kfree(dev);
+			} else {
+				/* If at least one device OK, we do not fail */
+				ret = 0;
+			}
+		}		/* if there is something at the address */
+	}			/* Loop on all addresses. */
+
+#ifdef DEBUG_CONFIG_ERROR
+	if (wavelan_list == (net_local *) NULL)
+		printk(KERN_WARNING
+		       "WaveLAN init_module(): no device found\n");
+#endif
+
+#ifdef DEBUG_MODULE_TRACE
+	printk(KERN_DEBUG "<- init_module()\n");
+#endif
+	return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Removal of the module
+ */
+void cleanup_module(void)
+{
+#ifdef DEBUG_MODULE_TRACE
+	printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
+
+	/* Loop on all devices and release them. */
+	while (wavelan_list != (net_local *) NULL) {
+		device *dev = wavelan_list->dev;
+
+#ifdef DEBUG_CONFIG_INFO
+		printk(KERN_DEBUG
+		       "%s: cleanup_module(): removing device at 0x%x\n",
+		       dev->name, (unsigned int) dev);
+#endif
+
+		/* Release the ioport region. */
+		release_region(dev->base_addr, sizeof(ha_t));
+
+		/* Definitely remove the device. */
+		unregister_netdev(dev);
+
+		/* Unlink the device. */
+		wavelan_list = wavelan_list->next;
+
+		/* Free pieces. */
+		kfree(dev->priv);
+		kfree(dev);
+	}
+
+#ifdef DEBUG_MODULE_TRACE
+	printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
+}
+#endif				/* MODULE */
+MODULE_LICENSE("GPL");
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU General Public License.
+ *
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ *	Ajay Bakre (bakre@paul.rutgers.edu),
+ *	Donald Becker (becker@scyld.com),
+ *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ *	Anders Klemets (klemets@it.kth.se),
+ *	Vladimir V. Kolpakov (w@stier.koenig.ru),
+ *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ *	Pauline Middelink (middelin@polyware.iaf.nl),
+ *	Robert Morris (rtm@das.harvard.edu),
+ *	Jean Tourrilhes (jt@hplb.hpl.hp.com),
+ *	Girish Welling (welling@paul.rutgers.edu),
+ *
+ * Thanks go also to:
+ *	James Ashton (jaa101@syseng.anu.edu.au),
+ *	Alan Cox (alan@redhat.com),
+ *	Allan Creighton (allanc@cs.usyd.edu.au),
+ *	Matthew Geier (matthew@cs.usyd.edu.au),
+ *	Remo di Giovanni (remo@cs.usyd.edu.au),
+ *	Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ *	Vipul Gupta (vgupta@cs.binghamton.edu),
+ *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ *	Tim Nicholson (tim@cs.usyd.edu.au),
+ *	Ian Parkin (ian@cs.usyd.edu.au),
+ *	John Rosenberg (johnr@cs.usyd.edu.au),
+ *	George Rossi (george@phm.gov.au),
+ *	Arthur Scott (arthur@cs.usyd.edu.au),
+ *	Peter Storey,
+ * for their assistance and advice.
+ *
+ * Please send bug reports, updates, comments to:
+ *
+ * Bruce Janson                                    Email:  bruce@cs.usyd.edu.au
+ * Basser Department of Computer Science           Phone:  +61-2-9351-3423
+ * University of Sydney, N.S.W., 2006, AUSTRALIA   Fax:    +61-2-9351-3838
+ */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan.h linux/drivers/net/wireless/wavelan.h
--- linux-2.5.3-pre2/drivers/net/wireless/wavelan.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/wavelan.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,370 @@
+/*
+ *	WaveLAN ISA driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follows. See wavelan.p.h for details.
+ *
+ * This file contains the declarations for the WaveLAN hardware. Note that
+ * the WaveLAN ISA includes a i82586 controller (see definitions in
+ * file i82586.h).
+ *
+ * The main difference between the ISA hardware and the PCMCIA one is
+ * the Ethernet controller (i82586 instead of i82593).
+ * The i82586 allows multiple transmit buffers.  The PSA needs to be accessed
+ * through the host interface.
+ */
+
+#ifndef _WAVELAN_H
+#define	_WAVELAN_H
+
+/************************** MAGIC NUMBERS ***************************/
+
+/* Detection of the WaveLAN card is done by reading the MAC
+ * address from the card and checking it.  If you have a non-AT&T
+ * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
+ * you might need to modify this part to accommodate your hardware.
+ */
+static const char	MAC_ADDRESSES[][3] =
+{
+  { 0x08, 0x00, 0x0E },		/* AT&T WaveLAN (standard) & DEC RoamAbout */
+  { 0x08, 0x00, 0x6A },		/* AT&T WaveLAN (alternate) */
+  { 0x00, 0x00, 0xE1 },		/* Hitachi Wavelan */
+  { 0x00, 0x60, 0x1D }		/* Lucent Wavelan (another one) */
+  /* Add your card here and send me the patch! */
+};
+
+#define WAVELAN_ADDR_SIZE	6	/* Size of a MAC address */
+
+#define WAVELAN_MTU		1500	/* Maximum size of WaveLAN packet */
+
+#define	MAXDATAZ		(WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+
+/*
+ * Constants used to convert channels to frequencies
+ */
+
+/* Frequency available in the 2.0 modem, in units of 250 kHz
+ * (as read in the offset register of the dac area).
+ * Used to map channel numbers used by `wfreqsel' to frequencies
+ */
+static const short	channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+				    0xD0, 0xF0, 0xF8, 0x150 };
+
+/* Frequencies of the 1.0 modem (fixed frequencies).
+ * Use to map the PSA `subband' to a frequency
+ * Note : all frequencies apart from the first one need to be multiplied by 10
+ */
+static const int	fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+
+
+
+/*************************** PC INTERFACE ****************************/
+
+/*
+ * Host Adaptor structure.
+ * (base is board port address).
+ */
+typedef union hacs_u	hacs_u;
+union hacs_u
+{
+	unsigned short	hu_command;		/* Command register */
+#define		HACR_RESET		0x0001	/* Reset board */
+#define		HACR_CA			0x0002	/* Set Channel Attention for 82586 */
+#define		HACR_16BITS		0x0004	/* 16-bit operation (0 => 8bits) */
+#define		HACR_OUT0		0x0008	/* General purpose output pin 0 */
+						/* not used - must be 1 */
+#define		HACR_OUT1		0x0010	/* General purpose output pin 1 */
+						/* not used - must be 1 */
+#define		HACR_82586_INT_ENABLE	0x0020	/* Enable 82586 interrupts */
+#define		HACR_MMC_INT_ENABLE	0x0040	/* Enable MMC interrupts */
+#define		HACR_INTR_CLR_ENABLE	0x0080	/* Enable interrupt status read/clear */
+	unsigned short	hu_status;		/* Status Register */
+#define		HASR_82586_INTR		0x0001	/* Interrupt request from 82586 */
+#define		HASR_MMC_INTR		0x0002	/* Interrupt request from MMC */
+#define		HASR_MMC_BUSY		0x0004	/* MMC busy indication */
+#define		HASR_PSA_BUSY		0x0008	/* LAN parameter storage area busy */
+};
+
+typedef struct ha_t	ha_t;
+struct ha_t
+{
+	hacs_u		ha_cs;		/* Command and status registers */
+#define 		ha_command	ha_cs.hu_command
+#define 		ha_status	ha_cs.hu_status
+	unsigned short	ha_mmcr;	/* Modem Management Ctrl Register */
+	unsigned short	ha_pior0;	/* Program I/O Address Register Port 0 */
+	unsigned short	ha_piop0;	/* Program I/O Port 0 */
+	unsigned short	ha_pior1;	/* Program I/O Address Register Port 1 */
+	unsigned short	ha_piop1;	/* Program I/O Port 1 */
+	unsigned short	ha_pior2;	/* Program I/O Address Register Port 2 */
+	unsigned short	ha_piop2;	/* Program I/O Port 2 */
+};
+
+#define HA_SIZE		16
+
+#define	hoff(p,f) 	(unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
+#define	HACR(p)		hoff(p, ha_command)
+#define	HASR(p)		hoff(p, ha_status)
+#define	MMCR(p)		hoff(p, ha_mmcr)
+#define	PIOR0(p)	hoff(p, ha_pior0)
+#define	PIOP0(p)	hoff(p, ha_piop0)
+#define	PIOR1(p)	hoff(p, ha_pior1)
+#define	PIOP1(p)	hoff(p, ha_piop1)
+#define	PIOR2(p)	hoff(p, ha_pior2)
+#define	PIOP2(p)	hoff(p, ha_piop2)
+
+/*
+ * Program I/O Mode Register values.
+ */
+#define STATIC_PIO		0	/* Mode 1: static mode */
+					/* RAM access ??? */
+#define AUTOINCR_PIO		1	/* Mode 2: auto increment mode */
+					/* RAM access ??? */
+#define AUTODECR_PIO		2	/* Mode 3: auto decrement mode */
+					/* RAM access ??? */
+#define PARAM_ACCESS_PIO	3	/* Mode 4: LAN parameter access mode */
+					/* Parameter access. */
+#define PIO_MASK		3	/* register mask */
+#define PIOM(cmd,piono)		((u_short)cmd << 10 << (piono * 2))
+
+#define	HACR_DEFAULT		(HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
+#define	HACR_INTRON		(HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
+
+/************************** MEMORY LAYOUT **************************/
+
+/*
+ * Onboard 64 k RAM layout.
+ * (Offsets from 0x0000.)
+ */
+#define OFFSET_RU		0x0000		/* 75% memory */
+#define OFFSET_CU		0xC000		/* 25% memory */
+#define OFFSET_SCB		(OFFSET_ISCP - sizeof(scb_t))
+#define OFFSET_ISCP		(OFFSET_SCP - sizeof(iscp_t))
+#define OFFSET_SCP		I82586_SCP_ADDR
+
+#define	RXBLOCKZ		(sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
+#define	TXBLOCKZ		(sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
+
+#define	NRXBLOCKS		((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
+#define	NTXBLOCKS		((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
+
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t	psa_t;
+struct psa_t
+{
+  unsigned char	psa_io_base_addr_1;	/* [0x00] Base address 1 ??? */
+  unsigned char	psa_io_base_addr_2;	/* [0x01] Base address 2 */
+  unsigned char	psa_io_base_addr_3;	/* [0x02] Base address 3 */
+  unsigned char	psa_io_base_addr_4;	/* [0x03] Base address 4 */
+  unsigned char	psa_rem_boot_addr_1;	/* [0x04] Remote Boot Address 1 */
+  unsigned char	psa_rem_boot_addr_2;	/* [0x05] Remote Boot Address 2 */
+  unsigned char	psa_rem_boot_addr_3;	/* [0x06] Remote Boot Address 3 */
+  unsigned char	psa_holi_params;	/* [0x07] HOst Lan Interface (HOLI) Parameters */
+  unsigned char	psa_int_req_no;		/* [0x08] Interrupt Request Line */
+  unsigned char	psa_unused0[7];		/* [0x09-0x0F] unused */
+
+  unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x10-0x15] Universal (factory) MAC Address */
+  unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x16-1B] Local MAC Address */
+  unsigned char	psa_univ_local_sel;	/* [0x1C] Universal Local Selection */
+#define		PSA_UNIVERSAL	0		/* Universal (factory) */
+#define		PSA_LOCAL	1		/* Local */
+  unsigned char	psa_comp_number;	/* [0x1D] Compatibility Number:	 */
+#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz 	 */
+#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	 */
+#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	 */
+#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz 	 */
+#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz or 2.0 */
+  unsigned char	psa_thr_pre_set;	/* [0x1E] Modem Threshold Preset */
+  unsigned char	psa_feature_select;	/* [0x1F] Call code required (1=on) */
+#define		PSA_FEATURE_CALL_CODE	0x01 	/* Call code required (Japan) */
+  unsigned char	psa_subband;		/* [0x20] Subband	  */
+#define		PSA_SUBBAND_915		0	/* 915 MHz or 2.0 */
+#define		PSA_SUBBAND_2425	1	/* 2425 MHz	  */
+#define		PSA_SUBBAND_2460	2	/* 2460 MHz	  */
+#define		PSA_SUBBAND_2484	3	/* 2484 MHz	  */
+#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	  */
+  unsigned char	psa_quality_thr;	/* [0x21] Modem Quality Threshold */
+  unsigned char	psa_mod_delay;		/* [0x22] Modem Delay (?) (reserved) */
+  unsigned char	psa_nwid[2];		/* [0x23-0x24] Network ID */
+  unsigned char	psa_nwid_select;	/* [0x25] Network ID Select On/Off */
+  unsigned char	psa_encryption_select;	/* [0x26] Encryption On/Off */
+  unsigned char	psa_encryption_key[8];	/* [0x27-0x2E] Encryption Key */
+  unsigned char	psa_databus_width;	/* [0x2F] AT bus width select 8/16 */
+  unsigned char	psa_call_code[8];	/* [0x30-0x37] (Japan) Call Code */
+  unsigned char	psa_nwid_prefix[2];	/* [0x38-0x39] Roaming domain */
+  unsigned char	psa_reserved[2];	/* [0x3A-0x3B] Reserved - fixed 00 */
+  unsigned char	psa_conf_status;	/* [0x3C] Conf Status, bit 0=1:config*/
+  unsigned char	psa_crc[2];		/* [0x3D] CRC-16 over PSA */
+  unsigned char	psa_crc_status;		/* [0x3F] CRC Valid Flag */
+};
+
+#define	PSA_SIZE	64
+
+/* Calculate offset of a field in the above structure.
+ * Warning:  only even addresses are used. */
+#define	psaoff(p,f) 	((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t	mmw_t;
+struct mmw_t
+{
+  unsigned char	mmw_encr_key[8];	/* encryption key */
+  unsigned char	mmw_encr_enable;	/* Enable or disable encryption. */
+#define	MMW_ENCR_ENABLE_MODE	0x02	/* mode of security option */
+#define	MMW_ENCR_ENABLE_EN	0x01	/* Enable security option. */
+  unsigned char	mmw_unused0[1];		/* unused */
+  unsigned char	mmw_des_io_invert;	/* encryption option */
+#define	MMW_DES_IO_INVERT_RES	0x0F	/* reserved */
+#define	MMW_DES_IO_INVERT_CTRL	0xF0	/* control (?) (set to 0) */
+  unsigned char	mmw_unused1[5];		/* unused */
+  unsigned char	mmw_loopt_sel;		/* looptest selection */
+#define	MMW_LOOPT_SEL_DIS_NWID	0x40	/* Disable NWID filtering. */
+#define	MMW_LOOPT_SEL_INT	0x20	/* Activate Attention Request. */
+#define	MMW_LOOPT_SEL_LS	0x10	/* looptest, no collision avoidance */
+#define MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */
+#define	MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */
+#define	MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */
+#define	MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */
+  unsigned char	mmw_jabber_enable;	/* jabber timer enable */
+  /* Abort transmissions > 200 ms */
+  unsigned char	mmw_freeze;		/* freeze or unfreeze signal level */
+  /* 0 : signal level & qual updated for every new message, 1 : frozen */
+  unsigned char	mmw_anten_sel;		/* antenna selection */
+#define MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */
+#define	MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algo. enable */
+  unsigned char	mmw_ifs;		/* inter frame spacing */
+  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+  unsigned char	mmw_mod_delay;	 	/* modem delay (synchro) */
+  unsigned char	mmw_jam_time;		/* jamming time (after collision) */
+  unsigned char	mmw_unused2[1];		/* unused */
+  unsigned char	mmw_thr_pre_set;	/* level threshold preset */
+  /* Discard all packet with signal < this value (4) */
+  unsigned char	mmw_decay_prm;		/* decay parameters */
+  unsigned char	mmw_decay_updat_prm;	/* decay update parameters */
+  unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
+  /* Discard all packet with quality < this value (3) */
+  unsigned char	mmw_netw_id_l;		/* NWID low order byte */
+  unsigned char	mmw_netw_id_h;		/* NWID high order byte */
+  /* Network ID or Domain : create virtual net on the air */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char	mmw_mode_select;	/* for analog tests (set to 0) */
+  unsigned char	mmw_unused3[1];		/* unused */
+  unsigned char	mmw_fee_ctrl;		/* frequency EEPROM control */
+#define	MMW_FEE_CTRL_PRE	0x10	/* Enable protected instructions. */
+#define	MMW_FEE_CTRL_DWLD	0x08	/* Download EEPROM to mmc. */
+#define	MMW_FEE_CTRL_CMD	0x07	/* EEPROM commands:  */
+#define	MMW_FEE_CTRL_READ	0x06	/* Read */
+#define	MMW_FEE_CTRL_WREN	0x04	/* Write enable */
+#define	MMW_FEE_CTRL_WRITE	0x05	/* Write data to address. */
+#define	MMW_FEE_CTRL_WRALL	0x04	/* Write data to all addresses. */
+#define	MMW_FEE_CTRL_WDS	0x04	/* Write disable */
+#define	MMW_FEE_CTRL_PRREAD	0x16	/* Read addr from protect register */
+#define	MMW_FEE_CTRL_PREN	0x14	/* Protect register enable */
+#define	MMW_FEE_CTRL_PRCLEAR	0x17	/* Unprotect all registers. */
+#define	MMW_FEE_CTRL_PRWRITE	0x15	/* Write address in protect register */
+#define	MMW_FEE_CTRL_PRDS	0x14	/* Protect register disable */
+  /* Never issue the PRDS command:  it's irreversible! */
+
+  unsigned char	mmw_fee_addr;		/* EEPROM address */
+#define	MMW_FEE_ADDR_CHANNEL	0xF0	/* Select the channel. */
+#define	MMW_FEE_ADDR_OFFSET	0x0F	/* Offset in channel data */
+#define	MMW_FEE_ADDR_EN		0xC0	/* FEE_CTRL enable operations */
+#define	MMW_FEE_ADDR_DS		0x00	/* FEE_CTRL disable operations */
+#define	MMW_FEE_ADDR_ALL	0x40	/* FEE_CTRL all operations */
+#define	MMW_FEE_ADDR_CLEAR	0xFF	/* FEE_CTRL clear operations */
+
+  unsigned char	mmw_fee_data_l;		/* Write data to EEPROM. */
+  unsigned char	mmw_fee_data_h;		/* high octet */
+  unsigned char	mmw_ext_ant;		/* Setting for external antenna */
+#define	MMW_EXT_ANT_EXTANT	0x01	/* Select external antenna */
+#define	MMW_EXT_ANT_POL		0x02	/* Polarity of the antenna */
+#define	MMW_EXT_ANT_INTERNAL	0x00	/* Internal antenna */
+#define	MMW_EXT_ANT_EXTERNAL	0x03	/* External antenna */
+#define	MMW_EXT_ANT_IQ_TEST	0x1C	/* IQ test pattern (set to 0) */
+};
+
+#define	MMW_SIZE	37
+
+#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t	mmr_t;
+struct mmr_t
+{
+  unsigned char	mmr_unused0[8];		/* unused */
+  unsigned char	mmr_des_status;		/* encryption status */
+  unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */
+#define	MMR_DES_AVAIL_DES	0x55		/* DES available */
+#define	MMR_DES_AVAIL_AES	0x33		/* AES (AT&T) available */
+  unsigned char	mmr_des_io_invert;	/* des I/O invert register */
+  unsigned char	mmr_unused1[5];		/* unused */
+  unsigned char	mmr_dce_status;		/* DCE status */
+#define	MMR_DCE_STATUS_RX_BUSY		0x01	/* receiver busy */
+#define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
+#define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */
+#define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
+#define MMR_DCE_STATUS			0x0F	/* mask to get the bits */
+  unsigned char	mmr_dsp_id;		/* DSP ID (AA = Daedalus rev A) */
+  unsigned char	mmr_unused2[2];		/* unused */
+  unsigned char	mmr_correct_nwid_l;	/* # of correct NWIDs rxd (low) */
+  unsigned char	mmr_correct_nwid_h;	/* # of correct NWIDs rxd (high) */
+  /* Warning:  read high-order octet first! */
+  unsigned char	mmr_wrong_nwid_l;	/* # of wrong NWIDs rxd (low) */
+  unsigned char	mmr_wrong_nwid_h;	/* # of wrong NWIDs rxd (high) */
+  unsigned char	mmr_thr_pre_set;	/* level threshold preset */
+#define	MMR_THR_PRE_SET		0x3F		/* level threshold preset */
+#define	MMR_THR_PRE_SET_CUR	0x80		/* Current signal above it */
+  unsigned char	mmr_signal_lvl;		/* signal level */
+#define	MMR_SIGNAL_LVL		0x3F		/* signal level */
+#define	MMR_SIGNAL_LVL_VALID	0x80		/* Updated since last read */
+  unsigned char	mmr_silence_lvl;	/* silence level (noise) */
+#define	MMR_SILENCE_LVL		0x3F		/* silence level */
+#define	MMR_SILENCE_LVL_VALID	0x80		/* Updated since last read */
+  unsigned char	mmr_sgnl_qual;		/* signal quality */
+#define	MMR_SGNL_QUAL		0x0F		/* signal quality */
+#define	MMR_SGNL_QUAL_ANT	0x80		/* current antenna used */
+  unsigned char	mmr_netw_id_l;		/* NWID low order byte (?) */
+  unsigned char	mmr_unused3[3];		/* unused */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char	mmr_fee_status;		/* Status of frequency EEPROM */
+#define	MMR_FEE_STATUS_ID	0xF0		/* Modem revision ID */
+#define	MMR_FEE_STATUS_DWLD	0x08		/* Download in progress */
+#define	MMR_FEE_STATUS_BUSY	0x04		/* EEPROM busy */
+  unsigned char	mmr_unused4[1];		/* unused */
+  unsigned char	mmr_fee_data_l;		/* Read data from EEPROM (low) */
+  unsigned char	mmr_fee_data_h;		/* Read data from EEPROM (high) */
+};
+
+#define	MMR_SIZE	36
+
+#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+  struct mmw_t	w;	/* Write to the mmc */
+  struct mmr_t	r;	/* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_H */
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU General Public License.
+ *
+ * For more details, see wavelan.c.
+ */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan.p.h linux/drivers/net/wireless/wavelan.p.h
--- linux-2.5.3-pre2/drivers/net/wireless/wavelan.p.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/wavelan.p.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,713 @@
+/*
+ *	WaveLAN ISA driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contains all definitions and declarations necessary for the
+ * WaveLAN ISA driver.  This file is a private header, so it should
+ * be included only in wavelan.c!
+ */
+
+#ifndef WAVELAN_P_H
+#define WAVELAN_P_H
+
+/************************** DOCUMENTATION ***************************/
+/*
+ * This driver provides a Linux interface to the WaveLAN ISA hardware.
+ * The WaveLAN is a product of Lucent (http://www.wavelan.com/).
+ * This division was formerly part of NCR and then AT&T.
+ * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean.
+ *
+ * To learn how to use this driver, read the NET3 HOWTO.
+ * If you want to exploit the many other functionalities, read the comments
+ * in the code.
+ *
+ * This driver is the result of the effort of many people (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * Web page
+ * --------
+ *	I try to maintain a web page with the Wireless LAN Howto at :
+ *	    http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
+ *
+ * SMP
+ * ---
+ *	We now are SMP compliant (I eventually fixed the remaining bugs).
+ *	The driver has been tested on a dual P6-150 and survived my usual
+ *	set of torture tests.
+ *	Anyway, I spent enough time chasing interrupt re-entrancy during
+ *	errors or reconfigure, and I designed the locked/unlocked sections
+ *	of the driver with great care, and with the recent addition of
+ *	the spinlock (thanks to the new API), we should be quite close to
+ *	the truth.
+ *	The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
+ *	but better safe than sorry (especially at 2 Mb/s ;-).
+ *
+ *	I have also looked into disabling only our interrupt on the card
+ *	(via HACR) instead of all interrupts in the processor (via cli),
+ *	so that other driver are not impacted, and it look like it's
+ *	possible, but it's very tricky to do right (full of races). As
+ *	the gain would be mostly for SMP systems, it can wait...
+ *
+ * Debugging and options
+ * ---------------------
+ *	You will find below a set of '#define" allowing a very fine control
+ *	on the driver behaviour and the debug messages printed.
+ *	The main options are :
+ *	o SET_PSA_CRC, to have your card correctly recognised by
+ *	  an access point and the Point-to-Point diagnostic tool.
+ *	o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
+ *	  (otherwise we always start afresh with some defaults)
+ *
+ * wavelan.o is too darned big
+ * ---------------------------
+ *	That's true!  There is a very simple way to reduce the driver
+ *	object by 33%!  Comment out the following line:
+ *		#include <linux/wireless.h>
+ *	Other compile options can also reduce the size of it...
+ *
+ * MAC address and hardware detection:
+ * -----------------------------------
+ *	The detection code for the WaveLAN checks that the first three
+ *	octets of the MAC address fit the company code.  This type of
+ *	detection works well for AT&T cards (because the AT&T code is
+ *	hardcoded in wavelan.h), but of course will fail for other
+ *	manufacturers.
+ *
+ *	If you are sure that your card is derived from the WaveLAN,
+ *	here is the way to configure it:
+ *	1) Get your MAC address
+ *		a) With your card utilities (wfreqsel, instconf, etc.)
+ *		b) With the driver:
+ *			o compile the kernel with DEBUG_CONFIG_INFO enabled
+ *			o Boot and look the card messages
+ *	2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
+ *	3) Compile and verify
+ *	4) Send me the MAC code.  I will include it in the next version.
+ *
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first to support "wireless extensions".
+ * This set of extensions provides a standard way to control the wireless
+ * characteristics of the hardware.  Applications such as mobile IP may
+ * take advantage of it.
+ *
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
+ *
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan.c:		actual code for the driver:  C functions
+ *
+ * wavelan.p.h:		private header:  local types and variables for driver
+ *
+ * wavelan.h:		description of the hardware interface and structs
+ *
+ * i82586.h:		description of the Ethernet controller
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * This is based on information in the drivers' headers. It may not be
+ * accurate, and I guarantee only my best effort.
+ *
+ * The history of the WaveLAN drivers is as complicated as the history of
+ * the WaveLAN itself (NCR -> AT&T -> Lucent).
+ *
+ * It all started with Anders Klemets <klemets@paul.rutgers.edu>
+ * writing a WaveLAN ISA driver for the Mach microkernel.  Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modified this for the PCMCIA hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> ported these two drivers to BSDI
+ * and added specific PCMCIA support (there is currently no equivalent
+ * of the PCMCIA package under BSD).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> ported both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> ported the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started to modify Bruce's driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished this work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * 2.00 cards correctly (2.4 GHz with frequency selection).
+ * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
+ * PCMCIA package (and bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patches to the PCMCIA driver.  Later, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards.  Then, I did the same to the PCMCIA driver, and did some
+ * reorganisation.  Finally, I came back to the ISA driver to
+ * upgrade it at the same level as the PCMCIA one and reorganise
+ * the code.
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed information on the WaveLAN hardware.
+ */
+
+/* The original copyrights and literature mention others' names and
+ * credits.  I don't know what their part in this development was.
+ */
+
+/* By the way, for the copyright and legal stuff:
+ * almost everybody wrote code under the GNU or BSD license (or similar),
+ * and want their original copyright to remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody wants to take responsibility for anything, except the fame.
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ *	Ajay Bakre <bakre@paul.rutgers.edu>,
+ *	Donald Becker <becker@cesdis.gsfc.nasa.gov>,
+ *	Loeke Brederveld <Loeke.Brederveld@Utrecht.NCR.com>,
+ *	Brent Elphick <belphick@uwaterloo.ca>,
+ *	Anders Klemets <klemets@it.kth.se>,
+ *	Vladimir V. Kolpakov <w@stier.koenig.ru>,
+ *	Marc Meertens <Marc.Meertens@Utrecht.NCR.com>,
+ *	Pauline Middelink <middelin@polyware.iaf.nl>,
+ *	Robert Morris <rtm@das.harvard.edu>,
+ *	Jean Tourrilhes <jt@hpl.hp.com>,
+ *	Girish Welling <welling@paul.rutgers.edu>,
+ *	Clark Woodworth <clark@hiway1.exit109.com>
+ *	Yongguang Zhang <ygz@isl.hrl.hac.com>
+ *
+ * Thanks go also to:
+ *	James Ashton <jaa101@syseng.anu.edu.au>,
+ *	Alan Cox <alan@redhat.com>,
+ *	Allan Creighton <allanc@cs.usyd.edu.au>,
+ *	Matthew Geier <matthew@cs.usyd.edu.au>,
+ *	Remo di Giovanni <remo@cs.usyd.edu.au>,
+ *	Eckhard Grah <grah@wrcs1.urz.uni-wuppertal.de>,
+ *	Vipul Gupta <vgupta@cs.binghamton.edu>,
+ *	Mark Hagan <mhagan@wtcpost.daytonoh.NCR.COM>,
+ *	Tim Nicholson <tim@cs.usyd.edu.au>,
+ *	Ian Parkin <ian@cs.usyd.edu.au>,
+ *	John Rosenberg <johnr@cs.usyd.edu.au>,
+ *	George Rossi <george@phm.gov.au>,
+ *	Arthur Scott <arthur@cs.usyd.edu.au>,
+ *	Stanislav Sinyagin <stas@isf.ru>
+ *	and Peter Storey for their assistance and advice.
+ *
+ * Additional Credits:
+ *
+ *	My development has been done initially under Debian 1.1 (Linux 2.0.x)
+ *	and now	under Debian 2.2, initially with an HP Vectra XP/60, and now
+ *	an HP Vectra XP/90.
+ *
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present:
+ *
+ * Changes made in first pre-release:
+ * ----------------------------------
+ *	- reorganisation of the code, function name change
+ *	- creation of private header (wavelan.p.h)
+ *	- reorganised debug messages
+ *	- more comments, history, etc.
+ *	- mmc_init:  configure the PSA if not done
+ *	- mmc_init:  correct default value of level threshold for PCMCIA
+ *	- mmc_init:  2.00 detection better code for 2.00 initialization
+ *	- better info at startup
+ *	- IRQ setting (note:  this setting is permanent)
+ *	- watchdog:  change strategy (and solve module removal problems)
+ *	- add wireless extensions (ioctl and get_wireless_stats)
+ *	  get/set nwid/frequency on fly, info for /proc/net/wireless
+ *	- more wireless extensions:  SETSPY and GETSPY
+ *	- make wireless extensions optional
+ *	- private ioctl to set/get quality and level threshold, histogram
+ *	- remove /proc/net/wavelan
+ *	- suppress useless stuff from lp (net_local)
+ *	- kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ *	- add message level (debug stuff in /var/adm/debug and errors not
+ *	  displayed at console and still in /var/adm/messages)
+ *	- multi device support
+ *	- start fixing the probe (init code)
+ *	- more inlines
+ *	- man page
+ *	- many other minor details and cleanups
+ *
+ * Changes made in second pre-release:
+ * -----------------------------------
+ *	- clean up init code (probe and module init)
+ *	- better multiple device support (module)
+ *	- name assignment (module)
+ *
+ * Changes made in third pre-release:
+ * ----------------------------------
+ *	- be more conservative on timers
+ *	- preliminary support for multicast (I still lack some details)
+ *
+ * Changes made in fourth pre-release:
+ * -----------------------------------
+ *	- multicast (revisited and finished)
+ *	- avoid reset in set_multicast_list (a really big hack)
+ *	  if somebody could apply this code for other i82586 based drivers
+ *	- share onboard memory 75% RU and 25% CU (instead of 50/50)
+ *
+ * Changes made for release in 2.1.15:
+ * -----------------------------------
+ *	- change the detection code for multi manufacturer code support
+ *
+ * Changes made for release in 2.1.17:
+ * -----------------------------------
+ *	- update to wireless extensions changes
+ *	- silly bug in card initial configuration (psa_conf_status)
+ *
+ * Changes made for release in 2.1.27 & 2.0.30:
+ * --------------------------------------------
+ *	- small bug in debug code (probably not the last one...)
+ *	- remove extern keyword for wavelan_probe()
+ *	- level threshold is now a standard wireless extension (version 4 !)
+ *	- modules parameters types (new module interface)
+ *
+ * Changes made for release in 2.1.36:
+ * -----------------------------------
+ *	- byte count stats (courtesy of David Hinds)
+ *	- remove dev_tint stuff (courtesy of David Hinds)
+ *	- encryption setting from Brent Elphick (thanks a lot!)
+ *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
+ * Other changes (not by me) :
+ * -------------------------
+ *	- Spelling and gramar "rectification".
+ *
+ * Changes made for release in 2.0.37 & 2.2.2 :
+ * ------------------------------------------
+ *	- Correct status in /proc/net/wireless
+ *	- Set PSA CRC to make PtP diagnostic tool happy (Bob Gray)
+ *	- Module init code don't fail if we found at least one card in
+ *	  the address list (Karlis Peisenieks)
+ *	- Missing parenthesis (Christopher Peterson)
+ *	- Correct i82586 configuration parameters
+ *	- Encryption initialisation bug (Robert McCormack)
+ *	- New mac addresses detected in the probe
+ *	- Increase watchdog for busy environments
+ *
+ * Changes made for release in 2.0.38 & 2.2.7 :
+ * ------------------------------------------
+ *	- Correct the reception logic to better report errors and avoid
+ *	  sending bogus packet up the stack
+ *	- Delay RU config to avoid corrupting first received packet
+ *	- Change config completion code (to actually check something)
+ *	- Avoid reading out of bound in skbuf to transmit
+ *	- Rectify a lot of (useless) debugging code
+ *	- Change the way to `#ifdef SET_PSA_CRC'
+ *
+ * Changes made for release in 2.2.11 & 2.3.13 :
+ * -------------------------------------------
+ *	- Change e-mail and web page addresses
+ *	- Watchdog timer is now correctly expressed in HZ, not in jiffies
+ *	- Add channel number to the list of frequencies in range
+ *	- Add the (short) list of bit-rates in range
+ *	- Developp a new sensitivity... (sens.value & sens.fixed)
+ *
+ * Changes made for release in 2.2.14 & 2.3.23 :
+ * -------------------------------------------
+ *	- Fix check for root permission (break instead of exit)
+ *	- New nwid & encoding setting (Wireless Extension 9)
+ *
+ * Changes made for release in 2.3.49 :
+ * ----------------------------------
+ *	- Indentation reformating (Alan)
+ *	- Update to new network API (softnet - 2.3.43) :
+ *		o replace dev->tbusy (Alan)
+ *		o replace dev->tstart (Alan)
+ *		o remove dev->interrupt (Alan)
+ *		o add SMP locking via spinlock in splxx (me)
+ *		o add spinlock in interrupt handler (me)
+ *		o use kernel watchdog instead of ours (me)
+ *		o increase watchdog timeout (kernel is more sensitive) (me)
+ *		o verify that all the changes make sense and work (me)
+ *	- Fixup a potential gotcha when reconfiguring and thighten a bit
+ *		the interactions with Tx queue.
+ *
+ * Changes made for release in 2.4.0 :
+ * ---------------------------------
+ *	- Fix spinlock stupid bugs that I left in. The driver is now SMP
+ *		compliant and doesn't lockup at startup.
+ *
+ * Wishes & dreams:
+ * ----------------
+ *	- roaming (see Pcmcia driver)
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include	<linux/module.h>
+
+#include	<linux/kernel.h>
+#include	<linux/sched.h>
+#include	<linux/types.h>
+#include	<linux/fcntl.h>
+#include	<linux/interrupt.h>
+#include	<linux/stat.h>
+#include	<linux/ptrace.h>
+#include	<linux/ioport.h>
+#include	<linux/in.h>
+#include	<linux/string.h>
+#include	<linux/delay.h>
+#include	<asm/system.h>
+#include	<asm/bitops.h>
+#include	<asm/io.h>
+#include	<asm/dma.h>
+#include	<asm/uaccess.h>
+#include	<linux/errno.h>
+#include	<linux/netdevice.h>
+#include	<linux/etherdevice.h>
+#include	<linux/skbuff.h>
+#include	<linux/slab.h>
+#include	<linux/timer.h>
+#include	<linux/init.h>
+
+#include <linux/wireless.h>		/* Wireless extensions */
+
+/* WaveLAN declarations */
+#include	"i82586.h"
+#include	"wavelan.h"
+
+/************************** DRIVER OPTIONS **************************/
+/*
+ * `#define' or `#undef' the following constant to change the behaviour
+ * of the driver...
+ */
+#undef SET_PSA_CRC		/* Calculate and set the CRC on PSA (slower) */
+#define USE_PSA_CONFIG		/* Use info from the PSA. */
+#undef STRUCT_CHECK		/* Verify padding of structures. */
+#undef EEPROM_IS_PROTECTED	/* doesn't seem to be necessary */
+#define MULTICAST_AVOID		/* Avoid extra multicast (I'm sceptical). */
+#undef SET_MAC_ADDRESS		/* Experimental */
+
+#ifdef WIRELESS_EXT	/* If wireless extensions exist in the kernel */
+/* Warning:  this stuff will slow down the driver. */
+#define WIRELESS_SPY		/* Enable spying addresses. */
+#undef HISTOGRAM		/* Enable histogram of signal level. */
+#endif
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE	/* module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE	/* calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE	/* calls to handler */
+#undef DEBUG_INTERRUPT_INFO	/* type of interrupt and so on */
+#define DEBUG_INTERRUPT_ERROR	/* problems */
+#undef DEBUG_CONFIG_TRACE	/* Trace the config functions. */
+#undef DEBUG_CONFIG_INFO	/* what's going on */
+#define DEBUG_CONFIG_ERROR	/* errors on configuration */
+#undef DEBUG_TX_TRACE		/* transmission calls */
+#undef DEBUG_TX_INFO		/* header of the transmitted packet */
+#undef DEBUG_TX_FAIL		/* Normal failure conditions */
+#define DEBUG_TX_ERROR		/* Unexpected conditions */
+#undef DEBUG_RX_TRACE		/* transmission calls */
+#undef DEBUG_RX_INFO		/* header of the received packet */
+#undef DEBUG_RX_FAIL		/* Normal failure conditions */
+#define DEBUG_RX_ERROR		/* Unexpected conditions */
+
+#undef DEBUG_PACKET_DUMP	/* Dump packet on the screen if defined to 32. */
+#undef DEBUG_IOCTL_TRACE	/* misc. call by Linux */
+#undef DEBUG_IOCTL_INFO		/* various debugging info */
+#define DEBUG_IOCTL_ERROR	/* what's going wrong */
+#define DEBUG_BASIC_SHOW	/* Show basic startup info. */
+#undef DEBUG_VERSION_SHOW	/* Print version info. */
+#undef DEBUG_PSA_SHOW		/* Dump PSA to screen. */
+#undef DEBUG_MMC_SHOW		/* Dump mmc to screen. */
+#undef DEBUG_SHOW_UNUSED	/* Show unused fields too. */
+#undef DEBUG_I82586_SHOW	/* Show i82586 status. */
+#undef DEBUG_DEVICE_SHOW	/* Show device parameters. */
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char	*version	= "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n";
+#endif
+
+/* Watchdog temporisation */
+#define	WATCHDOG_JIFFIES	(512*HZ/100)
+
+/* Macro to get the number of elements in an array */
+#define	NELS(a)				(sizeof(a) / sizeof(a[0]))
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+#define SIOCSIPQTHR	SIOCIWFIRSTPRIV		/* Set quality threshold */
+#define SIOCGIPQTHR	SIOCIWFIRSTPRIV + 1	/* Get quality threshold */
+#define SIOCSIPLTHR	SIOCIWFIRSTPRIV + 2	/* Set level threshold */
+#define SIOCGIPLTHR	SIOCIWFIRSTPRIV + 3	/* Get level threshold */
+
+#define SIOCSIPHISTO	SIOCIWFIRSTPRIV + 6	/* Set histogram ranges */
+#define SIOCGIPHISTO	SIOCIWFIRSTPRIV + 7	/* Get histogram values */
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct net_device		device;
+typedef struct net_device_stats	en_stats;
+typedef struct iw_statistics	iw_stats;
+typedef struct iw_quality	iw_qual;
+typedef struct iw_freq		iw_freq;
+typedef struct net_local	net_local;
+typedef struct timer_list	timer_list;
+
+/* Basic types */
+typedef u_char		mac_addr[WAVELAN_ADDR_SIZE];	/* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keeps data in two structures:  "device"
+ * keeps the generic data (same format for everybody) and "net_local" keeps
+ * additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+  net_local *	next;		/* linked list of the devices */
+  device *	dev;		/* reverse link */
+  spinlock_t	spinlock;	/* Serialize access to the hardware (SMP) */
+  en_stats	stats;		/* Ethernet interface statistics */
+  int		nresets;	/* number of hardware resets */
+  u_char	reconfig_82586;	/* We need to reconfigure the controller. */
+  u_char	promiscuous;	/* promiscuous mode */
+  int		mc_count;	/* number of multicast addresses */
+  u_short	hacr;		/* current host interface state */
+
+  int		tx_n_in_use;
+  u_short	rx_head;
+  u_short	rx_last;
+  u_short	tx_first_free;
+  u_short	tx_first_in_use;
+
+#ifdef WIRELESS_EXT
+  iw_stats	wstats;		/* Wireless-specific statistics */
+#endif
+
+#ifdef WIRELESS_SPY
+  int		spy_number;			/* number of addresses to spy */
+  mac_addr	spy_address[IW_MAX_SPY];	/* the addresses to spy */
+  iw_qual	spy_stat[IW_MAX_SPY];		/* statistics gathered */
+#endif	/* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+  int		his_number;		/* number of intervals */
+  u_char	his_range[16];		/* boundaries of interval ]n-1; n] */
+  u_long	his_sum[16];		/* sum in interval */
+#endif	/* HISTOGRAM */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- MISC. SUBROUTINES ------------------------ */
+static inline void
+	wv_splhi(net_local *,		/* Disable interrupts, lock driver */
+		 unsigned long *);	/* flags */
+static inline void
+	wv_splx(net_local *,		/* Enable interrupts, unlock driver */
+		unsigned long *);	/* flags */
+static u_char
+	wv_irq_to_psa(int);
+static int
+	wv_psa_to_irq(u_char);
+/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
+static inline u_short		/* data */
+	hasr_read(u_long);	/* Read the host interface:  base address */
+static inline void
+	hacr_write(u_long,	/* Write to host interface:  base address */
+		   u_short),	/* data */
+	hacr_write_slow(u_long,
+		   u_short),
+	set_chan_attn(u_long,	/* ioaddr */
+		      u_short),	/* hacr   */
+	wv_hacr_reset(u_long),	/* ioaddr */
+	wv_16_off(u_long,	/* ioaddr */
+		  u_short),	/* hacr   */
+	wv_16_on(u_long,	/* ioaddr */
+		 u_short),	/* hacr   */
+	wv_ints_off(device *),
+	wv_ints_on(device *);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static void
+	psa_read(u_long,	/* Read the Parameter Storage Area. */
+		 u_short,	/* hacr */
+		 int,		/* offset in PSA */
+		 u_char *,	/* buffer to fill */
+		 int),		/* size to read */
+	psa_write(u_long, 	/* Write to the PSA. */
+		  u_short,	/* hacr */
+		  int,		/* offset in PSA */
+		  u_char *,	/* buffer in memory */
+		  int);		/* length of buffer */
+static inline void
+	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control. */
+		u_short,
+		u_char),
+	mmc_write(u_long,	/* Write n bytes to the MMC. */
+		  u_char,
+		  u_char *,
+		  int);
+static inline u_char		/* Read 1 byte from the MMC. */
+	mmc_in(u_long,
+	       u_short);
+static inline void
+	mmc_read(u_long,	/* Read n bytes from the MMC. */
+		 u_char,
+		 u_char *,
+		 int),
+	fee_wait(u_long,	/* Wait for frequency EEPROM:  base address */
+		 int,		/* base delay to wait for */
+		 int);		/* time to wait */
+static void
+	fee_read(u_long,	/* Read the frequency EEPROM:  base address */
+		 u_short,	/* destination offset */
+		 u_short *,	/* data buffer */
+		 int);		/* number of registers */
+/* ---------------------- I82586 SUBROUTINES ----------------------- */
+static /*inline*/ void
+	obram_read(u_long,	/* ioaddr */
+		   u_short,	/* o */
+		   u_char *,	/* b */
+		   int);	/* n */
+static inline void
+	obram_write(u_long,	/* ioaddr */
+		    u_short,	/* o */
+		    u_char *,	/* b */
+		    int);	/* n */
+static void
+	wv_ack(device *);
+static inline int
+	wv_synchronous_cmd(device *,
+			   const char *),
+	wv_config_complete(device *,
+			   u_long,
+			   net_local *);
+static int
+	wv_complete(device *,
+		    u_long,
+		    net_local *);
+static inline void
+	wv_82586_reconfig(device *);
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+#ifdef DEBUG_I82586_SHOW
+static void
+	wv_scb_show(unsigned short);
+#endif
+static inline void
+	wv_init_info(device *);	/* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats	*
+	wavelan_get_stats(device *);	/* Give stats /proc/net/dev */
+static void
+	wavelan_set_multicast_list(device *);
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline void
+	wv_packet_read(device *,	/* Read a packet from a frame. */
+		       u_short,
+		       int),
+	wv_receive(device *);	/* Read all packets waiting. */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline int
+	wv_packet_write(device *,	/* Write a packet to the Tx buffer. */
+			void *,
+			short);
+static int
+	wavelan_packet_xmit(struct sk_buff *,	/* Send a packet. */
+			    device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+	wv_mmc_init(device *),		/* Initialize the modem. */
+	wv_ru_start(device *),		/* Start the i82586 receiver unit. */
+	wv_cu_start(device *),		/* Start the i82586 command unit. */
+	wv_82586_start(device *);	/* Start the i82586. */
+static void
+	wv_82586_config(device *);	/* Configure the i82586. */
+static inline void
+	wv_82586_stop(device *);
+static int
+	wv_hw_reset(device *),		/* Reset the WaveLAN hardware. */
+	wv_check_ioaddr(u_long,		/* ioaddr */
+			u_char *);	/* mac address (read) */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+	wavelan_interrupt(int,		/* interrupt handler */
+			  void *,
+			  struct pt_regs *);
+static void
+	wavelan_watchdog(device *);	/* transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+	wavelan_open(device *),		/* Open the device. */
+	wavelan_close(device *),	/* Close the device. */
+	wavelan_config(device *);	/* Configure one device. */
+extern int
+	wavelan_probe(device *);	/* See Space.c. */
+
+/**************************** VARIABLES ****************************/
+
+/*
+ * This is the root of the linked list of WaveLAN drivers
+ * It is use to verify that we don't reuse the same base address
+ * for two different drivers and to clean up when removing the module.
+ */
+static net_local *	wavelan_list	= (net_local *) NULL;
+
+/*
+ * This table is used to translate the PSA value to IRQ number
+ * and vice versa.
+ */
+static u_char	irqvals[]	=
+{
+	   0,    0,    0, 0x01,
+	0x02, 0x04,    0, 0x08,
+	   0,    0, 0x10, 0x20,
+	0x40,    0,    0, 0x80,
+};
+
+/*
+ * Table of the available I/O addresses (base addresses) for WaveLAN
+ */
+static unsigned short	iobase[]	=
+{
+#if	0
+  /* Leave out 0x3C0 for now -- seems to clash with some video
+   * controllers.
+   * Leave out the others too -- we will always use 0x390 and leave
+   * 0x300 for the Ethernet device.
+   * Jean II:  0x3E0 is fine as well.
+   */
+  0x300, 0x390, 0x3E0, 0x3C0
+#endif	/* 0 */
+  0x390, 0x3E0
+};
+
+#ifdef	MODULE
+/* Parameters set by insmod */
+static int	io[4];
+static int	irq[4];
+static char	name[4][IFNAMSIZ];
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(irq, "1-4i");
+MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ));
+MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required");
+MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)");
+MODULE_PARM_DESC(name, "WaveLAN interface neme(s)");
+#endif	/* MODULE */
+
+#endif	/* WAVELAN_P_H */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.c linux/drivers/net/wireless/wavelan_cs.c
--- linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/wavelan_cs.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,4837 @@
+/*
+ *	Wavelan Pcmcia driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follow. See wavelan_cs.p.h for details.
+ *
+ * This code is derived from Anthony D. Joseph's code and all the changes here
+ * are also under the original copyright below.
+ *
+ * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and
+ * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services
+ *
+ * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added
+ * critical code in the routine to initialize the Modem Management Controller.
+ *
+ * Thanks to Alan Cox and Bruce Janson for their advice.
+ *
+ *	-- Yunzhou Li (scip4166@nus.sg)
+ *
+#ifdef WAVELAN_ROAMING	
+ * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu)
+ * based on patch by Joe Finney from Lancaster University.
+#endif
+ *
+ * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An
+ * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor.
+ *
+ *   A non-shared memory PCMCIA ethernet driver for linux
+ *
+ * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu)
+ *
+ *
+ * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu)
+ *
+ * Apr 2 '98  made changes to bring the i82593 control/int handling in line
+ *             with offical specs...
+ *
+ ****************************************************************************
+ *   Copyright 1995
+ *   Anthony D. Joseph
+ *   Massachusetts Institute of Technology
+ *
+ *   Permission to use, copy, modify, and distribute this program
+ *   for any purpose and without fee is hereby granted, provided
+ *   that this copyright and permission notice appear on all copies
+ *   and supporting documentation, the name of M.I.T. not be used
+ *   in advertising or publicity pertaining to distribution of the
+ *   program without specific prior permission, and notice be given
+ *   in supporting documentation that copying and distribution is
+ *   by permission of M.I.T.  M.I.T. makes no representations about
+ *   the suitability of this software for any purpose.  It is pro-
+ *   vided "as is" without express or implied warranty.         
+ ****************************************************************************
+ *
+ */
+
+#include "wavelan_cs.p.h"		/* Private header */
+
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (wavelan modem or i82593)
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts.
+ * (note : inline, so optimised away)
+ */
+static inline void
+wv_splhi(net_local *		lp,
+	 unsigned long *	pflags)
+{
+  spin_lock_irqsave(&lp->spinlock, *pflags);
+  /* Note : above does the cli(); itself */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(net_local *		lp,
+	unsigned long *		pflags)
+{
+  spin_unlock_irqrestore(&lp->spinlock, *pflags);
+
+  /* Note : enabling interrupts on the hardware is done in wv_ru_start()
+   * via : outb(OP1_INT_ENABLE, LCCR(base));
+   */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for reporting error to cardservices
+ */
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *
+wv_structuct_check(void)
+{
+#define	SC(t,s,n)	if (sizeof(t) != s) return(n);
+
+  SC(psa_t, PSA_SIZE, "psa_t");
+  SC(mmw_t, MMW_SIZE, "mmw_t");
+  SC(mmr_t, MMR_SIZE, "mmr_t");
+
+#undef	SC
+
+  return((char *) NULL);
+} /* wv_structuct_check */
+#endif	/* STRUCT_CHECK */
+
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Useful subroutines to manage the modem of the wavelan
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u_char
+hasr_read(u_long	base)
+{
+  return(inb(HASR(base)));
+} /* hasr_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void
+hacr_write(u_long	base,
+	   u_char	hacr)
+{
+  outb(hacr, HACR(base));
+} /* hacr_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void
+hacr_write_slow(u_long	base,
+		u_char	hacr)
+{
+  hacr_write(base, hacr);
+  /* delay might only be needed sometimes */
+  mdelay(1);
+} /* hacr_write_slow */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+static void
+psa_read(device *	dev,
+	 int		o,	/* offset in PSA */
+	 u_char *	b,	/* buffer to fill */
+	 int		n)	/* size to read */
+{
+  u_char *	ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1);
+
+  while(n-- > 0)
+    {
+      *b++ = readb(ptr);
+      /* Due to a lack of address decode pins, the WaveLAN PCMCIA card
+       * only supports reading even memory addresses. That means the
+       * increment here MUST be two.
+       * Because of that, we can't use memcpy_fromio()...
+       */
+      ptr += 2;
+    }
+} /* psa_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write the Paramter Storage Area to the WaveLAN card's memory
+ */
+static void
+psa_write(device *	dev,
+	  int		o,	/* Offset in psa */
+	  u_char *	b,	/* Buffer in memory */
+	  int		n)	/* Length of buffer */
+{
+  u_char *	ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1);
+  int		count = 0;
+  ioaddr_t	base = dev->base_addr;
+  /* As there seem to have no flag PSA_BUSY as in the ISA model, we are
+   * oblige to verify this address to know when the PSA is ready... */
+  volatile u_char *	verify = ((u_char *) dev->mem_start) + PSA_ADDR +
+    (psaoff(0, psa_comp_number) << 1);
+
+  /* Authorize writting to PSA */
+  hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN);
+
+  while(n-- > 0)
+    {
+      /* write to PSA */
+      writeb(*b++, ptr);
+      ptr += 2;
+
+      /* I don't have the spec, so I don't know what the correct
+       * sequence to write is. This hack seem to work for me... */
+      count = 0;
+      while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100))
+	mdelay(1);
+    }
+
+  /* Put the host interface back in standard state */
+  hacr_write(base, HACR_DEFAULT);
+} /* psa_write */
+
+#ifdef SET_PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static u_short
+psa_crc(unsigned char *	psa,	/* The PSA */
+	int		size)	/* Number of short for CRC */
+{
+  int		byte_cnt;	/* Loop on the PSA */
+  u_short	crc_bytes = 0;	/* Data in the PSA */
+  int		bit_cnt;	/* Loop on the bits of the short */
+
+  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
+    {
+      crc_bytes ^= psa[byte_cnt];	/* Its an xor */
+
+      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
+	{
+	  if(crc_bytes & 0x0001)
+	    crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+	  else
+	    crc_bytes >>= 1 ;
+        }
+    }
+
+  return crc_bytes;
+} /* psa_crc */
+#endif	/* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void
+update_psa_checksum(device *	dev)
+{
+#ifdef SET_PSA_CRC
+  psa_t		psa;
+  u_short	crc;
+
+  /* read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /* update the checksum */
+  crc = psa_crc((unsigned char *) &psa,
+		sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
+		- sizeof(psa.psa_crc_status));
+
+  psa.psa_crc[0] = crc & 0xFF;
+  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+  /* Write it ! */
+  psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
+	    (unsigned char *)&psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+  /* Check again (luxury !) */
+  crc = psa_crc((unsigned char *) &psa,
+		 sizeof(psa) - sizeof(psa.psa_crc_status));
+
+  if(crc != 0)
+    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+#endif	/* SET_PSA_CRC */
+} /* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void
+mmc_out(u_long		base,
+	u_short		o,
+	u_char		d)
+{
+  /* Wait for MMC to go idle */
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+
+  outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
+  outb(d, MMD(base));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_write(u_long	base,
+	  u_char	o,
+	  u_char *	b,
+	  int		n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0 )
+    mmc_out(base, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read 1 byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory...
+ */
+static inline u_char
+mmc_in(u_long	base,
+       u_short	o)
+{
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+  outb(o << 1, MMR(base));		/* Set the read address */
+
+  outb(0, MMD(base));			/* Required dummy write */
+
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+  return (u_char) (inb(MMD(base)));	/* Now do the actual read */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_read(u_long		base,
+	 u_char		o,
+	 u_char *	b,
+	 int		n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0)
+    *(--b) = mmc_in(base, --o);
+} /* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available...
+ */
+static inline int
+mmc_encr(u_long		base)	/* i/o port of the card */
+{
+  int	temp;
+
+  temp = mmc_in(base, mmroff(0, mmr_des_avail));
+  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+    return 0;
+  else
+    return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEprom to complete a command...
+ * I hope this one will be optimally inlined...
+ */
+static inline void
+fee_wait(u_long		base,	/* i/o port of the card */
+	 int		delay,	/* Base delay to wait for */
+	 int		number)	/* Number of time to wait */
+{
+  int		count = 0;	/* Wait only a limited time */
+
+  while((count++ < number) &&
+	(mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
+    udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEprom (frequency select cards).
+ */
+static void
+fee_read(u_long		base,	/* i/o port of the card */
+	 u_short	o,	/* destination offset */
+	 u_short *	b,	/* data buffer */
+	 int		n)	/* number of registers */
+{
+  b += n;		/* Position at the end of the area */
+
+  /* Write the address */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the read command */
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
+
+      /* Wait until EEprom is ready (should be quick !) */
+      fee_wait(base, 10, 100);
+
+      /* Read the value */
+      *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
+	      mmc_in(base, mmroff(0, mmr_fee_data_l)));
+    }
+}
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEprom (frequency select cards).
+ * This is a bit complicated, because the frequency eeprom has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void
+fee_write(u_long	base,	/* i/o port of the card */
+	  u_short	o,	/* destination offset */
+	  u_short *	b,	/* data buffer */
+	  int		n)	/* number of registers */
+{
+  b += n;		/* Position at the end of the area */
+
+#ifdef EEPROM_IS_PROTECTED	/* disabled */
+#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
+  /* Ask to read the protected register */
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+  fee_wait(base, 10, 100);
+
+  /* Read the protected register */
+  printk("Protected 2 : %02X-%02X\n",
+	 mmc_in(base, mmroff(0, mmr_fee_data_h)),
+	 mmc_in(base, mmroff(0, mmr_fee_data_l)));
+#endif	/* DOESNT_SEEM_TO_WORK */
+
+  /* Enable protected register */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+  fee_wait(base, 10, 100);
+
+  /* Unprotect area */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
+  /* Or use : */
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif	/* DOESNT_SEEM_TO_WORK */
+
+  fee_wait(base, 10, 100);
+#endif	/* EEPROM_IS_PROTECTED */
+
+  /* Write enable */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+  fee_wait(base, 10, 100);
+
+  /* Write the EEprom address */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the value */
+      mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+      mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+      /* Write the write command */
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
+
+      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
+      mdelay(10);
+      fee_wait(base, 10, 100);
+    }
+
+  /* Write disable */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
+
+  fee_wait(base, 10, 100);
+
+#ifdef EEPROM_IS_PROTECTED	/* disabled */
+  /* Reprotect EEprom */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+
+  fee_wait(base, 10, 100);
+#endif	/* EEPROM_IS_PROTECTED */
+}
+#endif	/* WIRELESS_EXT */
+
+/******************* WaveLAN Roaming routines... ********************/
+
+#ifdef WAVELAN_ROAMING	/* Conditional compile, see wavelan_cs.h */
+
+unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
+  
+void wv_roam_init(struct net_device *dev)
+{
+  net_local  *lp= (net_local *)dev->priv;
+
+  /* Do not remove this unless you have a good reason */
+  printk(KERN_NOTICE "%s: Warning, you have enabled roaming on"
+	 " device %s !\n", dev->name, dev->name);
+  printk(KERN_NOTICE "Roaming is currently an experimental unsupported feature"
+	 " of the Wavelan driver.\n");
+  printk(KERN_NOTICE "It may work, but may also make the driver behave in"
+	 " erratic ways or crash.\n");
+
+  lp->wavepoint_table.head=NULL;           /* Initialise WavePoint table */
+  lp->wavepoint_table.num_wavepoints=0;
+  lp->wavepoint_table.locked=0;
+  lp->curr_point=NULL;                        /* No default WavePoint */
+  lp->cell_search=0;
+  
+  lp->cell_timer.data=(long)lp;               /* Start cell expiry timer */
+  lp->cell_timer.function=wl_cell_expiry;
+  lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
+  add_timer(&lp->cell_timer);
+  
+  wv_nwid_filter(NWID_PROMISC,lp) ;    /* Enter NWID promiscuous mode */
+  /* to build up a good WavePoint */
+                                           /* table... */
+  printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
+}
+ 
+void wv_roam_cleanup(struct net_device *dev)
+{
+  wavepoint_history *ptr,*old_ptr;
+  net_local *lp= (net_local *)dev->priv;
+  
+  printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name);
+  
+  /* Fixme : maybe we should check that the timer exist before deleting it */
+  del_timer(&lp->cell_timer);          /* Remove cell expiry timer       */
+  ptr=lp->wavepoint_table.head;        /* Clear device's WavePoint table */
+  while(ptr!=NULL)
+    {
+      old_ptr=ptr;
+      ptr=ptr->next;	
+      wl_del_wavepoint(old_ptr,lp);	
+    }
+}
+
+/* Enable/Disable NWID promiscuous mode on a given device */
+void wv_nwid_filter(unsigned char mode, net_local *lp)
+{
+  mm_t                  m;
+  unsigned long         flags;
+  
+#ifdef WAVELAN_ROAMING_DEBUG
+  printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name);
+#endif
+  
+  /* Disable interrupts & save flags */
+  wv_splhi(lp, &flags);
+  
+  m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00;
+  mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1);
+  
+  if(mode==NWID_PROMISC)
+    lp->cell_search=1;
+  else
+    lp->cell_search=0;
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(lp, &flags);
+}
+
+/* Find a record in the WavePoint table matching a given NWID */
+wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
+{
+  wavepoint_history	*ptr=lp->wavepoint_table.head;
+  
+  while(ptr!=NULL){
+    if(ptr->nwid==nwid)
+      return ptr;	
+    ptr=ptr->next;
+  }
+  return NULL;
+}
+
+/* Create a new wavepoint table entry */
+wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
+{
+  wavepoint_history *new_wavepoint;
+
+#ifdef WAVELAN_ROAMING_DEBUG	
+  printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid);
+#endif
+  
+  if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS)
+    return NULL;
+  
+  new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
+  if(new_wavepoint==NULL)
+    return NULL;
+  
+  new_wavepoint->nwid=nwid;                       /* New WavePoints NWID */
+  new_wavepoint->average_fast=0;                    /* Running Averages..*/
+  new_wavepoint->average_slow=0;
+  new_wavepoint->qualptr=0;                       /* Start of ringbuffer */
+  new_wavepoint->last_seq=seq-1;                /* Last sequence no.seen */
+  memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */
+  
+  new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */
+  new_wavepoint->prev=NULL;
+  
+  if(lp->wavepoint_table.head!=NULL)
+    lp->wavepoint_table.head->prev=new_wavepoint;
+  
+  lp->wavepoint_table.head=new_wavepoint;
+  
+  lp->wavepoint_table.num_wavepoints++;     /* no. of visible wavepoints */
+  
+  return new_wavepoint;
+}
+
+/* Remove a wavepoint entry from WavePoint table */
+void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
+{
+  if(wavepoint==NULL)
+    return;
+  
+  if(lp->curr_point==wavepoint)
+    lp->curr_point=NULL;
+  
+  if(wavepoint->prev!=NULL)
+    wavepoint->prev->next=wavepoint->next;
+  
+  if(wavepoint->next!=NULL)
+    wavepoint->next->prev=wavepoint->prev;
+  
+  if(lp->wavepoint_table.head==wavepoint)
+    lp->wavepoint_table.head=wavepoint->next;
+  
+  lp->wavepoint_table.num_wavepoints--;
+  kfree(wavepoint);
+}
+
+/* Timer callback function - checks WavePoint table for stale entries */ 
+void wl_cell_expiry(unsigned long data)
+{
+  net_local *lp=(net_local *)data;
+  wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
+  
+#if WAVELAN_ROAMING_DEBUG > 1
+  printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name);
+#endif
+  
+  if(lp->wavepoint_table.locked)
+    {
+#if WAVELAN_ROAMING_DEBUG > 1
+      printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n");
+#endif
+      
+      lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */
+      add_timer(&lp->cell_timer);
+      return;
+    }
+  
+  while(wavepoint!=NULL)
+    {
+      if(wavepoint->last_seen < jiffies-CELL_TIMEOUT)
+	{
+#ifdef WAVELAN_ROAMING_DEBUG
+	  printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid);
+#endif
+	  
+	  old_point=wavepoint;
+	  wavepoint=wavepoint->next;
+	  wl_del_wavepoint(old_point,lp);
+	}
+      else
+	wavepoint=wavepoint->next;
+    }
+  lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
+  add_timer(&lp->cell_timer);
+}
+
+/* Update SNR history of a wavepoint */
+void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)	
+{
+  int i=0,num_missed=0,ptr=0;
+  int average_fast=0,average_slow=0;
+  
+  num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed
+							    any beacons? */
+  if(num_missed)
+    for(i=0;i<num_missed;i++)
+      {
+	wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
+	wavepoint->qualptr %=WAVEPOINT_HISTORY;    /* in the ringbuffer. */
+      }
+  wavepoint->last_seen=jiffies;                 /* Add beacon to history */
+  wavepoint->last_seq=seq;	
+  wavepoint->sigqual[wavepoint->qualptr++]=sigqual;          
+  wavepoint->qualptr %=WAVEPOINT_HISTORY;
+  ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
+  
+  for(i=0;i<WAVEPOINT_FAST_HISTORY;i++)       /* Update running averages */
+    {
+      average_fast+=wavepoint->sigqual[ptr++];
+      ptr %=WAVEPOINT_HISTORY;
+    }
+  
+  average_slow=average_fast;
+  for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
+    {
+      average_slow+=wavepoint->sigqual[ptr++];
+      ptr %=WAVEPOINT_HISTORY;
+    }
+  
+  wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
+  wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;	
+}
+
+/* Perform a handover to a new WavePoint */
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
+{
+  ioaddr_t              base = lp->dev->base_addr;  
+  mm_t                  m;
+  unsigned long         flags;
+
+  if(wavepoint==lp->curr_point)          /* Sanity check... */
+    {
+      wv_nwid_filter(!NWID_PROMISC,lp);
+      return;
+    }
+  
+#ifdef WAVELAN_ROAMING_DEBUG
+  printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name);
+#endif
+ 	
+  /* Disable interrupts & save flags */
+  wv_splhi(lp, &flags);
+
+  m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF;
+  m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8;
+  
+  mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2);
+  
+  /* ReEnable interrupts & restore flags */
+  wv_splx(lp, &flags);
+
+  wv_nwid_filter(!NWID_PROMISC,lp);
+  lp->curr_point=wavepoint;
+}
+
+/* Called when a WavePoint beacon is received */
+static inline void wl_roam_gather(device *  dev,
+				  u_char *  hdr,   /* Beacon header */
+				  u_char *  stats) /* SNR, Signal quality 
+						      of packet */
+{
+  wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */
+  unsigned short nwid=ntohs(beacon->nwid);  
+  unsigned short sigqual=stats[2] & MMR_SGNL_QUAL;   /* SNR of beacon */
+  wavepoint_history *wavepoint=NULL;                /* WavePoint table entry */
+  net_local *lp=(net_local *)dev->priv;              /* Device info */
+
+#if 0
+  /* Some people don't need this, some other may need it */
+  nwid=nwid^ntohs(beacon->domain_id);
+#endif
+
+#if WAVELAN_ROAMING_DEBUG > 1
+  printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name);
+  printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual);
+#endif
+  
+  lp->wavepoint_table.locked=1;                            /* <Mutex> */
+  
+  wavepoint=wl_roam_check(nwid,lp);            /* Find WavePoint table entry */
+  if(wavepoint==NULL)                    /* If no entry, Create a new one... */
+    {
+      wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
+      if(wavepoint==NULL)
+	goto out;
+    }
+  if(lp->curr_point==NULL)             /* If this is the only WavePoint, */
+    wv_roam_handover(wavepoint, lp);	         /* Jump on it! */
+  
+  wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
+							 stats. */
+  
+  if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
+    if(!lp->cell_search)                  /* WavePoint is getting faint, */
+      wv_nwid_filter(NWID_PROMISC,lp);    /* start looking for a new one */
+  
+  if(wavepoint->average_slow > 
+     lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
+    wv_roam_handover(wavepoint, lp);   /* Handover to a better WavePoint */
+  
+  if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
+    if(lp->cell_search)  /* getting better, drop out of cell search mode */
+      wv_nwid_filter(!NWID_PROMISC,lp);
+  
+out:
+  lp->wavepoint_table.locked=0;                        /* </MUTEX>   :-) */
+}
+
+/* Test this MAC frame a WavePoint beacon */
+static inline int WAVELAN_BEACON(unsigned char *data)
+{
+  wavepoint_beacon *beacon= (wavepoint_beacon *)data;
+  static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
+  
+  if(memcmp(beacon,&beacon_template,9)==0)
+    return 1;
+  else
+    return 0;
+}
+#endif	/* WAVELAN_ROAMING */
+
+/************************ I82593 SUBROUTINES *************************/
+/*
+ * Useful subroutines to manage the Ethernet controller
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to synchronously send a command to the i82593 chip. 
+ * Should be called with interrupts disabled.
+ * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(),
+ *  wv_82593_config() & wv_diag())
+ */
+static int
+wv_82593_cmd(device *	dev,
+	     char *	str,
+	     int	cmd,
+	     int	result)
+{
+  ioaddr_t	base = dev->base_addr;
+  int		status;
+  int		wait_completed;
+  long		spin;
+
+  /* Spin until the chip finishes executing its current command (if any) */
+  spin = 1000;
+  do
+    {
+      /* Time calibration of the loop */
+      udelay(10);
+
+      /* Read the interrupt register */
+      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+      status = inb(LCSR(base));
+    }
+  while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
+
+  /* If the interrupt hasn't be posted */
+  if(spin <= 0)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n",
+	     str, status);
+#endif
+      return(FALSE);
+    }
+
+  /* Issue the command to the controller */
+  outb(cmd, LCCR(base));
+
+  /* If we don't have to check the result of the command
+   * Note : this mean that the irq handler will deal with that */
+  if(result == SR0_NO_RESULT)
+    return(TRUE);
+
+  /* We are waiting for command completion */
+  wait_completed = TRUE;
+
+  /* Busy wait while the LAN controller executes the command. */
+  spin = 1000;
+  do
+    {
+      /* Time calibration of the loop */
+      udelay(10);
+
+      /* Read the interrupt register */
+      outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
+      status = inb(LCSR(base));
+
+      /* Check if there was an interrupt posted */
+      if((status & SR0_INTERRUPT))
+	{
+	  /* Acknowledge the interrupt */
+	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+
+	  /* Check if interrupt is a command completion */
+	  if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) &&
+	     ((status & SR0_BOTH_RX_TX) != 0x0) &&
+	     !(status & SR0_RECEPTION))
+	    {
+	      /* Signal command completion */
+	      wait_completed = FALSE;
+	    }
+	  else
+	    {
+	      /* Note : Rx interrupts will be handled later, because we can
+	       * handle multiple Rx packets at once */
+#ifdef DEBUG_INTERRUPT_INFO
+	      printk(KERN_INFO "wv_82593_cmd: not our interrupt\n");
+#endif
+	    }
+	}
+    }
+  while(wait_completed && (spin-- > 0));
+
+  /* If the interrupt hasn't be posted */
+  if(wait_completed)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n",
+	     str, status);
+#endif
+      return(FALSE);
+    }
+
+  /* Check the return code returned by the card (see above) against
+   * the expected return code provided by the caller */
+  if((status & SR0_EVENT_MASK) != result)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n",
+	     str, status);
+#endif
+      return(FALSE);
+    }
+
+  return(TRUE);
+} /* wv_82593_cmd */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a 593 op-code number 7, and obtains the diagnose
+ * status for the WaveLAN.
+ */
+static inline int
+wv_diag(device *	dev)
+{
+  int		ret = FALSE;
+
+  if(wv_82593_cmd(dev, "wv_diag(): diagnose",
+		  OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
+    ret = TRUE;
+
+#ifdef DEBUG_CONFIG_ERROR
+  printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
+#endif
+  return(ret);
+} /* wv_diag */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read len bytes from the i82593's ring buffer, starting at
+ * chip address addr. The results read from the chip are stored in buf.
+ * The return value is the address to use for next the call.
+ */
+static int
+read_ringbuf(device *	dev,
+	     int	addr,
+	     char *	buf,
+	     int	len)
+{
+  ioaddr_t	base = dev->base_addr;
+  int		ring_ptr = addr;
+  int		chunk_len;
+  char *	buf_ptr = buf;
+
+  /* Get all the buffer */
+  while(len > 0)
+    {
+      /* Position the Program I/O Register at the ring buffer pointer */
+      outb(ring_ptr & 0xff, PIORL(base));
+      outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base));
+
+      /* First, determine how much we can read without wrapping around the
+	 ring buffer */
+      if((addr + len) < (RX_BASE + RX_SIZE))
+	chunk_len = len;
+      else
+	chunk_len = RX_BASE + RX_SIZE - addr;
+      insb(PIOP(base), buf_ptr, chunk_len);
+      buf_ptr += chunk_len;
+      len -= chunk_len;
+      ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE;
+    }
+  return(ring_ptr);
+} /* read_ringbuf */
+
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82593, or at least ask for it...
+ * Because wv_82593_config use the transmission buffer, we must do it
+ * when we are sure that there is no transmission, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option...), so you may experience
+ * some delay sometime...
+ */
+static inline void
+wv_82593_reconfig(device *	dev)
+{
+  net_local *		lp = (net_local *)dev->priv;
+  dev_link_t *		link = ((net_local *) dev->priv)->link;
+  unsigned long		flags;
+
+  /* Arm the flag, will be cleard in wv_82593_config() */
+  lp->reconfig_82593 = TRUE;
+
+  /* Check if we can do it now ! */
+  if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev)))
+    {
+      wv_splhi(lp, &flags);	/* Disable interrupts */
+      wv_82593_config(dev);
+      wv_splx(lp, &flags);	/* Re-enable interrupts */
+    }
+  else
+    {
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG
+	     "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n",
+	     dev->name, dev->state, link->open);
+#endif
+    }
+}
+
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routines are used in the code to show debug informations.
+ * Most of the time, it dump the content of hardware structures...
+ */
+
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void
+wv_psa_show(psa_t *	p)
+{
+  printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
+  printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+	 p->psa_io_base_addr_1,
+	 p->psa_io_base_addr_2,
+	 p->psa_io_base_addr_3,
+	 p->psa_io_base_addr_4);
+  printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+	 p->psa_rem_boot_addr_1,
+	 p->psa_rem_boot_addr_2,
+	 p->psa_rem_boot_addr_3);
+  printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+  printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	 p->psa_unused0[0],
+	 p->psa_unused0[1],
+	 p->psa_unused0[2],
+	 p->psa_unused0[3],
+	 p->psa_unused0[4],
+	 p->psa_unused0[5],
+	 p->psa_unused0[6]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	 p->psa_univ_mac_addr[0],
+	 p->psa_univ_mac_addr[1],
+	 p->psa_univ_mac_addr[2],
+	 p->psa_univ_mac_addr[3],
+	 p->psa_univ_mac_addr[4],
+	 p->psa_univ_mac_addr[5]);
+  printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	 p->psa_local_mac_addr[0],
+	 p->psa_local_mac_addr[1],
+	 p->psa_local_mac_addr[2],
+	 p->psa_local_mac_addr[3],
+	 p->psa_local_mac_addr[4],
+	 p->psa_local_mac_addr[5]);
+  printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
+  printk("psa_comp_number: %d, ", p->psa_comp_number);
+  printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+  printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+	 p->psa_feature_select);
+  printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+  printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+  printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+  printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
+  printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+  printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
+  printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	 p->psa_encryption_key[0],
+	 p->psa_encryption_key[1],
+	 p->psa_encryption_key[2],
+	 p->psa_encryption_key[3],
+	 p->psa_encryption_key[4],
+	 p->psa_encryption_key[5],
+	 p->psa_encryption_key[6],
+	 p->psa_encryption_key[7]);
+  printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+  printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+	 p->psa_call_code[0]);
+  printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	 p->psa_call_code[0],
+	 p->psa_call_code[1],
+	 p->psa_call_code[2],
+	 p->psa_call_code[3],
+	 p->psa_call_code[4],
+	 p->psa_call_code[5],
+	 p->psa_call_code[6],
+	 p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+	 p->psa_reserved[0],
+	 p->psa_reserved[1],
+	 p->psa_reserved[2],
+	 p->psa_reserved[3]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+  printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+  printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif	/* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function need to be completed...
+ */
+static void
+wv_mmc_show(device *	dev)
+{
+  ioaddr_t	base = dev->base_addr;
+  net_local *	lp = (net_local *)dev->priv;
+  mmr_t		m;
+
+  /* Basic check */
+  if(hasr_read(base) & HASR_NO_CLK)
+    {
+      printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
+	     dev->name);
+      return;
+    }
+
+  wv_splhi(lp, &flags);
+
+  /* Read the mmc */
+  mmc_out(base, mmwoff(0, mmw_freeze), 1);
+  mmc_read(base, 0, (u_char *)&m, sizeof(m));
+  mmc_out(base, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+  /* Don't forget to update statistics */
+  lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif	/* WIRELESS_EXT */
+
+  wv_splx(lp, &flags);
+
+  printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	 m.mmr_unused0[0],
+	 m.mmr_unused0[1],
+	 m.mmr_unused0[2],
+	 m.mmr_unused0[3],
+	 m.mmr_unused0[4],
+	 m.mmr_unused0[5],
+	 m.mmr_unused0[6],
+	 m.mmr_unused0[7]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
+	 m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+	 m.mmr_unused1[0],
+	 m.mmr_unused1[1],
+	 m.mmr_unused1[2],
+	 m.mmr_unused1[3],
+	 m.mmr_unused1[4]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+	 m.mmr_dce_status,
+	 (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
+	 (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+	 "loop test indicated," : "",
+	 (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
+	 (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+	 "jabber timer expired," : "");
+  printk(KERN_DEBUG "Dsp ID: %02X\n",
+	 m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+	 m.mmr_unused2[0],
+	 m.mmr_unused2[1]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+	 (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+	 (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+  printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+	 m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+	 (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
+  printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+	 m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+	 (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
+  printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
+	 (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
+  printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+	 (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif	/* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif	/* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82593_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the i82593's receive unit.
+ */
+static void
+wv_ru_show(device *	dev)
+{
+  net_local *lp = (net_local *) dev->priv;
+
+  printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n");
+  printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop);
+  /*
+   * Not implemented yet...
+   */
+  printk("\n");
+} /* wv_ru_show */
+#endif	/* DEBUG_I82593_SHOW */
+
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
+ */
+static void
+wv_dev_show(device *	dev)
+{
+  printk(KERN_DEBUG "dev:");
+  printk(" state=%lX,", dev->state);
+  printk(" trans_start=%ld,", dev->trans_start);
+  printk(" flags=0x%x,", dev->flags);
+  printk("\n");
+} /* wv_dev_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void
+wv_local_show(device *	dev)
+{
+  net_local *lp;
+
+  lp = (net_local *)dev->priv;
+
+  printk(KERN_DEBUG "local:");
+  /*
+   * Not implemented yet...
+   */
+  printk("\n");
+} /* wv_local_show */
+#endif	/* DEBUG_DEVICE_SHOW */
+
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void
+wv_packet_info(u_char *		p,		/* Packet to dump */
+	       int		length,		/* Length of the packet */
+	       char *		msg1,		/* Name of the device */
+	       char *		msg2)		/* Name of the function */
+{
+  int		i;
+  int		maxi;
+
+  printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+	 msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+  printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+	 msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
+
+#ifdef DEBUG_PACKET_DUMP
+
+  printk(KERN_DEBUG "data=\"");
+
+  if((maxi = length) > DEBUG_PACKET_DUMP)
+    maxi = DEBUG_PACKET_DUMP;
+  for(i = 14; i < maxi; i++)
+    if(p[i] >= ' ' && p[i] <= '~')
+      printk(" %c", p[i]);
+    else
+      printk("%02X", p[i]);
+  if(maxi < length)
+    printk("..");
+  printk("\"\n");
+  printk(KERN_DEBUG "\n");
+#endif	/* DEBUG_PACKET_DUMP */
+}
+#endif	/* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
+
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup
+ * There  is a lot of flag to configure it at your will...
+ */
+static inline void
+wv_init_info(device *	dev)
+{
+  ioaddr_t	base = dev->base_addr;
+  psa_t		psa;
+  int		i;
+
+  /* Read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+  wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82593_SHOW
+  wv_ru_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+  /* Now, let's go for the basic stuff */
+  printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr",
+	 dev->name, base, dev->irq);
+  for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
+    printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+
+  /* Print current network id */
+  if(psa.psa_nwid_select)
+    printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
+  else
+    printk(", nwid off");
+
+  /* If 2.00 card */
+  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      unsigned short	freq;
+
+      /* Ask the EEprom to read the frequency from the first area */
+      fee_read(base, 0x00 /* 1st area - frequency... */,
+	       &freq, 1);
+
+      /* Print frequency */
+      printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+      /* Hack !!! */
+      if(freq & 0x20)
+	printk(".5");
+    }
+  else
+    {
+      printk(", PCMCIA, ");
+      switch (psa.psa_subband)
+	{
+	case PSA_SUBBAND_915:
+	  printk("915");
+	  break;
+	case PSA_SUBBAND_2425:
+	  printk("2425");
+	  break;
+	case PSA_SUBBAND_2460:
+	  printk("2460");
+	  break;
+	case PSA_SUBBAND_2484:
+	  printk("2484");
+	  break;
+	case PSA_SUBBAND_2430_5:
+	  printk("2430.5");
+	  break;
+	default:
+	  printk("???");
+	}
+    }
+
+  printk(" MHz\n");
+#endif	/* DEBUG_BASIC_SHOW */
+
+#ifdef DEBUG_VERSION_SHOW
+  /* Print version information */
+  printk(KERN_NOTICE "%s", version);
+#endif
+} /* wv_init_info */
+
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on differents
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the current ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats	*
+wavelan_get_stats(device *	dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
+
+  return(&((net_local *) dev->priv)->stats);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+
+static void
+wavelan_set_multicast_list(device *	dev)
+{
+  net_local *	lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+  printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+	 dev->name, dev->flags, dev->mc_count);
+#endif
+
+  if(dev->flags & IFF_PROMISC)
+    {
+      /*
+       * Enable promiscuous mode: receive all packets.
+       */
+      if(!lp->promiscuous)
+	{
+	  lp->promiscuous = 1;
+	  lp->allmulticast = 0;
+	  lp->mc_count = 0;
+
+	  wv_82593_reconfig(dev);
+
+	  /* Tell the kernel that we are doing a really bad job... */
+	  dev->flags |= IFF_PROMISC;
+	}
+    }
+  else
+    /* If all multicast addresses
+     * or too much multicast addresses for the hardware filter */
+    if((dev->flags & IFF_ALLMULTI) ||
+       (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES))
+      {
+	/*
+	 * Disable promiscuous mode, but active the all multicast mode
+	 */
+	if(!lp->allmulticast)
+	  {
+	    lp->promiscuous = 0;
+	    lp->allmulticast = 1;
+	    lp->mc_count = 0;
+
+	    wv_82593_reconfig(dev);
+
+	    /* Tell the kernel that we are doing a really bad job... */
+	    dev->flags |= IFF_ALLMULTI;
+	  }
+      }
+    else
+      /* If there is some multicast addresses to send */
+      if(dev->mc_list != (struct dev_mc_list *) NULL)
+	{
+	  /*
+	   * Disable promiscuous mode, but receive all packets
+	   * in multicast list
+	   */
+#ifdef MULTICAST_AVOID
+	  if(lp->promiscuous || lp->allmulticast ||
+	     (dev->mc_count != lp->mc_count))
+#endif
+	    {
+	      lp->promiscuous = 0;
+	      lp->allmulticast = 0;
+	      lp->mc_count = dev->mc_count;
+
+	      wv_82593_reconfig(dev);
+	    }
+	}
+      else
+	{
+	  /*
+	   * Switch to normal mode: disable promiscuous mode and 
+	   * clear the multicast list.
+	   */
+	  if(lp->promiscuous || lp->mc_count == 0)
+	    {
+	      lp->promiscuous = 0;
+	      lp->allmulticast = 0;
+	      lp->mc_count = 0;
+
+	      wv_82593_reconfig(dev);
+	    }
+	}
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This function doesn't exist...
+ * (Note : it was a nice way to test the reconfigure stuff...)
+ */
+#ifdef SET_MAC_ADDRESS
+static int
+wavelan_set_mac_address(device *	dev,
+			void *		addr)
+{
+  struct sockaddr *	mac = addr;
+
+  /* Copy the address */
+  memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
+
+  /* Reconfig the beast */
+  wv_82593_reconfig(dev);
+
+  return 0;
+}
+#endif	/* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware able of it)
+ * It's a bit complicated and you don't really want to look into it...
+ * (called in wavelan_ioctl)
+ */
+static inline int
+wv_set_frequency(u_long		base,	/* i/o port of the card */
+		 iw_freq *	frequency)
+{
+  const int	BAND_NUM = 10;	/* Number of bands */
+  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+  int		i;
+#endif
+
+  /* Setting by frequency */
+  /* Theoritically, you may set any frequency between
+   * the two limits with a 0.5 MHz precision. In practice,
+   * I don't want you to have trouble with local
+   * regulations... */
+  if((frequency->e == 1) &&
+     (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8))
+    {
+      freq = ((frequency->m / 10000) - 24000L) / 5;
+    }
+
+  /* Setting by channel (same as wfreqsel) */
+  /* Warning : each channel is 22MHz wide, so some of the channels
+   * will interfere... */
+  if((frequency->e == 0) &&
+     (frequency->m >= 0) && (frequency->m < BAND_NUM))
+    {
+      /* Get frequency offset. */
+      freq = channel_bands[frequency->m] >> 1;
+    }
+
+  /* Verify if the frequency is allowed */
+  if(freq != 0L)
+    {
+      u_short	table[10];	/* Authorized frequency table */
+
+      /* Read the frequency table */
+      fee_read(base, 0x71 /* frequency table */,
+	       table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Frequency table :");
+      for(i = 0; i < 10; i++)
+	{
+	  printk(" %04X",
+		 table[i]);
+	}
+      printk("\n");
+#endif
+
+      /* Look in the table if the frequency is allowed */
+      if(!(table[9 - ((freq - 24) / 16)] &
+	   (1 << ((freq - 24) % 16))))
+	return -EINVAL;		/* not allowed */
+    }
+  else
+    return -EINVAL;
+
+  /* If we get a usable frequency */
+  if(freq != 0L)
+    {
+      unsigned short	area[16];
+      unsigned short	dac[2];
+      unsigned short	area_verify[16];
+      unsigned short	dac_verify[2];
+      /* Corresponding gain (in the power adjust value table)
+       * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
+       * & WCIN062D.DOC, page 6.2.9 */
+      unsigned short	power_limit[] = { 40, 80, 120, 160, 0 };
+      int		power_band = 0;		/* Selected band */
+      unsigned short	power_adjust;		/* Correct value */
+
+      /* Search for the gain */
+      power_band = 0;
+      while((freq > power_limit[power_band]) &&
+	    (power_limit[++power_band] != 0))
+	;
+
+      /* Read the first area */
+      fee_read(base, 0x00,
+	       area, 16);
+
+      /* Read the DAC */
+      fee_read(base, 0x60,
+	       dac, 2);
+
+      /* Read the new power adjust value */
+      fee_read(base, 0x6B - (power_band >> 1),
+	       &power_adjust, 1);
+      if(power_band & 0x1)
+	power_adjust >>= 8;
+      else
+	power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+	{
+	  printk(" %04X",
+		 area[i]);
+	}
+      printk("\n");
+
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+	     dac[0], dac[1]);
+#endif
+
+      /* Frequency offset (for info only...) */
+      area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
+
+      /* Receiver Principle main divider coefficient */
+      area[3] = (freq >> 1) + 2400L - 352L;
+      area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+      /* Transmitter Main divider coefficient */
+      area[13] = (freq >> 1) + 2400L;
+      area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+      /* Others part of the area are flags, bit streams or unused... */
+
+      /* Set the value in the DAC */
+      dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+      dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
+
+      /* Write the first area */
+      fee_write(base, 0x00,
+		area, 16);
+
+      /* Write the DAC */
+      fee_write(base, 0x60,
+		dac, 2);
+
+      /* We now should verify here that the EEprom writting was ok */
+
+      /* ReRead the first area */
+      fee_read(base, 0x00,
+	       area_verify, 16);
+
+      /* ReRead the DAC */
+      fee_read(base, 0x60,
+	       dac_verify, 2);
+
+      /* Compare */
+      if(memcmp(area, area_verify, 16 * 2) ||
+	 memcmp(dac, dac_verify, 2 * 2))
+	{
+#ifdef DEBUG_IOCTL_ERROR
+	  printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
+#endif
+	  return -EOPNOTSUPP;
+	}
+
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F);
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl),
+	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+      /* Wait until the download is finished */
+      fee_wait(base, 100, 100);
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61);
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl),
+	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+      /* Wait until the download is finished */
+      fee_wait(base, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+      /* Verification of what we have done... */
+
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+	{
+	  printk(" %04X",
+		 area_verify[i]);
+	}
+      printk("\n");
+
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+	     dac_verify[0], dac_verify[1]);
+#endif
+
+      return 0;
+    }
+  else
+    return -EINVAL;		/* Bah, never get there... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Give the list of available frequencies
+ */
+static inline int
+wv_frequency_list(u_long	base,	/* i/o port of the card */
+		  iw_freq *	list,	/* List of frequency to fill */
+		  int		max)	/* Maximum number of frequencies */
+{
+  u_short	table[10];	/* Authorized frequency table */
+  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz + 12 MHz */
+  int		i;		/* index in the table */
+#if WIRELESS_EXT > 7
+  const int	BAND_NUM = 10;	/* Number of bands */
+  int		c = 0;		/* Channel number */
+#endif /* WIRELESS_EXT */
+
+  /* Read the frequency table */
+  fee_read(base, 0x71 /* frequency table */,
+	   table, 10);
+
+  /* Look all frequencies */
+  i = 0;
+  for(freq = 0; freq < 150; freq++)
+    /* Look in the table if the frequency is allowed */
+    if(table[9 - (freq / 16)] & (1 << (freq % 16)))
+      {
+#if WIRELESS_EXT > 7
+	/* Compute approximate channel number */
+	while((((channel_bands[c] >> 1) - 24) < freq) &&
+	      (c < BAND_NUM))
+	  c++;
+	list[i].i = c;	/* Set the list index */
+#endif /* WIRELESS_EXT */
+
+	/* put in the list */
+	list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
+	list[i++].e = 1;
+
+	/* Check number */
+	if(i >= max)
+	  return(i);
+      }
+
+  return(i);
+}
+
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
+/*
+ * Gather wireless spy statistics : for each packet, compare the source
+ * address with out list, and if match, get the stats...
+ * Sorry, but this function really need wireless extensions...
+ */
+static inline void
+wl_spy_gather(device *	dev,
+	      u_char *	mac,		/* MAC address */
+	      u_char *	stats)		/* Statistics to gather */
+{
+  net_local *	lp = (net_local *) dev->priv;
+  int		i;
+
+  /* Look all addresses */
+  for(i = 0; i < lp->spy_number; i++)
+    /* If match */
+    if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
+      {
+	/* Update statistics */
+	lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+	lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+	lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+	lp->spy_stat[i].updated = 0x7;
+      }
+}
+#endif	/* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculate an histogram on the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one wavelan is really weak,
+ * or you may also calculate the mean and standard deviation of the level...
+ */
+static inline void
+wl_his_gather(device *	dev,
+	      u_char *	stats)		/* Statistics to gather */
+{
+  net_local *	lp = (net_local *) dev->priv;
+  u_char	level = stats[0] & MMR_SIGNAL_LVL;
+  int		i;
+
+  /* Find the correct interval */
+  i = 0;
+  while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
+    ;
+
+  /* Increment interval counter */
+  (lp->his_sum[i])++;
+}
+#endif	/* HISTOGRAM */
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl : config & info stuff
+ * This is here that are treated the wireless extensions (iwconfig)
+ */
+static int
+wavelan_ioctl(struct net_device *	dev,	/* Device on wich the ioctl apply */
+	      struct ifreq *	rq,	/* Data passed */
+	      int		cmd)	/* Ioctl number */
+{
+  ioaddr_t		base = dev->base_addr;
+  net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */
+  struct iwreq *	wrq = (struct iwreq *) rq;
+  psa_t			psa;
+  mm_t			m;
+  unsigned long		flags;
+  int			ret = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
+#endif
+
+  /* Disable interrupts & save flags */
+  wv_splhi(lp, &flags);
+
+  /* Look what is the request */
+  switch(cmd)
+    {
+      /* --------------- WIRELESS EXTENSIONS --------------- */
+
+    case SIOCGIWNAME:
+      strcpy(wrq->u.name, "Wavelan");
+      break;
+
+    case SIOCSIWNWID:
+      /* Set NWID in wavelan */
+#if WIRELESS_EXT > 8
+      if(!wrq->u.nwid.disabled)
+	{
+	  /* Set NWID in psa */
+	  psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
+	  psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
+#else	/* WIRELESS_EXT > 8 */
+      if(wrq->u.nwid.on)
+	{
+	  /* Set NWID in psa */
+	  psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+	  psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+#endif	/* WIRELESS_EXT > 8 */
+	  psa.psa_nwid_select = 0x01;
+	  psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
+		    (unsigned char *)psa.psa_nwid, 3);
+
+	  /* Set NWID in mmc */
+	  m.w.mmw_netw_id_l = psa.psa_nwid[1];
+	  m.w.mmw_netw_id_h = psa.psa_nwid[0];
+	  mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m,
+		    (unsigned char *)&m.w.mmw_netw_id_l, 2);
+	  mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00);
+	}
+      else
+	{
+	  /* Disable nwid in the psa */
+	  psa.psa_nwid_select = 0x00;
+	  psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa,
+		    (unsigned char *)&psa.psa_nwid_select, 1);
+
+	  /* Disable nwid in the mmc (no filtering) */
+	  mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
+	}
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      break;
+
+    case SIOCGIWNWID:
+      /* Read the NWID */
+      psa_read(dev, (char *)psa.psa_nwid - (char *)&psa,
+	       (unsigned char *)psa.psa_nwid, 3);
+#if WIRELESS_EXT > 8
+      wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+      wrq->u.nwid.fixed = 1;	/* Superfluous */
+#else	/* WIRELESS_EXT > 8 */
+      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.on = psa.psa_nwid_select;
+#endif	/* WIRELESS_EXT > 8 */
+      break;
+
+    case SIOCSIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+      if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+	ret = wv_set_frequency(base, &(wrq->u.freq));
+      else
+	ret = -EOPNOTSUPP;
+      break;
+
+    case SIOCGIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+       * (does it work for everybody ??? - especially old cards...) */
+      if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+	{
+	  unsigned short	freq;
+
+	  /* Ask the EEprom to read the frequency from the first area */
+	  fee_read(base, 0x00 /* 1st area - frequency... */,
+		   &freq, 1);
+	  wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
+	  wrq->u.freq.e = 1;
+	}
+      else
+	{
+	  psa_read(dev, (char *)&psa.psa_subband - (char *)&psa,
+		   (unsigned char *)&psa.psa_subband, 1);
+
+	  if(psa.psa_subband <= 4)
+	    {
+	      wrq->u.freq.m = fixed_bands[psa.psa_subband];
+	      wrq->u.freq.e = (psa.psa_subband != 0);
+	    }
+	  else
+	    ret = -EOPNOTSUPP;
+	}
+      break;
+
+    case SIOCSIWSENS:
+      /* Set the level threshold */
+#if WIRELESS_EXT > 7
+      /* We should complain loudly if wrq->u.sens.fixed = 0, because we
+       * can't set auto mode... */
+      psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
+#else	/* WIRELESS_EXT > 7 */
+      psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
+#endif	/* WIRELESS_EXT > 7 */
+      psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+	       (unsigned char *)&psa.psa_thr_pre_set, 1);
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
+      break;
+
+    case SIOCGIWSENS:
+      /* Read the level threshold */
+      psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+	       (unsigned char *)&psa.psa_thr_pre_set, 1);
+#if WIRELESS_EXT > 7
+      wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
+      wrq->u.sens.fixed = 1;
+#else	/* WIRELESS_EXT > 7 */
+      wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
+#endif	/* WIRELESS_EXT > 7 */
+      break;
+
+#if WIRELESS_EXT > 8
+    case SIOCSIWENCODE:
+      /* Set encryption key */
+      if(!mmc_encr(base))
+	{
+	  ret = -EOPNOTSUPP;
+	  break;
+	}
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+	{
+	  /* Check the size of the key */
+	  if(wrq->u.encoding.length != 8)
+	    {
+	      ret = -EINVAL;
+	      break;
+	    }
+
+	  /* Copy the key in the driver */
+	  if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
+			    wrq->u.encoding.length))
+	    {
+	      ret = -EFAULT;
+	      break;
+	    }
+
+	  psa.psa_encryption_select = 1;
+	  psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+		    (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+	  mmc_out(base, mmwoff(0, mmw_encr_enable),
+		  MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+	  mmc_write(base, mmwoff(0, mmw_encr_key),
+		    (unsigned char *) &psa.psa_encryption_key, 8);
+	}
+
+      if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
+	{	/* disable encryption */
+	  psa.psa_encryption_select = 0;
+	  psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+		    (unsigned char *) &psa.psa_encryption_select, 1);
+
+	  mmc_out(base, mmwoff(0, mmw_encr_enable), 0);
+	}
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      break;
+
+    case SIOCGIWENCODE:
+      /* Read the encryption key */
+      if(!mmc_encr(base))
+	{
+	  ret = -EOPNOTSUPP;
+	  break;
+	}
+
+      /* only super-user can see encryption key */
+      if(!capable(CAP_NET_ADMIN))
+	{
+	  ret = -EPERM;
+	  break;
+	}
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+	{
+	  psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+		   (unsigned char *) &psa.psa_encryption_select, 1+8);
+
+	  /* encryption is enabled ? */
+	  if(psa.psa_encryption_select)
+	    wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+	  else
+	    wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+	  wrq->u.encoding.flags |= mmc_encr(base);
+
+	  /* Copy the key to the user buffer */
+	  wrq->u.encoding.length = 8;
+	  if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
+	    ret = -EFAULT;
+	}
+      break;
+#endif	/* WIRELESS_EXT > 8 */
+
+#ifdef WAVELAN_ROAMING_EXT
+#if WIRELESS_EXT > 5
+    case SIOCSIWESSID:
+      /* Check if disable */
+      if(wrq->u.data.flags == 0)
+	lp->filter_domains = 0;
+      else
+	/* Basic checking... */
+	if(wrq->u.data.pointer != (caddr_t) 0)
+	  {
+	    char	essid[IW_ESSID_MAX_SIZE + 1];
+	    char *	endp;
+
+	    /* Check the size of the string */
+	    if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1)
+	      {
+		ret = -E2BIG;
+		break;
+	      }
+
+	    /* Copy the string in the driver */
+	    if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length))
+	      {
+		ret = -EFAULT;
+		break;
+	      }
+	    essid[IW_ESSID_MAX_SIZE] = '\0';
+
+#ifdef DEBUG_IOCTL_INFO
+	    printk(KERN_DEBUG "SetEssid : ``%s''\n", essid);
+#endif	/* DEBUG_IOCTL_INFO */
+
+	    /* Convert to a number (note : Wavelan specific) */
+	    lp->domain_id = simple_strtoul(essid, &endp, 16);
+	    /* Has it worked  ? */
+	    if(endp > essid)
+	      lp->filter_domains = 1;
+	    else
+	      {
+		lp->filter_domains = 0;
+		ret = -EINVAL;
+	      }
+	  }
+      break;
+
+    case SIOCGIWESSID:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+	{
+	  char		essid[IW_ESSID_MAX_SIZE + 1];
+
+	  /* Is the domain ID active ? */
+	  wrq->u.data.flags = lp->filter_domains;
+
+	  /* Copy Domain ID into a string (Wavelan specific) */
+	  /* Sound crazy, be we can't have a snprintf in the kernel !!! */
+	  sprintf(essid, "%lX", lp->domain_id);
+	  essid[IW_ESSID_MAX_SIZE] = '\0';
+
+	  /* Set the length */
+	  wrq->u.data.length = strlen(essid) + 1;
+
+	  /* Copy structure to the user buffer */
+	  if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length))
+	    ret = -EFAULT;
+	}
+      break;
+
+    case SIOCSIWAP:
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n",
+	     wrq->u.ap_addr.sa_data[0],
+	     wrq->u.ap_addr.sa_data[1],
+	     wrq->u.ap_addr.sa_data[2],
+	     wrq->u.ap_addr.sa_data[3],
+	     wrq->u.ap_addr.sa_data[4],
+	     wrq->u.ap_addr.sa_data[5]);
+#endif	/* DEBUG_IOCTL_INFO */
+
+      ret = -EOPNOTSUPP;	/* Not supported yet */
+      break;
+
+    case SIOCGIWAP:
+      /* Should get the real McCoy instead of own Ethernet address */
+      memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE);
+      wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
+
+      ret = -EOPNOTSUPP;	/* Not supported yet */
+      break;
+#endif	/* WIRELESS_EXT > 5 */
+#endif	/* WAVELAN_ROAMING_EXT */
+
+#if WIRELESS_EXT > 8
+#ifdef WAVELAN_ROAMING
+    case SIOCSIWMODE:
+      switch(wrq->u.mode)
+	{
+	case IW_MODE_ADHOC:
+	  if(do_roaming)
+	    {
+	      wv_roam_cleanup(dev);
+	      do_roaming = 0;
+	    }
+	  break;
+	case IW_MODE_INFRA:
+	  if(!do_roaming)
+	    {
+	      wv_roam_init(dev);
+	      do_roaming = 1;
+	    }
+	  break;
+	default:
+	  ret = -EINVAL;
+	}
+      break;
+
+    case SIOCGIWMODE:
+      if(do_roaming)
+	wrq->u.mode = IW_MODE_INFRA;
+      else
+	wrq->u.mode = IW_MODE_ADHOC;
+      break;
+#endif	/* WAVELAN_ROAMING */
+#endif /* WIRELESS_EXT > 8 */
+
+    case SIOCGIWRANGE:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+	{
+	  struct iw_range	range;
+
+	   /* Set the length (very important for backward compatibility) */
+	   wrq->u.data.length = sizeof(struct iw_range);
+
+	   /* Set all the info we don't care or don't know about to zero */
+	   memset(&range, 0, sizeof(range));
+
+#if WIRELESS_EXT > 10
+	   /* Set the Wireless Extension versions */
+	   range.we_version_compiled = WIRELESS_EXT;
+	   range.we_version_source = 9;	/* Nothing for us in v10 and v11 */
+#endif /* WIRELESS_EXT > 10 */
+
+	  /* Set information in the range struct */
+	  range.throughput = 1.4 * 1000 * 1000;	/* don't argue on this ! */
+	  range.min_nwid = 0x0000;
+	  range.max_nwid = 0xFFFF;
+
+	  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+	  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+	       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+	    {
+	      range.num_channels = 10;
+	      range.num_frequency = wv_frequency_list(base, range.freq,
+						      IW_MAX_FREQUENCIES);
+	    }
+	  else
+	    range.num_channels = range.num_frequency = 0;
+
+	  range.sensitivity = 0x3F;
+	  range.max_qual.qual = MMR_SGNL_QUAL;
+	  range.max_qual.level = MMR_SIGNAL_LVL;
+	  range.max_qual.noise = MMR_SILENCE_LVL;
+#if WIRELESS_EXT > 11
+	  range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
+	  /* Need to get better values for those two */
+	  range.avg_qual.level = 30;
+	  range.avg_qual.noise = 8;
+#endif /* WIRELESS_EXT > 11 */
+
+#if WIRELESS_EXT > 7
+	  range.num_bitrates = 1;
+	  range.bitrate[0] = 2000000;	/* 2 Mb/s */
+#endif /* WIRELESS_EXT > 7 */
+
+#if WIRELESS_EXT > 8
+	  /* Encryption supported ? */
+	  if(mmc_encr(base))
+	    {
+	      range.encoding_size[0] = 8;	/* DES = 64 bits key */
+	      range.num_encoding_sizes = 1;
+	      range.max_encoding_tokens = 1;	/* Only one key possible */
+	    }
+	  else
+	    {
+	      range.num_encoding_sizes = 0;
+	      range.max_encoding_tokens = 0;
+	    }
+#endif /* WIRELESS_EXT > 8 */
+
+	  /* Copy structure to the user buffer */
+	  if(copy_to_user(wrq->u.data.pointer, &range,
+			  sizeof(struct iw_range)))
+	    ret = -EFAULT;
+	}
+      break;
+
+    case SIOCGIWPRIV:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+	{
+	  struct iw_priv_args	priv[] =
+	  {	/* cmd,		set_args,	get_args,	name */
+	    { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
+	    { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
+	    { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,	0, "sethisto" },
+	    { SIOCGIPHISTO, 0,	    IW_PRIV_TYPE_INT | 16, "gethisto" },
+	    { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" },
+	    { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" },
+	  };
+
+	  /* Set the number of ioctl available */
+	  wrq->u.data.length = 6;
+
+	  /* Copy structure to the user buffer */
+	  if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+		       sizeof(priv)))
+	    ret = -EFAULT;
+	}
+      break;
+
+#ifdef WIRELESS_SPY
+    case SIOCSIWSPY:
+      /* Set the spy list */
+
+      /* Check the number of addresses */
+      if(wrq->u.data.length > IW_MAX_SPY)
+	{
+	  ret = -E2BIG;
+	  break;
+	}
+      lp->spy_number = wrq->u.data.length;
+
+      /* If there is some addresses to copy */
+      if(lp->spy_number > 0)
+	{
+	  struct sockaddr	address[IW_MAX_SPY];
+	  int			i;
+
+	  /* Copy addresses to the driver */
+	  if(copy_from_user(address, wrq->u.data.pointer,
+			    sizeof(struct sockaddr) * lp->spy_number))
+	    {
+	      ret = -EFAULT;
+	      break;
+	    }
+
+	  /* Copy addresses to the lp structure */
+	  for(i = 0; i < lp->spy_number; i++)
+	    {
+	      memcpy(lp->spy_address[i], address[i].sa_data,
+		     WAVELAN_ADDR_SIZE);
+	    }
+
+	  /* Reset structure... */
+	  memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+	  printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
+	  for(i = 0; i < wrq->u.data.length; i++)
+	    printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n",
+		   lp->spy_address[i][0],
+		   lp->spy_address[i][1],
+		   lp->spy_address[i][2],
+		   lp->spy_address[i][3],
+		   lp->spy_address[i][4],
+		   lp->spy_address[i][5]);
+#endif	/* DEBUG_IOCTL_INFO */
+	}
+
+      break;
+
+    case SIOCGIWSPY:
+      /* Get the spy list and spy stats */
+
+      /* Set the number of addresses */
+      wrq->u.data.length = lp->spy_number;
+
+      /* If the user want to have the addresses back... */
+      if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+	{
+	  struct sockaddr	address[IW_MAX_SPY];
+	  int			i;
+
+	  /* Copy addresses from the lp structure */
+	  for(i = 0; i < lp->spy_number; i++)
+	    {
+	      memcpy(address[i].sa_data, lp->spy_address[i],
+		     WAVELAN_ADDR_SIZE);
+	      address[i].sa_family = ARPHRD_ETHER;
+	    }
+
+	  /* Copy addresses to the user buffer */
+	  if(copy_to_user(wrq->u.data.pointer, address,
+		       sizeof(struct sockaddr) * lp->spy_number))
+	    {
+	      ret = -EFAULT;
+	      break;
+	    }
+
+	  /* Copy stats to the user buffer (just after) */
+	  if(copy_to_user(wrq->u.data.pointer +
+		       (sizeof(struct sockaddr) * lp->spy_number),
+		       lp->spy_stat, sizeof(iw_qual) * lp->spy_number))
+	    {
+	      ret = -EFAULT;
+	      break;
+	    }
+
+	  /* Reset updated flags */
+	  for(i = 0; i < lp->spy_number; i++)
+	    lp->spy_stat[i].updated = 0x0;
+	}	/* if(pointer != NULL) */
+
+      break;
+#endif	/* WIRELESS_SPY */
+
+      /* ------------------ PRIVATE IOCTL ------------------ */
+
+    case SIOCSIPQTHR:
+      if(!capable(CAP_NET_ADMIN))
+	{
+	  ret = -EPERM;
+	  break;
+	}
+      psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+      psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+	       (unsigned char *)&psa.psa_quality_thr, 1);
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
+      break;
+
+    case SIOCGIPQTHR:
+      psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+	       (unsigned char *)&psa.psa_quality_thr, 1);
+      *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+      break;
+
+#ifdef WAVELAN_ROAMING
+    case SIOCSIPROAM:
+      /* Note : should check if user == root */
+      if(do_roaming && (*wrq->u.name)==0)
+	wv_roam_cleanup(dev);
+      else if(do_roaming==0 && (*wrq->u.name)!=0)
+	wv_roam_init(dev);
+
+      do_roaming = (*wrq->u.name);
+	  
+      break;
+
+    case SIOCGIPROAM:
+      *(wrq->u.name) = do_roaming;
+      break;
+#endif	/* WAVELAN_ROAMING */
+
+#ifdef HISTOGRAM
+    case SIOCSIPHISTO:
+      /* Verif if the user is root */
+      if(!capable(CAP_NET_ADMIN))
+	{
+	  ret = -EPERM;
+	}
+
+      /* Check the number of intervals */
+      if(wrq->u.data.length > 16)
+	{
+	  ret = -E2BIG;
+	  break;
+	}
+      lp->his_number = wrq->u.data.length;
+
+      /* If there is some addresses to copy */
+      if(lp->his_number > 0)
+	{
+	  /* Copy interval ranges to the driver */
+	  if(copy_from_user(lp->his_range, wrq->u.data.pointer,
+			 sizeof(char) * lp->his_number))
+	    {
+	      ret = -EFAULT;
+	      break;
+	    }
+
+	  /* Reset structure... */
+	  memset(lp->his_sum, 0x00, sizeof(long) * 16);
+	}
+      break;
+
+    case SIOCGIPHISTO:
+      /* Set the number of intervals */
+      wrq->u.data.length = lp->his_number;
+
+      /* Give back the distribution statistics */
+      if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+	{
+	  /* Copy data to the user buffer */
+	  if(copy_to_user(wrq->u.data.pointer, lp->his_sum,
+		       sizeof(long) * lp->his_number))
+	    ret = -EFAULT;
+
+	}	/* if(pointer != NULL) */
+      break;
+#endif	/* HISTOGRAM */
+
+      /* ------------------- OTHER IOCTL ------------------- */
+
+    default:
+      ret = -EOPNOTSUPP;
+    }
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+  return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics
+ * Called by /proc/net/wireless...
+ */
+static iw_stats *
+wavelan_get_wireless_stats(device *	dev)
+{
+  ioaddr_t		base = dev->base_addr;
+  net_local *		lp = (net_local *) dev->priv;
+  mmr_t			m;
+  iw_stats *		wstats;
+  unsigned long		flags;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
+#endif
+
+  /* Disable interrupts & save flags */
+  wv_splhi(lp, &flags);
+
+  wstats = &lp->wstats;
+
+  /* Get data from the mmc */
+  mmc_out(base, mmwoff(0, mmw_freeze), 1);
+
+  mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+  mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
+  mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
+
+  mmc_out(base, mmwoff(0, mmw_freeze), 0);
+
+  /* Copy data to wireless stuff */
+  wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
+  wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+  wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+  wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+  wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
+			  ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
+			  ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+  wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+  wstats->discard.code = 0L;
+  wstats->discard.misc = 0L;
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
+#endif
+  return &lp->wstats;
+}
+#endif	/* WIRELESS_EXT */
+
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deal with receiving the packets.
+ * The interrupt handler get an interrupt when a packet has been
+ * successfully received and called this part...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the starting address of the frame pointed to by the receive
+ * frame pointer and verify that the frame seem correct
+ * (called by wv_packet_rcv())
+ */
+static inline int
+wv_start_of_frame(device *	dev,
+		  int		rfp,	/* end of frame */
+		  int		wrap)	/* start of buffer */
+{
+  ioaddr_t	base = dev->base_addr;
+  int		rp;
+  int		len;
+
+  rp = (rfp - 5 + RX_SIZE) % RX_SIZE;
+  outb(rp & 0xff, PIORL(base));
+  outb(((rp >> 8) & PIORH_MASK), PIORH(base));
+  len = inb(PIOP(base));
+  len |= inb(PIOP(base)) << 8;
+
+  /* Sanity checks on size */
+  /* Frame too big */
+  if(len > MAXDATAZ + 100)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n",
+	     dev->name, rfp, len);
+#endif
+      return(-1);
+    }
+  
+  /* Frame too short */
+  if(len < 7)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n",
+	     dev->name, rfp, len);
+#endif
+      return(-1);
+    }
+  
+  /* Wrap around buffer */
+  if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE))	/* magic formula ! */
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",
+	     dev->name, wrap, rfp, len);
+#endif
+      return(-1);
+    }
+
+  return((rp - len + RX_SIZE) % RX_SIZE);
+} /* wv_start_of_frame */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copy of data (including the ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: We
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here.  The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor"
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device *		dev,
+	       int		fd_p,
+	       int		sksize)
+{
+  net_local *		lp = (net_local *) dev->priv;
+  struct sk_buff *	skb;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+	 dev->name, fd_p, sksize);
+#endif
+
+  /* Allocate some buffer for the new packet */
+  if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n",
+	     dev->name, sksize);
+#endif
+      lp->stats.rx_dropped++;
+      /*
+       * Not only do we want to return here, but we also need to drop the
+       * packet on the floor to clear the interrupt.
+       */
+      return;
+    }
+
+  skb->dev = dev;
+
+  skb_reserve(skb, 2);
+  fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
+  skb->protocol = eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif	/* DEBUG_RX_INFO */
+     
+  /* Statistics gathering & stuff associated.
+   * It seem a bit messy with all the define, but it's really simple... */
+  if(
+#ifdef WIRELESS_SPY
+     (lp->spy_number > 0) ||
+#endif	/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+     (lp->his_number > 0) ||
+#endif	/* HISTOGRAM */
+#ifdef WAVELAN_ROAMING
+     (do_roaming) ||
+#endif	/* WAVELAN_ROAMING */
+     0)
+    {
+      u_char	stats[3];	/* Signal level, Noise level, Signal quality */
+
+      /* read signal level, silence level and signal quality bytes */
+      fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE,
+			  stats, 3);
+#ifdef DEBUG_RX_INFO
+      printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+	     dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
+#endif
+
+#ifdef WAVELAN_ROAMING
+      if(do_roaming)
+	if(WAVELAN_BEACON(skb->data))
+	  wl_roam_gather(dev, skb->data, stats);
+#endif	/* WAVELAN_ROAMING */
+	  
+#ifdef WIRELESS_SPY
+      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+#endif	/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+      wl_his_gather(dev, stats);
+#endif	/* HISTOGRAM */
+    }
+
+  /*
+   * Hand the packet to the Network Module
+   */
+  netif_rx(skb);
+
+  /* Keep stats up to date */
+  dev->last_rx = jiffies;
+  lp->stats.rx_packets++;
+  lp->stats.rx_bytes += sksize;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
+  return;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the card to the network interface layer above
+ * this driver.  This routine checks if a buffer has been successfully
+ * received by the WaveLAN card.  If so, the routine wv_packet_read is
+ * called to do the actual transfer of the card's data including the
+ * ethernet header into a packet consisting of an sk_buff chain.
+ * (called by wavelan_interrupt())
+ * Note : the spinlock is already grabbed for us and irq are disabled.
+ */
+static inline void
+wv_packet_rcv(device *	dev)
+{
+  ioaddr_t	base = dev->base_addr;
+  net_local *	lp = (net_local *) dev->priv;
+  int		newrfp;
+  int		rp;
+  int		len;
+  int		f_start;
+  int		status;
+  int		i593_rfp;
+  int		stat_ptr;
+  u_char	c[4];
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name);
+#endif
+
+  /* Get the new receive frame pointer from the i82593 chip */
+  outb(CR0_STATUS_2 | OP0_NOP, LCCR(base));
+  i593_rfp = inb(LCSR(base));
+  i593_rfp |= inb(LCSR(base)) << 8;
+  i593_rfp %= RX_SIZE;
+
+  /* Get the new receive frame pointer from the WaveLAN card.
+   * It is 3 bytes more than the increment of the i82593 receive
+   * frame pointer, for each packet. This is because it includes the
+   * 3 roaming bytes added by the mmc.
+   */
+  newrfp = inb(RPLL(base));
+  newrfp |= inb(RPLH(base)) << 8;
+  newrfp %= RX_SIZE;
+
+#ifdef DEBUG_RX_INFO
+  printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+	 dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+
+#ifdef DEBUG_RX_ERROR
+  /* If no new frame pointer... */
+  if(lp->overrunning || newrfp == lp->rfp)
+    printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+	   dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+
+  /* Read all frames (packets) received */
+  while(newrfp != lp->rfp)
+    {
+      /* A frame is composed of the packet, followed by a status word,
+       * the length of the frame (word) and the mmc info (SNR & qual).
+       * It's because the length is at the end that we can only scan
+       * frames backward. */
+
+      /* Find the first frame by skipping backwards over the frames */
+      rp = newrfp;	/* End of last frame */
+      while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) &&
+	    (f_start != -1))
+	  rp = f_start;
+
+      /* If we had a problem */
+      if(f_start == -1)
+	{
+#ifdef DEBUG_RX_ERROR
+	  printk(KERN_INFO "wavelan_cs: cannot find start of frame ");
+	  printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+		 i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+	  lp->rfp = rp;		/* Get to the last usable frame */
+	  continue;
+	}
+
+      /* f_start point to the beggining of the first frame received
+       * and rp to the beggining of the next one */
+
+      /* Read status & length of the frame */
+      stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE;
+      stat_ptr = read_ringbuf(dev, stat_ptr, c, 4);
+      status = c[0] | (c[1] << 8);
+      len = c[2] | (c[3] << 8);
+
+      /* Check status */
+      if((status & RX_RCV_OK) != RX_RCV_OK)
+	{
+	  lp->stats.rx_errors++;
+	  if(status & RX_NO_SFD)
+	    lp->stats.rx_frame_errors++;
+	  if(status & RX_CRC_ERR)
+	    lp->stats.rx_crc_errors++;
+	  if(status & RX_OVRRUN)
+	    lp->stats.rx_over_errors++;
+
+#ifdef DEBUG_RX_FAIL
+	  printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n",
+		 dev->name, status);
+#endif
+	}
+      else
+	/* Read the packet and transmit to Linux */
+	wv_packet_read(dev, f_start, len - 2);
+
+      /* One frame has been processed, skip it */
+      lp->rfp = rp;
+    }
+
+  /*
+   * Update the frame stop register, but set it to less than
+   * the full 8K to allow space for 3 bytes of signal strength
+   * per packet.
+   */
+  lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+  outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
+  outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
+  outb(OP1_SWIT_TO_PORT_0, LCCR(base));
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name);
+#endif
+}
+
+/*********************** PACKET TRANSMISSION ***********************/
+/*
+ * This part deal with sending packet through the wavelan
+ * We copy the packet to the send buffer and then issue the send
+ * command to the i82593. The result of this operation will be
+ * checked in wavelan_interrupt()
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ * (called in wavelan_packet_xmit())
+ */
+static inline void
+wv_packet_write(device *	dev,
+		void *		buf,
+		short		length)
+{
+  net_local *		lp = (net_local *) dev->priv;
+  ioaddr_t		base = dev->base_addr;
+  unsigned long		flags;
+  int			clen = length;
+  register u_short	xmtdata_base = TX_BASE;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
+#endif
+
+  wv_splhi(lp, &flags);
+
+  /* Check if we need some padding */
+  if(clen < ETH_ZLEN)
+    clen = ETH_ZLEN;
+
+  /* Write the length of data buffer followed by the buffer */
+  outb(xmtdata_base & 0xff, PIORL(base));
+  outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+  outb(clen & 0xff, PIOP(base));	/* lsb */
+  outb(clen >> 8, PIOP(base));  	/* msb */
+
+  /* Send the data */
+  outsb(PIOP(base), buf, clen);
+
+  /* Indicate end of transmit chain */
+  outb(OP0_NOP, PIOP(base));
+  /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */
+  outb(OP0_NOP, PIOP(base));
+
+  /* Reset the transmit DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+  /* Send the transmit command */
+  wv_82593_cmd(dev, "wv_packet_write(): transmit",
+	       OP0_TRANSMIT, SR0_NO_RESULT);
+
+  /* Keep stats up to date */
+  lp->stats.tx_bytes += length;
+
+  wv_splx(lp, &flags);
+
+#ifdef DEBUG_TX_INFO
+  wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
+#endif	/* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the harware is ready to accept
+ * the packet. We also prevent reentrance. Then, we call the function
+ * to send the packet...
+ */
+static int
+wavelan_packet_xmit(struct sk_buff *	skb,
+		    device *		dev)
+{
+  net_local *		lp = (net_local *)dev->priv;
+  unsigned long		flags;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+	 (unsigned) skb);
+#endif
+
+  /*
+   * Block a timer-based transmit from overlapping a previous transmit.
+   * In other words, prevent reentering this routine.
+   */
+  netif_stop_queue(dev);
+
+  /* If somebody has asked to reconfigure the controller,
+   * we can do it now */
+  if(lp->reconfig_82593)
+    {
+      wv_splhi(lp, &flags);	/* Disable interrupts */
+      wv_82593_config(dev);
+      wv_splx(lp, &flags);	/* Re-enable interrupts */
+      /* Note : the configure procedure was totally synchronous,
+       * so the Tx buffer is now free */
+    }
+
+#ifdef DEBUG_TX_ERROR
+	if (skb->next)
+		printk(KERN_INFO "skb has next\n");
+#endif
+
+  wv_packet_write(dev, skb->data, skb->len);
+
+  dev_kfree_skb(skb);
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+  return(0);
+}
+
+/********************** HARDWARE CONFIGURATION **********************/
+/*
+ * This part do the real job of starting and configuring the hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_config())
+ */
+static inline int
+wv_mmc_init(device *	dev)
+{
+  ioaddr_t	base = dev->base_addr;
+  psa_t		psa;
+  mmw_t		m;
+  int		configured;
+  int		i;		/* Loop counter */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+  /* Read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /*
+   * Check the first three octets of the MAC addr for the manufacturer's code.
+   * Note: If you get the error message below, you've got a
+   * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on
+   * how to configure your card...
+   */
+  for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+    if((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) &&
+       (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) &&
+       (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2]))
+      break;
+
+  /* If we have not found it... */
+  if(i == (sizeof(MAC_ADDRESSES) / sizeof(char) / 3))
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n",
+	     dev->name, psa.psa_univ_mac_addr[0],
+	     psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]);
+#endif
+      return FALSE;
+    }
+
+  /* Get the MAC address */
+  memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
+
+#ifdef USE_PSA_CONFIG
+  configured = psa.psa_conf_status & 1;
+#else
+  configured = 0;
+#endif
+
+  /* Is the PSA is not configured */
+  if(!configured)
+    {
+      /* User will be able to configure NWID after (with iwconfig) */
+      psa.psa_nwid[0] = 0;
+      psa.psa_nwid[1] = 0;
+
+      /* As NWID is not set : no NWID checking */
+      psa.psa_nwid_select = 0;
+
+      /* Disable encryption */
+      psa.psa_encryption_select = 0;
+
+      /* Set to standard values
+       * 0x04 for AT,
+       * 0x01 for MCA,
+       * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+       */
+      if (psa.psa_comp_number & 1)
+	psa.psa_thr_pre_set = 0x01;
+      else
+	psa.psa_thr_pre_set = 0x04;
+      psa.psa_quality_thr = 0x03;
+
+      /* It is configured */
+      psa.psa_conf_status |= 1;
+
+#ifdef USE_PSA_CONFIG
+      /* Write the psa */
+      psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
+		(unsigned char *)psa.psa_nwid, 4);
+      psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+		(unsigned char *)&psa.psa_thr_pre_set, 1);
+      psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+		(unsigned char *)&psa.psa_quality_thr, 1);
+      psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa,
+		(unsigned char *)&psa.psa_conf_status, 1);
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+#endif	/* USE_PSA_CONFIG */
+    }
+
+  /* Zero the mmc structure */
+  memset(&m, 0x00, sizeof(m));
+
+  /* Copy PSA info to the mmc */
+  m.mmw_netw_id_l = psa.psa_nwid[1];
+  m.mmw_netw_id_h = psa.psa_nwid[0];
+  
+  if(psa.psa_nwid_select & 1)
+    m.mmw_loopt_sel = 0x00;
+  else
+    m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+  memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, 
+	 sizeof(m.mmw_encr_key));
+
+  if(psa.psa_encryption_select)
+    m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+  else
+    m.mmw_encr_enable = 0;
+
+  m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+  m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+  /*
+   * Set default modem control parameters.
+   * See NCR document 407-0024326 Rev. A.
+   */
+  m.mmw_jabber_enable = 0x01;
+  m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+  m.mmw_ifs = 0x20;
+  m.mmw_mod_delay = 0x04;
+  m.mmw_jam_time = 0x38;
+
+  m.mmw_des_io_invert = 0;
+  m.mmw_freeze = 0;
+  m.mmw_decay_prm = 0;
+  m.mmw_decay_updat_prm = 0;
+
+  /* Write all info to mmc */
+  mmc_write(base, 0, (u_char *)&m, sizeof(m));
+
+  /* The following code start the modem of the 2.00 frequency
+   * selectable cards at power on. It's not strictly needed for the
+   * following boots...
+   * The original patch was by Joe Finney for the PCMCIA driver, but
+   * I've cleaned it a bit and add documentation.
+   * Thanks to Loeke Brederveld from Lucent for the info.
+   */
+
+  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+   * (does it work for everybody ??? - especially old cards...) */
+  /* Note : WFREQSEL verify that it is able to read from EEprom
+   * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
+   * is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * My test is more crude but do work... */
+  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      m.mmw_fee_addr = 0x0F;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
+		(unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+      fee_wait(base, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+      /* The frequency was in the last word downloaded... */
+      mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m,
+	       (unsigned char *)&m.mmw_fee_data_l, 2);
+
+      /* Print some info for the user */
+      printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
+	     dev->name,
+	     ((m.mmw_fee_data_h << 4) |
+	      (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
+#endif
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      m.mmw_fee_addr = 0x61;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
+		(unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+    }	/* if 2.00 card */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to gracefully turn off reception, and wait for any commands
+ * to complete.
+ * (called in wv_ru_start() and wavelan_close() and wavelan_event())
+ */
+static int
+wv_ru_stop(device *	dev)
+{
+  ioaddr_t	base = dev->base_addr;
+  net_local *	lp = (net_local *) dev->priv;
+  unsigned long	flags;
+  int		status;
+  int		spin;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
+#endif
+
+  wv_splhi(lp, &flags);
+
+  /* First, send the LAN controller a stop receive command */
+  wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv",
+	       OP0_STOP_RCV, SR0_NO_RESULT);
+
+  /* Then, spin until the receive unit goes idle */
+  spin = 300;
+  do
+    {
+      udelay(10);
+      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+      status = inb(LCSR(base));
+    }
+  while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0));
+
+  /* Now, spin until the chip finishes executing its current command */
+  do
+    {
+      udelay(10);
+      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+      status = inb(LCSR(base));
+    }
+  while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
+
+  wv_splx(lp, &flags);
+
+  /* If there was a problem */
+  if(spin <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n",
+	     dev->name);
+#endif
+      return FALSE;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name);
+#endif
+  return TRUE;
+} /* wv_ru_stop */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine starts the receive unit running.  First, it checks if
+ * the card is actually ready. Then the card is instructed to receive
+ * packets again.
+ * (called in wv_hw_reset() & wavelan_open())
+ */
+static int
+wv_ru_start(device *	dev)
+{
+  ioaddr_t	base = dev->base_addr;
+  net_local *	lp = (net_local *) dev->priv;
+  unsigned long	flags;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+  /*
+   * We need to start from a quiescent state. To do so, we could check
+   * if the card is already running, but instead we just try to shut
+   * it down. First, we disable reception (in case it was already enabled).
+   */
+  if(!wv_ru_stop(dev))
+    return FALSE;
+
+  wv_splhi(lp, &flags);
+
+  /* Now we know that no command is being executed. */
+
+  /* Set the receive frame pointer and stop pointer */
+  lp->rfp = 0;
+  outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
+
+  /* Reset ring management.  This sets the receive frame pointer to 1 */
+  outb(OP1_RESET_RING_MNGMT, LCCR(base));
+
+#if 0
+  /* XXX the i82593 manual page 6-4 seems to indicate that the stop register
+     should be set as below */
+  /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/
+#elif 0
+  /* but I set it 0 instead */
+  lp->stop = 0;
+#else
+  /* but I set it to 3 bytes per packet less than 8K */
+  lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+#endif
+  outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
+  outb(OP1_INT_ENABLE, LCCR(base));
+  outb(OP1_SWIT_TO_PORT_0, LCCR(base));
+
+  /* Reset receive DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write_slow(base, HACR_DEFAULT);
+
+  /* Receive DMA on channel 1 */
+  wv_82593_cmd(dev, "wv_ru_start(): rcv-enable",
+	       CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT);
+
+#ifdef DEBUG_I82593_SHOW
+  {
+    int	status;
+    int	opri;
+    int	spin = 10000;
+
+    /* spin until the chip starts receiving */
+    do
+      {
+	outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+	status = inb(LCSR(base));
+	if(spin-- <= 0)
+	  break;
+      }
+    while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) &&
+	  ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY));
+    printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n",
+	   (status & SR3_RCV_STATE_MASK), i);
+  }
+#endif
+
+  wv_splx(lp, &flags);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard config of the WaveLAN controller (i82593).
+ * In the ISA driver, this is integrated in wavelan_hardware_reset()
+ * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit())
+ */
+static int
+wv_82593_config(device *	dev)
+{
+  ioaddr_t			base = dev->base_addr;
+  net_local *			lp = (net_local *) dev->priv;
+  struct i82593_conf_block	cfblk;
+  int				ret = TRUE;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name);
+#endif
+
+  /* Create & fill i82593 config block
+   *
+   * Now conform to Wavelan document WCIN085B
+   */
+  memset(&cfblk, 0x00, sizeof(struct i82593_conf_block));
+  cfblk.d6mod = FALSE;  	/* Run in i82593 advanced mode */
+  cfblk.fifo_limit = 5;         /* = 56 B rx and 40 B tx fifo thresholds */
+  cfblk.forgnesi = FALSE;       /* 0=82C501, 1=AMD7992B compatibility */
+  cfblk.fifo_32 = 1;
+  cfblk.throttle_enb = FALSE;
+  cfblk.contin = TRUE;          /* enable continuous mode */
+  cfblk.cntrxint = FALSE;       /* enable continuous mode receive interrupts */
+  cfblk.addr_len = WAVELAN_ADDR_SIZE;
+  cfblk.acloc = TRUE;           /* Disable source addr insertion by i82593 */
+  cfblk.preamb_len = 0;         /* 2 bytes preamble (SFD) */
+  cfblk.loopback = FALSE;
+  cfblk.lin_prio = 0;   	/* conform to 802.3 backoff algoritm */
+  cfblk.exp_prio = 5;	        /* conform to 802.3 backoff algoritm */
+  cfblk.bof_met = 1;	        /* conform to 802.3 backoff algoritm */
+  cfblk.ifrm_spc = 0x20;	/* 32 bit times interframe spacing */
+  cfblk.slottim_low = 0x20;	/* 32 bit times slot time */
+  cfblk.slottim_hi = 0x0;
+  cfblk.max_retr = 15;
+  cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE);	/* Promiscuous mode */
+  cfblk.bc_dis = FALSE;         /* Enable broadcast reception */
+  cfblk.crs_1 = TRUE;		/* Transmit without carrier sense */
+  cfblk.nocrc_ins = FALSE;	/* i82593 generates CRC */	
+  cfblk.crc_1632 = FALSE;	/* 32-bit Autodin-II CRC */
+  cfblk.crs_cdt = FALSE;	/* CD not to be interpreted as CS */
+  cfblk.cs_filter = 0;  	/* CS is recognized immediately */
+  cfblk.crs_src = FALSE;	/* External carrier sense */
+  cfblk.cd_filter = 0;  	/* CD is recognized immediately */
+  cfblk.min_fr_len = ETH_ZLEN >> 2;     /* Minimum frame length 64 bytes */
+  cfblk.lng_typ = FALSE;	/* Length field > 1500 = type field */
+  cfblk.lng_fld = TRUE; 	/* Disable 802.3 length field check */
+  cfblk.rxcrc_xf = TRUE;	/* Don't transfer CRC to memory */
+  cfblk.artx = TRUE;		/* Disable automatic retransmission */
+  cfblk.sarec = TRUE;		/* Disable source addr trig of CD */
+  cfblk.tx_jabber = TRUE;	/* Disable jabber jam sequence */
+  cfblk.hash_1 = FALSE; 	/* Use bits 0-5 in mc address hash */
+  cfblk.lbpkpol = TRUE; 	/* Loopback pin active high */
+  cfblk.fdx = FALSE;		/* Disable full duplex operation */
+  cfblk.dummy_6 = 0x3f; 	/* all ones */
+  cfblk.mult_ia = FALSE;	/* No multiple individual addresses */
+  cfblk.dis_bof = FALSE;	/* Disable the backoff algorithm ?! */
+  cfblk.dummy_1 = TRUE; 	/* set to 1 */
+  cfblk.tx_ifs_retrig = 3;	/* Hmm... Disabled */
+#ifdef MULTICAST_ALL
+  cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE);	/* Allow all multicasts */
+#else
+  cfblk.mc_all = FALSE;		/* No multicast all mode */
+#endif
+  cfblk.rcv_mon = 0;		/* Monitor mode disabled */
+  cfblk.frag_acpt = TRUE;	/* Do not accept fragments */
+  cfblk.tstrttrs = FALSE;	/* No start transmission threshold */
+  cfblk.fretx = TRUE;		/* FIFO automatic retransmission */
+  cfblk.syncrqs = FALSE; 	/* Synchronous DRQ deassertion... */
+  cfblk.sttlen = TRUE;  	/* 6 byte status registers */
+  cfblk.rx_eop = TRUE;  	/* Signal EOP on packet reception */
+  cfblk.tx_eop = TRUE;  	/* Signal EOP on packet transmission */
+  cfblk.rbuf_size = RX_SIZE>>11;	/* Set receive buffer size */
+  cfblk.rcvstop = TRUE; 	/* Enable Receive Stop Register */
+
+#ifdef DEBUG_I82593_SHOW
+  {
+    u_char *c = (u_char *) &cfblk;
+    int i;
+    printk(KERN_DEBUG "wavelan_cs: config block:");
+    for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++)
+      {
+	if((i % 16) == 0) printk("\n" KERN_DEBUG);
+	printk("%02x ", *c);
+      }
+    printk("\n");
+  }
+#endif
+
+  /* Copy the config block to the i82593 */
+  outb(TX_BASE & 0xff, PIORL(base));
+  outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+  outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base));    /* lsb */
+  outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base));	/* msb */
+  outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block));
+
+  /* reset transmit DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+  if(!wv_82593_cmd(dev, "wv_82593_config(): configure",
+		   OP0_CONFIGURE, SR0_CONFIGURE_DONE))
+    ret = FALSE;
+
+  /* Initialize adapter's ethernet MAC address */
+  outb(TX_BASE & 0xff, PIORL(base));
+  outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+  outb(WAVELAN_ADDR_SIZE, PIOP(base));	/* byte count lsb */
+  outb(0, PIOP(base));			/* byte count msb */
+  outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE);
+
+  /* reset transmit DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+  if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup",
+		   OP0_IA_SETUP, SR0_IA_SETUP_DONE))
+    ret = FALSE;
+
+#ifdef WAVELAN_ROAMING
+    /* If roaming is enabled, join the "Beacon Request" multicast group... */
+    /* But only if it's not in there already! */
+  if(do_roaming)
+    dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1);
+#endif	/* WAVELAN_ROAMING */
+
+  /* If any multicast address to set */
+  if(lp->mc_count)
+    {
+      struct dev_mc_list *	dmi;
+      int			addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count;
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n",
+	     dev->name, lp->mc_count);
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+	printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
+	       dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
+#endif
+
+      /* Initialize adapter's ethernet multicast addresses */
+      outb(TX_BASE & 0xff, PIORL(base));
+      outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+      outb(addrs_len & 0xff, PIOP(base));	/* byte count lsb */
+      outb((addrs_len >> 8), PIOP(base));	/* byte count msb */
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+	outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen);
+
+      /* reset transmit DMA pointer */
+      hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+      hacr_write(base, HACR_DEFAULT);
+      if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup",
+		       OP0_MC_SETUP, SR0_MC_SETUP_DONE))
+	ret = FALSE;
+      lp->mc_count = dev->mc_count;	/* remember to avoid repeated reset */
+    }
+
+  /* Job done, clear the flag */
+  lp->reconfig_82593 = FALSE;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
+#endif
+  return(ret);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Access Configuration Register, perform a software reset,
+ * and then re-enable the card's software.
+ *
+ * If I understand correctly : reset the pcmcia interface of the
+ * wavelan.
+ * (called by wv_config())
+ */
+static inline int
+wv_pcmcia_reset(device *	dev)
+{
+  int		i;
+  conf_reg_t	reg = { 0, CS_READ, CISREG_COR, 0 };
+  dev_link_t *	link = ((net_local *) dev->priv)->link;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name);
+#endif
+
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+      
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
+	 dev->name, (u_int) reg.Value);
+#endif
+
+  reg.Action = CS_WRITE;
+  reg.Value = reg.Value | COR_SW_RESET;
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+      
+  reg.Action = CS_WRITE;
+  reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_hw_config() is called after a CARD_INSERTION event is
+ * received, to configure the wavelan hardware.
+ * Note that the reception will be enabled in wavelan->open(), so the
+ * device is configured but idle...
+ * Performs the following actions:
+ * 	1. A pcmcia software reset (using wv_pcmcia_reset())
+ *	2. A power reset (reset DMA)
+ *	3. Reset the LAN controller
+ *	4. Initialize the radio modem (using wv_mmc_init)
+ *	5. Configure LAN controller (using wv_82593_config)
+ *	6. Perform a diagnostic on the LAN controller
+ * (called by wavelan_event() & wv_hw_reset())
+ */
+static int
+wv_hw_config(device *	dev)
+{
+  net_local *		lp = (net_local *) dev->priv;
+  ioaddr_t		base = dev->base_addr;
+  unsigned long		flags;
+  int			ret = FALSE;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name);
+#endif
+
+#ifdef STRUCT_CHECK
+  if(wv_structuct_check() != (char *) NULL)
+    {
+      printk(KERN_WARNING "%s: wv_hw_config: structure/compiler botch: \"%s\"\n",
+	     dev->name, wv_structuct_check());
+      return FALSE;
+    }
+#endif	/* STRUCT_CHECK == 1 */
+
+  /* Reset the pcmcia interface */
+  if(wv_pcmcia_reset(dev) == FALSE)
+    return FALSE;
+
+  /* Disable interrupts */
+  wv_splhi(lp, &flags);
+
+  /* Disguised goto ;-) */
+  do
+    {
+      /* Power UP the module + reset the modem + reset host adapter
+       * (in fact, reset DMA channels) */
+      hacr_write_slow(base, HACR_RESET);
+      hacr_write(base, HACR_DEFAULT);
+
+      /* Check if the module has been powered up... */
+      if(hasr_read(base) & HASR_NO_CLK)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
+		 dev->name);
+#endif
+	  break;
+	}
+
+      /* initialize the modem */
+      if(wv_mmc_init(dev) == FALSE)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n",
+		 dev->name);
+#endif
+	  break;
+	}
+
+      /* reset the LAN controller (i82593) */
+      outb(OP0_RESET, LCCR(base));
+      mdelay(1);	/* A bit crude ! */
+
+      /* Initialize the LAN controller */
+      if(wv_82593_config(dev) == FALSE)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n",
+		 dev->name);
+#endif
+	  break;
+	}
+
+      /* Diagnostic */
+      if(wv_diag(dev) == FALSE)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n",
+		 dev->name);
+#endif
+	  break;
+	}
+
+      /* 
+       * insert code for loopback test here
+       */
+
+      /* The device is now configured */
+      lp->configured = 1;
+      ret = TRUE;
+    }
+  while(0);
+
+  /* Re-enable interrupts */
+  wv_splx(lp, &flags);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
+#endif
+  return(ret);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the wavelan and restart it.
+ * Performs the following actions:
+ * 	1. Call wv_hw_config()
+ *	2. Start the LAN controller's receive unit
+ * (called by wavelan_event(), wavelan_watchdog() and wavelan_open())
+ */
+static inline void
+wv_hw_reset(device *	dev)
+{
+  net_local *	lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name);
+#endif
+
+  lp->nresets++;
+  lp->configured = 0;
+  
+  /* Call wv_hw_config() for most of the reset & init stuff */
+  if(wv_hw_config(dev) == FALSE)
+    return;
+
+  /* start receive unit */
+  wv_ru_start(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wv_pcmcia_config() is called after a CARD_INSERTION event is
+ * received, to configure the PCMCIA socket, and to make the ethernet
+ * device available to the system.
+ * (called by wavelan_event())
+ */
+static inline int
+wv_pcmcia_config(dev_link_t *	link)
+{
+  client_handle_t	handle;
+  tuple_t		tuple;
+  cisparse_t		parse;
+  struct net_device *	dev;
+  int			i;
+  u_char		buf[64];
+  win_req_t		req;
+  memreq_t		mem;
+
+  handle = link->handle;
+  dev = (device *) link->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link);
+#endif
+
+  /*
+   * This reads the card's CONFIG tuple to find its configuration
+   * registers.
+   */
+  do
+    {
+      tuple.Attributes = 0;
+      tuple.DesiredTuple = CISTPL_CONFIG;
+      i = CardServices(GetFirstTuple, handle, &tuple);
+      if(i != CS_SUCCESS)
+	break;
+      tuple.TupleData = (cisdata_t *)buf;
+      tuple.TupleDataMax = 64;
+      tuple.TupleOffset = 0;
+      i = CardServices(GetTupleData, handle, &tuple);
+      if(i != CS_SUCCESS)
+	break;
+      i = CardServices(ParseTuple, handle, &tuple, &parse);
+      if(i != CS_SUCCESS)
+	break;
+      link->conf.ConfigBase = parse.config.base;
+      link->conf.Present = parse.config.rmask[0];
+    }
+  while(0);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, ParseTuple, i);
+      link->state &= ~DEV_CONFIG_PENDING;
+      return FALSE;
+    }
+    
+  /* Configure card */
+  link->state |= DEV_CONFIG;
+  do
+    {
+      i = CardServices(RequestIO, link->handle, &link->io);
+      if(i != CS_SUCCESS)
+	{
+	  cs_error(link->handle, RequestIO, i);
+	  break;
+	}
+
+      /*
+       * Now allocate an interrupt line.  Note that this does not
+       * actually assign a handler to the interrupt.
+       */
+      i = CardServices(RequestIRQ, link->handle, &link->irq);
+      if(i != CS_SUCCESS)
+	{
+	  cs_error(link->handle, RequestIRQ, i);
+	  break;
+	}
+
+      /*
+       * This actually configures the PCMCIA socket -- setting up
+       * the I/O windows and the interrupt mapping.
+       */
+      link->conf.ConfigIndex = 1;
+      i = CardServices(RequestConfiguration, link->handle, &link->conf);
+      if(i != CS_SUCCESS)
+	{
+	  cs_error(link->handle, RequestConfiguration, i);
+	  break;
+	}
+
+      /*
+       * Allocate a small memory window.  Note that the dev_link_t
+       * structure provides space for one window handle -- if your
+       * device needs several windows, you'll need to keep track of
+       * the handles in your private data structure, link->priv.
+       */
+      req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+      req.Base = req.Size = 0;
+      req.AccessSpeed = mem_speed;
+      link->win = (window_handle_t)link->handle;
+      i = CardServices(RequestWindow, &link->win, &req);
+      if(i != CS_SUCCESS)
+	{
+	  cs_error(link->handle, RequestWindow, i);
+	  break;
+	}
+
+      dev->rmem_start = dev->mem_start =
+	  (u_long)ioremap(req.Base, req.Size);
+      dev->rmem_end = dev->mem_end = dev->mem_start + req.Size;
+
+      mem.CardOffset = 0; mem.Page = 0;
+      i = CardServices(MapMemPage, link->win, &mem);
+      if(i != CS_SUCCESS)
+	{
+	  cs_error(link->handle, MapMemPage, i);
+	  break;
+	}
+
+      /* Feed device with this info... */
+      dev->irq = link->irq.AssignedIRQ;
+      dev->base_addr = link->io.BasePort1;
+      netif_start_queue(dev);
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n",
+	     (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr);
+#endif
+
+      i = register_netdev(dev);
+      if(i != 0)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n");
+#endif
+	  break;
+	}
+    }
+  while(0);		/* Humm... Disguised goto !!! */
+
+  link->state &= ~DEV_CONFIG_PENDING;
+  /* If any step failed, release any partially configured state */
+  if(i != 0)
+    {
+      wv_pcmcia_release((u_long) link);
+      return FALSE;
+    }
+
+  strcpy(((net_local *) dev->priv)->node.dev_name, dev->name);
+  link->dev = &((net_local *) dev->priv)->node;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "<-wv_pcmcia_config()\n");
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * After a card is removed, wv_pcmcia_release() will unregister the net
+ * device, and release the PCMCIA configuration.  If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+wv_pcmcia_release(u_long	arg)	/* Address of the interface struct */
+{
+  dev_link_t *	link = (dev_link_t *) arg;
+  device *	dev = (device *) link->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
+#endif
+
+  /* If the device is currently in use, we won't release until it is
+   * actually closed. */
+  if(link->open)
+    {
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_pcmcia_release: release postponed, device still open\n",
+	     dev->name);
+#endif
+      link->state |= DEV_STALE_CONFIG;
+      return;
+    }
+
+  /* Don't bother checking to see if these succeed or not */
+  iounmap((u_char *)dev->mem_start);
+  CardServices(ReleaseWindow, link->win);
+  CardServices(ReleaseConfiguration, link->handle);
+  CardServices(ReleaseIO, link->handle, &link->io);
+  CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+  link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
+#endif
+} /* wv_pcmcia_release */
+
+/*------------------------------------------------------------------*/
+/*
+ * Sometimes, wavelan_detach can't be performed following a call from
+ * cardmgr (device still open, pcmcia_release not done) and the device
+ * is put in a STALE_LINK state and remains in memory.
+ *
+ * This function run through our current list of device and attempt
+ * another time to remove them. We hope that since last time the
+ * device has properly been closed.
+ *
+ * (called by wavelan_attach() & cleanup_module())
+ */
+static void
+wv_flush_stale_links(void)
+{
+  dev_link_t *	link;		/* Current node in linked list */
+  dev_link_t *	next;		/* Next node in linked list */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "-> wv_flush_stale_links(0x%p)\n", dev_list);
+#endif
+
+  /* Go through the list */
+  for (link = dev_list; link; link = next)
+    {
+      next = link->next;
+
+      /* Check if in need of being removed */
+      if((link->state & DEV_STALE_LINK) ||
+	 (! (link->state & DEV_PRESENT)))
+	wavelan_detach(link);
+
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "<- wv_flush_stale_links()\n");
+#endif
+}
+
+/************************ INTERRUPT HANDLING ************************/
+
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever: 
+ *	1. A packet is received.
+ *	2. A packet has successfully been transferred and the unit is
+ *	   ready to transmit another packet.
+ *	3. A command has completed execution.
+ */
+static void
+wavelan_interrupt(int		irq,
+		  void *	dev_id,
+		  struct pt_regs * regs)
+{
+  device *	dev;
+  net_local *	lp;
+  ioaddr_t	base;
+  int		status0;
+  u_int		tx_status;
+
+  if((dev = (device *)dev_id) == (device *) NULL)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
+	     irq);
+#endif
+      return;
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+  lp = (net_local *) dev->priv;
+  base = dev->base_addr;
+
+#ifdef DEBUG_INTERRUPT_INFO
+  /* Check state of our spinlock (it should be cleared) */
+  if(spin_is_locked(&lp->spinlock))
+    printk(KERN_DEBUG
+	   "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
+	   dev->name);
+#endif
+
+  /* Prevent reentrancy. We need to do that because we may have
+   * multiple interrupt handler running concurently.
+   * It is safe because wv_splhi() disable interrupts before aquiring
+   * the spinlock. */
+  spin_lock(&lp->spinlock);
+
+  /* Treat all pending interrupts */
+  while(1)
+    {
+      /* ---------------- INTERRUPT CHECKING ---------------- */
+      /*
+       * Look for the interrupt and verify the validity
+       */
+      outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
+      status0 = inb(LCSR(base));
+
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0, 
+	     (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT);
+      if(status0&SR0_INTERRUPT)
+	{
+	  printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" :
+		 ((status0 & SR0_EXECUTION) ? "cmd" :
+		  ((status0 & SR0_RECEPTION) ? "recv" : "unknown")),
+		 (status0 & SR0_EVENT_MASK));
+	}
+      else
+	printk("\n");
+#endif
+
+      /* Return if no actual interrupt from i82593 (normal exit) */
+      if(!(status0 & SR0_INTERRUPT))
+	break;
+
+      /* If interrupt is both Rx and Tx or none...
+       * This code in fact is there to catch the spurious interrupt
+       * when you remove the wavelan pcmcia card from the socket */
+      if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) ||
+	 ((status0 & SR0_BOTH_RX_TX) == 0x0))
+	{
+#ifdef DEBUG_INTERRUPT_INFO
+	  printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n",
+		 dev->name, status0);
+#endif
+	  /* Acknowledge the interrupt */
+	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+	  break;
+	}
+
+      /* ----------------- RECEIVING PACKET ----------------- */
+      /*
+       * When the wavelan signal the reception of a new packet,
+       * we call wv_packet_rcv() to copy if from the buffer and
+       * send it to NET3
+       */
+      if(status0 & SR0_RECEPTION)
+	{
+#ifdef DEBUG_INTERRUPT_INFO
+	  printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name);
+#endif
+
+	  if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT)
+	    {
+#ifdef DEBUG_INTERRUPT_ERROR
+	      printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n",
+		     dev->name);
+#endif
+	      lp->stats.rx_over_errors++;
+	      lp->overrunning = 1;
+      	    }
+
+	  /* Get the packet */
+	  wv_packet_rcv(dev);
+	  lp->overrunning = 0;
+
+	  /* Acknowledge the interrupt */
+	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+	  continue;
+    	}
+
+      /* ---------------- COMMAND COMPLETION ---------------- */
+      /*
+       * Interrupts issued when the i82593 has completed a command.
+       * Most likely : transmission done
+       */
+
+      /* If a transmission has been done */
+      if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
+	 (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
+	 (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
+	{
+#ifdef DEBUG_TX_ERROR
+	  if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
+	    printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n",
+		   dev->name);
+#endif
+
+	  /* Get transmission status */
+	  tx_status = inb(LCSR(base));
+	  tx_status |= (inb(LCSR(base)) << 8);
+#ifdef DEBUG_INTERRUPT_INFO
+	  printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n",
+		 dev->name);
+	  {
+	    u_int	rcv_bytes;
+	    u_char	status3;
+	    rcv_bytes = inb(LCSR(base));
+	    rcv_bytes |= (inb(LCSR(base)) << 8);
+	    status3 = inb(LCSR(base));
+	    printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n",
+		   tx_status, rcv_bytes, (u_int) status3);
+	  }
+#endif
+	  /* Check for possible errors */
+	  if((tx_status & TX_OK) != TX_OK)
+	    {
+	      lp->stats.tx_errors++;
+
+	      if(tx_status & TX_FRTL)
+		{
+#ifdef DEBUG_TX_ERROR
+		  printk(KERN_INFO "%s: wv_interrupt(): frame too long\n",
+			 dev->name);
+#endif
+		}
+	      if(tx_status & TX_UND_RUN)
+		{
+#ifdef DEBUG_TX_FAIL
+		  printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n",
+			 dev->name);
+#endif
+		  lp->stats.tx_aborted_errors++;
+		}
+	      if(tx_status & TX_LOST_CTS)
+		{
+#ifdef DEBUG_TX_FAIL
+		  printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name);
+#endif
+		  lp->stats.tx_carrier_errors++;
+		}
+	      if(tx_status & TX_LOST_CRS)
+		{
+#ifdef DEBUG_TX_FAIL
+		  printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n",
+			 dev->name);
+#endif
+		  lp->stats.tx_carrier_errors++;
+		}
+	      if(tx_status & TX_HRT_BEAT)
+		{
+#ifdef DEBUG_TX_FAIL
+		  printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name);
+#endif
+		  lp->stats.tx_heartbeat_errors++;
+		}
+	      if(tx_status & TX_DEFER)
+		{
+#ifdef DEBUG_TX_FAIL
+		  printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n",
+			 dev->name);
+#endif
+		}
+	      /* Ignore late collisions since they're more likely to happen
+	       * here (the WaveLAN design prevents the LAN controller from
+	       * receiving while it is transmitting). We take action only when
+	       * the maximum retransmit attempts is exceeded.
+	       */
+	      if(tx_status & TX_COLL)
+		{
+		  if(tx_status & TX_MAX_COL)
+		    {
+#ifdef DEBUG_TX_FAIL
+		      printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n",
+			     dev->name);
+#endif
+		      if(!(tx_status & TX_NCOL_MASK))
+			{
+			  lp->stats.collisions += 0x10;
+			}
+		    }
+		}
+	    }	/* if(!(tx_status & TX_OK)) */
+
+	  lp->stats.collisions += (tx_status & TX_NCOL_MASK);
+	  lp->stats.tx_packets++;
+
+	  netif_wake_queue(dev);
+	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));	/* Acknowledge the interrupt */
+    	} 
+      else	/* if interrupt = transmit done or retransmit done */
+	{
+#ifdef DEBUG_INTERRUPT_ERROR
+	  printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n",
+		 status0);
+#endif
+	  outb(CR0_INT_ACK | OP0_NOP, LCCR(base));	/* Acknowledge the interrupt */
+    	}
+    }	/* while(1) */
+
+  spin_unlock(&lp->spinlock);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
+} /* wv_interrupt */
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog: when we start a transmission, a timer is set for us in the
+ * kernel.  If the transmission completes, this timer is disabled. If
+ * the timer expires, we are called and we try to unlock the hardware.
+ *
+ * Note : This watchdog is move clever than the one in the ISA driver,
+ * because it try to abort the current command before reseting
+ * everything...
+ * On the other hand, it's a bit simpler, because we don't have to
+ * deal with the multiple Tx buffers...
+ */
+static void
+wavelan_watchdog(device *	dev)
+{
+  net_local *		lp = (net_local *) dev->priv;
+  ioaddr_t		base = dev->base_addr;
+  unsigned long		flags;
+  int			aborted = FALSE;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+  printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+	 dev->name);
+#endif
+
+  wv_splhi(lp, &flags);
+
+  /* Ask to abort the current command */
+  outb(OP0_ABORT, LCCR(base));
+
+  /* Wait for the end of the command (a bit hackish) */
+  if(wv_82593_cmd(dev, "wavelan_watchdog(): abort",
+		  OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED))
+    aborted = TRUE;
+
+  /* Release spinlock here so that wv_hw_reset() can grab it */
+  wv_splx(lp, &flags);
+
+  /* Check if we were successful in aborting it */
+  if(!aborted)
+    {
+      /* It seem that it wasn't enough */
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n",
+	     dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+
+#ifdef DEBUG_PSA_SHOW
+  {
+    psa_t		psa;
+    psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+    wv_psa_show(&psa);
+  }
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82593_SHOW
+  wv_ru_show(dev);
+#endif
+
+  /* We are no more waiting for something... */
+  netif_wake_queue(dev);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
+}
+
+/********************* CONFIGURATION CALLBACKS *********************/
+/*
+ * Here are the functions called by the pcmcia package (cardmgr) and
+ * linux networking (NET3) for initialization, configuration and
+ * deinstallations of the Wavelan Pcmcia Hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "open" the device.
+ */
+static int
+wavelan_open(device *	dev)
+{
+  dev_link_t *	link = ((net_local *) dev->priv)->link;
+  net_local *	lp = (net_local *)dev->priv;
+  ioaddr_t	base = dev->base_addr;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+	 (unsigned int) dev);
+#endif
+
+  /* Check if the modem is powered up (wavelan_close() power it down */
+  if(hasr_read(base) & HASR_NO_CLK)
+    {
+      /* Power up (power up time is 250us) */
+      hacr_write(base, HACR_DEFAULT);
+
+      /* Check if the module has been powered up... */
+      if(hasr_read(base) & HASR_NO_CLK)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n",
+		 dev->name);
+#endif
+	  return FALSE;
+	}
+    }
+
+  /* Start reception and declare the driver ready */
+  if(!lp->configured)
+    return FALSE;
+  if(!wv_ru_start(dev))
+    wv_hw_reset(dev);		/* If problem : reset */
+  netif_start_queue(dev);
+
+  /* Mark the device as used */
+  link->open++;
+  MOD_INC_USE_COUNT;
+
+#ifdef WAVELAN_ROAMING
+  if(do_roaming)
+    wv_roam_init(dev);
+#endif	/* WAVELAN_ROAMING */
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Shutdown the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "close" the device.
+ */
+static int
+wavelan_close(device *	dev)
+{
+  dev_link_t *	link = ((net_local *) dev->priv)->link;
+  ioaddr_t	base = dev->base_addr;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+	 (unsigned int) dev);
+#endif
+
+  /* If the device isn't open, then nothing to do */
+  if(!link->open)
+    {
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name);
+#endif
+      return 0;
+    }
+
+#ifdef WAVELAN_ROAMING
+  /* Cleanup of roaming stuff... */
+  if(do_roaming)
+    wv_roam_cleanup(dev);
+#endif	/* WAVELAN_ROAMING */
+
+  link->open--;
+  MOD_DEC_USE_COUNT;
+
+  /* If the card is still present */
+  if(netif_running(dev))
+    {
+      netif_stop_queue(dev);
+
+      /* Stop receiving new messages and wait end of transmission */
+      wv_ru_stop(dev);
+
+      /* Power down the module */
+      hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT));
+    }
+  else
+    /* The card is no more there (flag is activated in wv_pcmcia_release) */
+    if(link->state & DEV_STALE_CONFIG)
+      wv_pcmcia_release((u_long)link);
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device (one interface).  The device
+ * is registered with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+static dev_link_t *
+wavelan_attach(void)
+{
+  client_reg_t	client_reg;	/* Register with cardmgr */
+  dev_link_t *	link;		/* Info for cardmgr */
+  device *	dev;		/* Interface generic data */
+  net_local *	lp;		/* Interface specific data */
+  int		i, ret;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "-> wavelan_attach()\n");
+#endif
+
+  /* Perform some cleanup */
+  wv_flush_stale_links();
+
+  /* Initialize the dev_link_t structure */
+  link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+  if (!link) return NULL;
+  memset(link, 0, sizeof(struct dev_link_t));
+
+  /* Unused for the Wavelan */
+  link->release.function = &wv_pcmcia_release;
+  link->release.data = (u_long) link;
+
+  /* The io structure describes IO port mapping */
+  link->io.NumPorts1 = 8;
+  link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+  link->io.IOAddrLines = 3;
+
+  /* Interrupt setup */
+  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+  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->irq.Handler = wavelan_interrupt;
+
+  /* General socket configuration */
+  link->conf.Attributes = CONF_ENABLE_IRQ;
+  link->conf.Vcc = 50;
+  link->conf.IntType = INT_MEMORY_AND_IO;
+
+  /* Chain drivers */
+  link->next = dev_list;
+  dev_list = link;
+
+  /* Allocate the generic data structure */
+  dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+  if (!dev) {
+      kfree(link);
+      return NULL;
+  }
+  memset(dev, 0x00, sizeof(struct net_device));
+  link->priv = link->irq.Instance = dev;
+
+  /* Allocate the wavelan-specific data structure. */
+  dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
+  if (!lp) {
+      kfree(link);
+      kfree(dev);
+      return NULL;
+  }
+  memset(lp, 0x00, sizeof(net_local));
+
+  /* Init specific data */
+  lp->configured = 0;
+  lp->reconfig_82593 = FALSE;
+  lp->nresets = 0;
+  /* Multicast stuff */
+  lp->promiscuous = 0;
+  lp->allmulticast = 0;
+  lp->mc_count = 0;
+
+  /* Init spinlock */
+  spin_lock_init(&lp->spinlock);
+
+  /* back links */
+  lp->link = link;
+  lp->dev = dev;
+
+  /* Standard setup for generic data */
+  ether_setup(dev);
+
+  /* wavelan NET3 callbacks */
+  dev->open = &wavelan_open;
+  dev->stop = &wavelan_close;
+  dev->hard_start_xmit = &wavelan_packet_xmit;
+  dev->get_stats = &wavelan_get_stats;
+  dev->set_multicast_list = &wavelan_set_multicast_list;
+#ifdef SET_MAC_ADDRESS
+  dev->set_mac_address = &wavelan_set_mac_address;
+#endif	/* SET_MAC_ADDRESS */
+
+  /* Set the watchdog timer */
+  dev->tx_timeout	= &wavelan_watchdog;
+  dev->watchdog_timeo	= WATCHDOG_JIFFIES;
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+  dev->do_ioctl = wavelan_ioctl;	/* wireless extensions */
+  dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+  /* Other specific data */
+  dev->mtu = WAVELAN_MTU;
+
+  /* Register with Card Services */
+  client_reg.dev_info = &dev_info;
+  client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+  client_reg.EventMask = 
+    CS_EVENT_REGISTRATION_COMPLETE |
+    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+  client_reg.event_handler = &wavelan_event;
+  client_reg.Version = 0x0210;
+  client_reg.event_callback_args.client_data = link;
+
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "wavelan_attach(): almost done, calling CardServices\n");
+#endif
+
+  ret = CardServices(RegisterClient, &link->handle, &client_reg);
+  if(ret != 0)
+    {
+      cs_error(link->handle, RegisterClient, ret);
+      wavelan_detach(link);
+      return NULL;
+    }
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<- wavelan_attach()\n");
+#endif
+
+  return link;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This deletes a driver "instance".  The device is de-registered with
+ * Card Services.  If it has been released, all local data structures
+ * are freed.  Otherwise, the structures will be freed when the device
+ * is released.
+ */
+static void
+wavelan_detach(dev_link_t *	link)
+{
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link);
+#endif
+
+  /*
+   * If the device is currently configured and active, we won't
+   * actually delete it yet.  Instead, it is marked so that when the
+   * release() function is called, that will trigger a proper
+   * detach().
+   */
+  if(link->state & DEV_CONFIG)
+    {
+      /* Some others haven't done their job : give them another chance */
+      wv_pcmcia_release((u_long) link);
+      if(link->state & DEV_STALE_CONFIG)
+	{
+#ifdef DEBUG_CONFIG_INFO
+	  printk(KERN_DEBUG "wavelan_detach: detach postponed,"
+		 " '%s' still locked\n", link->dev->dev_name);
+#endif
+	  link->state |= DEV_STALE_LINK;
+	  return;
+	}
+    }
+
+  /* Break the link with Card Services */
+  if(link->handle)
+    CardServices(DeregisterClient, link->handle);
+    
+  /* Remove the interface data from the linked list */
+  if(dev_list == link)
+    dev_list = link->next;
+  else
+    {
+      dev_link_t *	prev = dev_list;
+
+      while((prev != (dev_link_t *) NULL) && (prev->next != link))
+	prev = prev->next;
+
+      if(prev == (dev_link_t *) NULL)
+	{
+#ifdef DEBUG_CONFIG_ERRORS
+	  printk(KERN_WARNING "wavelan_detach : Attempting to remove a nonexistent device.\n");
+#endif
+	  return;
+	}
+
+      prev->next = link->next;
+    }
+
+  /* Free pieces */
+  if(link->priv)
+    {
+      device *	dev = (device *) link->priv;
+
+      /* Remove ourselves from the kernel list of ethernet devices */
+      /* Warning : can't be called from interrupt, timer or wavelan_close() */
+      if(link->dev != NULL)
+	unregister_netdev(dev);
+      link->dev = NULL;
+
+      if(dev->priv)
+	{
+	  /* Sound strange, but safe... */
+	  ((net_local *) dev->priv)->link = (dev_link_t *) NULL;
+	  ((net_local *) dev->priv)->dev = (device *) NULL;
+	  kfree(dev->priv);
+	}
+      kfree(link->priv);
+    }
+  kfree(link);
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<- wavelan_detach()\n");
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * The card status event handler. Mostly, this schedules other stuff
+ * to run after an event is received. A CARD_REMOVAL event also sets
+ * some flags to discourage the net drivers from trying to talk to the
+ * card any more.
+ */
+static int
+wavelan_event(event_t		event,		/* The event received */
+	      int		priority,
+	      event_callback_args_t *	args)
+{
+  dev_link_t *	link = (dev_link_t *) args->client_data;
+  device *	dev = (device *) link->priv;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "->wavelan_event(): %s\n",
+	 ((event == CS_EVENT_REGISTRATION_COMPLETE)?"registration complete" :
+	  ((event == CS_EVENT_CARD_REMOVAL) ? "card removal" :
+	   ((event == CS_EVENT_CARD_INSERTION) ? "card insertion" :
+	    ((event == CS_EVENT_PM_SUSPEND) ? "pm suspend" :
+	     ((event == CS_EVENT_RESET_PHYSICAL) ? "physical reset" :
+	      ((event == CS_EVENT_PM_RESUME) ? "pm resume" :
+	       ((event == CS_EVENT_CARD_RESET) ? "card reset" :
+		"unknown"))))))));
+#endif
+
+    switch(event)
+      {
+      case CS_EVENT_REGISTRATION_COMPLETE:
+#ifdef DEBUG_CONFIG_INFO
+	printk(KERN_DEBUG "wavelan_cs: registration complete\n");
+#endif
+	break;
+
+      case CS_EVENT_CARD_REMOVAL:
+	/* Oups ! The card is no more there */
+	link->state &= ~DEV_PRESENT;
+	if(link->state & DEV_CONFIG)
+	  {
+	    /* Accept no more transmissions */
+	    netif_device_detach(dev);
+
+	    /* Release the card */
+	    wv_pcmcia_release((u_long) link);
+	  }
+	break;
+
+      case CS_EVENT_CARD_INSERTION:
+	/* Reset and configure the card */
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	if(wv_pcmcia_config(link) &&
+	   wv_hw_config(dev))
+	  wv_init_info(dev);
+	else
+	  dev->irq = 0;
+	break;
+
+      case CS_EVENT_PM_SUSPEND:
+	/* NB: wavelan_close will be called, but too late, so we are
+	 * obliged to close nicely the wavelan here. David, could you
+	 * close the device before suspending them ? And, by the way,
+	 * could you, on resume, add a "route add -net ..." after the
+	 * ifconfig up ??? Thanks... */
+
+	/* Stop receiving new messages and wait end of transmission */
+	wv_ru_stop(dev);
+
+	/* Power down the module */
+	hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT));
+
+	/* The card is now suspended */
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+      case CS_EVENT_RESET_PHYSICAL:
+    	if(link->state & DEV_CONFIG)
+	  {
+      	    if(link->open)
+	      netif_device_detach(dev);
+      	    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);
+      	    if(link->open)	/* If RESET -> True, If RESUME -> False ??? */
+	      {
+		wv_hw_reset(dev);
+		netif_device_attach(dev);
+	      }
+	  }
+	break;
+    }
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<-wavelan_event()\n");
+#endif
+  return 0;
+}
+
+/****************************** MODULE ******************************/
+/*
+ * Module entry points : insertion & removal
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Module insertion : initialisation of the module.
+ * Register the card with cardmgr...
+ */
+static int __init
+init_wavelan_cs(void)
+{
+  servinfo_t	serv;
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> init_wavelan_cs()\n");
+#ifdef DEBUG_VERSION_SHOW
+  printk(KERN_DEBUG "%s", version);
+#endif
+#endif
+
+  CardServices(GetCardServicesInfo, &serv);
+  if(serv.Revision != CS_RELEASE_CODE)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "init_wavelan_cs: Card Services release does not match!\n");
+#endif
+      return -1;
+    }
+
+  register_pccard_driver(&dev_info, &wavelan_attach, &wavelan_detach);
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- init_wavelan_cs()\n");
+#endif
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module removal
+ */
+static void __exit
+exit_wavelan_cs(void)
+{
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
+#ifdef DEBUG_BASIC_SHOW
+  printk(KERN_NOTICE "wavelan_cs: unloading\n");
+#endif
+
+  /* Do some cleanup of the device list */
+  wv_flush_stale_links();
+
+  /* If there remain some devices... */
+#ifdef DEBUG_CONFIG_ERRORS
+  if(dev_list != NULL)
+    {
+      /* Honestly, if this happen we are in a deep s**t */
+      printk(KERN_INFO "wavelan_cs: devices remaining when removing module\n");
+      printk(KERN_INFO "Please flush your disks and reboot NOW !\n");
+    }
+#endif
+
+  unregister_pccard_driver(&dev_info);
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
+}
+
+module_init(init_wavelan_cs);
+module_exit(exit_wavelan_cs);
diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.h linux/drivers/net/wireless/wavelan_cs.h
--- linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/wavelan_cs.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,386 @@
+/*
+ *	Wavelan Pcmcia driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganization and extension of the driver.
+ * Original copyright follow. See wavelan_cs.h for details.
+ *
+ * This file contain the declarations of the Wavelan hardware. Note that
+ * the Pcmcia Wavelan include a i82593 controller (see definitions in
+ * file i82593.h).
+ *
+ * The main difference between the pcmcia hardware and the ISA one is
+ * the Ethernet Controller (i82593 instead of i82586). The i82593 allow
+ * only one send buffer. The PSA (Parameter Storage Area : EEprom for
+ * permanent storage of various info) is memory mapped, but not the
+ * MMI (Modem Management Interface).
+ */
+
+/*
+ * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card: 
+ *   An Ethernet-like radio transceiver controlled by an Intel 82593
+ *   coprocessor.
+ *
+ *
+ ****************************************************************************
+ *   Copyright 1995
+ *   Anthony D. Joseph
+ *   Massachusetts Institute of Technology
+ *
+ *   Permission to use, copy, modify, and distribute this program
+ *   for any purpose and without fee is hereby granted, provided
+ *   that this copyright and permission notice appear on all copies
+ *   and supporting documentation, the name of M.I.T. not be used
+ *   in advertising or publicity pertaining to distribution of the
+ *   program without specific prior permission, and notice be given
+ *   in supporting documentation that copying and distribution is
+ *   by permission of M.I.T.  M.I.T. makes no representations about
+ *   the suitability of this software for any purpose.  It is pro-
+ *   vided "as is" without express or implied warranty.         
+ ****************************************************************************
+ *
+ *
+ * Credits:
+ *     Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for
+ *       providing extremely useful information about WaveLAN PCMCIA hardware
+ *
+ *     This driver is based upon several other drivers, in particular:
+ *       David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ *       Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ *	 Anders Klemets' PCMCIA WaveLAN adapter driver
+ *       Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ */
+
+#ifndef _WAVELAN_CS_H
+#define	_WAVELAN_CS_H
+
+/************************** MAGIC NUMBERS ***************************/
+
+/* The detection of the wavelan card is made by reading the MAC address
+ * from the card and checking it. If you have a non AT&T product (OEM,
+ * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
+ * part to accomodate your hardware...
+ */
+const unsigned char	MAC_ADDRESSES[][3] =
+{
+  { 0x08, 0x00, 0x0E },		/* AT&T Wavelan (standard) & DEC RoamAbout */
+  { 0x08, 0x00, 0x6A },		/* AT&T Wavelan (alternate) */
+  { 0x00, 0x00, 0xE1 },		/* Hitachi Wavelan */
+  { 0x00, 0x60, 0x1D }		/* Lucent Wavelan (another one) */
+  /* Add your card here and send me the patch ! */
+};
+
+/*
+ * Constants used to convert channels to frequencies
+ */
+
+/* Frequency available in the 2.0 modem, in units of 250 kHz
+ * (as read in the offset register of the dac area).
+ * Used to map channel numbers used by `wfreqsel' to frequencies
+ */
+const short	channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+				    0xD0, 0xF0, 0xF8, 0x150 };
+
+/* Frequencies of the 1.0 modem (fixed frequencies).
+ * Use to map the PSA `subband' to a frequency
+ * Note : all frequencies apart from the first one need to be multiplied by 10
+ */
+const int	fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+
+
+/*************************** PC INTERFACE ****************************/
+
+/* WaveLAN host interface definitions */
+
+#define	LCCR(base)	(base)		/* LAN Controller Command Register */
+#define	LCSR(base)	(base)		/* LAN Controller Status Register */
+#define	HACR(base)	(base+0x1)	/* Host Adapter Command Register */
+#define	HASR(base)	(base+0x1)	/* Host Adapter Status Register */
+#define PIORL(base)	(base+0x2)	/* Program I/O Register Low */
+#define RPLL(base)	(base+0x2)	/* Receive Pointer Latched Low */
+#define PIORH(base)	(base+0x3)	/* Program I/O Register High */
+#define RPLH(base)	(base+0x3)	/* Receive Pointer Latched High */
+#define PIOP(base)	(base+0x4)	/* Program I/O Port */
+#define MMR(base)	(base+0x6)	/* MMI Address Register */
+#define MMD(base)	(base+0x7)	/* MMI Data Register */
+
+/* Host Adaptor Command Register bit definitions */
+
+#define HACR_LOF	  (1 << 3)	/* Lock Out Flag, toggle every 250ms */
+#define HACR_PWR_STAT	  (1 << 4)	/* Power State, 1=active, 0=sleep */
+#define HACR_TX_DMA_RESET (1 << 5)	/* Reset transmit DMA ptr on high */
+#define HACR_RX_DMA_RESET (1 << 6)	/* Reset receive DMA ptr on high */
+#define HACR_ROM_WEN	  (1 << 7)	/* EEPROM write enabled when true */
+
+#define HACR_RESET              (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET)
+#define	HACR_DEFAULT		(HACR_PWR_STAT)
+
+/* Host Adapter Status Register bit definitions */
+
+#define HASR_MMI_BUSY	(1 << 2)	/* MMI is busy when true */
+#define HASR_LOF	(1 << 3)	/* Lock out flag status */
+#define HASR_NO_CLK	(1 << 4)	/* active when modem not connected */
+
+/* Miscellaneous bit definitions */
+
+#define PIORH_SEL_TX	(1 << 5)	/* PIOR points to 0=rx/1=tx buffer */
+#define MMR_MMI_WR	(1 << 0)	/* Next MMI cycle is 0=read, 1=write */
+#define PIORH_MASK	0x1f		/* only low 5 bits are significant */
+#define RPLH_MASK	0x1f		/* only low 5 bits are significant */
+#define MMI_ADDR_MASK	0x7e		/* Bits 1-6 of MMR are significant */
+
+/* Attribute Memory map */
+
+#define CIS_ADDR	0x0000		/* Card Information Status Register */
+#define PSA_ADDR	0x0e00		/* Parameter Storage Area address */
+#define EEPROM_ADDR	0x1000		/* EEPROM address (unused ?) */
+#define COR_ADDR	0x4000		/* Configuration Option Register */
+
+/* Configuration Option Register bit definitions */
+
+#define COR_CONFIG	(1 << 0)	/* Config Index, 0 when unconfigured */
+#define COR_SW_RESET	(1 << 7)	/* Software Reset on true */
+#define COR_LEVEL_IRQ	(1 << 6)	/* Level IRQ */
+
+/* Local Memory map */
+
+#define RX_BASE		0x0000		/* Receive memory, 8 kB */
+#define TX_BASE		0x2000		/* Transmit memory, 2 kB */
+#define UNUSED_BASE	0x2800		/* Unused, 22 kB */
+#define RX_SIZE		(TX_BASE-RX_BASE)	/* Size of receive area */
+#define RX_SIZE_SHIFT	6		/* Bits to shift in stop register */
+
+#define TRUE  1
+#define FALSE 0
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
+
+/* Size of a MAC address */
+#define WAVELAN_ADDR_SIZE	6
+
+/* Maximum size of Wavelan packet */
+#define WAVELAN_MTU	1500
+
+#define	MAXDATAZ		(6 + 6 + 2 + WAVELAN_MTU)
+
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t	psa_t;
+struct psa_t
+{
+  /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */
+  unsigned char	psa_io_base_addr_1;	/* [0x00] Base address 1 ??? */
+  unsigned char	psa_io_base_addr_2;	/* [0x01] Base address 2 */
+  unsigned char	psa_io_base_addr_3;	/* [0x02] Base address 3 */
+  unsigned char	psa_io_base_addr_4;	/* [0x03] Base address 4 */
+  unsigned char	psa_rem_boot_addr_1;	/* [0x04] Remote Boot Address 1 */
+  unsigned char	psa_rem_boot_addr_2;	/* [0x05] Remote Boot Address 2 */
+  unsigned char	psa_rem_boot_addr_3;	/* [0x06] Remote Boot Address 3 */
+  unsigned char	psa_holi_params;	/* [0x07] HOst Lan Interface (HOLI) Parameters */
+  unsigned char	psa_int_req_no;		/* [0x08] Interrupt Request Line */
+  unsigned char	psa_unused0[7];		/* [0x09-0x0F] unused */
+
+  unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x10-0x15] Universal (factory) MAC Address */
+  unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x16-1B] Local MAC Address */
+  unsigned char	psa_univ_local_sel;	/* [0x1C] Universal Local Selection */
+#define		PSA_UNIVERSAL	0		/* Universal (factory) */
+#define		PSA_LOCAL	1		/* Local */
+  unsigned char	psa_comp_number;	/* [0x1D] Compatability Number: */
+#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz	*/
+#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	*/
+#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	*/
+#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz	*/
+#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz or 2.0 */
+  unsigned char	psa_thr_pre_set;	/* [0x1E] Modem Threshold Preset */
+  unsigned char	psa_feature_select;	/* [0x1F] Call code required (1=on) */
+#define		PSA_FEATURE_CALL_CODE	0x01 	/* Call code required (Japan) */
+  unsigned char	psa_subband;		/* [0x20] Subband	*/
+#define		PSA_SUBBAND_915		0	/* 915 MHz or 2.0 */
+#define		PSA_SUBBAND_2425	1	/* 2425 MHz	*/
+#define		PSA_SUBBAND_2460	2	/* 2460 MHz	*/
+#define		PSA_SUBBAND_2484	3	/* 2484 MHz	*/
+#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	*/
+  unsigned char	psa_quality_thr;	/* [0x21] Modem Quality Threshold */
+  unsigned char	psa_mod_delay;		/* [0x22] Modem Delay ??? (reserved) */
+  unsigned char	psa_nwid[2];		/* [0x23-0x24] Network ID */
+  unsigned char	psa_nwid_select;	/* [0x25] Network ID Select On Off */
+  unsigned char	psa_encryption_select;	/* [0x26] Encryption On Off */
+  unsigned char	psa_encryption_key[8];	/* [0x27-0x2E] Encryption Key */
+  unsigned char	psa_databus_width;	/* [0x2F] AT bus width select 8/16 */
+  unsigned char	psa_call_code[8];	/* [0x30-0x37] (Japan) Call Code */
+  unsigned char	psa_nwid_prefix[2];	/* [0x38-0x39] Roaming domain */
+  unsigned char	psa_reserved[2];	/* [0x3A-0x3B] Reserved - fixed 00 */
+  unsigned char	psa_conf_status;	/* [0x3C] Conf Status, bit 0=1:config*/
+  unsigned char	psa_crc[2];		/* [0x3D] CRC-16 over PSA */
+  unsigned char	psa_crc_status;		/* [0x3F] CRC Valid Flag */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define	PSA_SIZE	64
+
+/* Calculate offset of a field in the above structure
+ * Warning : only even addresses are used */
+#define	psaoff(p,f) 	((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t	mmw_t;
+struct mmw_t
+{
+  unsigned char	mmw_encr_key[8];	/* encryption key */
+  unsigned char	mmw_encr_enable;	/* enable/disable encryption */
+#define	MMW_ENCR_ENABLE_MODE	0x02	/* Mode of security option */
+#define	MMW_ENCR_ENABLE_EN	0x01	/* Enable security option */
+  unsigned char	mmw_unused0[1];		/* unused */
+  unsigned char	mmw_des_io_invert;	/* Encryption option */
+#define	MMW_DES_IO_INVERT_RES	0x0F	/* Reserved */
+#define	MMW_DES_IO_INVERT_CTRL	0xF0	/* Control ??? (set to 0) */
+  unsigned char	mmw_unused1[5];		/* unused */
+  unsigned char	mmw_loopt_sel;		/* looptest selection */
+#define	MMW_LOOPT_SEL_DIS_NWID	0x40	/* disable NWID filtering */
+#define	MMW_LOOPT_SEL_INT	0x20	/* activate Attention Request */
+#define	MMW_LOOPT_SEL_LS	0x10	/* looptest w/o collision avoidance */
+#define MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */
+#define	MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */
+#define	MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */
+#define	MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */
+  unsigned char	mmw_jabber_enable;	/* jabber timer enable */
+  /* Abort transmissions > 200 ms */
+  unsigned char	mmw_freeze;		/* freeze / unfreeeze signal level */
+  /* 0 : signal level & qual updated for every new message, 1 : frozen */
+  unsigned char	mmw_anten_sel;		/* antenna selection */
+#define MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */
+#define	MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algo. enable */
+  unsigned char	mmw_ifs;		/* inter frame spacing */
+  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+  unsigned char	mmw_mod_delay;	 	/* modem delay (synchro) */
+  unsigned char	mmw_jam_time;		/* jamming time (after collision) */
+  unsigned char	mmw_unused2[1];		/* unused */
+  unsigned char	mmw_thr_pre_set;	/* level threshold preset */
+  /* Discard all packet with signal < this value (4) */
+  unsigned char	mmw_decay_prm;		/* decay parameters */
+  unsigned char	mmw_decay_updat_prm;	/* decay update parameterz */
+  unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
+  /* Discard all packet with quality < this value (3) */
+  unsigned char	mmw_netw_id_l;		/* NWID low order byte */
+  unsigned char	mmw_netw_id_h;		/* NWID high order byte */
+  /* Network ID or Domain : create virtual net on the air */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char	mmw_mode_select;	/* for analog tests (set to 0) */
+  unsigned char	mmw_unused3[1];		/* unused */
+  unsigned char	mmw_fee_ctrl;		/* frequency eeprom control */
+#define	MMW_FEE_CTRL_PRE	0x10	/* Enable protected instructions */
+#define	MMW_FEE_CTRL_DWLD	0x08	/* Download eeprom to mmc */
+#define	MMW_FEE_CTRL_CMD	0x07	/* EEprom commands : */
+#define	MMW_FEE_CTRL_READ	0x06	/* Read */
+#define	MMW_FEE_CTRL_WREN	0x04	/* Write enable */
+#define	MMW_FEE_CTRL_WRITE	0x05	/* Write data to address */
+#define	MMW_FEE_CTRL_WRALL	0x04	/* Write data to all addresses */
+#define	MMW_FEE_CTRL_WDS	0x04	/* Write disable */
+#define	MMW_FEE_CTRL_PRREAD	0x16	/* Read addr from protect register */
+#define	MMW_FEE_CTRL_PREN	0x14	/* Protect register enable */
+#define	MMW_FEE_CTRL_PRCLEAR	0x17	/* Unprotect all registers */
+#define	MMW_FEE_CTRL_PRWRITE	0x15	/* Write addr in protect register */
+#define	MMW_FEE_CTRL_PRDS	0x14	/* Protect register disable */
+  /* Never issue this command (PRDS) : it's irreversible !!! */
+
+  unsigned char	mmw_fee_addr;		/* EEprom address */
+#define	MMW_FEE_ADDR_CHANNEL	0xF0	/* Select the channel */
+#define	MMW_FEE_ADDR_OFFSET	0x0F	/* Offset in channel data */
+#define	MMW_FEE_ADDR_EN		0xC0	/* FEE_CTRL enable operations */
+#define	MMW_FEE_ADDR_DS		0x00	/* FEE_CTRL disable operations */
+#define	MMW_FEE_ADDR_ALL	0x40	/* FEE_CTRL all operations */
+#define	MMW_FEE_ADDR_CLEAR	0xFF	/* FEE_CTRL clear operations */
+
+  unsigned char	mmw_fee_data_l;		/* Write data to EEprom */
+  unsigned char	mmw_fee_data_h;		/* high octet */
+  unsigned char	mmw_ext_ant;		/* Setting for external antenna */
+#define	MMW_EXT_ANT_EXTANT	0x01	/* Select external antenna */
+#define	MMW_EXT_ANT_POL		0x02	/* Polarity of the antenna */
+#define	MMW_EXT_ANT_INTERNAL	0x00	/* Internal antenna */
+#define	MMW_EXT_ANT_EXTERNAL	0x03	/* External antenna */
+#define	MMW_EXT_ANT_IQ_TEST	0x1C	/* IQ test pattern (set to 0) */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define	MMW_SIZE	37
+
+/* Calculate offset of a field in the above structure */
+#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t	mmr_t;
+struct mmr_t
+{
+  unsigned char	mmr_unused0[8];		/* unused */
+  unsigned char	mmr_des_status;		/* encryption status */
+  unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */
+#define	MMR_DES_AVAIL_DES	0x55		/* DES available */
+#define	MMR_DES_AVAIL_AES	0x33		/* AES (AT&T) available */
+  unsigned char	mmr_des_io_invert;	/* des I/O invert register */
+  unsigned char	mmr_unused1[5];		/* unused */
+  unsigned char	mmr_dce_status;		/* DCE status */
+#define	MMR_DCE_STATUS_RX_BUSY		0x01	/* receiver busy */
+#define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
+#define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */
+#define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
+#define MMR_DCE_STATUS			0x0F	/* mask to get the bits */
+  unsigned char	mmr_dsp_id;		/* DSP id (AA = Daedalus rev A) */
+  unsigned char	mmr_unused2[2];		/* unused */
+  unsigned char	mmr_correct_nwid_l;	/* # of correct NWID's rxd (low) */
+  unsigned char	mmr_correct_nwid_h;	/* # of correct NWID's rxd (high) */
+  /* Warning : Read high order octet first !!! */
+  unsigned char	mmr_wrong_nwid_l;	/* # of wrong NWID's rxd (low) */
+  unsigned char	mmr_wrong_nwid_h;	/* # of wrong NWID's rxd (high) */
+  unsigned char	mmr_thr_pre_set;	/* level threshold preset */
+#define	MMR_THR_PRE_SET		0x3F		/* level threshold preset */
+#define	MMR_THR_PRE_SET_CUR	0x80		/* Current signal above it */
+  unsigned char	mmr_signal_lvl;		/* signal level */
+#define	MMR_SIGNAL_LVL		0x3F		/* signal level */
+#define	MMR_SIGNAL_LVL_VALID	0x80		/* Updated since last read */
+  unsigned char	mmr_silence_lvl;	/* silence level (noise) */
+#define	MMR_SILENCE_LVL		0x3F		/* silence level */
+#define	MMR_SILENCE_LVL_VALID	0x80		/* Updated since last read */
+  unsigned char	mmr_sgnl_qual;		/* signal quality */
+#define	MMR_SGNL_QUAL		0x0F		/* signal quality */
+#define	MMR_SGNL_QUAL_ANT	0x80		/* current antenna used */
+  unsigned char	mmr_netw_id_l;		/* NWID low order byte ??? */
+  unsigned char	mmr_unused3[3];		/* unused */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char	mmr_fee_status;		/* Status of frequency eeprom */
+#define	MMR_FEE_STATUS_ID	0xF0		/* Modem revision id */
+#define	MMR_FEE_STATUS_DWLD	0x08		/* Download in progress */
+#define	MMR_FEE_STATUS_BUSY	0x04		/* EEprom busy */
+  unsigned char	mmr_unused4[1];		/* unused */
+  unsigned char	mmr_fee_data_l;		/* Read data from eeprom (low) */
+  unsigned char	mmr_fee_data_h;		/* Read data from eeprom (high) */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define	MMR_SIZE	36
+
+/* Calculate offset of a field in the above structure */
+#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+  struct mmw_t	w;	/* Write to the mmc */
+  struct mmr_t	r;	/* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_CS_H */
diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.p.h linux/drivers/net/wireless/wavelan_cs.p.h
--- linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.p.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wireless/wavelan_cs.p.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,825 @@
+/*
+ *	Wavelan Pcmcia driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contain all definition and declarations necessary for the
+ * wavelan pcmcia driver. This file is a private header, so it should
+ * be included only on wavelan_cs.c !!!
+ */
+
+#ifndef WAVELAN_CS_P_H
+#define WAVELAN_CS_P_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * This driver provide a Linux interface to the Wavelan Pcmcia hardware
+ * The Wavelan is a product of Lucent (http://www.wavelan.com/).
+ * This division was formerly part of NCR and then AT&T.
+ * Wavelan are also distributed by DEC (RoamAbout DS)...
+ *
+ * To know how to use this driver, read the PCMCIA HOWTO.
+ * If you want to exploit the many other fonctionalities, look comments
+ * in the code...
+ *
+ * This driver is the result of the effort of many peoples (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * Web page
+ * --------
+ *	I try to maintain a web page with the Wireless LAN Howto at :
+ *	    http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
+ *
+ * SMP
+ * ---
+ *	We now are SMP compliant (I eventually fixed the remaining bugs).
+ *	The driver has been tested on a dual P6-150 and survived my usual
+ *	set of torture tests.
+ *	Anyway, I spent enough time chasing interrupt re-entrancy during
+ *	errors or reconfigure, and I designed the locked/unlocked sections
+ *	of the driver with great care, and with the recent addition of
+ *	the spinlock (thanks to the new API), we should be quite close to
+ *	the truth.
+ *	The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
+ *	but better safe than sorry (especially at 2 Mb/s ;-).
+ *
+ *	I have also looked into disabling only our interrupt on the card
+ *	(via HACR) instead of all interrupts in the processor (via cli),
+ *	so that other driver are not impacted, and it look like it's
+ *	possible, but it's very tricky to do right (full of races). As
+ *	the gain would be mostly for SMP systems, it can wait...
+ *
+ * Debugging and options
+ * ---------------------
+ *	You will find below a set of '#define" allowing a very fine control
+ *	on the driver behaviour and the debug messages printed.
+ *	The main options are :
+ *	o WAVELAN_ROAMING, for the experimental roaming support.
+ *	o SET_PSA_CRC, to have your card correctly recognised by
+ *	  an access point and the Point-to-Point diagnostic tool.
+ *	o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
+ *	  (otherwise we always start afresh with some defaults)
+ *
+ * wavelan_cs.o is darn too big
+ * -------------------------
+ *	That's true ! There is a very simple way to reduce the driver
+ *	object by 33% (yes !). Comment out the following line :
+ *		#include <linux/wireless.h>
+ *	Other compile options can also reduce the size of it...
+ *
+ * MAC address and hardware detection :
+ * ----------------------------------
+ *	The detection code of the wavelan chech that the first 3
+ *	octets of the MAC address fit the company code. This type of
+ *	detection work well for AT&T cards (because the AT&T code is
+ *	hardcoded in wavelan_cs.h), but of course will fail for other
+ *	manufacturer.
+ *
+ *	If you are sure that your card is derived from the wavelan,
+ *	here is the way to configure it :
+ *	1) Get your MAC address
+ *		a) With your card utilities (wfreqsel, instconf, ...)
+ *		b) With the driver :
+ *			o compile the kernel with DEBUG_CONFIG_INFO enabled
+ *			o Boot and look the card messages
+ *	2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan_cs.h)
+ *	3) Compile & verify
+ *	4) Send me the MAC code - I will include it in the next version...
+ *
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first one to support "wireless extensions".
+ * This set of extensions provide you some way to control the wireless
+ * caracteristics of the hardware in a standard way and support for
+ * applications for taking advantage of it (like Mobile IP).
+ *
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
+ *
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan_cs.c :	The actual code for the driver - C functions
+ *
+ * wavelan_cs.p.h :	Private header : local types / vars for the driver
+ *
+ * wavelan_cs.h :	Description of the hardware interface & structs
+ *
+ * i82593.h :		Description if the Ethernet controller
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * The history of the Wavelan drivers is as complicated as history of
+ * the Wavelan itself (NCR -> AT&T -> Lucent).
+ *
+ * All started with Anders Klemets <klemets@paul.rutgers.edu>,
+ * writting a Wavelan ISA driver for the MACH microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modify this for the Pcmcia hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
+ * and add specific Pcmcia support (there is currently no equivalent
+ * of the PCMCIA package under BSD...).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * correctly 2.00 cards (2.4 GHz with frequency selection).
+ * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
+ * Pcmcia package (+ bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patchs to the Pcmcia driver. After, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Now, I'm doing the same to the Pcmcia driver + some
+ * reorganisation.
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed informations on the Wavelan hardware.
+ */
+
+/* By the way : for the copyright & legal stuff :
+ * Almost everybody wrote code under GNU or BSD license (or alike),
+ * and want that their original copyright remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody want to take responsibility for anything, except the fame...
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * Credits:
+ *    Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
+ *	Loeke Brederveld of Lucent for providing extremely useful
+ *	information about WaveLAN PCMCIA hardware
+ *
+ *    This driver is based upon several other drivers, in particular:
+ *	David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ *	Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ *	Anders Klemets' PCMCIA WaveLAN adapter driver
+ *	Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ *
+ * Additional Credits:
+ *
+ *    This software was originally developed under Linux 1.2.3
+ *	(Slackware 2.0 distribution).
+ *    And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+)
+ *	with an HP OmniBook 4000 and then a 5500.
+ *
+ *    It is based on other device drivers and information either written
+ *    or supplied by:
+ *	James Ashton (jaa101@syseng.anu.edu.au),
+ *	Ajay Bakre (bakre@paul.rutgers.edu),
+ *	Donald Becker (becker@super.org),
+ *	Jim Binkley <jrb@cs.pdx.edu>,
+ *	Loeke Brederveld <lbrederv@wavelan.com>,
+ *	Allan Creighton (allanc@cs.su.oz.au),
+ *	Brent Elphick <belphick@uwaterloo.ca>,
+ *	Joe Finney <joe@comp.lancs.ac.uk>,
+ *	Matthew Geier (matthew@cs.su.oz.au),
+ *	Remo di Giovanni (remo@cs.su.oz.au),
+ *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ *	David Hinds <dahinds@users.sourceforge.net>,
+ *	Jan Hoogendoorn (c/o marteijn@lucent.com),
+ *      Bruce Janson <bruce@cs.usyd.edu.au>,
+ *	Anthony D. Joseph <adj@lcs.mit.edu>,
+ *	Anders Klemets (klemets@paul.rutgers.edu),
+ *	Yunzhou Li <yunzhou@strat.iol.unh.edu>,
+ *	Marc Meertens (mmeertens@lucent.com),
+ *	Keith Moore,
+ *	Robert Morris (rtm@das.harvard.edu),
+ *	Ian Parkin (ian@cs.su.oz.au),
+ *	John Rosenberg (johnr@cs.su.oz.au),
+ *	George Rossi (george@phm.gov.au),
+ *	Arthur Scott (arthur@cs.su.oz.au),
+ *	Stanislav Sinyagin <stas@isf.ru>
+ *	Peter Storey,
+ *	Jean Tourrilhes <jt@hpl.hp.com>,
+ *	Girish Welling (welling@paul.rutgers.edu)
+ *	Clark Woodworth <clark@hiway1.exit109.com>
+ *	Yongguang Zhang <ygz@isl.hrl.hac.com>...
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present :
+ *
+ * Changes made in 2.8.22 :
+ * ----------------------
+ *	- improved wv_set_multicast_list
+ *	- catch spurious interrupt
+ *	- correct release of the device
+ *
+ * Changes mades in release :
+ * ------------------------
+ *	- Reorganisation of the code, function name change
+ *	- Creation of private header (wavelan_cs.h)
+ *	- Reorganised debug messages
+ *	- More comments, history, ...
+ *	- Configure earlier (in "insert" instead of "open")
+ *        and do things only once
+ *	- mmc_init : configure the PSA if not done
+ *	- mmc_init : 2.00 detection better code for 2.00 init
+ *	- better info at startup
+ *	- Correct a HUGE bug (volatile & uncalibrated busy loop)
+ *	  in wv_82593_cmd => config speedup
+ *	- Stop receiving & power down on close (and power up on open)
+ *	  use "ifconfig down" & "ifconfig up ; route add -net ..."
+ *	- Send packets : add watchdog instead of pooling
+ *	- Receive : check frame wrap around & try to recover some frames
+ *	- wavelan_set_multicast_list : avoid reset
+ *	- add wireless extensions (ioctl & get_wireless_stats)
+ *	  get/set nwid/frequency on fly, info for /proc/net/wireless
+ *	- Suppress useless stuff from lp (net_local), but add link
+ *	- More inlines
+ *	- Lot of others minor details & cleanups
+ *
+ * Changes made in second release :
+ * ------------------------------
+ *	- Optimise wv_85893_reconfig stuff, fix potential problems
+ *	- Change error values for ioctl
+ *	- Non blocking wv_ru_stop() + call wv_reset() in case of problems
+ *	- Remove development printk from wavelan_watchdog()
+ *	- Remove of the watchdog to wavelan_close instead of wavelan_release
+ *	  fix potential problems...
+ *	- Start debugging suspend stuff (but it's still a bit weird)
+ *	- Debug & optimize dump header/packet in Rx & Tx (debug)
+ *	- Use "readb" and "writeb" to be kernel 2.1 compliant
+ *	- Better handling of bogus interrupts
+ *	- Wireless extension : SETSPY and GETSPY
+ *	- Remove old stuff (stats - for those needing it, just ask me...)
+ *	- Make wireless extensions optional
+ *
+ * Changes made in third release :
+ * -----------------------------
+ *	- cleanups & typos
+ *	- modif wireless ext (spy -> only one pointer)
+ *	- new private ioctl to set/get quality & level threshold
+ *	- Init : correct default value of level threshold for pcmcia
+ *	- kill watchdog in hw_reset
+ *	- more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ *	- Add message level (debug stuff in /var/adm/debug & errors not
+ *	  displayed at console and still in /var/adm/messages)
+ *
+ * Changes made in fourth release :
+ * ------------------------------
+ *	- multicast support (yes !) thanks to Yongguang Zhang.
+ *
+ * Changes made in fifth release (2.9.0) :
+ * -------------------------------------
+ *	- Revisited multicast code (it was mostly wrong).
+ *	- protect code in wv_82593_reconfig with dev->tbusy (oups !)
+ *
+ * Changes made in sixth release (2.9.1a) :
+ * --------------------------------------
+ *	- Change the detection code for multi manufacturer code support
+ *	- Correct bug (hang kernel) in init when we were "rejecting" a card 
+ *
+ * Changes made in seventh release (2.9.1b) :
+ * ----------------------------------------
+ *	- Update to wireless extensions changes
+ *	- Silly bug in card initial configuration (psa_conf_status)
+ *
+ * Changes made in eigth release :
+ * -----------------------------
+ *	- Small bug in debug code (probably not the last one...)
+ *	- 1.2.13 support (thanks to Clark Woodworth)
+ *
+ * Changes made for release in 2.9.2b :
+ * ----------------------------------
+ *	- Level threshold is now a standard wireless extension (version 4 !)
+ *	- modules parameters types for kernel > 2.1.17
+ *	- updated man page
+ *	- Others cleanup from David Hinds
+ *
+ * Changes made for release in 2.9.5 :
+ * ---------------------------------
+ *	- byte count stats (courtesy of David Hinds)
+ *	- Remove dev_tint stuff (courtesy of David Hinds)
+ *	- Others cleanup from David Hinds
+ *	- Encryption setting from Brent Elphick (thanks a lot !)
+ *	- 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
+ * Changes made for release in 2.9.6 :
+ * ---------------------------------
+ *	- fix bug : no longuer disable watchdog in case of bogus interrupt
+ *	- increase timeout in config code for picky hardware
+ *	- mask unused bits in status (Wireless Extensions)
+ *
+ * Changes integrated by Justin Seger <jseger@MIT.EDU> & David Hinds :
+ * -----------------------------------------------------------------
+ *	- Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
+ *	- PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
+ *	- Better initialisation of the i82593 controller
+ *	  from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
+ *
+ * Changes made for release in 3.0.10 :
+ * ----------------------------------
+ *	- Fix eject "hang" of the driver under 2.2.X :
+ *		o create wv_flush_stale_links()
+ *		o Rename wavelan_release to wv_pcmcia_release & move up
+ *		o move unregister_netdev to wavelan_detach()
+ *		o wavelan_release() no longer call wavelan_detach()
+ *		o Suppress "release" timer
+ *		o Other cleanups & fixes
+ *	- New MAC address in the probe
+ *	- Reorg PSA_CRC code (endian neutral & cleaner)
+ *	- Correct initialisation of the i82593 from Lucent manual
+ *	- Put back the watchdog, with larger timeout
+ *	- TRANSMIT_NO_CRC is a "normal" error, so recover from it
+ *	  from Derrick J Brashear <shadow@dementia.org>
+ *	- Better handling of TX and RX normal failure conditions
+ *	- #ifdef out all the roaming code
+ *	- Add ESSID & "AP current address" ioctl stubs
+ *	- General cleanup of the code
+ *
+ * Changes made for release in 3.0.13 :
+ * ----------------------------------
+ *	- Re-enable compilation of roaming code by default, but with
+ *	  do_roaming = 0
+ *	- Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
+ *	  at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
+ *	- Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
+ *
+ * Changes made for release in 3.0.15 :
+ * ----------------------------------
+ *	- Change e-mail and web page addresses
+ *	- Watchdog timer is now correctly expressed in HZ, not in jiffies
+ *	- Add channel number to the list of frequencies in range
+ *	- Add the (short) list of bit-rates in range
+ *	- Developp a new sensitivity... (sens.value & sens.fixed)
+ *
+ * Changes made for release in 3.1.2 :
+ * ---------------------------------
+ *	- Fix check for root permission (break instead of exit)
+ *	- New nwid & encoding setting (Wireless Extension 9)
+ *
+ * Changes made for release in 3.1.12 :
+ * ----------------------------------
+ *	- reworked wv_82593_cmd to avoid using the IRQ handler and doing
+ *	  ugly things with interrupts.
+ *	- Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog
+ *	- Update to new network API (softnet - 2.3.43) :
+ *		o replace dev->tbusy (David + me)
+ *		o replace dev->tstart (David + me)
+ *		o remove dev->interrupt (David)
+ *		o add SMP locking via spinlock in splxx (me)
+ *		o add spinlock in interrupt handler (me)
+ *		o use kernel watchdog instead of ours (me)
+ *		o verify that all the changes make sense and work (me)
+ *	- Re-sync kernel/pcmcia versions (not much actually)
+ *	- A few other cleanups (David & me)...
+ *
+ * Changes made for release in 3.1.22 :
+ * ----------------------------------
+ *	- Check that SMP works, remove annoying log message
+ *
+ * Changes made for release in 3.1.24 :
+ * ----------------------------------
+ *	- Fix unfrequent card lockup when watchdog was reseting the hardware :
+ *		o control first busy loop in wv_82593_cmd()
+ *		o Extend spinlock protection in wv_hw_config()
+ *
+ * Wishes & dreams:
+ * ----------------
+ *	- Cleanup and integrate the roaming code
+ *	  (std debug, set DomainID, decay avg and co...)
+ */
+
+/***************************** INCLUDES *****************************/
+
+/* Linux headers that we need */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>		/* Wireless extensions */
+#endif
+
+/* Pcmcia headers that we need */
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/version.h>
+
+/* Wavelan declarations */
+#include "i82593.h"	/* Definitions for the Intel chip */
+
+#include "wavelan_cs.h"	/* Others bits of the hardware */
+
+/************************** DRIVER OPTIONS **************************/
+/*
+ * `#define' or `#undef' the following constant to change the behaviour
+ * of the driver...
+ */
+#define WAVELAN_ROAMING		/* Include experimental roaming code */
+#undef WAVELAN_ROAMING_EXT	/* Enable roaming wireless extensions */
+#undef SET_PSA_CRC		/* Set the CRC in PSA (slower) */
+#define USE_PSA_CONFIG		/* Use info from the PSA */
+#undef STRUCT_CHECK		/* Verify padding of structures */
+#undef EEPROM_IS_PROTECTED	/* Doesn't seem to be necessary */
+#define MULTICAST_AVOID		/* Avoid extra multicast (I'm sceptical) */
+#undef SET_MAC_ADDRESS		/* Experimental */
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+/* Warning : these stuff will slow down the driver... */
+#define WIRELESS_SPY		/* Enable spying addresses */
+#undef HISTOGRAM		/* Enable histogram of sig level... */
+#endif
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE	/* Module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE	/* Calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE	/* Calls to handler */
+#undef DEBUG_INTERRUPT_INFO	/* type of interrupt & so on */
+#define DEBUG_INTERRUPT_ERROR	/* problems */
+#undef DEBUG_CONFIG_TRACE	/* Trace the config functions */
+#undef DEBUG_CONFIG_INFO	/* What's going on... */
+#define DEBUG_CONFIG_ERRORS	/* Errors on configuration */
+#undef DEBUG_TX_TRACE		/* Transmission calls */
+#undef DEBUG_TX_INFO		/* Header of the transmitted packet */
+#undef DEBUG_TX_FAIL		/* Normal failure conditions */
+#define DEBUG_TX_ERROR		/* Unexpected conditions */
+#undef DEBUG_RX_TRACE		/* Transmission calls */
+#undef DEBUG_RX_INFO		/* Header of the transmitted packet */
+#undef DEBUG_RX_FAIL		/* Normal failure conditions */
+#define DEBUG_RX_ERROR		/* Unexpected conditions */
+#undef DEBUG_PACKET_DUMP	32	/* Dump packet on the screen */
+#undef DEBUG_IOCTL_TRACE	/* Misc call by Linux */
+#undef DEBUG_IOCTL_INFO		/* Various debug info */
+#define DEBUG_IOCTL_ERROR	/* What's going wrong */
+#define DEBUG_BASIC_SHOW	/* Show basic startup info */
+#undef DEBUG_VERSION_SHOW	/* Print version info */
+#undef DEBUG_PSA_SHOW		/* Dump psa to screen */
+#undef DEBUG_MMC_SHOW		/* Dump mmc to screen */
+#undef DEBUG_SHOW_UNUSED	/* Show also unused fields */
+#undef DEBUG_I82593_SHOW	/* Show i82593 status */
+#undef DEBUG_DEVICE_SHOW	/* Show device parameters */
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char *version = "wavelan_cs.c : v23 (SMP + wireless extensions) 20/12/00\n";
+#endif
+
+/* Watchdog temporisation */
+#define	WATCHDOG_JIFFIES	(256*HZ/100)
+
+/* Fix a bug in some old wireless extension definitions */
+#ifndef IW_ESSID_MAX_SIZE
+#define IW_ESSID_MAX_SIZE	32
+#endif
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+/* Wireless Extension Backward compatibility - Jean II
+ * If the new wireless device private ioctl range is not defined,
+ * default to standard device private ioctl range */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV	SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+#define SIOCSIPQTHR	SIOCIWFIRSTPRIV		/* Set quality threshold */
+#define SIOCGIPQTHR	SIOCIWFIRSTPRIV + 1	/* Get quality threshold */
+#define SIOCSIPROAM     SIOCIWFIRSTPRIV + 2	/* Set roaming state */
+#define SIOCGIPROAM     SIOCIWFIRSTPRIV + 3	/* Get roaming state */
+
+#define SIOCSIPHISTO	SIOCIWFIRSTPRIV + 6	/* Set histogram ranges */
+#define SIOCGIPHISTO	SIOCIWFIRSTPRIV + 7	/* Get histogram values */
+
+/*************************** WaveLAN Roaming  **************************/
+#ifdef WAVELAN_ROAMING		/* Conditional compile, see above in options */
+
+#define WAVELAN_ROAMING_DEBUG	 0	/* 1 = Trace of handover decisions */
+					/* 2 = Info on each beacon rcvd... */
+#define MAX_WAVEPOINTS		7	/* Max visible at one time */
+#define WAVEPOINT_HISTORY	5	/* SNR sample history slow search */
+#define WAVEPOINT_FAST_HISTORY	2	/* SNR sample history fast search */
+#define SEARCH_THRESH_LOW	10	/* SNR to enter cell search */
+#define SEARCH_THRESH_HIGH	13	/* SNR to leave cell search */
+#define WAVELAN_ROAMING_DELTA	1	/* Hysteresis value (+/- SNR) */
+#define CELL_TIMEOUT		2*HZ	/* in jiffies */
+
+#define FAST_CELL_SEARCH	1	/* Boolean values... */
+#define NWID_PROMISC		1	/* for code clarity. */
+
+typedef struct wavepoint_beacon
+{
+  unsigned char		dsap,		/* Unused */
+			ssap,		/* Unused */
+			ctrl,		/* Unused */
+			O,U,I,		/* Unused */
+			spec_id1,	/* Unused */
+			spec_id2,	/* Unused */
+			pdu_type,	/* Unused */
+			seq;		/* WavePoint beacon sequence number */
+  unsigned short	domain_id,	/* WavePoint Domain ID */
+			nwid;		/* WavePoint NWID */
+} wavepoint_beacon;
+
+typedef struct wavepoint_history
+{
+  unsigned short	nwid;		/* WavePoint's NWID */
+  int			average_slow;	/* SNR running average */
+  int			average_fast;	/* SNR running average */
+  unsigned char	  sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */
+  unsigned char		qualptr;	/* Index into ringbuffer */
+  unsigned char		last_seq;	/* Last seq. no seen for WavePoint */
+  struct wavepoint_history *next;	/* Next WavePoint in table */
+  struct wavepoint_history *prev;	/* Previous WavePoint in table */
+  unsigned long		last_seen;	/* Time of last beacon recvd, jiffies */
+} wavepoint_history;
+
+struct wavepoint_table
+{
+  wavepoint_history	*head;		/* Start of ringbuffer */
+  int			num_wavepoints;	/* No. of WavePoints visible */
+  unsigned char		locked;		/* Table lock */
+};
+
+#endif	/* WAVELAN_ROAMING */
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct net_device	device;
+typedef struct net_device_stats	en_stats;
+typedef struct iw_statistics	iw_stats;
+typedef struct iw_quality	iw_qual;
+typedef struct iw_freq		iw_freq;
+typedef struct net_local	net_local;
+typedef struct timer_list	timer_list;
+
+/* Basic types */
+typedef u_char		mac_addr[WAVELAN_ADDR_SIZE];	/* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keep data in two structure. "device"
+ * keep the generic data (same format for everybody) and "net_local" keep
+ * the additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+  dev_node_t 	node;		/* ???? What is this stuff ???? */
+  device *	dev;		/* Reverse link... */
+  spinlock_t	spinlock;	/* Serialize access to the hardware (SMP) */
+  dev_link_t *	link;		/* pcmcia structure */
+  en_stats	stats;		/* Ethernet interface statistics */
+  int		nresets;	/* Number of hw resets */
+  u_char	configured;	/* If it is configured */
+  u_char	reconfig_82593;	/* Need to reconfigure the controller */
+  u_char	promiscuous;	/* Promiscuous mode */
+  u_char	allmulticast;	/* All Multicast mode */
+  int		mc_count;	/* Number of multicast addresses */
+
+  int   	stop;		/* Current i82593 Stop Hit Register */
+  int   	rfp;		/* Last DMA machine receive pointer */
+  int		overrunning;	/* Receiver overrun flag */
+
+#ifdef WIRELESS_EXT
+  iw_stats	wstats;		/* Wireless specific stats */
+#endif
+
+#ifdef WIRELESS_SPY
+  int		spy_number;		/* Number of addresses to spy */
+  mac_addr	spy_address[IW_MAX_SPY];	/* The addresses to spy */
+  iw_qual	spy_stat[IW_MAX_SPY];		/* Statistics gathered */
+#endif	/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+  int		his_number;		/* Number of intervals */
+  u_char	his_range[16];		/* Boundaries of interval ]n-1; n] */
+  u_long	his_sum[16];		/* Sum in interval */
+#endif	/* HISTOGRAM */
+#ifdef WAVELAN_ROAMING
+  u_long	domain_id;	/* Domain ID we lock on for roaming */
+  int		filter_domains;	/* Check Domain ID of beacon found */
+ struct wavepoint_table	wavepoint_table;	/* Table of visible WavePoints*/
+  wavepoint_history *	curr_point;		/* Current wavepoint */
+  int			cell_search;		/* Searching for new cell? */
+  struct timer_list	cell_timer;		/* Garbage collection */
+#endif	/* WAVELAN_ROAMING */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+#ifdef WAVELAN_ROAMING
+/* ---------------------- ROAMING SUBROUTINES -----------------------*/
+
+wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
+wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
+void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
+void wl_cell_expiry(unsigned long data);
+wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
+void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
+void wv_nwid_filter(unsigned char mode, net_local *lp);
+void wv_roam_init(struct net_device *dev);
+void wv_roam_cleanup(struct net_device *dev);
+#endif	/* WAVELAN_ROAMING */
+
+/* ----------------------- MISC SUBROUTINES ------------------------ */
+static inline void
+	wv_splhi(net_local *,		/* Disable interrupts */
+		 unsigned long *);	/* flags */
+static inline void
+	wv_splx(net_local *,		/* ReEnable interrupts */
+		unsigned long *);	/* flags */
+static void
+	cs_error(client_handle_t,	/* Report error to cardmgr */
+		 int,
+		 int);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static inline u_char		/* data */
+	hasr_read(u_long);	/* Read the host interface : base address */
+static inline void
+	hacr_write(u_long,	/* Write to host interface : base address */
+		   u_char),	/* data */
+	hacr_write_slow(u_long,
+		   u_char);
+static void
+	psa_read(device *,	/* Read the Parameter Storage Area */
+		 int,		/* offset in PSA */
+		 u_char *,	/* buffer to fill */
+		 int),		/* size to read */
+	psa_write(device *,	/* Write to the PSA */
+		  int,		/* Offset in psa */
+		  u_char *,	/* Buffer in memory */
+		  int);		/* Length of buffer */
+static inline void
+	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control */
+		u_short,
+		u_char),
+	mmc_write(u_long,	/* Write n bytes to the MMC */
+		  u_char,
+		  u_char *,
+		  int);
+static inline u_char		/* Read 1 byte from the MMC */
+	mmc_in(u_long,
+	       u_short);
+static inline void
+	mmc_read(u_long,	/* Read n bytes from the MMC */
+		 u_char,
+		 u_char *,
+		 int),
+	fee_wait(u_long,	/* Wait for frequency EEprom : base address */
+		 int,		/* Base delay to wait for */
+		 int);		/* Number of time to wait */
+static void
+	fee_read(u_long,	/* Read the frequency EEprom : base address */
+		 u_short,	/* destination offset */
+		 u_short *,	/* data buffer */
+		 int);		/* number of registers */
+/* ---------------------- I82593 SUBROUTINES ----------------------- */
+static int
+	wv_82593_cmd(device *,	/* synchronously send a command to i82593 */ 
+		     char *,
+		     int,
+		     int);
+static inline int
+	wv_diag(device *);	/* Diagnostique the i82593 */
+static int
+	read_ringbuf(device *,	/* Read a receive buffer */
+		     int,
+		     char *,
+		     int);
+static inline void
+	wv_82593_reconfig(device *);	/* Reconfigure the controller */
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+static inline void
+	wv_init_info(device *);	/* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats	*
+	wavelan_get_stats(device *);	/* Give stats /proc/net/dev */
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline int
+	wv_start_of_frame(device *,	/* Seek beggining of current frame */
+			  int,	/* end of frame */
+			  int);	/* start of buffer */
+static inline void
+	wv_packet_read(device *,	/* Read a packet from a frame */
+		       int,
+		       int),
+	wv_packet_rcv(device *);	/* Read all packets waiting */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline void
+	wv_packet_write(device *,	/* Write a packet to the Tx buffer */
+			void *,
+			short);
+static int
+	wavelan_packet_xmit(struct sk_buff *,	/* Send a packet */
+			    device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+	wv_mmc_init(device *);	/* Initialize the modem */
+static int
+	wv_ru_stop(device *),	/* Stop the i82593 receiver unit */
+	wv_ru_start(device *);	/* Start the i82593 receiver unit */
+static int
+	wv_82593_config(device *);	/* Configure the i82593 */
+static inline int
+	wv_pcmcia_reset(device *);	/* Reset the pcmcia interface */
+static int
+	wv_hw_config(device *);	/* Reset & configure the whole hardware */
+static inline void
+	wv_hw_reset(device *);	/* Same, + start receiver unit */
+static inline int
+	wv_pcmcia_config(dev_link_t *);	/* Configure the pcmcia interface */
+static void
+	wv_pcmcia_release(u_long),	/* Remove a device */
+	wv_flush_stale_links(void);	/* "detach" all possible devices */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+	wavelan_interrupt(int,	/* Interrupt handler */
+			  void *,
+			  struct pt_regs *);
+static void
+	wavelan_watchdog(device *);	/* Transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+	wavelan_open(device *),		/* Open the device */
+	wavelan_close(device *);	/* Close the device */
+static dev_link_t *
+	wavelan_attach(void);		/* Create a new device */
+static void
+	wavelan_detach(dev_link_t *);	/* Destroy a removed device */
+static int
+	wavelan_event(event_t,		/* Manage pcmcia events */
+		      int,
+		      event_callback_args_t *);
+
+/**************************** VARIABLES ****************************/
+
+static dev_info_t dev_info = "wavelan_cs";
+static dev_link_t *dev_list = NULL;	/* Linked list of devices */
+
+/*
+ * Parameters that can be set with 'insmod'
+ * The exact syntax is 'insmod wavelan_cs.o <var>=<value>'
+ */
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
+static int	irq_mask = 0xdeb8;
+static int 	irq_list[4] = { -1 };
+
+/* Shared memory speed, in ns */
+static int	mem_speed = 0;
+
+/* New module interface */
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(mem_speed, "i");
+
+#ifdef WAVELAN_ROAMING		/* Conditional compile, see above in options */
+/* Enable roaming mode ? No ! Please keep this to 0 */
+static int	do_roaming = 0;
+MODULE_PARM(do_roaming, "i");
+#endif	/* WAVELAN_ROAMING */
+
+MODULE_LICENSE("GPL");
+
+#endif	/* WAVELAN_CS_P_H */
+
diff -urN linux-2.5.3-pre2/drivers/sound/emu10k1/audio.c linux/drivers/sound/emu10k1/audio.c
--- linux-2.5.3-pre2/drivers/sound/emu10k1/audio.c	Tue Oct  9 10:53:17 2001
+++ linux/drivers/sound/emu10k1/audio.c	Mon Jan 21 17:23:23 2002
@@ -1098,7 +1098,7 @@
 
 static int emu10k1_audio_open(struct inode *inode, struct file *file)
 {
-	int minor = MINOR(inode->i_rdev);
+	int minor = minor(inode->i_rdev);
 	struct emu10k1_card *card = NULL;
 	struct list_head *entry;
 	struct emu10k1_wavedevice *wave_dev;
diff -urN linux-2.5.3-pre2/drivers/sound/emu10k1/midi.c linux/drivers/sound/emu10k1/midi.c
--- linux-2.5.3-pre2/drivers/sound/emu10k1/midi.c	Tue Oct  9 10:53:18 2001
+++ linux/drivers/sound/emu10k1/midi.c	Mon Jan 21 17:23:23 2002
@@ -87,7 +87,7 @@
 
 static int emu10k1_midi_open(struct inode *inode, struct file *file)
 {
-	int minor = MINOR(inode->i_rdev);
+	int minor = minor(inode->i_rdev);
 	struct emu10k1_card *card = NULL;
 	struct emu10k1_mididevice *midi_dev;
 	struct list_head *entry;
diff -urN linux-2.5.3-pre2/drivers/sound/emu10k1/mixer.c linux/drivers/sound/emu10k1/mixer.c
--- linux-2.5.3-pre2/drivers/sound/emu10k1/mixer.c	Tue Oct  9 10:53:18 2001
+++ linux/drivers/sound/emu10k1/mixer.c	Mon Jan 21 17:23:23 2002
@@ -640,7 +640,7 @@
 
 static int emu10k1_mixer_open(struct inode *inode, struct file *file)
 {
-	int minor = MINOR(inode->i_rdev);
+	int minor = minor(inode->i_rdev);
 	struct emu10k1_card *card = NULL;
 	struct list_head *entry;
 
diff -urN linux-2.5.3-pre2/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux-2.5.3-pre2/drivers/usb/Makefile	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/Makefile	Mon Jan 21 17:23:23 2002
@@ -46,6 +46,10 @@
 	obj-y += hcd/ehci-hcd.o
 endif
 
+ifeq ($(CONFIG_USB_OHCI_HCD),y)
+	obj-y += hcd/ohci-hcd.o
+endif
+
 obj-$(CONFIG_USB_UHCI)		+= usb-uhci.o
 obj-$(CONFIG_USB_UHCI_ALT)	+= uhci.o
 obj-$(CONFIG_USB_OHCI)		+= usb-ohci.o
@@ -85,6 +89,7 @@
 mod-subdirs	:= serial hcd
 
 subdir-$(CONFIG_USB_EHCI_HCD)	+= hcd
+subdir-$(CONFIG_USB_OHCI_HCD)	+= hcd
 subdir-$(CONFIG_USB_SERIAL)	+= serial
 subdir-$(CONFIG_USB_STORAGE)	+= storage
 
diff -urN linux-2.5.3-pre2/drivers/usb/audio.c linux/drivers/usb/audio.c
--- linux-2.5.3-pre2/drivers/usb/audio.c	Mon Jan 21 17:22:33 2002
+++ linux/drivers/usb/audio.c	Mon Jan 21 17:23:23 2002
@@ -297,13 +297,13 @@
 #define FLG_CONNECTED    32
 
 struct my_data_urb {
-	urb_t urb;
-	iso_packet_descriptor_t isoframe[DESCFRAMES];
+	struct urb urb;
+	struct usb_iso_packet_descriptor isoframe[DESCFRAMES];
 };
 
 struct my_sync_urb {
-	urb_t urb;
-	iso_packet_descriptor_t isoframe[SYNCFRAMES];
+	struct urb urb;
+	struct usb_iso_packet_descriptor isoframe[SYNCFRAMES];
 };
 
 
diff -urN linux-2.5.3-pre2/drivers/usb/auerswald.c linux/drivers/usb/auerswald.c
--- linux-2.5.3-pre2/drivers/usb/auerswald.c	Tue Jan  8 13:15:52 2002
+++ linux/drivers/usb/auerswald.c	Mon Jan 21 17:23:23 2002
@@ -176,7 +176,7 @@
 typedef struct
 {
         struct auerchain *chain;        /* pointer to the chain to which this element belongs */
-        urb_t * urbp;                   /* pointer to attached urb */
+        struct urb * urbp;                   /* pointer to attached urb */
         void *context;                  /* saved URB context */
         usb_complete_t complete;        /* saved URB completion function */
         struct list_head list;          /* to include element into a list */
@@ -200,7 +200,7 @@
         unsigned int len;               /* number of characters in data buffer */
 	unsigned int retries;		/* for urb retries */
         struct usb_ctrlrequest *dr;	/* for setup data in control messages */
-        urb_t * urbp;                   /* USB urb */
+        struct urb * urbp;                   /* USB urb */
         struct auerbufctl *list;        /* pointer to list */
         struct list_head buff_list;     /* reference to next buffer in list */
 } auerbuf_t,*pauerbuf_t;
@@ -237,7 +237,7 @@
 	int			open_count;	    /* count the number of open character channels */
         char 			dev_desc[AUSI_DLEN];/* for storing a textual description */
         unsigned int 		maxControlLength;   /* max. Length of control paket (without header) */
-        urb_t * 		inturbp;            /* interrupt urb */
+        struct urb * 		inturbp;            /* interrupt urb */
         char *			intbufp;            /* data buffer for interrupt urb */
 	unsigned int 		irqsize;	    /* size of interrupt endpoint 1 */
         struct auerchain 	controlchain;  	    /* for chaining of control messages */
@@ -274,7 +274,7 @@
 
 /*-------------------------------------------------------------------*/
 /* Forwards */
-static void auerswald_ctrlread_complete (urb_t * urb);
+static void auerswald_ctrlread_complete (struct urb * urb);
 static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp);
 
 
@@ -283,7 +283,7 @@
 /* --------------------------                                        */
 
 /* completion function for chained urbs */
-static void auerchain_complete (urb_t * urb)
+static void auerchain_complete (struct urb * urb)
 {
 	unsigned long flags;
         int result;
@@ -350,7 +350,7 @@
    this function may be called from completion context or from user space!
    early = 1 -> submit in front of chain
 */
-static int auerchain_submit_urb_list (pauerchain_t acp, urb_t * urb, int early)
+static int auerchain_submit_urb_list (pauerchain_t acp, struct urb * urb, int early)
 {
         int result;
         unsigned long flags;
@@ -424,7 +424,7 @@
 /* submit function for chained urbs
    this function may be called from completion context or from user space!
 */
-static int auerchain_submit_urb (pauerchain_t acp, urb_t * urb)
+static int auerchain_submit_urb (pauerchain_t acp, struct urb * urb)
 {
 	return auerchain_submit_urb_list (acp, urb, 0);
 }
@@ -433,10 +433,10 @@
    the result is 0 if the urb is cancelled, or -EINPROGRESS if
    USB_ASYNC_UNLINK is set and the function is successfully started.
 */
-static int auerchain_unlink_urb (pauerchain_t acp, urb_t * urb)
+static int auerchain_unlink_urb (pauerchain_t acp, struct urb * urb)
 {
 	unsigned long flags;
-        urb_t * urbp;
+        struct urb * urbp;
         pauerchainelement_t acep;
         struct list_head *tmp;
 
@@ -492,7 +492,7 @@
 static void auerchain_unlink_all (pauerchain_t acp)
 {
 	unsigned long flags;
-        urb_t * urbp;
+        struct urb * urbp;
         pauerchainelement_t acep;
 
         dbg ("auerchain_unlink_all called");
@@ -598,7 +598,7 @@
 
 
 /* completion handler for synchronous chained URBs */
-static void auerchain_blocking_completion (urb_t *urb)
+static void auerchain_blocking_completion (struct urb *urb)
 {
 	wait_queue_head_t *wakeup = (wait_queue_head_t *)urb->context;
 	wake_up (wakeup);
@@ -606,7 +606,7 @@
 
 
 /* Starts chained urb and waits for completion or timeout */
-static int auerchain_start_wait_urb (pauerchain_t acp, urb_t *urb, int timeout, int* actual_length)
+static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length)
 {
 	DECLARE_WAITQUEUE (wait, current);
 	DECLARE_WAIT_QUEUE_HEAD (wqh);
@@ -675,7 +675,7 @@
 {
 	int ret;
 	struct usb_ctrlrequest *dr;
-	urb_t *urb;
+	struct urb *urb;
         int length;
 
         dbg ("auerchain_control_msg");
@@ -858,7 +858,7 @@
 }
 
 /* Completion of asynchronous write block */
-static void auerchar_ctrlwrite_complete (urb_t * urb)
+static void auerchar_ctrlwrite_complete (struct urb * urb)
 {
 	pauerbuf_t bp = (pauerbuf_t) urb->context;
 	pauerswald_t cp = ((pauerswald_t)((char *)(bp->list)-(unsigned long)(&((pauerswald_t)0)->bufctl)));
@@ -871,7 +871,7 @@
 }
 
 /* Completion handler for dummy retry packet */
-static void auerswald_ctrlread_wretcomplete (urb_t * urb)
+static void auerswald_ctrlread_wretcomplete (struct urb * urb)
 {
         pauerbuf_t bp = (pauerbuf_t) urb->context;
         pauerswald_t cp;
@@ -910,7 +910,7 @@
 }
 
 /* completion handler for receiving of control messages */
-static void auerswald_ctrlread_complete (urb_t * urb)
+static void auerswald_ctrlread_complete (struct urb * urb)
 {
         unsigned int  serviceid;
         pauerswald_t  cp;
@@ -980,7 +980,7 @@
    messages from the USB device.
 */
 /* int completion handler. */
-static void auerswald_int_complete (urb_t * urb)
+static void auerswald_int_complete (struct urb * urb)
 {
         unsigned long flags;
         unsigned  int channelid;
diff -urN linux-2.5.3-pre2/drivers/usb/devio.c linux/drivers/usb/devio.c
--- linux-2.5.3-pre2/drivers/usb/devio.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/devio.c	Mon Jan 21 17:23:23 2002
@@ -53,7 +53,7 @@
 	unsigned int signr;
 	void *userbuffer;
         void *userurb;
-        urb_t urb;
+        struct urb urb;
 };
 
 static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
@@ -170,7 +170,7 @@
 
 static struct async *alloc_async(unsigned int numisoframes)
 {
-        unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t);
+        unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);
         struct async *as = kmalloc(assize, GFP_KERNEL);
         if (!as)
                 return NULL;
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/Config.in linux/drivers/usb/hcd/Config.in
--- linux-2.5.3-pre2/drivers/usb/hcd/Config.in	Thu Jan  3 13:21:28 2002
+++ linux/drivers/usb/hcd/Config.in	Mon Jan 21 17:23:23 2002
@@ -2,6 +2,6 @@
 # USB Host Controller Drivers
 #
 dep_tristate '  EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
-# dep_tristate '  OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+dep_tristate '  OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
 # dep_tristate '  UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
 
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/Makefile linux/drivers/usb/hcd/Makefile
--- linux-2.5.3-pre2/drivers/usb/hcd/Makefile	Tue Jan  8 13:15:52 2002
+++ linux/drivers/usb/hcd/Makefile	Mon Jan 21 17:23:23 2002
@@ -6,7 +6,7 @@
 O_TARGET	:=
 
 obj-$(CONFIG_USB_EHCI_HCD)			+= ehci-hcd.o
-# obj-$(CONFIG_USB_OHCI_HCD)			+= ohci-hcd.o
+obj-$(CONFIG_USB_OHCI_HCD)			+= ohci-hcd.o
 # obj-$(CONFIG_USB_UHCI_HCD)			+= uhci-hcd.o
 
 # Extract lists of the multi-part drivers.
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ehci-sched.c linux/drivers/usb/hcd/ehci-sched.c
--- linux-2.5.3-pre2/drivers/usb/hcd/ehci-sched.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/hcd/ehci-sched.c	Mon Jan 21 17:23:23 2002
@@ -630,7 +630,7 @@
 	if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
 			&& ehci->hcd.state != USB_STATE_HALT) {
 		int			i;
-		iso_packet_descriptor_t	*desc;
+		struct usb_iso_packet_descriptor	*desc;
 		struct ehci_itd		*first_itd = urb->hcpriv;
 
 		/* update status for this frame's transfers */
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-dbg.c linux/drivers/usb/hcd/ohci-dbg.c
--- linux-2.5.3-pre2/drivers/usb/hcd/ohci-dbg.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hcd/ohci-dbg.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,236 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * This file is licenced under GPL
+ * $Id: ohci-dbg.c,v 1.2 2002/01/19 00:15:45 dbrownell Exp $
+ */
+ 
+/*-------------------------------------------------------------------------*/
+ 
+#ifdef DEBUG
+
+#define pipestring(pipe) ({ char *temp; \
+	switch (usb_pipetype (pipe)) { \
+	case PIPE_CONTROL:	temp = "CTRL"; break; \
+	case PIPE_BULK:		temp = "BULK"; break; \
+	case PIPE_INTERRUPT:	temp = "INTR"; break; \
+	default: 		temp = "ISOC"; break; \
+	}; temp;})
+
+/* debug| print the main components of an URB     
+ * small: 0) header + data packets 1) just header
+ */
+static void urb_print (struct urb * urb, char * str, int small)
+{
+	unsigned int pipe= urb->pipe;
+	
+	if (!urb->dev || !urb->dev->bus) {
+		dbg("%s URB: no dev", str);
+		return;
+	}
+	
+#ifndef	OHCI_VERBOSE_DEBUG
+	if (urb->status != 0)
+#endif
+	dbg("%s:[%4x] dev:%d,ep=%d-%c,%s,flags:%4x,len:%d/%d,stat:%d", 
+		    str,
+		    usb_get_current_frame_number (urb->dev), 
+		    usb_pipedevice (pipe),
+		    usb_pipeendpoint (pipe), 
+		    usb_pipeout (pipe)? 'O': 'I',
+		    pipestring (pipe),
+		    urb->transfer_flags, 
+		    urb->actual_length, 
+		    urb->transfer_buffer_length,
+		    urb->status);
+
+#ifdef	OHCI_VERBOSE_DEBUG
+	if (!small) {
+		int i, len;
+
+		if (usb_pipecontrol (pipe)) {
+			printk (KERN_DEBUG __FILE__ ": cmd(8):");
+			for (i = 0; i < 8 ; i++) 
+				printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
+			printk ("\n");
+		}
+		if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+			printk (KERN_DEBUG __FILE__ ": data(%d/%d):", 
+				urb->actual_length, 
+				urb->transfer_buffer_length);
+			len = usb_pipeout (pipe)? 
+						urb->transfer_buffer_length: urb->actual_length;
+			for (i = 0; i < 16 && i < len; i++) 
+				printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
+			printk ("%s stat:%d\n", i < len? "...": "", urb->status);
+		}
+	} 
+#endif
+}
+
+static inline struct ed *
+dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma);
+
+/* print non-empty branches of the periodic ed tree */
+void ep_print_int_eds (struct ohci_hcd *ohci, char * str)
+{
+	int i, j;
+	 __u32 * ed_p;
+	for (i= 0; i < 32; i++) {
+		j = 5;
+		ed_p = &(ohci->hcca->int_table [i]);
+		if (*ed_p == 0)
+		    continue;
+		printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):",
+				str, i, i);
+		while (*ed_p != 0 && j--) {
+			struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
+			printk (" ed: %4x;", ed->hwINFO);
+			ed_p = &ed->hwNextED;
+		}
+		printk ("\n");
+	}
+}
+
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+		label,
+		mask,
+		(mask & OHCI_INTR_MIE) ? " MIE" : "",
+		(mask & OHCI_INTR_OC) ? " OC" : "",
+		(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+		(mask & OHCI_INTR_FNO) ? " FNO" : "",
+		(mask & OHCI_INTR_UE) ? " UE" : "",
+		(mask & OHCI_INTR_RD) ? " RD" : "",
+		(mask & OHCI_INTR_SF) ? " SF" : "",
+		(mask & OHCI_INTR_WDH) ? " WDH" : "",
+		(mask & OHCI_INTR_SO) ? " SO" : ""
+		);
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+	if (value)
+		dbg ("%s %08x", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+	switch (state) {
+		case OHCI_USB_RESET:	return "reset";
+		case OHCI_USB_RESUME:	return "resume";
+		case OHCI_USB_OPER:	return "operational";
+		case OHCI_USB_SUSPEND:	return "suspend";
+	}
+	return "?";
+}
+
+// dump control and status registers
+static void ohci_dump_status (struct ohci_hcd *controller)
+{
+	struct ohci_regs	*regs = controller->regs;
+	__u32			temp;
+
+	temp = readl (&regs->revision) & 0xff;
+	if (temp != 0x10)
+		dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+	temp = readl (&regs->control);
+	dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+		(temp & OHCI_CTRL_RWE) ? " RWE" : "",
+		(temp & OHCI_CTRL_RWC) ? " RWC" : "",
+		(temp & OHCI_CTRL_IR) ? " IR" : "",
+		hcfs2string (temp & OHCI_CTRL_HCFS),
+		(temp & OHCI_CTRL_BLE) ? " BLE" : "",
+		(temp & OHCI_CTRL_CLE) ? " CLE" : "",
+		(temp & OHCI_CTRL_IE) ? " IE" : "",
+		(temp & OHCI_CTRL_PLE) ? " PLE" : "",
+		temp & OHCI_CTRL_CBSR
+		);
+
+	temp = readl (&regs->cmdstatus);
+	dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+		(temp & OHCI_SOC) >> 16,
+		(temp & OHCI_OCR) ? " OCR" : "",
+		(temp & OHCI_BLF) ? " BLF" : "",
+		(temp & OHCI_CLF) ? " CLF" : "",
+		(temp & OHCI_HCR) ? " HCR" : ""
+		);
+
+	ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
+	ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+	// intrdisable always same as intrenable
+	// ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
+
+	maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+	maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+	maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+	maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+	maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+	maybe_print_eds ("donehead", readl (&regs->donehead));
+}
+
+static void ohci_dump_roothub (struct ohci_hcd *controller, int verbose)
+{
+	__u32			temp, ndp, i;
+
+	temp = roothub_a (controller);
+	ndp = (temp & RH_A_NDP);
+
+	if (verbose) {
+		dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+			((temp & RH_A_POTPGT) >> 24) & 0xff,
+			(temp & RH_A_NOCP) ? " NOCP" : "",
+			(temp & RH_A_OCPM) ? " OCPM" : "",
+			(temp & RH_A_DT) ? " DT" : "",
+			(temp & RH_A_NPS) ? " NPS" : "",
+			(temp & RH_A_PSM) ? " PSM" : "",
+			ndp
+			);
+		temp = roothub_b (controller);
+		dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+			temp,
+			(temp & RH_B_PPCM) >> 16,
+			(temp & RH_B_DR)
+			);
+		temp = roothub_status (controller);
+		dbg ("roothub.status: %08x%s%s%s%s%s%s",
+			temp,
+			(temp & RH_HS_CRWE) ? " CRWE" : "",
+			(temp & RH_HS_OCIC) ? " OCIC" : "",
+			(temp & RH_HS_LPSC) ? " LPSC" : "",
+			(temp & RH_HS_DRWE) ? " DRWE" : "",
+			(temp & RH_HS_OCI) ? " OCI" : "",
+			(temp & RH_HS_LPS) ? " LPS" : ""
+			);
+	}
+	
+	for (i = 0; i < ndp; i++) {
+		temp = roothub_portstatus (controller, i);
+		dbg_port (controller, "", i, temp);
+	}
+}
+
+static void ohci_dump (struct ohci_hcd *controller, int verbose)
+{
+	dbg ("OHCI controller %s state", controller->hcd.bus_name);
+
+	// dumps some of the state we know about
+	ohci_dump_status (controller);
+	if (verbose)
+		ep_print_int_eds (controller, "hcca");
+	dbg ("hcca frame #%04x", controller->hcca->frame_no);
+	ohci_dump_roothub (controller, 1);
+}
+
+
+#endif
+
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-hcd.c linux/drivers/usb/hcd/ohci-hcd.c
--- linux-2.5.3-pre2/drivers/usb/hcd/ohci-hcd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hcd/ohci-hcd.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,973 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * [ Initialisation is based on Linus'  ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds  ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ * 
+ * 
+ * History:
+ * 
+ * 2002/01/18 package as a patch for 2.5.3; this should match the
+ *	2.4.17 kernel modulo some bugs being fixed.
+ *
+ * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
+ *	from post-2.4.5 patches.
+ * 2001/09/20 USB_ZERO_PACKET support; hcca_dma portability, OPTi warning
+ * 2001/09/07 match PCI PM changes, errnos from Linus' tree
+ * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
+ *	pbook pci quirks gone (please fix pbook pci sw!) (db)
+ *
+ * 2001/04/08 Identify version on module load (gb)
+ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
+ 	pci_map_single (db)
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
+ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
+ *
+ * 2000/09/26 fixed races in removing the private portion of the urb
+ * 2000/09/07 disable bulk and control lists when unlinking the last
+ *	endpoint descriptor in order to avoid unrecoverable errors on
+ *	the Lucent chips. (rwc@sgi)
+ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
+ *	urb unlink probs, indentation fixes
+ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
+ *	device unplugs.
+ * 2000/06/28 use PCI hotplug framework, for better power management
+ *	and for Cardbus support (David Brownell)
+ * 2000/earlier:  fixes for NEC/Lucent chips; suspend/resume handling
+ *	when the controller loses power; handle UE; cleanup; ...
+ *
+ * v5.2 1999/12/07 URB 3rd preview, 
+ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
+ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume 
+ * 	i386: HUB, Keyboard, Mouse, Printer 
+ *
+ * v4.3 1999/10/27 multiple HCs, bulk_request
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18 
+ * v3.0 1999/06/25 
+ * v2.1 1999/05/09  code clean up
+ * v2.0 1999/05/04 
+ * v1.0 1999/04/27 initial release
+ *
+ * This file is licenced under GPL
+ * $Id: ohci-hcd.c,v 1.7 2002/01/19 00:20:56 dbrownell Exp $
+ */
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>  /* for in_interrupt () */
+
+#ifndef CONFIG_USB_DEBUG
+	#define CONFIG_USB_DEBUG	/* this is still experimental! */
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "../hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#ifndef CONFIG_PM
+#	define CONFIG_PM
+#endif
+#endif
+
+/*
+ * TO DO:
+ *
+ *	- "disabled" should be the hcd state
+ *	- bandwidth alloc to generic code
+ *	- lots more testing!!
+ */
+
+#define DRIVER_VERSION "$Revision: 1.7 $"
+#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
+#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
+
+/*-------------------------------------------------------------------------*/
+
+#define OHCI_USE_NPS		// force NoPowerSwitching mode
+// #define OHCI_VERBOSE_DEBUG	/* not always helpful */
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define	OHCI_CONTROL_INIT \
+	 (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+#define OHCI_UNLINK_TIMEOUT	 (HZ / 10)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ohci.h"
+
+#include "ohci-hub.c"
+#include "ohci-dbg.c"
+#include "ohci-mem.c"
+#include "ohci-q.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * queue up an urb for anything except the root hub
+ */
+static int ohci_urb_enqueue (
+	struct usb_hcd	*hcd,
+	struct urb	*urb,
+	int		mem_flags
+) {
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+	struct ed	*ed;
+	urb_priv_t	*urb_priv;
+	unsigned int	pipe = urb->pipe;
+	int		i, size = 0;
+	unsigned long	flags;
+	int		bustime = 0;
+	
+#ifdef OHCI_VERBOSE_DEBUG
+	urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+	
+	/* every endpoint has a ed, locate and fill it */
+	if (! (ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
+		usb_dec_dev_use (urb->dev);	
+		return -ENOMEM;
+	}
+
+	/* for the private part of the URB we need the number of TDs (size) */
+	switch (usb_pipetype (pipe)) {
+		case PIPE_CONTROL:
+			/* 1 TD for setup, 1 for ACK, plus ... */
+			size = 2;
+			/* FALLTHROUGH */
+		case PIPE_BULK:
+			/* one TD for every 4096 Bytes (can be upto 8K) */
+			size += urb->transfer_buffer_length / 4096;
+			/* ... and for any remaining bytes ... */
+			if ((urb->transfer_buffer_length % 4096) != 0)
+				size++;
+			/* ... and maybe a zero length packet to wrap it up */
+			if (size == 0)
+				size++;
+			else if ((urb->transfer_flags & USB_ZERO_PACKET) != 0
+				&& (urb->transfer_buffer_length
+					% usb_maxpacket (urb->dev, pipe,
+						usb_pipeout (pipe))) != 0)
+				size++;
+			break;
+		case PIPE_ISOCHRONOUS: /* number of packets from URB */
+			size = urb->number_of_packets;
+			if (size <= 0) {
+				usb_dec_dev_use (urb->dev);	
+				return -EINVAL;
+			}
+			for (i = 0; i < urb->number_of_packets; i++) {
+  				urb->iso_frame_desc [i].actual_length = 0;
+  				urb->iso_frame_desc [i].status = -EXDEV;
+  			}
+			break;
+		case PIPE_INTERRUPT: /* one TD */
+			size = 1;
+			break;
+	}
+
+	/* allocate the private part of the URB */
+	urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
+			mem_flags);
+	if (!urb_priv) {
+		usb_dec_dev_use (urb->dev);	
+		return -ENOMEM;
+	}
+	memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
+	
+	/* fill the private part of the URB */
+	urb_priv->length = size;
+	urb_priv->ed = ed;	
+
+	/* allocate the TDs (updating hash chains) */
+	spin_lock_irqsave (&ohci->lock, flags);
+	for (i = 0; i < size; i++) { 
+		urb_priv->td [i] = td_alloc (ohci, SLAB_ATOMIC);
+		if (!urb_priv->td [i]) {
+			urb_priv->length = i;
+			urb_free_priv (ohci, urb_priv);
+			spin_unlock_irqrestore (&ohci->lock, flags);
+			usb_dec_dev_use (urb->dev);	
+			return -ENOMEM;
+		}
+	}	
+
+// FIXME:  much of this switch should be generic, move to hcd code ...
+
+	/* allocate and claim bandwidth if needed; ISO
+	 * needs start frame index if it was't provided.
+	 */
+	switch (usb_pipetype (pipe)) {
+		case PIPE_ISOCHRONOUS:
+			if (urb->transfer_flags & USB_ISO_ASAP) { 
+				urb->start_frame = ( (ed->state == ED_OPER)
+					? (ed->last_iso + 1)
+					: (le16_to_cpu (ohci->hcca->frame_no)
+						+ 10)) & 0xffff;
+			}	
+			/* FALLTHROUGH */
+		case PIPE_INTERRUPT:
+			if (urb->bandwidth == 0) {
+				bustime = usb_check_bandwidth (urb->dev, urb);
+			}
+			if (bustime < 0) {
+				urb_free_priv (ohci, urb_priv);
+				spin_unlock_irqrestore (&ohci->lock, flags);
+				usb_dec_dev_use (urb->dev);	
+				return bustime;
+			}
+			usb_claim_bandwidth (urb->dev, urb,
+				bustime, usb_pipeisoc (urb->pipe));
+	}
+
+	urb->hcpriv = urb_priv;
+
+	/* link the ed into a chain if is not already */
+	if (ed->state != ED_OPER)
+		ep_link (ohci, ed);
+
+	/* fill the TDs and link it to the ed */
+	td_submit_urb (urb);
+
+	spin_unlock_irqrestore (&ohci->lock, flags);
+
+	return 0;	
+}
+
+/*
+ * decouple the URB from the HC queues (TDs, urb_priv); it's
+ * already marked for deletion.  reporting is always done
+ * asynchronously, and we might be dealing with an urb that's
+ * almost completed anyway...
+ */
+static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	unsigned long		flags;
+	
+#ifdef DEBUG
+	urb_print (urb, "UNLINK", 1);
+#endif		  
+
+	if (!ohci->disabled) {
+		urb_priv_t  *urb_priv;
+
+		/* flag the urb's data for deletion in some upcoming
+		 * SF interrupt's delete list processing
+		 */
+		spin_lock_irqsave (&ohci->lock, flags);
+		urb_priv = urb->hcpriv;
+
+		if (!urb_priv || (urb_priv->state == URB_DEL)) {
+			spin_unlock_irqrestore (&ohci->lock, flags);
+			return 0;
+		}
+			
+		urb_priv->state = URB_DEL; 
+		ed_unlink (urb->dev, urb_priv->ed);
+		spin_unlock_irqrestore (&ohci->lock, flags);
+	} else {
+		/*
+		 * with HC dead, we won't respect hc queue pointers
+		 * any more ... just clean up every urb's memory.
+		 */
+		finish_urb (ohci, urb);
+	}	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	struct hcd_dev		*dev = (struct hcd_dev *) udev->hcpriv;
+	int			i;
+	unsigned long		flags;
+
+	/* free any eds, and dummy tds, still hanging around */
+	spin_lock_irqsave (&ohci->lock, flags);
+	for (i = 0; i < 32; i++) {
+		struct ed	*ed = dev->ep [i];
+		struct td	*tdTailP;
+
+		if (!ed)
+			continue;
+
+		ed->state &= ~ED_URB_DEL;
+		if (ohci->disabled && ed->state == ED_OPER)
+			ed->state = ED_UNLINK;
+		switch (ed->state) {
+		case ED_NEW:
+			break;
+		case ED_UNLINK:
+			tdTailP = dma_to_td (ohci,
+				le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+			td_free (ohci, tdTailP); /* free dummy td */
+			hash_free_ed (ohci, ed);
+			break;
+
+		case ED_OPER:
+		default:
+			err ("illegal ED %d state in free_config, %d",
+				i, ed->state);
+#ifdef DEBUG
+			BUG ();
+#endif
+		}
+		ed_free (ohci, ed);
+	}
+	spin_unlock_irqrestore (&ohci->lock, flags);
+}
+
+static int ohci_get_frame (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+
+	dbg ("%s: ohci_get_frame", hcd->bus_name);
+	return le16_to_cpu (ohci->hcca->frame_no);
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (struct ohci_hcd *ohci)
+{
+	int timeout = 30;
+	int smm_timeout = 50; /* 0,5 sec */
+	 	
+	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+		writel (OHCI_INTR_OC, &ohci->regs->intrenable);
+		writel (OHCI_OCR, &ohci->regs->cmdstatus);
+		dbg ("USB HC TakeOver from SMM");
+		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+			wait_ms (10);
+			if (--smm_timeout == 0) {
+				err ("USB HC TakeOver failed!");
+				return -1;
+			}
+		}
+	}	
+		
+	/* Disable HC interrupts */
+	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+	dbg ("USB HC reset_hc %s: ctrl = 0x%x ;",
+		ohci->hcd.bus_name,
+		readl (&ohci->regs->control));
+
+  	/* Reset USB (needed by some controllers) */
+	writel (0, &ohci->regs->control);
+      	
+	/* HC Reset requires max 10 ms delay */
+	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
+	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+		if (--timeout == 0) {
+			err ("USB HC reset timed out!");
+			return -1;
+		}	
+		udelay (1);
+	}	 
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts 
+ * connect the virtual root hub
+ */
+static int hc_start (struct ohci_hcd *ohci)
+{
+  	__u32			mask;
+  	unsigned int		fminterval;
+  	struct usb_device	*udev;
+	
+	spin_lock_init (&ohci->lock);
+	ohci->disabled = 1;
+	ohci->sleeping = 0;
+
+	/* Tell the controller where the control and bulk lists are
+	 * The lists are empty now. */
+	 
+	writel (0, &ohci->regs->ed_controlhead);
+	writel (0, &ohci->regs->ed_bulkhead);
+	
+	/* a reset clears this */
+	writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
+   
+  	fminterval = 0x2edf;
+	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); 
+	writel (fminterval, &ohci->regs->fminterval);	
+	writel (0x628, &ohci->regs->lsthresh);
+
+ 	/* start controller operations */
+ 	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+	ohci->disabled = 0;
+ 	writel (ohci->hc_control, &ohci->regs->control);
+ 
+	/* Choose the interrupts we care about now, others later on demand */
+	mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+	writel (mask, &ohci->regs->intrstatus);
+	writel (mask, &ohci->regs->intrenable);
+
+#ifdef	OHCI_USE_NPS
+	/* required for AMD-756 and some Mac platforms */
+	writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
+		&ohci->regs->roothub.a);
+	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif	/* OHCI_USE_NPS */
+
+	// POTPGT delay is bits 24-31, in 2 ms units.
+	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+ 
+	/* connect the virtual root hub */
+	ohci->hcd.bus->root_hub = udev = usb_alloc_dev (NULL, ohci->hcd.bus);
+	ohci->hcd.state = USB_STATE_READY;
+	if (!udev) {
+	    ohci->disabled = 1;
+// FIXME cleanup
+	    return -ENOMEM;
+	}
+
+	usb_connect (udev);
+	udev->speed = USB_SPEED_FULL;
+	if (usb_new_device (udev) != 0) {
+		usb_free_dev (udev); 
+		ohci->disabled = 1;
+// FIXME cleanup
+		return -ENODEV;
+	}
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static void ohci_irq (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	struct ohci_regs	*regs = ohci->regs;
+ 	int			ints; 
+
+	if ((ohci->hcca->done_head != 0)
+			&& ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
+		ints =  OHCI_INTR_WDH;
+	} else if ((ints = (readl (&regs->intrstatus)
+			& readl (&regs->intrenable))) == 0) {
+		return;
+	} 
+
+	// dbg ("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
+
+	if (ints & OHCI_INTR_UE) {
+		ohci->disabled++;
+		err ("OHCI Unrecoverable Error, %s disabled", hcd->bus_name);
+		// e.g. due to PCI Master/Target Abort
+
+#ifdef	DEBUG
+		ohci_dump (ohci, 1);
+#endif
+		hc_reset (ohci);
+	}
+  
+	if (ints & OHCI_INTR_WDH) {
+		writel (OHCI_INTR_WDH, &regs->intrdisable);	
+		dl_done_list (ohci, dl_reverse_done_list (ohci));
+		writel (OHCI_INTR_WDH, &regs->intrenable); 
+	}
+  
+	if (ints & OHCI_INTR_SO) {
+		dbg ("USB Schedule overrun");
+		writel (OHCI_INTR_SO, &regs->intrenable); 	 
+	}
+
+	// FIXME:  this assumes SOF (1/ms) interrupts don't get lost...
+	if (ints & OHCI_INTR_SF) { 
+		unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
+		writel (OHCI_INTR_SF, &regs->intrdisable);	
+		if (ohci->ed_rm_list [!frame] != NULL) {
+			dl_del_list (ohci, !frame);
+		}
+		if (ohci->ed_rm_list [frame] != NULL)
+			writel (OHCI_INTR_SF, &regs->intrenable);	
+	}
+
+	writel (ints, &regs->intrstatus);
+	writel (OHCI_INTR_MIE, &regs->intrenable);	
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_stop (struct usb_hcd *hcd)
+{	
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+
+	dbg ("%s: stop %s controller%s",
+		hcd->bus_name,
+		hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+		ohci->disabled ? " (disabled)" : ""
+		);
+#ifdef	DEBUG
+	ohci_dump (ohci, 1);
+#endif
+
+	if (!ohci->disabled)
+		hc_reset (ohci);
+	
+	ohci_mem_cleanup (ohci);
+
+#ifdef CONFIG_PCI
+	pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
+		ohci->hcca, ohci->hcca_dma);
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_start (struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+	int		ret;
+
+#ifdef CONFIG_PCI
+	if (hcd->pdev) {
+		ohci->hcca = pci_alloc_consistent (hcd->pdev,
+				sizeof *ohci->hcca, &ohci->hcca_dma);
+		if (!ohci->hcca)
+			return -ENOMEM;
+
+		/* AMD 756, for most chips (early revs), corrupts register
+		 * values on read ... so enable the vendor workaround.
+		 */
+		if (hcd->pdev->vendor == 0x1022
+				&& hcd->pdev->device == 0x740c) {
+			ohci->flags = OHCI_QUIRK_AMD756;
+			info ("%s: AMD756 erratum 4 workaround",
+				hcd->bus_name);
+		}
+
+		/* Apple's OHCI driver has a lot of bizarre workarounds
+		 * for this chip.  Evidently control and bulk lists
+		 * can get confused.  (B&W G3 models, and ...)
+		 */
+		else if (hcd->pdev->vendor == 0x1045
+				&& hcd->pdev->device == 0xc861) {
+			info ("%s: WARNING: OPTi workarounds unavailable",
+				hcd->bus_name);
+		}
+	}
+#else
+#	error "where's hcca coming from?"
+#endif /* CONFIG_PCI */
+
+        memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+	if ((ret = ohci_mem_init (ohci)) < 0) {
+		ohci_stop (hcd);
+		return ret;
+	}
+	ohci->regs = hcd->regs;
+
+	if (hc_reset (ohci) < 0) {
+		ohci_stop (hcd);
+		return -ENODEV;
+	}
+
+	if (hc_start (ohci) < 0) {
+		err ("can't start %s", ohci->hcd.bus_name);
+		ohci_stop (hcd);
+		return -EBUSY;
+	}
+
+#ifdef	DEBUG
+	ohci_dump (ohci, 1);
+#endif
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+
+static int ohci_suspend (struct usb_hcd *hcd, u32 state)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	unsigned long		flags;
+	u16			cmd;
+
+	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
+		dbg ("can't suspend %s (state is %s)", hcd->bus_name,
+			hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
+		return -EIO;
+	}
+
+	/* act as if usb suspend can always be used */
+	dbg ("%s: suspend to %d", hcd->bus_name, state);
+	ohci->sleeping = 1;
+
+	/* First stop processing */
+  	spin_lock_irqsave (&ohci->lock, flags);
+	ohci->hc_control &=
+		~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
+	writel (ohci->hc_control, &ohci->regs->control);
+	writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+	(void) readl (&ohci->regs->intrstatus);
+  	spin_unlock_irqrestore (&ohci->lock, flags);
+
+	/* Wait a frame or two */
+	mdelay (1);
+	if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
+		mdelay (1);
+		
+ #ifdef CONFIG_PMAC_PBOOK
+	if (_machine == _MACH_Pmac)
+		disable_irq (ohci->irq);
+ 	/* else, 2.4 assumes shared irqs -- don't disable */
+ #endif
+
+	/* Enable remote wakeup */
+	writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
+		&ohci->regs->intrenable);
+
+	/* Suspend chip and let things settle down a bit */
+ 	ohci->hc_control = OHCI_USB_SUSPEND;
+ 	writel (ohci->hc_control, &ohci->regs->control);
+	(void) readl (&ohci->regs->control);
+	mdelay (500); /* No schedule here ! */
+
+	switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
+		case OHCI_USB_RESET:
+			dbg ("%s suspend->reset ?", hcd->bus_name);
+			break;
+		case OHCI_USB_RESUME:
+			dbg ("%s suspend->resume ?", hcd->bus_name);
+			break;
+		case OHCI_USB_OPER:
+			dbg ("%s suspend->operational ?", hcd->bus_name);
+			break;
+		case OHCI_USB_SUSPEND:
+			dbg ("%s suspended", hcd->bus_name);
+			break;
+	}
+
+	/* In some rare situations, Apple's OHCI have happily trashed
+	 * memory during sleep. We disable its bus master bit during
+	 * suspend
+	 */
+	pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);
+	cmd &= ~PCI_COMMAND_MASTER;
+	pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);
+#ifdef CONFIG_PMAC_PBOOK
+	{
+	   	struct device_node	*of_node;
+ 
+		/* Disable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (hcd->pdev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
+	}
+#endif
+	return 0;
+}
+
+
+// FIXME:  this restart logic should be generic,
+// and handle full hcd state cleanup
+
+/* controller died; cleanup debris, then restart */
+/* must not be called from interrupt context */
+
+static int hc_restart (struct ohci_hcd *ohci)
+{
+	int temp;
+	int i;
+
+	ohci->disabled = 1;
+	ohci->sleeping = 0;
+	if (ohci->hcd.bus->root_hub)
+		usb_disconnect (&ohci->hcd.bus->root_hub);
+	
+	/* empty the interrupt branches */
+	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0;
+	for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
+	
+	/* no EDs to remove */
+	ohci->ed_rm_list [0] = NULL;
+	ohci->ed_rm_list [1] = NULL;
+
+	/* empty control and bulk lists */	 
+	ohci->ed_isotail     = NULL;
+	ohci->ed_controltail = NULL;
+	ohci->ed_bulktail    = NULL;
+
+	if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+		err ("can't restart %s, %d", ohci->hcd.bus_name, temp);
+		return temp;
+	} else
+		dbg ("restart %s completed", ohci->hcd.bus_name);
+	return 0;
+}
+
+static int ohci_resume (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	int			temp;
+	int			retval = 0;
+	unsigned long		flags;
+
+#ifdef CONFIG_PMAC_PBOOK
+	{
+		struct device_node *of_node;
+
+		/* Re-enable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (hcd->pdev);
+		if (of_node)
+			pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+	}
+#endif
+	/* did we suspend, or were we powered off? */
+	ohci->hc_control = readl (&ohci->regs->control);
+	temp = ohci->hc_control & OHCI_CTRL_HCFS;
+
+#ifdef DEBUG
+	/* the registers may look crazy here */
+	ohci_dump_status (ohci);
+#endif
+
+	/* Re-enable bus mastering */
+	pci_set_master (ohci->hcd.pdev);
+	
+	switch (temp) {
+
+	case OHCI_USB_RESET:	// lost power
+		info ("USB restart: %s", hcd->bus_name);
+		retval = hc_restart (ohci);
+		break;
+
+	case OHCI_USB_SUSPEND:	// host wakeup
+	case OHCI_USB_RESUME:	// remote wakeup
+		info ("USB continue: %s from %s wakeup", hcd->bus_name,
+			 (temp == OHCI_USB_SUSPEND)
+				? "host" : "remote");
+		ohci->hc_control = OHCI_USB_RESUME;
+		writel (ohci->hc_control, &ohci->regs->control);
+		(void) readl (&ohci->regs->control);
+		mdelay (20); /* no schedule here ! */
+		/* Some controllers (lucent) need a longer delay here */
+		mdelay (15);
+
+		temp = readl (&ohci->regs->control);
+		temp = ohci->hc_control & OHCI_CTRL_HCFS;
+		if (temp != OHCI_USB_RESUME) {
+			err ("controller %s won't resume", hcd->bus_name);
+			ohci->disabled = 1;
+			retval = -EIO;
+			break;
+		}
+
+		/* Some chips likes being resumed first */
+		writel (OHCI_USB_OPER, &ohci->regs->control);
+		(void) readl (&ohci->regs->control);
+		mdelay (3);
+
+		/* Then re-enable operations */
+		spin_lock_irqsave (&ohci->lock, flags);
+		ohci->disabled = 0;
+		ohci->sleeping = 0;
+		ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+		if (!ohci->ed_rm_list [0] && !ohci->ed_rm_list [1]) {
+			if (ohci->ed_controltail)
+				ohci->hc_control |= OHCI_CTRL_CLE;
+			if (ohci->ed_bulktail)
+				ohci->hc_control |= OHCI_CTRL_BLE;
+		}
+		hcd->state = USB_STATE_READY;
+		writel (ohci->hc_control, &ohci->regs->control);
+
+		/* trigger a start-frame interrupt (why?) */
+		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+
+		/* Check for a pending done list */
+		writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);	
+		(void) readl (&ohci->regs->intrdisable);
+		spin_unlock_irqrestore (&ohci->lock, flags);
+
+ #ifdef CONFIG_PMAC_PBOOK
+		if (_machine == _MACH_Pmac)
+			enable_irq (ohci->irq);
+ #endif
+		if (ohci->hcca->done_head)
+			dl_done_list (ohci, dl_reverse_done_list (ohci));
+		writel (OHCI_INTR_WDH, &ohci->regs->intrenable); 
+
+//		writel (OHCI_BLF, &ohci->regs->cmdstatus);
+//		writel (OHCI_CLF, &ohci->regs->cmdstatus);
+ohci_dump_status (ohci);
+dbg ("sleeping = %d, disabled = %d", ohci->sleeping, ohci->disabled);
+		break;
+
+	default:
+		warn ("odd PCI resume for %s", hcd->bus_name);
+	}
+	return retval;
+}
+
+#endif	/* CONFIG_PM */
+
+
+/*-------------------------------------------------------------------------*/
+
+static const char	hcd_name [] = "ohci-hcd";
+
+static const struct hc_driver ohci_driver = {
+	description:		hcd_name,
+
+	/*
+	 * generic hardware linkage
+	 */
+	irq:			ohci_irq,
+	flags:			HCD_MEMORY | HCD_USB11,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	start:			ohci_start,
+#ifdef	CONFIG_PM
+	suspend:		ohci_suspend,
+	resume:			ohci_resume,
+#endif
+	stop:			ohci_stop,
+
+	/*
+	 * memory lifecycle (except per-request)
+	 */
+	hcd_alloc:		ohci_hcd_alloc,
+	hcd_free:		ohci_hcd_free,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	urb_enqueue:		ohci_urb_enqueue,
+	urb_dequeue:		ohci_urb_dequeue,
+	free_config:		ohci_free_config,
+
+	/*
+	 * scheduling support
+	 */
+	get_frame_number:	ohci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	hub_status_data:	ohci_hub_status_data,
+	hub_control:		ohci_hub_control,
+};
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_LICENSE ("GPL");
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PCI
+
+/* There do exist non-PCI implementations of OHCI ...
+ * Examples include the SA-1111 (ARM) and some MIPS
+ * and related hardware.
+ */
+
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+	/* handle any USB OHCI controller */
+	class:		(PCI_CLASS_SERIAL_USB << 8) | 0x10,
+	class_mask:	~0,
+	driver_data:	(unsigned long) &ohci_driver,
+
+	/* no matter who makes it */
+	vendor:		PCI_ANY_ID,
+	device:		PCI_ANY_ID,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver ohci_pci_driver = {
+	name:		(char *) hcd_name,
+	id_table:	pci_ids,
+
+	probe:		usb_hcd_pci_probe,
+	remove:		usb_hcd_pci_remove,
+
+#ifdef	CONFIG_PM
+	suspend:	usb_hcd_pci_suspend,
+	resume:		usb_hcd_pci_resume,
+#endif
+};
+
+ 
+static int __init ohci_hcd_init (void) 
+{
+	dbg (DRIVER_INFO);
+	dbg ("block sizes: ed %d td %d",
+		sizeof (struct ed), sizeof (struct td));
+	return pci_module_init (&ohci_pci_driver);
+}
+module_init (ohci_hcd_init);
+
+/*-------------------------------------------------------------------------*/
+
+static void __exit ohci_hcd_cleanup (void) 
+{	
+	pci_unregister_driver (&ohci_pci_driver);
+}
+module_exit (ohci_hcd_cleanup);
+
+#endif /* CONFIG_PCI */
+
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-hub.c linux/drivers/usb/hcd/ohci-hub.c
--- linux-2.5.3-pre2/drivers/usb/hcd/ohci-hub.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hcd/ohci-hub.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,267 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * This file is licenced under GPL
+ * $Id: ohci-hub.c,v 1.2 2002/01/19 00:21:49 dbrownell Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * OHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect.  AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+	u32 temp = readl (&hc->regs->roothub.register); \
+	if (hc->flags & OHCI_QUIRK_AMD756) \
+		while (temp & mask) \
+			temp = readl (&hc->regs->roothub.register); \
+	temp; })
+
+static u32 roothub_a (struct ohci_hcd *hc)
+	{ return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+	{ return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+	{ return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+/*-------------------------------------------------------------------------*/
+
+#define dbg_port(hc,label,num,value) \
+	dbg ("%s: %s roothub.portstatus [%d] " \
+		"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", \
+		hc->hcd.bus_name, label, num, temp, \
+		(temp & RH_PS_PRSC) ? " PRSC" : "", \
+		(temp & RH_PS_OCIC) ? " OCIC" : "", \
+		(temp & RH_PS_PSSC) ? " PSSC" : "", \
+		(temp & RH_PS_PESC) ? " PESC" : "", \
+		(temp & RH_PS_CSC) ? " CSC" : "", \
+ 		\
+		(temp & RH_PS_LSDA) ? " LSDA" : "", \
+		(temp & RH_PS_PPS) ? " PPS" : "", \
+		(temp & RH_PS_PRS) ? " PRS" : "", \
+		(temp & RH_PS_POCI) ? " POCI" : "", \
+		(temp & RH_PS_PSS) ? " PSS" : "", \
+ 		\
+		(temp & RH_PS_PES) ? " PES" : "", \
+		(temp & RH_PS_CCS) ? " CCS" : "" \
+		);
+
+
+/*-------------------------------------------------------------------------*/
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+	int		ports, i, changed = 0, length = 1;
+
+	ports = roothub_a (ohci) & RH_A_NDP; 
+	if (ports > MAX_ROOT_PORTS) {
+		err ("%s: bogus NDP=%d", hcd->bus_name, ports);
+		err ("rereads as NDP=%d",
+			readl (&ohci->regs->roothub.a) & RH_A_NDP);
+		/* retry later; "should not happen" */
+		return 0;
+	}
+
+	/* init status */
+	if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
+		buf [0] = changed = 1;
+	else
+		buf [0] = 0;
+	if (ports > 7) {
+		buf [1] = 0;
+		length++;
+	}
+
+	/* look at each port */
+	for (i = 0; i < ports; i++) {
+		u32	status = roothub_portstatus (ohci, i);
+
+		status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+				| RH_PS_OCIC | RH_PS_PRSC;
+		if (status) {
+			changed = 1;
+			set_bit (i + 1, buf);
+		}
+	}
+	return changed ? length : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_hub_descriptor (
+	struct ohci_hcd			*ohci,
+	struct usb_hub_descriptor	*desc
+) {
+	u32		rh = roothub_a (ohci);
+	int		ports = rh & RH_A_NDP; 
+	u16		temp;
+
+	desc->bDescriptorType = 0x29;
+	desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
+	desc->bHubContrCurrent = 0;
+
+	desc->bNbrPorts = ports;
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 + 2 * temp;
+
+	temp = 0;
+	if (rh & RH_A_PSM) 		/* per-port power switching? */
+	    temp |= 0x0001;
+	if (rh & RH_A_NOCP)		/* no overcurrent reporting? */
+	    temp |= 0x0010;
+	else if (rh & RH_A_OCPM)	/* per-port overcurrent reporting? */
+	    temp |= 0x0008;
+	desc->wHubCharacteristics = cpu_to_le16 (temp);
+
+	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+	rh = roothub_b (ohci);
+	desc->bitmap [0] = rh & RH_B_DR;
+	if (ports > 7) {
+		desc->bitmap [1] = (rh & RH_B_DR) >> 8;
+		desc->bitmap [2] = desc->bitmap [3] = 0xff;
+	} else
+		desc->bitmap [1] = 0xff;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hub_control (
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+) {
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+	int		ports;
+	u32		temp;
+	int		retval = 0;
+
+	// if (port request)
+		ports = roothub_a (ohci) & RH_A_NDP; 
+	switch (typeReq) {
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+			writel (RH_HS_OCIC, &ohci->regs->roothub.status);
+		case C_HUB_LOCAL_POWER:
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case ClearPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			temp = RH_PS_CCS;
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			temp = RH_PS_PESC;
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			temp = RH_PS_POCI;
+			break;
+		case USB_PORT_FEAT_C_SUSPEND:
+			temp = RH_PS_PSSC;
+			break;
+		case USB_PORT_FEAT_POWER:
+			temp = RH_PS_LSDA;
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			temp = RH_PS_CSC;
+			break;
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			temp = RH_PS_OCIC;
+			break;
+		case USB_PORT_FEAT_C_RESET:
+			temp = RH_PS_PRSC;
+			break;
+		default:
+			goto error;
+		}
+		writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
+		// readl (&ohci->regs->roothub.portstatus [wIndex]);
+		break;
+	case GetHubDescriptor:
+		ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
+		break;
+	case GetHubStatus:
+		temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
+		*(u32 *) buf = cpu_to_le32 (temp);
+		break;
+	case GetPortStatus:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		temp = roothub_portstatus (ohci, wIndex);
+		*(u32 *) buf = cpu_to_le32 (temp);
+
+#ifndef	OHCI_VERBOSE_DEBUG
+	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */
+#endif
+		dbg_port (ohci, "GetStatus", wIndex + 1, temp);
+		break;
+	case SetHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+			// FIXME:  this can be cleared, yes?
+		case C_HUB_LOCAL_POWER:
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case SetPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			writel (RH_PS_PSS,
+				&ohci->regs->roothub.portstatus [wIndex]);
+			break;
+		case USB_PORT_FEAT_POWER:
+			writel (RH_PS_PPS,
+				&ohci->regs->roothub.portstatus [wIndex]);
+			break;
+		case USB_PORT_FEAT_RESET:
+			temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
+			if (temp & RH_PS_CCS)
+				writel (RH_PS_PRS,
+				    &ohci->regs->roothub.portstatus [wIndex]);
+			break;
+		default:
+			goto error;
+		}
+		break;
+
+	default:
+error:
+		/* "protocol stall" on error */
+		retval = -EPIPE;
+	}
+	return retval;
+}
+
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-mem.c linux/drivers/usb/hcd/ohci-mem.c
--- linux-2.5.3-pre2/drivers/usb/hcd/ohci-mem.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hcd/ohci-mem.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,251 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * This file is licenced under GPL
+ * $Id: ohci-mem.c,v 1.2 2002/01/19 00:22:13 dbrownell Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ *	- data used only by the HCD ... kmalloc is fine
+ *	- async and periodic schedules, shared by HC and HCD ... these
+ *	  need to use pci_pool or pci_alloc_consistent
+ *	- driver buffers, read/written by HC ... single shot DMA mapped 
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_hcd *ohci_hcd_alloc (void)
+{
+	struct ohci_hcd *ohci;
+
+	ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL);
+	if (ohci != 0) {
+		memset (ohci, 0, sizeof (struct ohci_hcd));
+		return &ohci->hcd;
+	}
+	return 0;
+}
+
+static void ohci_hcd_free (struct usb_hcd *hcd)
+{
+	kfree (hcd_to_ohci (hcd));
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#	define OHCI_MEM_FLAGS	SLAB_POISON
+#else
+#	define OHCI_MEM_FLAGS	0
+#endif
+ 
+#ifndef CONFIG_PCI
+#	error "usb-ohci currently requires PCI-based controllers"
+	/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
+#endif
+
+
+/* Recover a TD/ED using its collision chain */
+static inline void *
+dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
+{
+	struct hash_t * scan = entry->head;
+	while (scan && scan->dma != dma)
+		scan = scan->next;
+	return scan->virt;
+}
+
+static inline struct ed *
+dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma)
+{
+	return (struct ed *) dma_to_ed_td(&(hc->ed_hash [ED_HASH_FUNC(ed_dma)]),
+				      ed_dma);
+}
+
+static inline struct td *
+dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
+{
+	return (struct td *) dma_to_ed_td(&(hc->td_hash [TD_HASH_FUNC(td_dma)]),
+				      td_dma);
+}
+
+// FIXME:  when updating the hashtables this way, mem_flags is unusable...
+
+/* Add a hash entry for a TD/ED; return true on success */
+static int
+hash_add_ed_td (
+	struct hash_list_t *entry,
+	void *virt,
+	dma_addr_t dma,
+	int mem_flags
+)
+{
+	struct hash_t * scan;
+	
+	scan = (struct hash_t *) kmalloc (sizeof *scan, mem_flags);
+	if (!scan)
+		return 0;
+	
+	if (!entry->tail) {
+		entry->head = entry->tail = scan;
+	} else {
+		entry->tail->next = scan;
+		entry->tail = scan;
+	}
+	
+	scan->virt = virt;
+	scan->dma = dma;
+	scan->next = NULL;
+	return 1;
+}
+
+static inline int
+hash_add_ed (struct ohci_hcd *hc, struct ed *ed, int mem_flags)
+{
+	return hash_add_ed_td (&(hc->ed_hash [ED_HASH_FUNC (ed->dma)]),
+			ed, ed->dma, mem_flags);
+}
+
+static inline int
+hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags)
+{
+	return hash_add_ed_td (&(hc->td_hash [TD_HASH_FUNC (td->td_dma)]),
+			td, td->td_dma, mem_flags);
+}
+
+
+static void
+hash_free_ed_td (struct hash_list_t *entry, void *virt)
+{
+	struct hash_t *scan, *prev;
+	scan = prev = entry->head;
+
+	// Find and unlink hash entry
+	while (scan && scan->virt != virt) {
+		prev = scan;
+		scan = scan->next;
+	}
+	if (scan) {
+		if (scan == entry->head) {
+			if (entry->head == entry->tail)
+				entry->head = entry->tail = NULL;
+			else
+				entry->head = scan->next;
+		} else if (scan == entry->tail) {
+			entry->tail = prev;
+			prev->next = NULL;
+		} else
+			prev->next = scan->next;
+		kfree(scan);
+	}
+}
+
+static inline void
+hash_free_ed (struct ohci_hcd *hc, struct ed * ed)
+{
+	hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
+}
+
+static inline void
+hash_free_td (struct ohci_hcd *hc, struct td * td)
+{
+	hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
+}
+
+
+static int ohci_mem_init (struct ohci_hcd *ohci)
+{
+	ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,
+		sizeof (struct td),
+		32 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		GFP_KERNEL | OHCI_MEM_FLAGS);
+	if (!ohci->td_cache)
+		return -ENOMEM;
+	ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev,
+		sizeof (struct ed),
+		16 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		GFP_KERNEL | OHCI_MEM_FLAGS);
+	if (!ohci->ed_cache) {
+		pci_pool_destroy (ohci->td_cache);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void ohci_mem_cleanup (struct ohci_hcd *ohci)
+{
+	if (ohci->td_cache) {
+		pci_pool_destroy (ohci->td_cache);
+		ohci->td_cache = 0;
+	}
+	if (ohci->ed_cache) {
+		pci_pool_destroy (ohci->ed_cache);
+		ohci->ed_cache = 0;
+	}
+}
+
+/* TDs ... */
+static struct td *
+td_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+	dma_addr_t	dma;
+	struct td	*td;
+
+	td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
+	if (td) {
+		td->td_dma = dma;
+		/* hash it for later reverse mapping */
+		if (!hash_add_td (hc, td, mem_flags)) {
+			pci_pool_free (hc->td_cache, td, dma);
+			return NULL;
+		}
+	}
+	return td;
+}
+
+static inline void
+td_free (struct ohci_hcd *hc, struct td *td)
+{
+	hash_free_td (hc, td);
+	pci_pool_free (hc->td_cache, td, td->td_dma);
+}
+
+
+/* EDs ... */
+static struct ed *
+ed_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+	dma_addr_t	dma;
+	struct ed	*ed;
+
+	ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma);
+	if (ed) {
+		memset (ed, 0, sizeof (*ed));
+		ed->dma = dma;
+		/* hash it for later reverse mapping */
+		if (!hash_add_ed (hc, ed, mem_flags)) {
+			pci_pool_free (hc->ed_cache, ed, dma);
+			return NULL;
+		}
+	}
+	return ed;
+}
+
+static inline void
+ed_free (struct ohci_hcd *hc, struct ed *ed)
+{
+	hash_free_ed (hc, ed);
+	pci_pool_free (hc->ed_cache, ed, ed->dma);
+}
+
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-q.c linux/drivers/usb/hcd/ohci-q.c
--- linux-2.5.3-pre2/drivers/usb/hcd/ohci-q.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hcd/ohci-q.c	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,1000 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * This file is licenced under GPL
+ * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $
+ */
+ 
+static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
+{
+	int		last = urb_priv->length - 1;
+
+	if (last >= 0) {
+		int		i;
+		struct td	*td = urb_priv->td [0];
+#ifdef CONFIG_PCI
+		int		len = td->urb->transfer_buffer_length;
+		int		dir = usb_pipeout (td->urb->pipe)
+					? PCI_DMA_TODEVICE
+					: PCI_DMA_FROMDEVICE;
+
+		/* unmap CTRL URB setup buffer (always td 0) */
+		if (usb_pipecontrol (td->urb->pipe)) {
+			pci_unmap_single (hc->hcd.pdev, 
+					td->data_dma, 8, PCI_DMA_TODEVICE);
+			
+			/* CTRL data buffer starts at td 1 if len > 0 */
+			if (len && last > 0)
+				td = urb_priv->td [1]; 		
+		}
+		/* else:  ISOC, BULK, INTR data buffer starts at td 0 */
+
+		/* unmap data buffer */
+		if (len && td->data_dma)
+			pci_unmap_single (hc->hcd.pdev,
+					td->data_dma, len, dir);
+#else
+#	warning "assuming no buffer unmapping is needed"
+#endif
+
+		for (i = 0; i <= last; i++) {
+			td = urb_priv->td [i];
+			if (td)
+				td_free (hc, td);
+		}
+	}
+
+	kfree (urb_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * URB goes back to driver, and isn't reissued.
+ * It's completely gone from HC data structures, so no locking
+ * is needed ... or desired! (Giveback can call back to hcd.)
+ */
+static inline void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
+{
+	if (urb->hcpriv) {
+		urb_free_priv (ohci, urb->hcpriv);
+		urb->hcpriv = NULL;
+	}
+	usb_hcd_giveback_urb (&ohci->hcd, urb);
+}
+
+static void td_submit_urb (struct urb *urb);
+
+/*
+ * URB is reported to driver, is reissued if it's periodic.
+ */
+static int return_urb (struct ohci_hcd *hc, struct urb *urb)
+{
+	urb_priv_t	*urb_priv = urb->hcpriv;
+	struct urb	*urbt;
+	unsigned long	flags;
+	int		i;
+
+#ifdef DEBUG
+	if (!urb_priv) {
+		err ("already unlinked!");
+		BUG ();
+	}
+
+	/* just to be sure */
+	if (!urb->complete) {
+		err ("no completion!");
+		BUG ();
+	}
+#endif
+
+#ifdef OHCI_VERBOSE_DEBUG
+	urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+
+// FIXME:  but if urb->status says it was was unlinked ...
+
+	switch (usb_pipetype (urb->pipe)) {
+  		case PIPE_INTERRUPT:
+#ifdef CONFIG_PCI
+			pci_unmap_single (hc->hcd.pdev,
+				urb_priv->td [0]->data_dma,
+				urb->transfer_buffer_length,
+				usb_pipeout (urb->pipe)
+					? PCI_DMA_TODEVICE
+					: PCI_DMA_FROMDEVICE);
+#endif
+			urb->complete (urb);
+
+			/* implicitly requeued */
+  			urb->actual_length = 0;
+  			urb->status = -EINPROGRESS;
+  			if (urb_priv->state != URB_DEL) {
+				spin_lock_irqsave (&hc->lock, flags);
+  				td_submit_urb (urb);
+  				spin_unlock_irqrestore (&hc->lock, flags);
+			}
+  			break;
+
+		case PIPE_ISOCHRONOUS:
+			for (urbt = urb->next;
+					urbt && (urbt != urb);
+					urbt = urbt->next)
+				continue;
+			if (urbt) { /* send the reply and requeue URB */	
+#ifdef CONFIG_PCI
+// FIXME this style unmap is only done on this route ...
+				pci_unmap_single (hc->hcd.pdev,
+					urb_priv->td [0]->data_dma,
+					urb->transfer_buffer_length,
+					usb_pipeout (urb->pipe)
+						? PCI_DMA_TODEVICE
+						: PCI_DMA_FROMDEVICE);
+#endif
+				urb->complete (urb);
+				spin_lock_irqsave (&hc->lock, flags);
+				urb->actual_length = 0;
+  				urb->status = -EINPROGRESS;
+  				urb->start_frame = urb_priv->ed->last_iso + 1;
+  				if (urb_priv->state != URB_DEL) {
+  					for (i = 0; i < urb->number_of_packets;
+							i++) {
+  						urb->iso_frame_desc [i]
+							.actual_length = 0;
+  						urb->iso_frame_desc [i]
+							.status = -EXDEV;
+  					}
+  					td_submit_urb (urb);
+  				}
+// FIXME if not deleted, should have been "finished"
+  				spin_unlock_irqrestore (&hc->lock, flags);
+
+  			} else { /* not reissued */
+				finish_urb (hc, urb);
+			}		
+			break;
+
+		/*
+		 * C/B requests that get here are never reissued.
+		 */
+		case PIPE_BULK:
+		case PIPE_CONTROL:
+			finish_urb (hc, urb);
+			break;
+	}
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/  
+
+/* search for the right branch to insert an interrupt ed into the int tree 
+ * do some load balancing;
+ * returns the branch and 
+ * sets the interval to interval = 2^integer (ld (interval))
+ */
+static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load)
+{
+	int	i, branch = 0;
+
+	/* search for the least loaded interrupt endpoint branch */
+	for (i = 0; i < NUM_INTS ; i++) 
+		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])
+			branch = i; 
+
+	branch = branch % interval;
+	for (i = branch; i < NUM_INTS; i += interval)
+		ohci->ohci_int_load [i] += load;
+
+	return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*  2^int ( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{	
+	int	i;
+
+	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++)
+		continue;
+	return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree 
+ * in order to process it sequentially the indexes of the branches have
+ * to be mapped the mapping reverses the bits of a word of num_bits length
+ */
+static int ep_rev (int num_bits, int word)
+{
+	int			i, wout = 0;
+
+	for (i = 0; i < num_bits; i++)
+		wout |= (( (word >> i) & 1) << (num_bits - i - 1));
+	return wout;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (struct ohci_hcd *ohci, struct ed *edi)
+{	 
+	int			int_branch, i;
+	int			inter, interval, load;
+	__u32			*ed_p;
+	volatile struct ed	*ed = edi;
+
+	ed->state = ED_OPER;
+
+	switch (ed->type) {
+	case PIPE_CONTROL:
+		ed->hwNextED = 0;
+		if (ohci->ed_controltail == NULL) {
+			writel (ed->dma, &ohci->regs->ed_controlhead);
+		} else {
+			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
+		}
+		ed->ed_prev = ohci->ed_controltail;
+		if (!ohci->ed_controltail
+				&& !ohci->ed_rm_list [0]
+				&& !ohci->ed_rm_list [1]
+				&& !ohci->sleeping
+				) {
+			ohci->hc_control |= OHCI_CTRL_CLE;
+			writel (ohci->hc_control, &ohci->regs->control);
+		}
+		ohci->ed_controltail = edi;	  
+		break;
+
+	case PIPE_BULK:
+		ed->hwNextED = 0;
+		if (ohci->ed_bulktail == NULL) {
+			writel (ed->dma, &ohci->regs->ed_bulkhead);
+		} else {
+			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
+		}
+		ed->ed_prev = ohci->ed_bulktail;
+		if (!ohci->ed_bulktail
+				&& !ohci->ed_rm_list [0]
+				&& !ohci->ed_rm_list [1]
+				&& !ohci->sleeping
+				) {
+			ohci->hc_control |= OHCI_CTRL_BLE;
+			writel (ohci->hc_control, &ohci->regs->control);
+		}
+		ohci->ed_bulktail = edi;	  
+		break;
+
+	case PIPE_INTERRUPT:
+		load = ed->int_load;
+		interval = ep_2_n_interval (ed->int_period);
+		ed->int_interval = interval;
+		int_branch = ep_int_balance (ohci, interval, load);
+		ed->int_branch = int_branch;
+
+		for (i = 0; i < ep_rev (6, interval); i += inter) {
+			inter = 1;
+			for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]); 
+				 (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 
+				ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+					inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+			ed->hwNextED = *ed_p; 
+			*ed_p = cpu_to_le32 (ed->dma);
+		}
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "LINK_INT");
+#endif
+		break;
+
+	case PIPE_ISOCHRONOUS:
+		ed->hwNextED = 0;
+		ed->int_interval = 1;
+		if (ohci->ed_isotail != NULL) {
+			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
+			ed->ed_prev = ohci->ed_isotail;
+		} else {
+			for ( i = 0; i < NUM_INTS; i += inter) {
+				inter = 1;
+				for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); 
+					*ed_p != 0; 
+					ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+						inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+				*ed_p = cpu_to_le32 (ed->dma);	
+			}	
+			ed->ed_prev = NULL;
+		}	
+		ohci->ed_isotail = edi;  
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "LINK_ISO");
+#endif
+		break;
+	}	 	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* unlink an ed from one of the HC chains. 
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed
+ */
+static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) 
+{
+	int	int_branch;
+	int	i;
+	int	inter;
+	int	interval;
+	__u32	*ed_p;
+
+	ed->hwINFO |= __constant_cpu_to_le32 (OHCI_ED_SKIP);
+
+	switch (ed->type) {
+	case PIPE_CONTROL:
+		if (ed->ed_prev == NULL) {
+			if (!ed->hwNextED) {
+				ohci->hc_control &= ~OHCI_CTRL_CLE;
+				writel (ohci->hc_control, &ohci->regs->control);
+			}
+			writel (le32_to_cpup (&ed->hwNextED),
+				&ohci->regs->ed_controlhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_controltail == ed) {
+			ohci->ed_controltail = ed->ed_prev;
+		} else {
+			 (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+			 	->ed_prev = ed->ed_prev;
+		}
+		break;
+
+	case PIPE_BULK:
+		if (ed->ed_prev == NULL) {
+			if (!ed->hwNextED) {
+				ohci->hc_control &= ~OHCI_CTRL_BLE;
+				writel (ohci->hc_control, &ohci->regs->control);
+			}
+			writel (le32_to_cpup (&ed->hwNextED),
+				&ohci->regs->ed_bulkhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_bulktail == ed) {
+			ohci->ed_bulktail = ed->ed_prev;
+		} else {
+			 (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+			 	->ed_prev = ed->ed_prev;
+		}
+		break;
+
+	case PIPE_INTERRUPT:
+		int_branch = ed->int_branch;
+		interval = ed->int_interval;
+
+		for (i = 0; i < ep_rev (6, interval); i += inter) {
+			for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]), inter = 1; 
+				 (*ed_p != 0) && (*ed_p != ed->hwNextED); 
+				ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), 
+				inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) {				
+					if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+			  			*ed_p = ed->hwNextED;		
+			  			break;
+			  		}
+			  }
+		}
+		for (i = int_branch; i < NUM_INTS; i += interval)
+		    ohci->ohci_int_load [i] -= ed->int_load;
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "UNLINK_INT");
+#endif
+		break;
+
+	case PIPE_ISOCHRONOUS:
+		if (ohci->ed_isotail == ed)
+			ohci->ed_isotail = ed->ed_prev;
+		if (ed->hwNextED != 0) 
+			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+		    		->ed_prev = ed->ed_prev;
+
+		if (ed->ed_prev != NULL) {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		} else {
+			for (i = 0; i < NUM_INTS; i++) {
+				for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); 
+						*ed_p != 0; 
+						ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
+					// inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+					if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+						*ed_p = ed->hwNextED;		
+						break;
+					}
+				}
+			}	
+		}	
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "UNLINK_ISO");
+#endif
+		break;
+	}
+	ed->state = ED_UNLINK;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* (re)init an endpoint; this _should_ be done once at the
+ * usb_set_configuration command, but the USB stack is a bit stateless
+ * so we do it at every transaction.
+ * if the state of the ed is ED_NEW then a dummy td is added and the
+ * state is changed to ED_UNLINK
+ * in all other cases the state is left unchanged
+ * the ed info fields are set even though most of them should
+ * not change
+ */
+static struct ed *ep_add_ed (
+	struct usb_device	*udev,
+	unsigned int		pipe,
+	int			interval,
+	int			load,
+	int			mem_flags
+) {
+   	struct ohci_hcd		*ohci = hcd_to_ohci (udev->bus->hcpriv);
+	struct hcd_dev		*dev = (struct hcd_dev *) udev->hcpriv;
+	struct td		*td;
+	struct ed		*ed; 
+	unsigned		ep;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&ohci->lock, flags);
+
+	ep = usb_pipeendpoint (pipe) << 1;
+	if (!usb_pipecontrol (pipe) && usb_pipeout (pipe))
+		ep |= 1;
+	if (!(ed = dev->ep [ep])) {
+		ed = ed_alloc (ohci, SLAB_ATOMIC);
+		if (!ed) {
+			/* out of memory */
+			spin_unlock_irqrestore (&ohci->lock, flags);
+			return NULL;
+		}
+		dev->ep [ep] = ed;
+	}
+
+	if (ed->state & ED_URB_DEL) {
+		/* pending unlink request */
+		spin_unlock_irqrestore (&ohci->lock, flags);
+		return NULL;
+	}
+
+	if (ed->state == ED_NEW) {
+		ed->hwINFO = __constant_cpu_to_le32 (OHCI_ED_SKIP);
+  		/* dummy td; end of td list for ed */
+		td = td_alloc (ohci, SLAB_ATOMIC);
+ 		if (!td) {
+			/* out of memory */
+			spin_unlock_irqrestore (&ohci->lock, flags);
+			return NULL;
+		}
+		ed->hwTailP = cpu_to_le32 (td->td_dma);
+		ed->hwHeadP = ed->hwTailP;	
+		ed->state = ED_UNLINK;
+		ed->type = usb_pipetype (pipe);
+	}
+
+	ohci->dev [usb_pipedevice (pipe)] = udev;
+
+// FIXME:  don't do this if it's linked to the HC,
+// we might clobber data toggle or other state ...
+
+	ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
+			| usb_pipeendpoint (pipe) << 7
+			| (usb_pipeisoc (pipe)? 0x8000: 0)
+			| (usb_pipecontrol (pipe)
+				? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) 
+			| (udev->speed == USB_SPEED_LOW) << 13
+			| usb_maxpacket (udev, pipe, usb_pipeout (pipe))
+				<< 16);
+
+  	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
+  		ed->int_period = interval;
+  		ed->int_load = load;
+  	}
+
+	spin_unlock_irqrestore (&ohci->lock, flags);
+	return ed; 
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* request unlinking of an endpoint from an operational HC.
+ * put the ep on the rm_list and stop the bulk or ctrl list 
+ * real work is done at the next start frame (SF) hardware interrupt
+ */
+static void ed_unlink (struct usb_device *usb_dev, struct ed *ed)
+{    
+	unsigned int		frame;
+   	struct ohci_hcd		*ohci = hcd_to_ohci (usb_dev->bus->hcpriv);
+
+	/* already pending? */
+	if (ed->state & ED_URB_DEL)
+		return;
+	ed->state |= ED_URB_DEL;
+
+	ed->hwINFO |= __constant_cpu_to_le32 (OHCI_ED_SKIP);
+
+	switch (ed->type) {
+		case PIPE_CONTROL: /* stop control list */
+			ohci->hc_control &= ~OHCI_CTRL_CLE;
+			writel (ohci->hc_control,
+				&ohci->regs->control); 
+			break;
+		case PIPE_BULK: /* stop bulk list */
+			ohci->hc_control &= ~OHCI_CTRL_BLE;
+			writel (ohci->hc_control,
+				&ohci->regs->control); 
+			break;
+	}
+
+	frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
+	ed->ed_rm_list = ohci->ed_rm_list [frame];
+	ohci->ed_rm_list [frame] = ed;
+
+	/* enable SOF interrupt */
+	if (!ohci->sleeping) {
+		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+	}
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void
+td_fill (struct ohci_hcd *ohci, unsigned int info,
+	dma_addr_t data, int len,
+	struct urb *urb, int index)
+{
+	volatile struct td	*td, *td_pt;
+	urb_priv_t		*urb_priv = urb->hcpriv;
+
+	if (index >= urb_priv->length) {
+		err ("internal OHCI error: TD index > length");
+		return;
+	}
+
+	/* use this td as the next dummy */
+	td_pt = urb_priv->td [index];
+	td_pt->hwNextTD = 0;
+
+	/* fill the old dummy TD */
+	td = urb_priv->td [index] = dma_to_td (ohci,
+			le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
+
+	td->ed = urb_priv->ed;
+	td->next_dl_td = NULL;
+	td->index = index;
+	td->urb = urb; 
+	td->data_dma = data;
+	if (!len)
+		data = 0;
+
+	td->hwINFO = cpu_to_le32 (info);
+	if ((td->ed->type) == PIPE_ISOCHRONOUS) {
+		td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
+		td->ed->last_iso = info & 0xffff;
+	} else {
+		td->hwCBP = cpu_to_le32 (data); 
+	}			
+	if (data)
+		td->hwBE = cpu_to_le32 (data + len - 1);
+	else
+		td->hwBE = 0;
+	td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
+	td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+
+	/* append to queue */
+	td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare all TDs of a transfer */
+
+static void td_submit_urb (struct urb *urb)
+{ 
+	urb_priv_t	*urb_priv = urb->hcpriv;
+	struct ohci_hcd *ohci = hcd_to_ohci (urb->dev->bus->hcpriv);
+	dma_addr_t	data;
+	int		data_len = urb->transfer_buffer_length;
+	int		cnt = 0; 
+	__u32		info = 0;
+  	unsigned int	toggle = 0;
+
+	/* OHCI handles the DATA-toggles itself, we just use the
+	 * USB-toggle bits for resetting
+	 */
+  	if (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+			usb_pipeout (urb->pipe))) {
+  		toggle = TD_T_TOGGLE;
+	} else {
+  		toggle = TD_T_DATA0;
+		usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+			usb_pipeout (urb->pipe), 1);
+	}
+
+	urb_priv->td_cnt = 0;
+
+	if (data_len) {
+#ifdef CONFIG_PCI
+		data = pci_map_single (ohci->hcd.pdev,
+			urb->transfer_buffer, data_len,
+			usb_pipeout (urb->pipe)
+				? PCI_DMA_TODEVICE
+				: PCI_DMA_FROMDEVICE
+			);
+#else
+#	error "what dma addr to use"
+#endif
+	} else
+		data = 0;
+
+	switch (usb_pipetype (urb->pipe)) {
+		case PIPE_BULK:
+			info = usb_pipeout (urb->pipe)
+				? TD_CC | TD_DP_OUT
+				: TD_CC | TD_DP_IN ;
+			while (data_len > 4096) {		
+				td_fill (ohci,
+					info | (cnt? TD_T_TOGGLE:toggle),
+					data, 4096, urb, cnt);
+				data += 4096; data_len -= 4096; cnt++;
+			}
+			info = usb_pipeout (urb->pipe)?
+				TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle),
+				data, data_len, urb, cnt);
+			cnt++;
+			if ((urb->transfer_flags & USB_ZERO_PACKET)
+					&& cnt < urb_priv->length) {
+				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle),
+					0, 0, urb, cnt);
+				cnt++;
+			}
+			/* start bulk list */
+			if (!ohci->sleeping)
+				writel (OHCI_BLF, &ohci->regs->cmdstatus);
+			break;
+
+		case PIPE_INTERRUPT:
+			info = TD_CC | toggle;
+			info |= usb_pipeout (urb->pipe) 
+				?  TD_DP_OUT
+				:  TD_R | TD_DP_IN;
+			td_fill (ohci, info, data, data_len, urb, cnt++);
+			break;
+
+		case PIPE_CONTROL:
+			info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+			td_fill (ohci, info,
+#ifdef CONFIG_PCI
+				pci_map_single (ohci->hcd.pdev,
+					urb->setup_packet, 8,
+					PCI_DMA_TODEVICE),
+#else
+#	error "what dma addr to use"				
+#endif
+				8, urb, cnt++); 
+			if (data_len > 0) {  
+				info = TD_CC | TD_R | TD_T_DATA1;
+				info |= usb_pipeout (urb->pipe)
+				    ? TD_DP_OUT
+				    : TD_DP_IN;
+				/* NOTE:  mishandles transfers >8K, some >4K */
+				td_fill (ohci, info, data, data_len,
+						urb, cnt++);  
+			} 
+			info = usb_pipeout (urb->pipe)
+				? TD_CC | TD_DP_IN | TD_T_DATA1
+				: TD_CC | TD_DP_OUT | TD_T_DATA1;
+			td_fill (ohci, info, data, 0, urb, cnt++);
+			/* start control list */
+			if (!ohci->sleeping)
+				writel (OHCI_CLF, &ohci->regs->cmdstatus);
+			break;
+
+		case PIPE_ISOCHRONOUS:
+			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+				td_fill (ohci, TD_CC | TD_ISO
+					| ((urb->start_frame + cnt) & 0xffff), 
+				    data + urb->iso_frame_desc [cnt].offset, 
+				    urb->iso_frame_desc [cnt].length, urb, cnt); 
+			}
+			break;
+	} 
+	if (urb_priv->length != cnt) 
+		dbg ("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length (struct td *td)
+{
+	__u32		tdINFO, tdBE, tdCBP;
+ 	__u16		tdPSW;
+ 	struct urb	*urb = td->urb;
+ 	urb_priv_t	*urb_priv = urb->hcpriv;
+	int		dlen = 0;
+	int		cc = 0;
+
+	tdINFO = le32_to_cpup (&td->hwINFO);
+  	tdBE   = le32_to_cpup (&td->hwBE);
+  	tdCBP  = le32_to_cpup (&td->hwCBP);
+
+
+  	if (tdINFO & TD_ISO) {
+ 		tdPSW = le16_to_cpu (td->hwPSW [0]);
+ 		cc = (tdPSW >> 12) & 0xF;
+		if (cc < 0xE)  {
+			if (usb_pipeout (urb->pipe)) {
+				dlen = urb->iso_frame_desc [td->index].length;
+			} else {
+				dlen = tdPSW & 0x3ff;
+			}
+			urb->actual_length += dlen;
+			urb->iso_frame_desc [td->index].actual_length = dlen;
+			if (! (urb->transfer_flags & USB_DISABLE_SPD)
+					&& (cc == TD_DATAUNDERRUN))
+				cc = TD_CC_NOERROR;
+
+			urb->iso_frame_desc [td->index].status
+				= cc_to_error [cc];
+		}
+	} else { /* BULK, INT, CONTROL DATA */
+		if (! (usb_pipetype (urb->pipe) == PIPE_CONTROL && 
+				 ((td->index == 0)
+				 || (td->index == urb_priv->length - 1)))) {
+ 			if (tdBE != 0) {
+				urb->actual_length += (td->hwCBP == 0)
+					? (tdBE - td->data_dma + 1)
+					: (tdCBP - td->data_dma);
+			}
+  		}
+  	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we unreverse the hc-reversed done-list
+ */
+static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
+{
+	__u32		td_list_hc;
+	struct td	*td_rev = NULL;
+	struct td	*td_list = NULL;
+  	urb_priv_t	*urb_priv = NULL;
+  	unsigned long	flags;
+
+  	spin_lock_irqsave (&ohci->lock, flags);
+
+	td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
+	ohci->hcca->done_head = 0;
+
+	while (td_list_hc) {		
+		td_list = dma_to_td (ohci, td_list_hc);
+
+		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
+			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
+			dbg (" USB-error/status: %x : %p", 
+				TD_CC_GET (le32_to_cpup (&td_list->hwINFO)),
+				td_list);
+			if (td_list->ed->hwHeadP
+					& __constant_cpu_to_le32 (0x1)) {
+				if (urb_priv && ((td_list->index + 1)
+						< urb_priv->length)) {
+					td_list->ed->hwHeadP = 
+			    (urb_priv->td [urb_priv->length - 1]->hwNextTD
+				    & __constant_cpu_to_le32 (0xfffffff0))
+			    | (td_list->ed->hwHeadP
+				    & __constant_cpu_to_le32 (0x2));
+					urb_priv->td_cnt += urb_priv->length
+						- td_list->index - 1;
+				} else 
+					td_list->ed->hwHeadP &=
+					__constant_cpu_to_le32 (0xfffffff2);
+			}
+		}
+
+		td_list->next_dl_td = td_rev;	
+		td_rev = td_list;
+		td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;	
+	}	
+	spin_unlock_irqrestore (&ohci->lock, flags);
+	return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some pending requests to unlink 
+ * - some URBs/TDs if urb_priv->state == URB_DEL
+ */
+static void dl_del_list (struct ohci_hcd *ohci, unsigned int frame)
+{
+	unsigned long	flags;
+	struct ed	*ed;
+	__u32		edINFO;
+	__u32		tdINFO;
+	struct td	*td = NULL, *td_next = NULL,
+			*tdHeadP = NULL, *tdTailP;
+	__u32		*td_p;
+	int		ctrl = 0, bulk = 0;
+
+	spin_lock_irqsave (&ohci->lock, flags);
+
+	for (ed = ohci->ed_rm_list [frame]; ed != NULL; ed = ed->ed_rm_list) {
+
+		tdTailP = dma_to_td (ohci,
+			le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+		tdHeadP = dma_to_td (ohci,
+			le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+		edINFO = le32_to_cpup (&ed->hwINFO);
+		td_p = &ed->hwHeadP;
+
+		for (td = tdHeadP; td != tdTailP; td = td_next) {
+			struct urb *urb = td->urb;
+			urb_priv_t *urb_priv = td->urb->hcpriv;
+
+			td_next = dma_to_td (ohci,
+				le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
+			if ((urb_priv->state == URB_DEL)) {
+				tdINFO = le32_to_cpup (&td->hwINFO);
+				if (TD_CC_GET (tdINFO) < 0xE)
+					dl_transfer_length (td);
+				*td_p = td->hwNextTD | (*td_p
+					& __constant_cpu_to_le32 (0x3));
+
+				/* URB is done; clean up */
+				if (++ (urb_priv->td_cnt) == urb_priv->length)
+// FIXME:  we shouldn't hold ohci->lock here, else the
+// completion function can't talk to this hcd ...
+					finish_urb (ohci, urb);
+			} else {
+				td_p = &td->hwNextTD;
+			}
+		}
+
+		ed->state &= ~ED_URB_DEL;
+		tdHeadP = dma_to_td (ohci,
+			le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+
+		if (tdHeadP == tdTailP) {
+			if (ed->state == ED_OPER)
+				ep_unlink (ohci, ed);
+			td_free (ohci, tdTailP);
+			ed->hwINFO = __constant_cpu_to_le32 (OHCI_ED_SKIP);
+			ed->state = ED_NEW;
+		} else
+			ed->hwINFO &= ~__constant_cpu_to_le32 (OHCI_ED_SKIP);
+
+		switch (ed->type) {
+			case PIPE_CONTROL:
+				ctrl = 1;
+				break;
+			case PIPE_BULK:
+				bulk = 1;
+				break;
+		}
+   	}
+
+	/* maybe reenable control and bulk lists */ 
+	if (!ohci->disabled) {
+		if (ctrl) 	/* reset control list */
+			writel (0, &ohci->regs->ed_controlcurrent);
+		if (bulk)	/* reset bulk list */
+			writel (0, &ohci->regs->ed_bulkcurrent);
+		if (!ohci->ed_rm_list [!frame]) {
+			if (ohci->ed_controltail)
+				ohci->hc_control |= OHCI_CTRL_CLE;
+			if (ohci->ed_bulktail)
+				ohci->hc_control |= OHCI_CTRL_BLE;
+			writel (ohci->hc_control, &ohci->regs->control);   
+		}
+	}
+
+   	ohci->ed_rm_list [frame] = NULL;
+   	spin_unlock_irqrestore (&ohci->lock, flags);
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * process normal completions (error or success) and some unlinked eds
+ * this is the main path for handing urbs back to drivers
+ */
+static void dl_done_list (struct ohci_hcd *ohci, struct td *td_list)
+{
+  	struct td	*td_list_next = NULL;
+	struct ed	*ed;
+	int		cc = 0;
+	struct urb	*urb;
+	urb_priv_t	*urb_priv;
+ 	__u32		tdINFO, edHeadP, edTailP;
+
+ 	unsigned long flags;
+
+  	while (td_list) {
+   		td_list_next = td_list->next_dl_td;
+
+  		urb = td_list->urb;
+  		urb_priv = urb->hcpriv;
+  		tdINFO = le32_to_cpup (&td_list->hwINFO);
+
+   		ed = td_list->ed;
+
+   		dl_transfer_length (td_list);
+
+  		/* error code of transfer */
+  		cc = TD_CC_GET (tdINFO);
+  		if (cc == TD_CC_STALL)
+			usb_endpoint_halt (urb->dev,
+				usb_pipeendpoint (urb->pipe),
+				usb_pipeout (urb->pipe));
+
+  		if (! (urb->transfer_flags & USB_DISABLE_SPD)
+				&& (cc == TD_DATAUNDERRUN))
+			cc = TD_CC_NOERROR;
+
+  		if (++ (urb_priv->td_cnt) == urb_priv->length) {
+			/*
+			 * Except for periodic transfers, both branches do
+			 * the same thing.  Periodic urbs get reissued until
+			 * they're "deleted" with usb_unlink_urb.
+			 */
+			if ((ed->state & (ED_OPER | ED_UNLINK))
+					&& (urb_priv->state != URB_DEL)) {
+				spin_lock (&urb->lock);
+				if (urb->status == -EINPROGRESS)
+  					urb->status = cc_to_error [cc];
+				spin_unlock (&urb->lock);
+  				return_urb (ohci, urb);
+  			} else
+  				finish_urb (ohci, urb);
+  		}
+
+  		spin_lock_irqsave (&ohci->lock, flags);
+  		if (ed->state != ED_NEW) { 
+  			edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
+  			edTailP = le32_to_cpup (&ed->hwTailP);
+
+// FIXME:  ED_UNLINK is very fuzzy w.r.t. what the hc knows...
+
+			/* unlink eds if they are not busy */
+     			if ((edHeadP == edTailP) && (ed->state == ED_OPER)) 
+     				ep_unlink (ohci, ed);
+     		}	
+     		spin_unlock_irqrestore (&ohci->lock, flags);
+
+    		td_list = td_list_next;
+  	}  
+}
+
diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci.h linux/drivers/usb/hcd/ohci.h
--- linux-2.5.3-pre2/drivers/usb/hcd/ohci.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hcd/ohci.h	Mon Jan 21 17:23:23 2002
@@ -0,0 +1,360 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * This file is licenced under GPL
+ * $Id: ohci.h,v 1.5 2002/01/19 00:24:01 dbrownell Exp $
+ */
+ 
+static const int cc_to_error [16] = { 
+
+/* map OHCI status to errno values */ 
+	/* No  Error  */               0,
+	/* CRC Error  */               -EILSEQ,
+	/* Bit Stuff  */               -EPROTO,
+	/* Data Togg  */               -EILSEQ,
+	/* Stall      */               -EPIPE,
+	/* DevNotResp */               -ETIMEDOUT,
+	/* PIDCheck   */               -EPROTO,
+	/* UnExpPID   */               -EPROTO,
+	/* DataOver   */               -EOVERFLOW,
+	/* DataUnder  */               -EREMOTEIO,
+	/* (for hw)   */               -EIO,
+	/* (for hw)   */               -EIO,
+	/* BufferOver */               -ECOMM,
+	/* BuffUnder  */               -ENOSR,
+	/* (for HCD)  */               -EALREADY,
+	/* (for HCD)  */               -EALREADY 
+};
+
+
+/* ED States */
+
+#define ED_NEW 		0x00		/* unused, no dummy td */
+#define ED_UNLINK 	0x01		/* dummy td, maybe linked to hc */
+#define ED_OPER		0x02		/* dummy td, _is_ linked to hc */
+
+#define ED_URB_DEL  	0x08		/* masked in */
+
+/* usb_ohci_ed */
+struct ed {
+	/* first fields are hardware-specified */
+	__u32 hwINFO;       
+	__u32 hwTailP;
+	__u32 hwHeadP;
+	__u32 hwNextED;
+
+	struct ed * ed_prev;  
+	__u8 int_period;
+	__u8 int_branch;
+	__u8 int_load; 
+	__u8 int_interval;
+	__u8 state;			// ED_{NEW,UNLINK,OPER}
+	__u8 type; 
+	__u16 last_iso;
+	struct ed * ed_rm_list;
+
+	dma_addr_t dma;
+	__u32 unused [3];
+} __attribute((aligned(16)));
+
+ 
+/* TD info field */
+#define TD_CC       0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC       0x0C000000
+#define TD_T        0x03000000
+#define TD_T_DATA0  0x02000000
+#define TD_T_DATA1  0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R        0x00040000
+#define TD_DI       0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP       0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN    0x00100000
+#define TD_DP_OUT   0x00080000
+
+#define TD_ISO	    0x00010000
+#define TD_DEL      0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR      0x00
+#define TD_CC_CRC          0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL        0x04
+#define TD_DEVNOTRESP      0x05
+#define TD_PIDCHECKFAIL    0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN     0x08
+#define TD_DATAUNDERRUN    0x09
+    /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+    /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED     0x0F
+
+
+#define MAXPSW 1
+
+struct td {
+	/* first hardware fields are in all tds */
+	__u32		hwINFO;
+  	__u32		hwCBP;		/* Current Buffer Pointer */
+  	__u32		hwNextTD;	/* Next TD Pointer */
+  	__u32		hwBE;		/* Memory Buffer End Pointer */
+
+  	__u16		hwPSW [MAXPSW];	/* PSW is only for ISO */
+
+  	__u8		unused;
+  	__u8		index;
+  	struct ed	*ed;
+  	struct td	*next_dl_td;
+  	struct urb	*urb;
+
+	dma_addr_t	td_dma;
+	dma_addr_t	data_dma;
+	__u32		unused2 [2];
+} __attribute((aligned(32)));		/* iso needs 32 */
+
+#define OHCI_ED_SKIP	(1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. The host controller is
+ * told the base address of it.  It must be 256-byte aligned.
+ */
+#define NUM_INTS 32	/* part of the OHCI standard */
+struct ohci_hcca {
+	__u32	int_table [NUM_INTS];	/* Interrupt ED table */
+	__u16	frame_no;		/* current frame number */
+	__u16	pad1;			/* set to 0 on each frame_no change */
+	__u32	done_head;		/* info returned for an interrupt */
+	u8	reserved_for_hc [116];
+} __attribute((aligned(256)));
+
+  
+/*
+ * Maximum number of root hub ports.  
+ */
+#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region.  This is Memory Mapped I/O.  You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+	/* control and status registers */
+	__u32	revision;
+	__u32	control;
+	__u32	cmdstatus;
+	__u32	intrstatus;
+	__u32	intrenable;
+	__u32	intrdisable;
+
+	/* memory pointers */
+	__u32	hcca;
+	__u32	ed_periodcurrent;
+	__u32	ed_controlhead;
+	__u32	ed_controlcurrent;
+	__u32	ed_bulkhead;
+	__u32	ed_bulkcurrent;
+	__u32	donehead;
+
+	/* frame counters */
+	__u32	fminterval;
+	__u32	fmremaining;
+	__u32	fmnumber;
+	__u32	periodicstart;
+	__u32	lsthresh;
+
+	/* Root hub ports */
+	struct	ohci_roothub_regs {
+		__u32	a;
+		__u32	b;
+		__u32	status;
+		__u32	portstatus [MAX_ROOT_PORTS];
+	} roothub;
+
+	/* and some optional registers for legacy compatibility */
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */
+#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */
+#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */
+#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */
+#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */
+#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */
+#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */
+#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */
+#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#	define OHCI_USB_RESET	(0 << 6)
+#	define OHCI_USB_RESUME	(1 << 6)
+#	define OHCI_USB_OPER	(2 << 6)
+#	define OHCI_USB_SUSPEND	(3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR	(1 << 0)	/* host controller reset */
+#define OHCI_CLF  	(1 << 1)	/* control list filled */
+#define OHCI_BLF  	(1 << 2)	/* bulk list filled */
+#define OHCI_OCR  	(1 << 3)	/* ownership change request */
+#define OHCI_SOC  	(3 << 16)	/* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */
+#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */
+#define OHCI_INTR_SF	(1 << 2)	/* start frame */
+#define OHCI_INTR_RD	(1 << 3)	/* resume detect */
+#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */
+#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */
+#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */
+#define OHCI_INTR_OC	(1 << 30)	/* ownership change */
+#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+ 
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS            0x00000001   	/* current connect status */
+#define RH_PS_PES            0x00000002   	/* port enable status*/
+#define RH_PS_PSS            0x00000004   	/* port suspend status */
+#define RH_PS_POCI           0x00000008   	/* port over current indicator */
+#define RH_PS_PRS            0x00000010  	/* port reset status */
+#define RH_PS_PPS            0x00000100   	/* port power status */
+#define RH_PS_LSDA           0x00000200    	/* low speed device attached */
+#define RH_PS_CSC            0x00010000 	/* connect status change */
+#define RH_PS_PESC           0x00020000   	/* port enable status change */
+#define RH_PS_PSSC           0x00040000    	/* port suspend status change */
+#define RH_PS_OCIC           0x00080000    	/* over current indicator change */
+#define RH_PS_PRSC           0x00100000   	/* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS	     0x00000001		/* local power status */
+#define RH_HS_OCI	     0x00000002		/* over current indicator */
+#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */
+#define RH_HS_LPSC	     0x00010000		/* local power status change */
+#define RH_HS_OCIC	     0x00020000		/* over current indicator change */
+#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR		0x0000ffff		/* device removable flags */
+#define RH_B_PPCM	0xffff0000		/* port power control mask */
+
+/* roothub.a masks */
+#define	RH_A_NDP	(0xff << 0)		/* number of downstream ports */
+#define	RH_A_PSM	(1 << 8)		/* power switching mode */
+#define	RH_A_NPS	(1 << 9)		/* no power switching */
+#define	RH_A_DT		(1 << 10)		/* device type (mbz) */
+#define	RH_A_OCPM	(1 << 11)		/* over current protection mode */
+#define	RH_A_NOCP	(1 << 12)		/* no over current protection */
+#define	RH_A_POTPGT	(0xff << 24)		/* power on to power good time */
+
+
+/* urb */
+typedef struct urb_priv
+{
+	struct ed		*ed;
+	__u16			length;		// # tds in this request
+	__u16			td_cnt;		// tds already serviced
+	int   			state;
+	struct td		*td [0];	// all TDs in this request
+
+} urb_priv_t;
+
+#define URB_DEL 1
+
+
+/* Hash struct used for TD/ED hashing */
+struct hash_t {
+	void		*virt;
+	dma_addr_t	dma;
+	struct hash_t	*next; // chaining for collision cases
+};
+
+/* List of TD/ED hash entries */
+struct hash_list_t {
+	struct hash_t	*head;
+	struct hash_t	*tail;
+};
+
+#define TD_HASH_SIZE    64    /* power'o'two */
+#define ED_HASH_SIZE    64    /* power'o'two */
+
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
+#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
+
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+struct ohci_hcd {
+	spinlock_t		lock;
+
+	/*
+	 * I/O memory used to communicate with the HC (uncached);
+	 */
+	struct ohci_regs	*regs;
+
+	/*
+	 * main memory used to communicate with the HC (uncached)
+	 */
+	struct ohci_hcca	*hcca;
+	dma_addr_t		hcca_dma;
+
+	struct ed		*ed_rm_list [2];	/* to be removed */
+
+	struct ed		*ed_bulktail;		/* last in bulk list */
+	struct ed		*ed_controltail;	/* last in ctrl list */
+ 	struct ed		*ed_isotail;		/* last in iso list */
+
+#ifdef CONFIG_PCI
+	struct pci_pool		*td_cache;
+	struct pci_pool		*ed_cache;
+	struct hash_list_t	td_hash [TD_HASH_SIZE];
+	struct hash_list_t	ed_hash [ED_HASH_SIZE];
+#endif
+
+	/*
+	 * driver state
+	 */
+	int			disabled;	/* e.g. got a UE, we're hung */
+	int			sleeping;
+	int			ohci_int_load [NUM_INTS];
+	u32 			hc_control;	/* copy of hc control reg */
+	struct usb_device	*dev [128];
+
+	unsigned long		flags;		/* for HC bugs */
+#define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
+
+	/*
+	 * framework state
+	 */
+	struct usb_hcd		hcd;
+};
+
+#define hcd_to_ohci(hcd_ptr) list_entry(hcd_ptr, struct ohci_hcd, hcd)
+
diff -urN linux-2.5.3-pre2/drivers/usb/hcd.c linux/drivers/usb/hcd.c
--- linux-2.5.3-pre2/drivers/usb/hcd.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/hcd.c	Mon Jan 21 17:23:23 2002
@@ -606,7 +606,7 @@
 			return retval;
 		}
 	}
-	dev->driver_data = hcd;
+	pci_set_drvdata(dev, hcd);
 	hcd->driver = driver;
 	hcd->description = driver->description;
 	hcd->pdev = dev;
@@ -689,7 +689,7 @@
 	struct usb_hcd		*hcd;
 	struct usb_device	*hub;
 
-	hcd = (struct usb_hcd *) dev->driver_data;
+	hcd = pci_get_drvdata(dev);
 	if (!hcd)
 		return;
 	info ("remove: %s, state %x", hcd->bus_name, hcd->state);
@@ -769,7 +769,7 @@
 	struct usb_hcd		*hcd;
 	int			retval;
 
-	hcd = (struct usb_hcd *) dev->driver_data;
+	hcd = pci_get_drvdata(dev);
 	info ("suspend %s to state %d", hcd->bus_name, state);
 
 	pci_save_state (dev, hcd->pci_state);
@@ -798,7 +798,7 @@
 	struct usb_hcd		*hcd;
 	int			retval;
 
-	hcd = (struct usb_hcd *) dev->driver_data;
+	hcd = pci_get_drvdata(dev);
 	info ("resume %s", hcd->bus_name);
 
 	/* guard against multiple resumes (APM bug?) */
diff -urN linux-2.5.3-pre2/drivers/usb/ibmcam.h linux/drivers/usb/ibmcam.h
--- linux-2.5.3-pre2/drivers/usb/ibmcam.h	Mon Dec 11 13:17:55 2000
+++ linux/drivers/usb/ibmcam.h	Mon Jan 21 17:23:23 2002
@@ -147,7 +147,7 @@
 
 struct ibmcam_sbuf {
 	char *data;
-	urb_t *urb;
+	struct urb *urb;
 };
 
 struct ibmcam_frame {
diff -urN linux-2.5.3-pre2/drivers/usb/kaweth.c linux/drivers/usb/kaweth.c
--- linux-2.5.3-pre2/drivers/usb/kaweth.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/kaweth.c	Mon Jan 21 17:23:23 2002
@@ -953,7 +953,7 @@
 /*-------------------------------------------------------------------*
  * completion handler for compatibility wrappers (sync control/bulk) *
  *-------------------------------------------------------------------*/
-static void usb_api_blocking_completion(urb_t *urb)
+static void usb_api_blocking_completion(struct urb *urb)
 {
         struct usb_api_data *awd = (struct usb_api_data *)urb->context;
 
@@ -966,7 +966,7 @@
  *-------------------------------------------------------------------*/
 
 // Starts urb and waits for completion or timeout
-static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
 {
         DECLARE_WAITQUEUE(wait, current);
 	struct usb_api_data awd;
@@ -1017,7 +1017,7 @@
                             struct usb_ctrlrequest *cmd, void *data, int len,
 			    int timeout)
 {
-        urb_t *urb;
+        struct urb *urb;
         int retv;
         int length;
 
diff -urN linux-2.5.3-pre2/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.5.3-pre2/drivers/usb/ov511.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/ov511.c	Mon Jan 21 17:23:23 2002
@@ -3875,7 +3875,7 @@
  **********************************************************************/
 
 static int 
-ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
+ov511_move_data(struct usb_ov511 *ov511, struct urb *urb)
 {
 	unsigned char *cdata;
 	int data_size, num, offset, i, totlen = 0;
@@ -4108,7 +4108,7 @@
 }
 
 static int 
-ov518_move_data(struct usb_ov511 *ov511, urb_t *urb)
+ov518_move_data(struct usb_ov511 *ov511, struct urb *urb)
 {
 	unsigned char *cdata;
 	int i, data_size, totlen = 0;
@@ -4369,7 +4369,7 @@
 static int 
 ov511_init_isoc(struct usb_ov511 *ov511)
 {
-	urb_t *urb;
+	struct urb *urb;
 	int fx, err, n, size;
 
 	PDEBUG(3, "*** Initializing capture ***");
diff -urN linux-2.5.3-pre2/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux-2.5.3-pre2/drivers/usb/ov511.h	Thu Jan  3 18:52:28 2002
+++ linux/drivers/usb/ov511.h	Mon Jan 21 17:23:23 2002
@@ -366,7 +366,7 @@
 
 struct ov511_sbuf {
 	char *data;
-	urb_t *urb;
+	struct urb *urb;
 };
 
 enum {
diff -urN linux-2.5.3-pre2/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
--- linux-2.5.3-pre2/drivers/usb/pegasus.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/pegasus.c	Mon Jan 21 17:23:23 2002
@@ -94,7 +94,7 @@
 
 static int update_eth_regs_async( pegasus_t * );
 /* Aargh!!! I _really_ hate such tweaks */
-static void ctrl_callback( urb_t *urb )
+static void ctrl_callback( struct urb *urb )
 {
 	pegasus_t	*pegasus = urb->context;
 
diff -urN linux-2.5.3-pre2/drivers/usb/se401.c linux/drivers/usb/se401.c
--- linux-2.5.3-pre2/drivers/usb/se401.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/se401.c	Mon Jan 21 17:23:23 2002
@@ -610,7 +610,7 @@
 */
 static int se401_start_stream(struct usb_se401 *se401)
 {
-	urb_t *urb;
+	struct urb *urb;
 	int err=0, i;
 	se401->streaming=1;
 
diff -urN linux-2.5.3-pre2/drivers/usb/se401.h linux/drivers/usb/se401.h
--- linux-2.5.3-pre2/drivers/usb/se401.h	Fri Sep  7 10:59:04 2001
+++ linux/drivers/usb/se401.h	Mon Jan 21 17:23:23 2002
@@ -197,8 +197,8 @@
 
 	char *fbuf;		/* Videodev buffer area */
 
-	urb_t *urb[SE401_NUMSBUF];
-	urb_t *inturb;
+	struct urb *urb[SE401_NUMSBUF];
+	struct urb *inturb;
 	
 	int button;
 	int buttonpressed;
diff -urN linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.c linux/drivers/usb/serial/ftdi_sio.c
--- linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/serial/ftdi_sio.c	Mon Jan 21 17:23:23 2002
@@ -138,6 +138,7 @@
    
 static __devinitdata struct usb_device_id id_table_8U232AM [] = {
 	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
+	{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
 	{ }						/* Terminating entry */
 };
 
@@ -145,6 +146,7 @@
 static __devinitdata struct usb_device_id id_table_combined [] = {
 	{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
+	{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
 	{ }						/* Terminating entry */
 };
 
diff -urN linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.h linux/drivers/usb/serial/ftdi_sio.h
--- linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.h	Thu May 24 14:55:51 2001
+++ linux/drivers/usb/serial/ftdi_sio.h	Mon Jan 21 17:23:23 2002
@@ -22,6 +22,8 @@
 #define FTDI_VID	0x0403	/* Vendor Id */
 #define FTDI_SIO_PID	0x8372	/* Product Id SIO application of 8U100AX  */
 #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
+#define FTDI_NF_RIC_VID	0x0DCD	/* Vendor Id */
+#define FTDI_NF_RIC_PID	0x0001	/* Product Id */
 
 #define FTDI_SIO_RESET 		0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL 	1 /* Set the modem control register */
diff -urN linux-2.5.3-pre2/drivers/usb/serial/io_edgeport.c linux/drivers/usb/serial/io_edgeport.c
--- linux-2.5.3-pre2/drivers/usb/serial/io_edgeport.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/serial/io_edgeport.c	Mon Jan 21 17:23:23 2002
@@ -2456,7 +2456,7 @@
 {
 	struct edgeport_serial *edge_serial = (struct edgeport_serial *)edge_port->port->serial->private;
 	int status = 0;
-	urb_t *urb;
+	struct urb *urb;
 	int timeout;
 
 	usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer);
diff -urN linux-2.5.3-pre2/drivers/usb/serial/keyspan.c linux/drivers/usb/serial/keyspan.c
--- linux-2.5.3-pre2/drivers/usb/serial/keyspan.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/serial/keyspan.c	Mon Jan 21 17:23:23 2002
@@ -109,11 +109,11 @@
 
 	const keyspan_device_details	*device_details;
 
-	urb_t		*instat_urb;
+	struct urb	*instat_urb;
 	char		instat_buf[INSTAT_BUFLEN];
 
 	/* XXX this one probably will need a lock */
-	urb_t		*glocont_urb;
+	struct urb	*glocont_urb;
 	char		glocont_buf[GLOCONT_BUFLEN];
 };
 
@@ -128,18 +128,18 @@
 	const keyspan_device_details	*device_details;
 
 	/* Input endpoints and buffer for this port */
-	urb_t		*in_urbs[2];
+	struct urb	*in_urbs[2];
 	char		in_buffer[2][64];
 	/* Output endpoints and buffer for this port */
-	urb_t		*out_urbs[2];
+	struct urb	*out_urbs[2];
 	char		out_buffer[2][64];
 
 	/* Input ack endpoint */
-	urb_t		*inack_urb;
+	struct urb	*inack_urb;
 	char		inack_buffer[1];
 
 	/* Output control endpoint */
-	urb_t		*outcont_urb;
+	struct urb	*outcont_urb;
 	char		outcont_buffer[64];
 
 	/* Settings for the port */
@@ -324,7 +324,7 @@
 	const keyspan_device_details	*d_details;
 	int				flip;
 	int 				left, todo;
-	urb_t 				*this_urb;
+	struct urb			*this_urb;
 	int 				err;
 
 	p_priv = (struct keyspan_port_private *)(port->private);
@@ -853,7 +853,7 @@
 	struct usb_serial 		*serial = port->serial;
 	const keyspan_device_details	*d_details;
 	int				i, already_active, err;
-	urb_t *urb;
+	struct urb			*urb;
 
 	s_priv = (struct keyspan_serial_private *)(serial->private);
 	p_priv = (struct keyspan_port_private *)(port->private);
@@ -890,7 +890,7 @@
 	return (0);
 }
 
-static inline void stop_urb(urb_t *urb)
+static inline void stop_urb(struct urb *urb)
 {
 	if (urb && urb->status == -EINPROGRESS) {
 		urb->transfer_flags &= ~USB_ASYNC_UNLINK;
@@ -1041,11 +1041,11 @@
 }
 
 /* Helper functions used by keyspan_setup_urbs */
-static urb_t *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
-				int dir, void *ctx, char *buf, int len,
-				void (*callback)(urb_t *))
+static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
+				     int dir, void *ctx, char *buf, int len,
+				     void (*callback)(struct urb *))
 {
-	urb_t *urb;
+	struct urb *urb;
 
 	if (endpoint == -1)
 		return NULL;		/* endpoint not needed */
@@ -1066,12 +1066,12 @@
 }
 
 static struct callbacks {
-	void	(*instat_callback)(urb_t *);
-	void	(*glocont_callback)(urb_t *);
-	void	(*indat_callback)(urb_t *);
-	void	(*outdat_callback)(urb_t *);
-	void	(*inack_callback)(urb_t *);
-	void	(*outcont_callback)(urb_t *);
+	void	(*instat_callback)(struct urb *);
+	void	(*glocont_callback)(struct urb *);
+	void	(*indat_callback)(struct urb *);
+	void	(*outdat_callback)(struct urb *);
+	void	(*inack_callback)(struct urb *);
+	void	(*outcont_callback)(struct urb *);
 } keyspan_callbacks[] = {
 	{
 		/* msg_usa26 callbacks */
@@ -1295,7 +1295,7 @@
 	struct keyspan_port_private 		*p_priv;
 	const  keyspan_device_details		*d_details;
 	int 					outcont_urb;
-	urb_t *this_urb;
+	struct urb				*this_urb;
 	int err;
 
 	dbg ("%s reset=%d\n", __FUNCTION__, reset_port); 
@@ -1430,7 +1430,7 @@
 	struct keyspan_serial_private	 	*s_priv;
 	struct keyspan_port_private 		*p_priv;
 	const  keyspan_device_details		*d_details;
-	urb_t *this_urb;
+	struct urb				*this_urb;
 	int err;
 
 	s_priv = (struct keyspan_serial_private *)(serial->private);
@@ -1516,7 +1516,7 @@
 	struct keyspan_port_private 		*p_priv;
 	const  keyspan_device_details		*d_details;
 	int 					glocont_urb;
-	urb_t 					*this_urb;
+	struct urb				*this_urb;
 	int 					err;
 	int					device_port;
 
diff -urN linux-2.5.3-pre2/drivers/usb/storage/transport.c linux/drivers/usb/storage/transport.c
--- linux-2.5.3-pre2/drivers/usb/storage/transport.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/storage/transport.c	Mon Jan 21 17:23:23 2002
@@ -369,7 +369,7 @@
 /* This is the completion handler which will wake us up when an URB
  * completes.
  */
-static void usb_stor_blocking_completion(urb_t *urb)
+static void usb_stor_blocking_completion(struct urb *urb)
 {
 	struct completion *urb_done_ptr = (struct completion *)urb->context;
 
diff -urN linux-2.5.3-pre2/drivers/usb/stv680.c linux/drivers/usb/stv680.c
--- linux-2.5.3-pre2/drivers/usb/stv680.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/stv680.c	Mon Jan 21 17:23:23 2002
@@ -766,7 +766,7 @@
 
 static int stv680_start_stream (struct usb_stv *stv680)
 {
-	urb_t *urb;
+	struct urb *urb;
 	int err = 0, i;
 
 	stv680->streaming = 1;
diff -urN linux-2.5.3-pre2/drivers/usb/stv680.h linux/drivers/usb/stv680.h
--- linux-2.5.3-pre2/drivers/usb/stv680.h	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/stv680.h	Mon Jan 21 17:23:23 2002
@@ -118,7 +118,7 @@
 	int removed;		/* device disconnected */
 	int streaming;		/* Are we streaming video? */
 	char *fbuf;		/* Videodev buffer area */
-	urb_t *urb[STV680_NUMSBUF];	/* # of queued bulk transfers */
+	struct urb *urb[STV680_NUMSBUF];	/* # of queued bulk transfers */
 	int curframe;		/* Current receiving frame */
 	struct stv680_frame frame[STV680_NUMFRAMES];	/* # frames supported by v4l part */
 	int readcount;
diff -urN linux-2.5.3-pre2/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- linux-2.5.3-pre2/drivers/usb/uhci.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/uhci.c	Mon Jan 21 17:23:23 2002
@@ -520,7 +520,8 @@
 
 	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
 
-	uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1);
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
+		uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));
 
 	/* All qh's in the queue need to link to the next queue */
 	urbp->qh->link = eurbp->qh->link;
@@ -556,6 +557,7 @@
 
 	/* Fix up the toggle for the next URB's */
 	if (!urbp->queued)
+		/* We set the toggle when we unlink */
 		toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 	else {
 		/* If we're in the middle of the queue, grab the toggle */
@@ -1683,8 +1685,8 @@
 
 		/* Control and Isochronous ignore the toggle, so this */
 		/* is safe for all types */
-		if (!(td->status & TD_CTRL_ACTIVE) &&
-		    (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
+		if ((!(td->status & TD_CTRL_ACTIVE) &&
+		    (uhci_actual_length(td->status) < uhci_expected_length(td->info)) ||
 		    tmp == head)) {
 			usb_settoggle(urb->dev, uhci_endpoint(td->info),
 				uhci_packetout(td->info),
diff -urN linux-2.5.3-pre2/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- linux-2.5.3-pre2/drivers/usb/usb-ohci.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/usb-ohci.c	Mon Jan 21 17:23:23 2002
@@ -173,7 +173,7 @@
 	kfree (urb_priv);
 }
  
-static void urb_rm_priv_locked (urb_t * urb) 
+static void urb_rm_priv_locked (struct urb * urb) 
 {
 	urb_priv_t * urb_priv = urb->hcpriv;
 	
@@ -207,7 +207,7 @@
 	}
 }
 
-static void urb_rm_priv (urb_t * urb)
+static void urb_rm_priv (struct urb * urb)
 {
 	unsigned long flags;
 
@@ -224,7 +224,7 @@
 /* debug| print the main components of an URB     
  * small: 0) header + data packets 1) just header */
  
-static void urb_print (urb_t * urb, char * str, int small)
+static void urb_print (struct urb * urb, char * str, int small)
 {
 	unsigned int pipe= urb->pipe;
 	
@@ -453,10 +453,10 @@
 
 /* return a request to the completion handler */
  
-static int sohci_return_urb (struct ohci *hc, urb_t * urb)
+static int sohci_return_urb (struct ohci *hc, struct urb * urb)
 {
 	urb_priv_t * urb_priv = urb->hcpriv;
-	urb_t * urbt;
+	struct urb * urbt;
 	unsigned long flags;
 	int i;
 	
@@ -532,7 +532,7 @@
 
 /* get a transfer request */
  
-static int sohci_submit_urb (urb_t * urb)
+static int sohci_submit_urb (struct urb * urb)
 {
 	ohci_t * ohci;
 	ed_t * ed;
@@ -716,7 +716,7 @@
 /* deactivate all TDs and remove the private part of the URB */
 /* interrupt callers must use async unlink mode */
 
-static int sohci_unlink_urb (urb_t * urb)
+static int sohci_unlink_urb (struct urb * urb)
 {
 	unsigned long flags;
 	ohci_t * ohci;
@@ -1295,7 +1295,7 @@
 static void
 td_fill (ohci_t * ohci, unsigned int info,
 	dma_addr_t data, int len,
-	urb_t * urb, int index)
+	struct urb * urb, int index)
 {
 	volatile td_t  * td, * td_pt;
 	urb_priv_t * urb_priv = urb->hcpriv;
@@ -1343,7 +1343,7 @@
  
 /* prepare all TDs of a transfer */
 
-static void td_submit_urb (urb_t * urb)
+static void td_submit_urb (struct urb * urb)
 { 
 	urb_priv_t * urb_priv = urb->hcpriv;
 	ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
@@ -1452,7 +1452,7 @@
 {
 	__u32 tdINFO, tdBE, tdCBP;
  	__u16 tdPSW;
- 	urb_t * urb = td->urb;
+ 	struct urb * urb = td->urb;
  	urb_priv_t * urb_priv = urb->hcpriv;
 	int dlen = 0;
 	int cc = 0;
@@ -1493,7 +1493,7 @@
 
 /* handle an urb that is being unlinked */
 
-static void dl_del_urb (urb_t * urb)
+static void dl_del_urb (struct urb * urb)
 {
 	wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
 
@@ -1582,7 +1582,7 @@
 		td_p = &ed->hwHeadP;
 
 		for (td = tdHeadP; td != tdTailP; td = td_next) { 
-			urb_t * urb = td->urb;
+			struct urb * urb = td->urb;
 			urb_priv_t * urb_priv = td->urb->hcpriv;
 			
 			td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
@@ -1670,7 +1670,7 @@
   	td_t * td_list_next = NULL;
 	ed_t * ed;
 	int cc = 0;
-	urb_t * urb;
+	struct urb * urb;
 	urb_priv_t * urb_priv;
  	__u32 tdINFO, edHeadP, edTailP;
  	
@@ -1846,7 +1846,7 @@
 {
 	int len; 
 
-	urb_t * urb = (urb_t *) ptr;
+	struct urb * urb = (struct urb *) ptr;
 	ohci_t * ohci = urb->dev->bus->hcpriv;
 
 	if (ohci->disabled)
@@ -1875,7 +1875,7 @@
 
 /* Root Hub INTs are polled by this timer */
 
-static int rh_init_int_timer (urb_t * urb) 
+static int rh_init_int_timer (struct urb * urb) 
 {
 	ohci_t * ohci = urb->dev->bus->hcpriv;
 
@@ -1900,7 +1900,7 @@
 
 /* request to virtual root hub */
 
-static int rh_submit_urb (urb_t * urb)
+static int rh_submit_urb (struct urb * urb)
 {
 	struct usb_device * usb_dev = urb->dev;
 	ohci_t * ohci = usb_dev->bus->hcpriv;
@@ -2106,7 +2106,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static int rh_unlink_urb (urb_t * urb)
+static int rh_unlink_urb (struct urb * urb)
 {
 	ohci_t * ohci = urb->dev->bus->hcpriv;
  
diff -urN linux-2.5.3-pre2/drivers/usb/usb-ohci.h linux/drivers/usb/usb-ohci.h
--- linux-2.5.3-pre2/drivers/usb/usb-ohci.h	Sat Dec  8 20:28:45 2001
+++ linux/drivers/usb/usb-ohci.h	Mon Jan 21 17:23:23 2002
@@ -111,7 +111,7 @@
   	__u8 index;
   	struct ed * ed;
   	struct td * next_dl_td;
-  	urb_t * urb;
+  	struct urb * urb;
 
 	dma_addr_t td_dma;
 	dma_addr_t data_dma;
@@ -430,12 +430,12 @@
 static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags);
 static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
 /* td */
-static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, urb_t * urb, int index);
-static void td_submit_urb(urb_t * urb);
+static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, struct urb * urb, int index);
+static void td_submit_urb(struct urb * urb);
 /* root hub */
-static int rh_submit_urb(urb_t * urb);
-static int rh_unlink_urb(urb_t * urb);
-static int rh_init_int_timer(urb_t * urb);
+static int rh_submit_urb(struct urb * urb);
+static int rh_unlink_urb(struct urb * urb);
+static int rh_init_int_timer(struct urb * urb);
 
 /*-------------------------------------------------------------------------*/
 
diff -urN linux-2.5.3-pre2/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- linux-2.5.3-pre2/drivers/usb/usb-uhci.c	Sun Jan  6 18:49:21 2002
+++ linux/drivers/usb/usb-uhci.c	Mon Jan 21 17:23:23 2002
@@ -113,12 +113,12 @@
 // Suppress HC interrupt error messages for 5s
 #define ERROR_SUPPRESSION_TIME (HZ*5)
 
-_static int rh_submit_urb (urb_t *urb);
-_static int rh_unlink_urb (urb_t *urb);
+_static int rh_submit_urb (struct urb *urb);
+_static int rh_unlink_urb (struct urb *urb);
 _static int delete_qh (uhci_t *s, uhci_desc_t *qh);
-_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
-_static int process_interrupt (uhci_t *s, urb_t *urb);
-_static int process_iso (uhci_t *s, urb_t *urb, int force);
+_static int process_transfer (uhci_t *s, struct urb *urb, int mode);
+_static int process_interrupt (uhci_t *s, struct urb *urb);
+_static int process_iso (uhci_t *s, struct urb *urb, int force);
 
 // How much URBs with ->next are walked
 #define MAX_NEXT_COUNT 2048
@@ -164,7 +164,7 @@
 }
 /*-------------------------------------------------------------------*/
 #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-_static void enable_desc_loop(uhci_t *s, urb_t *urb)
+_static void enable_desc_loop(uhci_t *s, struct urb *urb)
 {
 	int flags;
 
@@ -179,7 +179,7 @@
 	spin_unlock_irqrestore (&s->qh_lock, flags);
 }
 /*-------------------------------------------------------------------*/
-_static void disable_desc_loop(uhci_t *s, urb_t *urb)
+_static void disable_desc_loop(uhci_t *s, struct urb *urb)
 {
 	int flags;
 
@@ -200,7 +200,7 @@
 }
 #endif
 /*-------------------------------------------------------------------*/
-_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, struct urb *urb)
 {
 	struct list_head *p=&urb->urb_list;
 #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
@@ -220,7 +220,7 @@
 	uhci_switch_timer_int(s);
 }
 /*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb (uhci_t *s, struct urb *urb)
 {
 	unsigned long flags=0;
 
@@ -229,7 +229,7 @@
 	spin_unlock_irqrestore (&s->urb_list_lock, flags);
 }
 /*-------------------------------------------------------------------*/
-_static void dequeue_urb (uhci_t *s, urb_t *urb)
+_static void dequeue_urb (uhci_t *s, struct urb *urb)
 {
 #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
 	int type;
@@ -694,7 +694,7 @@
 //                         LOW LEVEL STUFF
 //          assembles QHs und TDs for control, bulk and iso
 /*-------------------------------------------------------------------*/
-_static int uhci_submit_control_urb (urb_t *urb)
+_static int uhci_submit_control_urb (struct urb *urb)
 {
 	uhci_desc_t *qh, *td;
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
@@ -811,7 +811,7 @@
 // For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
 // Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
 
-_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
+_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb)
 {
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
 	urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
@@ -973,7 +973,7 @@
  looks a bit complicated because of all the bulk queueing goodies
 */
 
-_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode)
 {
 	uhci_desc_t *bqh, *nqh, *prevqh, *prevtd;
 	int now;
@@ -1027,7 +1027,7 @@
 		       urb, priv->prev_queued_urb,  priv->next_queued_urb, qh, bqh, priv->next_qh);	
        	
 		if (mode != CLEAN_TRANSFER_DELETION_MARK) {	// no work for cleanup at unlink-completion
-			urb_t *nurb;
+			struct urb *nurb;
 			unsigned long flags;
 
 			nurb = priv->next_queued_urb;
@@ -1065,7 +1065,7 @@
 }
 /*-------------------------------------------------------------------*/
 // Release bandwidth for Interrupt or Isoc. transfers 
-_static void uhci_release_bandwidth(urb_t *urb)
+_static void uhci_release_bandwidth(struct urb *urb)
 {       
 	if (urb->bandwidth) {
 		switch (usb_pipetype(urb->pipe)) {
@@ -1081,7 +1081,7 @@
 	}	
 }
 
-_static void uhci_urb_dma_sync(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv)
+_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
 {
 	if (urb_priv->setup_packet_dma)
 		pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma,
@@ -1095,7 +1095,7 @@
 				    PCI_DMA_TODEVICE);
 }
 
-_static void uhci_urb_dma_unmap(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv)
+_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
 {
 	if (urb_priv->setup_packet_dma) {
 		pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma,
@@ -1116,7 +1116,7 @@
    mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list
          UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list
 */
-_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb, int mode)
+_static int uhci_unlink_urb_async (uhci_t *s, struct urb *urb, int mode)
 {
 	uhci_desc_t *qh;
 	urb_priv_t *urb_priv;
@@ -1161,7 +1161,7 @@
 }
 /*-------------------------------------------------------------------*/
 // kills an urb by unlinking descriptors and waiting for at least one frame
-_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb)
 {
 	uhci_desc_t *qh;
 	urb_priv_t *urb_priv;
@@ -1224,7 +1224,7 @@
 _static void uhci_cleanup_unlink(uhci_t *s, int force)
 {
 	struct list_head *q;
-	urb_t *urb;
+	struct urb *urb;
 	struct usb_device *dev;
 	int now, type;
 	urb_priv_t *urb_priv;
@@ -1234,7 +1234,7 @@
 
 	while (q != &s->urb_unlinked) {
 
-		urb = list_entry (q, urb_t, urb_list);
+		urb = list_entry (q, struct urb, urb_list);
 
 		urb_priv = (urb_priv_t*)urb->hcpriv;
 		q = urb->urb_list.next;
@@ -1303,7 +1303,7 @@
 }
  
 /*-------------------------------------------------------------------*/
-_static int uhci_unlink_urb (urb_t *urb)
+_static int uhci_unlink_urb (struct urb *urb)
 {
 	uhci_t *s;
 	unsigned long flags=0;
@@ -1336,9 +1336,9 @@
 // In case of ASAP iso transfer, search the URB-list for already queued URBs
 // for this EP and calculate the earliest start frame for the new
 // URB (easy seamless URB continuation!)
-_static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
+_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end)
 {
-	urb_t *u, *last_urb = NULL;
+	struct urb *u, *last_urb = NULL;
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
 	struct list_head *p;
 	int ret=-1;
@@ -1348,7 +1348,7 @@
 	p=s->urb_list.prev;
 
 	for (; p != &s->urb_list; p = p->prev) {
-		u = list_entry (p, urb_t, urb_list);
+		u = list_entry (p, struct urb, urb_list);
 		// look for pending URBs with identical pipe handle
 		// works only because iso doesn't toggle the data bit!
 		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
@@ -1370,7 +1370,7 @@
 /*-------------------------------------------------------------------*/
 // adjust start_frame according to scheduling constraints (ASAP etc)
 
-_static int iso_find_start (urb_t *urb)
+_static int iso_find_start (struct urb *urb)
 {
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
 	unsigned int now;
@@ -1428,7 +1428,7 @@
 // ASAP-flag set implicitely
 // if period==0, the transfer is only done once
 
-_static int uhci_submit_int_urb (urb_t *urb)
+_static int uhci_submit_int_urb (struct urb *urb)
 {
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
 	urb_priv_t *urb_priv = urb->hcpriv;
@@ -1488,7 +1488,7 @@
 	return 0;
 }
 /*-------------------------------------------------------------------*/
-_static int uhci_submit_iso_urb (urb_t *urb)
+_static int uhci_submit_iso_urb (struct urb *urb)
 {
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
 	urb_priv_t *urb_priv = urb->hcpriv;
@@ -1581,10 +1581,10 @@
 /*-------------------------------------------------------------------*/
 // returns: 0 (no transfer queued), urb* (this urb already queued)
  
-_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
+_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb)
 {
 	struct list_head *p;
-	urb_t *tmp;
+	struct urb *tmp;
 	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
 
 	dbg("search_dev_ep:");
@@ -1592,7 +1592,7 @@
 	p=s->urb_list.next;
 
 	for (; p != &s->urb_list; p = p->next) {
-		tmp = list_entry (p, urb_t, urb_list);
+		tmp = list_entry (p, struct urb, urb_list);
 		dbg("urb: %p", tmp);
 		// we can accept this urb if it is not queued at this time 
 		// or if non-iso transfer requests should be scheduled for the same device and pipe
@@ -1605,13 +1605,13 @@
 	return 0;
 }
 /*-------------------------------------------------------------------*/
-_static int uhci_submit_urb (urb_t *urb)
+_static int uhci_submit_urb (struct urb *urb)
 {
 	uhci_t *s;
 	urb_priv_t *urb_priv;
 	int ret = 0, type;
 	unsigned long flags;
-	urb_t *queued_urb=NULL;
+	struct urb *queued_urb=NULL;
 	int bustime;
 		
 	if (!urb->dev || !urb->dev->bus)
@@ -1765,7 +1765,7 @@
 _static void uhci_check_timeouts(uhci_t *s)
 {
 	struct list_head *p,*p2;
-	urb_t *urb;
+	struct urb *urb;
 	int type;	
 
 	p = s->urb_list.prev;	
@@ -1775,7 +1775,7 @@
 
 		p2 = p;
 		p = p->prev;
-		urb = list_entry (p2, urb_t, urb_list);
+		urb = list_entry (p2, struct urb, urb_list);
 		type = usb_pipetype (urb->pipe);
 
 		hcpriv = (urb_priv_t*)urb->hcpriv;
@@ -1875,7 +1875,7 @@
 
 /*-------------------------------------------------------------------------*/
 /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-_static int rh_send_irq (urb_t *urb)
+_static int rh_send_irq (struct urb *urb)
 {
 	int len = 1;
 	int i;
@@ -1902,12 +1902,12 @@
 
 /*-------------------------------------------------------------------------*/
 /* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
-_static int rh_init_int_timer (urb_t *urb);
+_static int rh_init_int_timer (struct urb *urb);
 
 _static void rh_int_timer_do (unsigned long ptr)
 {
 	int len;
-	urb_t *urb = (urb_t*) ptr;
+	struct urb *urb = (struct urb *) ptr;
 	uhci_t *uhci = urb->dev->bus->hcpriv;
 
 	if (uhci->rh.send) {
@@ -1924,7 +1924,7 @@
 /*-------------------------------------------------------------------------*/
 /* Root Hub INTs are polled by this timer, polling interval 20ms */
 
-_static int rh_init_int_timer (urb_t *urb)
+_static int rh_init_int_timer (struct urb *urb)
 {
 	uhci_t *uhci = urb->dev->bus->hcpriv;
 
@@ -1958,7 +1958,7 @@
  *************************/
 
 
-_static int rh_submit_urb (urb_t *urb)
+_static int rh_submit_urb (struct urb *urb)
 {
 	struct usb_device *usb_dev = urb->dev;
 	uhci_t *uhci = usb_dev->bus->hcpriv;
@@ -2159,7 +2159,7 @@
 }
 /*-------------------------------------------------------------------------*/
 
-_static int rh_unlink_urb (urb_t *urb)
+_static int rh_unlink_urb (struct urb *urb)
 {
 	uhci_t *uhci = urb->dev->bus->hcpriv;
 
@@ -2217,14 +2217,14 @@
 	unsigned long flags;
 	struct list_head *p;
 	struct list_head *p2;
-	urb_t *urb;
+	struct urb *urb;
 
 	spin_lock_irqsave (&s->urb_list_lock, flags);
 	p = s->urb_list.prev;	
 	while (p != &s->urb_list) {
 		p2 = p;
 		p = p->prev ;
-		urb = list_entry (p2, urb_t, urb_list);
+		urb = list_entry (p2, struct urb, urb_list);
 		dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
 		
 		//urb->transfer_flags |=USB_ASYNC_UNLINK; 
@@ -2274,7 +2274,7 @@
 	uhci_unlink_urb
 };
 
-_static void correct_data_toggles(urb_t *urb)
+_static void correct_data_toggles(struct urb *urb)
 {
 	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), 
 		       !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)));
@@ -2304,7 +2304,7 @@
  *       PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb)
  */
 
-_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
+_static int process_transfer (uhci_t *s, struct urb *urb, int mode)
 {
 	int ret = 0;
 	urb_priv_t *urb_priv = urb->hcpriv;
@@ -2392,7 +2392,7 @@
 	if (usb_pipetype (urb->pipe) == PIPE_BULK ) {  /* toggle correction for short bulk transfers (nonqueued/queued) */
 
 		urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
-		urb_t *next_queued_urb=priv->next_queued_urb;
+		struct urb *next_queued_urb=priv->next_queued_urb;
 
 		if (next_queued_urb) {
 			urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv;
@@ -2423,7 +2423,7 @@
 	return ret;
 }
 
-_static int process_interrupt (uhci_t *s, urb_t *urb)
+_static int process_interrupt (uhci_t *s, struct urb *urb)
 {
 	int i, ret = -EINPROGRESS;
 	urb_priv_t *urb_priv = urb->hcpriv;
@@ -2520,7 +2520,7 @@
 // mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs
 // mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked)
 
-_static int process_iso (uhci_t *s, urb_t *urb, int mode)
+_static int process_iso (uhci_t *s, struct urb *urb, int mode)
 {
 	int i;
 	int ret = 0;
@@ -2589,9 +2589,9 @@
 _static int process_urb (uhci_t *s, struct list_head *p)
 {
 	int ret = 0;
-	urb_t *urb;
+	struct urb *urb;
 
-	urb=list_entry (p, urb_t, urb_list);
+	urb=list_entry (p, struct urb, urb_list);
 	//dbg("process_urb: found queued urb: %p", urb);
 
 	switch (usb_pipetype (urb->pipe)) {
@@ -2640,7 +2640,7 @@
 #endif
 
 		if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {  // process_interrupt does completion on its own		
-			urb_t *next_urb = urb->next;
+			struct urb *next_urb = urb->next;
 			int is_ring = 0;
 			int contains_killed = 0;
 			int loop_count=0;
diff -urN linux-2.5.3-pre2/drivers/usb/usb-uhci.h linux/drivers/usb/usb-uhci.h
--- linux-2.5.3-pre2/drivers/usb/usb-uhci.h	Tue Aug 28 11:21:02 2001
+++ linux/drivers/usb/usb-uhci.h	Mon Jan 21 17:23:23 2002
@@ -158,8 +158,8 @@
 	dma_addr_t setup_packet_dma;
 	dma_addr_t transfer_buffer_dma;
 	unsigned long started;
-	urb_t *next_queued_urb;         // next queued urb for this EP
-	urb_t *prev_queued_urb;
+	struct urb *next_queued_urb;	// next queued urb for this EP
+	struct urb *prev_queued_urb;
 	uhci_desc_t *bottom_qh;
 	uhci_desc_t *next_qh;       	// next helper QH
 	char use_loop;
diff -urN linux-2.5.3-pre2/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-2.5.3-pre2/drivers/usb/usb.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/usb.c	Mon Jan 21 17:23:23 2002
@@ -1084,12 +1084,13 @@
  *
  *	The driver should call usb_free_urb() when it is finished with the urb.
  */
-urb_t *usb_alloc_urb(int iso_packets)
+struct urb *usb_alloc_urb(int iso_packets)
 {
-	urb_t *urb;
+	struct urb *urb;
 
-	urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t),
-	      in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	urb = (struct urb *)kmalloc(sizeof(struct urb) + 
+		iso_packets * sizeof(struct usb_iso_packet_descriptor),
+		in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
 	if (!urb) {
 		err("alloc_urb: kmalloc failed");
 		return NULL;
@@ -1110,7 +1111,7 @@
  *	cleaned up with a call to usb_free_urb() when the driver is finished
  *	with it.
  */
-void usb_free_urb(urb_t* urb)
+void usb_free_urb(struct urb *urb)
 {
 	if (urb)
 		kfree(urb);
@@ -1171,7 +1172,7 @@
  * the periodic request, and bandwidth reservation is being done for
  * this controller, submitting such a periodic request will fail. 
  */
-int usb_submit_urb(urb_t *urb)
+int usb_submit_urb(struct urb *urb)
 {
 	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
 		return urb->dev->bus->op->submit_urb(urb);
@@ -1205,7 +1206,7 @@
  * and the completion function will see status -ECONNRESET.  Failure is
  * indicated by any other return value.
  */
-int usb_unlink_urb(urb_t *urb)
+int usb_unlink_urb(struct urb *urb)
 {
 	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
 		return urb->dev->bus->op->unlink_urb(urb);
@@ -1221,7 +1222,7 @@
 	int done;
 };
 
-static void usb_api_blocking_completion(urb_t *urb)
+static void usb_api_blocking_completion(struct urb *urb)
 {
 	struct usb_api_data *awd = (struct usb_api_data *)urb->context;
 
@@ -1231,7 +1232,7 @@
 }
 
 // Starts urb and waits for completion or timeout
-static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
 { 
 	DECLARE_WAITQUEUE(wait, current);
 	struct usb_api_data awd;
@@ -1289,7 +1290,7 @@
 int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, 
 			    struct usb_ctrlrequest *cmd,  void *data, int len, int timeout)
 {
-	urb_t *urb;
+	struct urb *urb;
 	int retv;
 	int length;
 
@@ -1376,7 +1377,7 @@
 int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, 
 			void *data, int len, int *actual_length, int timeout)
 {
-	urb_t *urb;
+	struct urb *urb;
 
 	if (len < 0)
 		return -EINVAL;
diff -urN linux-2.5.3-pre2/drivers/usb/usbvideo.c linux/drivers/usb/usbvideo.c
--- linux-2.5.3-pre2/drivers/usb/usbvideo.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/usbvideo.c	Mon Jan 21 17:23:23 2002
@@ -1776,7 +1776,7 @@
 /*
  * Make all of the blocks of data contiguous
  */
-static int usbvideo_CompressIsochronous(uvd_t *uvd, urb_t *urb)
+static int usbvideo_CompressIsochronous(uvd_t *uvd, struct urb *urb)
 {
 	char *cdata;
 	int i, totlen = 0;
@@ -1891,7 +1891,7 @@
 	/* We double buffer the Iso lists */
 	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
 		int j, k;
-		urb_t *urb = uvd->sbuf[i].urb;
+		struct urb *urb = uvd->sbuf[i].urb;
 		urb->dev = dev;
 		urb->context = uvd;
 		urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
diff -urN linux-2.5.3-pre2/drivers/usb/usbvideo.h linux/drivers/usb/usbvideo.h
--- linux-2.5.3-pre2/drivers/usb/usbvideo.h	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/usbvideo.h	Mon Jan 21 17:23:23 2002
@@ -165,7 +165,7 @@
 /* This structure represents one Isoc request - URB and buffer */
 typedef struct {
 	char *data;
-	urb_t *urb;
+	struct urb *urb;
 } usbvideo_sbuf_t;
 
 typedef struct {
diff -urN linux-2.5.3-pre2/drivers/usb/vicam.c linux/drivers/usb/vicam.c
--- linux-2.5.3-pre2/drivers/usb/vicam.c	Mon Jan 21 17:22:37 2002
+++ linux/drivers/usb/vicam.c	Mon Jan 21 17:23:23 2002
@@ -468,29 +468,23 @@
 	int err = 0;
 	
 	dbg("vicam_v4l_open");
-
-	MOD_INC_USE_COUNT; 
+ 
 	down(&vicam->sem);
 
-	if (vicam->open_count)		/* Maybe not needed? */
-		err = -EBUSY;
+	vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES);
+	if (!vicam->fbuf)
+		err=-ENOMEM;
 	else {
-		vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES);
-		if (!vicam->fbuf)
-			err=-ENOMEM;
-		else {
-			vicam->open_count = 1;
-		}
+		vicam->open_count = 1;
+	}
 #ifdef BLINKING
-		vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
-		info ("led on");
-		vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+	info ("led on");
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
 #endif
-	}
 
 	up(&vicam->sem);
-	if (err)
-		MOD_DEC_USE_COUNT;
+	
 	return err;
 }
 
@@ -515,7 +509,6 @@
 	up(&vicam->sem);
 	/* Why does se401.c have a usbdevice check here? */
 	/* If device is unplugged while open, I guess we only may unregister now */
-	MOD_DEC_USE_COUNT;
 }
 
 static long vicam_v4l_read(struct video_device *vdev, char *user_buf, unsigned long buflen, int noblock)
@@ -717,6 +710,7 @@
 
 /* FIXME - vicam_template - important */
 static struct video_device vicam_template = {
+	owner:		THIS_MODULE,
 	name:		"vicam USB camera",
 	type:		VID_TYPE_CAPTURE,
 	hardware:	VID_HARDWARE_SE401, /* need to ask for own id */
@@ -938,6 +932,7 @@
 
 /* usb specific object needed to register this driver with the usb subsystem */
 static struct usb_driver vicam_driver = {
+	owner:		THIS_MODULE,
 	name:		"vicam",
 	probe:		vicam_probe,
 	disconnect:	vicam_disconnect,
@@ -984,3 +979,4 @@
 
 module_init(usb_vicam_init);
 module_exit(usb_vicam_exit);
+
diff -urN linux-2.5.3-pre2/drivers/usb/vicam.h linux/drivers/usb/vicam.h
--- linux-2.5.3-pre2/drivers/usb/vicam.h	Thu Jan  3 13:21:28 2002
+++ linux/drivers/usb/vicam.h	Mon Jan 21 17:23:23 2002
@@ -68,7 +68,7 @@
 	/* v4l stuff */
 	char *camera_name;
 	char *fbuf;
-	urb_t *urb[VICAM_NUMSBUF];
+	struct urb *urb[VICAM_NUMSBUF];
 	int sizes;
 	int *width;
 	int *height;
diff -urN linux-2.5.3-pre2/fs/affs/amigaffs.c linux/fs/affs/amigaffs.c
--- linux-2.5.3-pre2/fs/affs/amigaffs.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/affs/amigaffs.c	Mon Jan 21 17:23:23 2002
@@ -419,7 +419,7 @@
 void
 mode_to_prot(struct inode *inode)
 {
-	u32 prot = AFFS_INODE->i_protect;
+	u32 prot = AFFS_I(inode)->i_protect;
 	mode_t mode = inode->i_mode;
 
 	if (!(mode & S_IXUSR))
@@ -441,7 +441,7 @@
 	if (mode & S_IWOTH)
 		prot |= FIBF_OTR_WRITE;
 
-	AFFS_INODE->i_protect = prot;
+	AFFS_I(inode)->i_protect = prot;
 }
 
 void
diff -urN linux-2.5.3-pre2/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- linux-2.5.3-pre2/fs/affs/bitmap.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/affs/bitmap.c	Mon Jan 21 17:23:23 2002
@@ -155,16 +155,16 @@
 
 	pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
 
-	if (inode->u.affs_i.i_pa_cnt) {
-		pr_debug("%d\n", inode->u.affs_i.i_lastalloc+1);
-		inode->u.affs_i.i_pa_cnt--;
-		return ++inode->u.affs_i.i_lastalloc;
+	if (AFFS_I(inode)->i_pa_cnt) {
+		pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1);
+		AFFS_I(inode)->i_pa_cnt--;
+		return ++AFFS_I(inode)->i_lastalloc;
 	}
 
 	if (!goal || goal > AFFS_SB->s_partition_size) {
 		if (goal)
 			affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
-		//if (!inode->u.affs_i.i_last_block)
+		//if (!AFFS_I(inode)->i_last_block)
 		//	affs_warning(sb, "affs_balloc", "no last alloc block");
 		goal = AFFS_SB->s_reserved;
 	}
@@ -233,16 +233,16 @@
 	bit = ffs(tmp) - 1;
 	blk += bit + AFFS_SB->s_reserved;
 	mask2 = mask = 1 << (bit & 31);
-	inode->u.affs_i.i_lastalloc = blk;
+	AFFS_I(inode)->i_lastalloc = blk;
 
 	/* prealloc as much as possible within this word */
 	while ((mask2 <<= 1)) {
 		if (!(tmp & mask2))
 			break;
-		inode->u.affs_i.i_pa_cnt++;
+		AFFS_I(inode)->i_pa_cnt++;
 		mask |= mask2;
 	}
-	bm->bm_free -= inode->u.affs_i.i_pa_cnt + 1;
+	bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
 
 	*data = cpu_to_be32(tmp & ~mask);
 
diff -urN linux-2.5.3-pre2/fs/affs/file.c linux/fs/affs/file.c
--- linux-2.5.3-pre2/fs/affs/file.c	Mon Jan 14 09:38:37 2002
+++ linux/fs/affs/file.c	Mon Jan 21 17:23:23 2002
@@ -62,8 +62,8 @@
 {
 	if (atomic_read(&filp->f_count) != 1)
 		return 0;
-	pr_debug("AFFS: open(%d)\n", AFFS_INODE->i_opencnt);
-	AFFS_INODE->i_opencnt++;
+	pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt);
+	AFFS_I(inode)->i_opencnt++;
 	return 0;
 }
 
@@ -72,9 +72,9 @@
 {
 	if (atomic_read(&filp->f_count) != 0)
 		return 0;
-	pr_debug("AFFS: release(%d)\n", AFFS_INODE->i_opencnt);
-	AFFS_INODE->i_opencnt--;
-	if (!AFFS_INODE->i_opencnt)
+	pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt);
+	AFFS_I(inode)->i_opencnt--;
+	if (!AFFS_I(inode)->i_opencnt)
 		affs_free_prealloc(inode);
 
 	return 0;
@@ -88,49 +88,49 @@
 	u32 lc_max;
 	int i, j, key;
 
-	if (!AFFS_INODE->i_lc) {
+	if (!AFFS_I(inode)->i_lc) {
 		char *ptr = (char *)get_zeroed_page(GFP_NOFS);
 		if (!ptr)
 			return -ENOMEM;
-		AFFS_INODE->i_lc = (u32 *)ptr;
-		AFFS_INODE->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
+		AFFS_I(inode)->i_lc = (u32 *)ptr;
+		AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
 	}
 
-	lc_max = AFFS_LC_SIZE << AFFS_INODE->i_lc_shift;
+	lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift;
 
-	if (AFFS_INODE->i_extcnt > lc_max) {
+	if (AFFS_I(inode)->i_extcnt > lc_max) {
 		u32 lc_shift, lc_mask, tmp, off;
 
 		/* need to recalculate linear cache, start from old size */
-		lc_shift = AFFS_INODE->i_lc_shift;
-		tmp = (AFFS_INODE->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
+		lc_shift = AFFS_I(inode)->i_lc_shift;
+		tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
 		for (; tmp; tmp >>= 1)
 			lc_shift++;
 		lc_mask = (1 << lc_shift) - 1;
 
 		/* fix idx and old size to new shift */
-		lc_idx >>= (lc_shift - AFFS_INODE->i_lc_shift);
-		AFFS_INODE->i_lc_size >>= (lc_shift - AFFS_INODE->i_lc_shift);
+		lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
+		AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
 
 		/* first shrink old cache to make more space */
-		off = 1 << (lc_shift - AFFS_INODE->i_lc_shift);
+		off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift);
 		for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
-			AFFS_INODE->i_ac[i] = AFFS_INODE->i_ac[j];
+			AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j];
 
-		AFFS_INODE->i_lc_shift = lc_shift;
-		AFFS_INODE->i_lc_mask = lc_mask;
+		AFFS_I(inode)->i_lc_shift = lc_shift;
+		AFFS_I(inode)->i_lc_mask = lc_mask;
 	}
 
 	/* fill cache to the needed index */
-	i = AFFS_INODE->i_lc_size;
-	AFFS_INODE->i_lc_size = lc_idx + 1;
+	i = AFFS_I(inode)->i_lc_size;
+	AFFS_I(inode)->i_lc_size = lc_idx + 1;
 	for (; i <= lc_idx; i++) {
 		if (!i) {
-			AFFS_INODE->i_lc[0] = inode->i_ino;
+			AFFS_I(inode)->i_lc[0] = inode->i_ino;
 			continue;
 		}
-		key = AFFS_INODE->i_lc[i - 1];
-		j = AFFS_INODE->i_lc_mask + 1;
+		key = AFFS_I(inode)->i_lc[i - 1];
+		j = AFFS_I(inode)->i_lc_mask + 1;
 		// unlock cache
 		for (; j > 0; j--) {
 			bh = affs_bread(sb, key);
@@ -140,7 +140,7 @@
 			affs_brelse(bh);
 		}
 		// lock cache
-		AFFS_INODE->i_lc[i] = key;
+		AFFS_I(inode)->i_lc[i] = key;
 	}
 
 	return 0;
@@ -182,7 +182,7 @@
 	affs_adjust_checksum(bh, blocknr - tmp);
 	mark_buffer_dirty_inode(bh, inode);
 
-	AFFS_INODE->i_extcnt++;
+	AFFS_I(inode)->i_extcnt++;
 	mark_inode_dirty(inode);
 
 	return new_bh;
@@ -192,8 +192,8 @@
 affs_get_extblock(struct inode *inode, u32 ext)
 {
 	/* inline the simplest case: same extended block as last time */
-	struct buffer_head *bh = AFFS_INODE->i_ext_bh;
-	if (ext == AFFS_INODE->i_ext_last)
+	struct buffer_head *bh = AFFS_I(inode)->i_ext_bh;
+	if (ext == AFFS_I(inode)->i_ext_last)
 		atomic_inc(&bh->b_count);
 	else
 		/* we have to do more (not inlined) */
@@ -211,13 +211,13 @@
 	u32 lc_idx, lc_off, ac_idx;
 	u32 tmp, idx;
 
-	if (ext == AFFS_INODE->i_ext_last + 1) {
+	if (ext == AFFS_I(inode)->i_ext_last + 1) {
 		/* read the next extended block from the current one */
-		bh = AFFS_INODE->i_ext_bh;
+		bh = AFFS_I(inode)->i_ext_bh;
 		ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
-		if (ext < AFFS_INODE->i_extcnt)
+		if (ext < AFFS_I(inode)->i_extcnt)
 			goto read_ext;
-		if (ext > AFFS_INODE->i_extcnt)
+		if (ext > AFFS_I(inode)->i_extcnt)
 			BUG();
 		bh = affs_alloc_extblock(inode, bh, ext);
 		if (IS_ERR(bh))
@@ -231,11 +231,11 @@
 		goto read_ext;
 	}
 
-	if (ext >= AFFS_INODE->i_extcnt) {
+	if (ext >= AFFS_I(inode)->i_extcnt) {
 		struct buffer_head *prev_bh;
 
 		/* allocate a new extended block */
-		if (ext > AFFS_INODE->i_extcnt)
+		if (ext > AFFS_I(inode)->i_extcnt)
 			BUG();
 
 		/* get previous extended block */
@@ -251,10 +251,10 @@
 
 again:
 	/* check if there is an extended cache and whether it's large enough */
-	lc_idx = ext >> AFFS_INODE->i_lc_shift;
-	lc_off = ext & AFFS_INODE->i_lc_mask;
+	lc_idx = ext >> AFFS_I(inode)->i_lc_shift;
+	lc_off = ext & AFFS_I(inode)->i_lc_mask;
 
-	if (lc_idx >= AFFS_INODE->i_lc_size) {
+	if (lc_idx >= AFFS_I(inode)->i_lc_size) {
 		int err;
 
 		err = affs_grow_extcache(inode, lc_idx);
@@ -265,14 +265,14 @@
 
 	/* every n'th key we find in the linear cache */
 	if (!lc_off) {
-		ext_key = AFFS_INODE->i_lc[lc_idx];
+		ext_key = AFFS_I(inode)->i_lc[lc_idx];
 		goto read_ext;
 	}
 
 	/* maybe it's still in the associative cache */
 	ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
-	if (AFFS_INODE->i_ac[ac_idx].ext == ext) {
-		ext_key = AFFS_INODE->i_ac[ac_idx].key;
+	if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) {
+		ext_key = AFFS_I(inode)->i_ac[ac_idx].key;
 		goto read_ext;
 	}
 
@@ -281,14 +281,14 @@
 	idx = ac_idx;
 	while (--tmp, --lc_off > 0) {
 		idx = (idx - 1) & AFFS_AC_MASK;
-		if (AFFS_INODE->i_ac[idx].ext == tmp) {
-			ext_key = AFFS_INODE->i_ac[idx].key;
+		if (AFFS_I(inode)->i_ac[idx].ext == tmp) {
+			ext_key = AFFS_I(inode)->i_ac[idx].key;
 			goto find_ext;
 		}
 	}
 
 	/* fall back to the linear cache */
-	ext_key = AFFS_INODE->i_lc[lc_idx];
+	ext_key = AFFS_I(inode)->i_lc[lc_idx];
 find_ext:
 	/* read all extended blocks until we find the one we need */
 	//unlock cache
@@ -304,8 +304,8 @@
 
 	/* store it in the associative cache */
 	// recalculate ac_idx?
-	AFFS_INODE->i_ac[ac_idx].ext = ext;
-	AFFS_INODE->i_ac[ac_idx].key = ext_key;
+	AFFS_I(inode)->i_ac[ac_idx].ext = ext;
+	AFFS_I(inode)->i_ac[ac_idx].key = ext_key;
 
 read_ext:
 	/* finally read the right extended block */
@@ -317,9 +317,9 @@
 
 store_ext:
 	/* release old cached extended block and store the new one */
-	affs_brelse(AFFS_INODE->i_ext_bh);
-	AFFS_INODE->i_ext_last = ext;
-	AFFS_INODE->i_ext_bh = bh;
+	affs_brelse(AFFS_I(inode)->i_ext_bh);
+	AFFS_I(inode)->i_ext_last = ext;
+	AFFS_I(inode)->i_ext_bh = bh;
 	atomic_inc(&bh->b_count);
 
 	return bh;
@@ -341,8 +341,8 @@
 	if (block < 0)
 		goto err_small;
 
-	if (block >= AFFS_INODE->i_blkcnt) {
-		if (block > AFFS_INODE->i_blkcnt || !create)
+	if (block >= AFFS_I(inode)->i_blkcnt) {
+		if (block > AFFS_I(inode)->i_blkcnt || !create)
 			goto err_big;
 	} else
 		create = 0;
@@ -362,8 +362,8 @@
 		if (!blocknr)
 			goto err_alloc;
 		bh_result->b_state |= (1UL << BH_New);
-		AFFS_INODE->mmu_private += AFFS_SB->s_data_blksize;
-		AFFS_INODE->i_blkcnt++;
+		AFFS_I(inode)->mmu_private += AFFS_SB->s_data_blksize;
+		AFFS_I(inode)->i_blkcnt++;
 
 		/* store new block */
 		if (bh_result->b_blocknr)
@@ -418,7 +418,7 @@
 static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page, from, to, affs_get_block,
-		&page->mapping->host->u.affs_i.mmu_private);
+		&AFFS_I(page->mapping->host)->mmu_private);
 }
 static int _affs_bmap(struct address_space *mapping, long block)
 {
@@ -593,11 +593,11 @@
 		bidx++;
 	}
 	affs_brelse(bh);
-	inode->i_size = AFFS_INODE->mmu_private = size;
+	inode->i_size = AFFS_I(inode)->mmu_private = size;
 	return 0;
 
 out:
-	inode->i_size = AFFS_INODE->mmu_private = size;
+	inode->i_size = AFFS_I(inode)->mmu_private = size;
 	return PTR_ERR(bh);
 }
 
@@ -759,7 +759,7 @@
 	affs_brelse(bh);
 	tmp = (page->index << PAGE_CACHE_SHIFT) + from;
 	if (tmp > inode->i_size)
-		inode->i_size = AFFS_INODE->mmu_private = tmp;
+		inode->i_size = AFFS_I(inode)->mmu_private = tmp;
 
 	return written;
 
@@ -787,9 +787,9 @@
 
 	pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
 
-	while (inode->u.affs_i.i_pa_cnt) {
-		inode->u.affs_i.i_pa_cnt--;
-		affs_free_block(sb, ++inode->u.affs_i.i_lastalloc);
+	while (AFFS_I(inode)->i_pa_cnt) {
+		AFFS_I(inode)->i_pa_cnt--;
+		affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc);
 	}
 }
 
@@ -814,7 +814,7 @@
 		ext = last_blk / AFFS_SB->s_hashsize;
 	}
 
-	if (inode->i_size > AFFS_INODE->mmu_private) {
+	if (inode->i_size > AFFS_I(inode)->mmu_private) {
 		struct address_space *mapping = inode->i_mapping;
 		struct page *page;
 		u32 size = inode->i_size - 1;
@@ -831,23 +831,23 @@
 		page_cache_release(page);
 		mark_inode_dirty(inode);
 		return;
-	} else if (inode->i_size == AFFS_INODE->mmu_private)
+	} else if (inode->i_size == AFFS_I(inode)->mmu_private)
 		return;
 
 	// lock cache
 	ext_bh = affs_get_extblock(inode, ext);
-	if (AFFS_INODE->i_lc) {
+	if (AFFS_I(inode)->i_lc) {
 		/* clear linear cache */
-		for (i = (ext + 1) >> AFFS_INODE->i_lc_shift; i < AFFS_LC_SIZE; i++)
-			AFFS_INODE->i_lc[i] = 0;
+		for (i = (ext + 1) >> AFFS_I(inode)->i_lc_shift; i < AFFS_LC_SIZE; i++)
+			AFFS_I(inode)->i_lc[i] = 0;
 		/* clear associative cache */
 		for (i = 0; i < AFFS_AC_SIZE; i++)
-			if (AFFS_INODE->i_ac[i].ext >= ext)
-				AFFS_INODE->i_ac[i].ext = 0;
+			if (AFFS_I(inode)->i_ac[i].ext >= ext)
+				AFFS_I(inode)->i_ac[i].ext = 0;
 	}
 	ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
 
-	blkcnt = AFFS_INODE->i_blkcnt;
+	blkcnt = AFFS_I(inode)->i_blkcnt;
 	i = 0;
 	blk = last_blk;
 	if (inode->i_size) {
@@ -868,13 +868,13 @@
 	affs_brelse(ext_bh);
 
 	if (inode->i_size) {
-		AFFS_INODE->i_blkcnt = last_blk + 1;
-		AFFS_INODE->i_extcnt = ext + 1;
+		AFFS_I(inode)->i_blkcnt = last_blk + 1;
+		AFFS_I(inode)->i_extcnt = ext + 1;
 	} else {
-		AFFS_INODE->i_blkcnt = 0;
-		AFFS_INODE->i_extcnt = 1;
+		AFFS_I(inode)->i_blkcnt = 0;
+		AFFS_I(inode)->i_extcnt = 1;
 	}
-	AFFS_INODE->mmu_private = inode->i_size;
+	AFFS_I(inode)->mmu_private = inode->i_size;
 	// unlock cache
 
 	while (ext_key) {
diff -urN linux-2.5.3-pre2/fs/affs/inode.c linux/fs/affs/inode.c
--- linux-2.5.3-pre2/fs/affs/inode.c	Sun Sep 30 12:26:08 2001
+++ linux/fs/affs/inode.c	Mon Jan 21 17:23:23 2002
@@ -68,12 +68,20 @@
 	inode->i_size = 0;
 	inode->i_nlink = 1;
 	inode->i_mode = 0;
-	memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
-	init_MUTEX(&AFFS_INODE->i_link_lock);
-	init_MUTEX(&AFFS_INODE->i_ext_lock);
-	AFFS_INODE->i_extcnt = 1;
-	AFFS_INODE->i_ext_last = ~1;
-	AFFS_INODE->i_protect = prot;
+	AFFS_I(inode)->i_extcnt = 1;
+	AFFS_I(inode)->i_ext_last = ~1;
+	AFFS_I(inode)->i_protect = prot;
+	AFFS_I(inode)->i_opencnt = 0;
+	AFFS_I(inode)->i_blkcnt = 0;
+	AFFS_I(inode)->i_lc = NULL;
+	AFFS_I(inode)->i_lc_size = 0;
+	AFFS_I(inode)->i_lc_shift = 0;
+	AFFS_I(inode)->i_lc_mask = 0;
+	AFFS_I(inode)->i_ac = NULL;
+	AFFS_I(inode)->i_ext_bh = NULL;
+	AFFS_I(inode)->mmu_private = 0;
+	AFFS_I(inode)->i_lastalloc = 0;
+	AFFS_I(inode)->i_pa_cnt = 0;
 
 	if (AFFS_SB->s_flags & SF_SETMODE)
 		inode->i_mode = AFFS_SB->s_mode;
@@ -136,11 +144,11 @@
 	case ST_FILE:
 		size = be32_to_cpu(tail->size);
 		inode->i_mode |= S_IFREG;
-		AFFS_INODE->mmu_private = inode->i_size = size;
+		AFFS_I(inode)->mmu_private = inode->i_size = size;
 		if (inode->i_size) {
-			AFFS_INODE->i_blkcnt = (size - 1) /
+			AFFS_I(inode)->i_blkcnt = (size - 1) /
 					       AFFS_SB->s_data_blksize + 1;
-			AFFS_INODE->i_extcnt = (AFFS_INODE->i_blkcnt - 1) /
+			AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) /
 					       AFFS_SB->s_hashsize + 1;
 		}
 		if (tail->link_chain)
@@ -196,7 +204,7 @@
 	if (tail->stype == be32_to_cpu(ST_ROOT)) {
 		secs_to_datestamp(inode->i_mtime,&AFFS_ROOT_TAIL(sb, bh)->root_change);
 	} else {
-		tail->protect = cpu_to_be32(AFFS_INODE->i_protect);
+		tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect);
 		tail->size = cpu_to_be32(inode->i_size);
 		secs_to_datestamp(inode->i_mtime,&tail->change);
 		if (!(inode->i_ino == AFFS_SB->s_root_block)) {
@@ -255,7 +263,7 @@
 	lock_kernel();
 	affs_free_prealloc(inode);
 	if (atomic_read(&inode->i_count) == 1) {
-		if (inode->i_size != AFFS_INODE->mmu_private)
+		if (inode->i_size != AFFS_I(inode)->mmu_private)
 			affs_truncate(inode);
 		//if (inode->i_nlink)
 		//	affs_clear_inode(inode);
@@ -279,18 +287,18 @@
 void
 affs_clear_inode(struct inode *inode)
 {
-	unsigned long cache_page = (unsigned long) inode->u.affs_i.i_lc;
+	unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc;
 
 	pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
 	if (cache_page) {
 		pr_debug("AFFS: freeing ext cache\n");
-		inode->u.affs_i.i_lc = NULL;
-		inode->u.affs_i.i_ac = NULL;
+		AFFS_I(inode)->i_lc = NULL;
+		AFFS_I(inode)->i_ac = NULL;
 		free_page(cache_page);
 	}
-	affs_brelse(AFFS_INODE->i_ext_bh);
-	AFFS_INODE->i_ext_last = ~1;
-	AFFS_INODE->i_ext_bh = NULL;
+	affs_brelse(AFFS_I(inode)->i_ext_bh);
+	AFFS_I(inode)->i_ext_last = ~1;
+	AFFS_I(inode)->i_ext_bh = NULL;
 }
 
 struct inode *
@@ -318,11 +326,20 @@
 	inode->i_ino     = block;
 	inode->i_nlink   = 1;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
-	AFFS_INODE->i_extcnt = 1;
-	AFFS_INODE->i_ext_last = ~1;
-	init_MUTEX(&AFFS_INODE->i_link_lock);
-	init_MUTEX(&AFFS_INODE->i_ext_lock);
+	AFFS_I(inode)->i_opencnt = 0;
+	AFFS_I(inode)->i_blkcnt = 0;
+	AFFS_I(inode)->i_lc = NULL;
+	AFFS_I(inode)->i_lc_size = 0;
+	AFFS_I(inode)->i_lc_shift = 0;
+	AFFS_I(inode)->i_lc_mask = 0;
+	AFFS_I(inode)->i_ac = NULL;
+	AFFS_I(inode)->i_ext_bh = NULL;
+	AFFS_I(inode)->mmu_private = 0;
+	AFFS_I(inode)->i_protect = 0;
+	AFFS_I(inode)->i_lastalloc = 0;
+	AFFS_I(inode)->i_pa_cnt = 0;
+	AFFS_I(inode)->i_extcnt = 1;
+	AFFS_I(inode)->i_ext_last = ~1;
 
 	insert_inode_hash(inode);
 
diff -urN linux-2.5.3-pre2/fs/affs/super.c linux/fs/affs/super.c
--- linux-2.5.3-pre2/fs/affs/super.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/affs/super.c	Mon Jan 21 17:23:23 2002
@@ -78,7 +78,54 @@
 	pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean);
 }
 
+static kmem_cache_t * affs_inode_cachep;
+
+static struct inode *affs_alloc_inode(struct super_block *sb)
+{
+	struct affs_inode_info *ei;
+	ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void affs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct affs_inode_info *ei = (struct affs_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		init_MUTEX(&ei->i_link_lock);
+		init_MUTEX(&ei->i_ext_lock);
+		inode_init_once(&ei->vfs_inode);
+	}
+}
+ 
+static int init_inodecache(void)
+{
+	affs_inode_cachep = kmem_cache_create("affs_inode_cache",
+					     sizeof(struct affs_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (affs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(affs_inode_cachep))
+		printk(KERN_INFO "affs_inode_cache: not all structures were freed\n");
+}
+
 static struct super_operations affs_sops = {
+	alloc_inode:	affs_alloc_inode,
+	destroy_inode:	affs_destroy_inode,
 	read_inode:	affs_read_inode,
 	write_inode:	affs_write_inode,
 	put_inode:	affs_put_inode,
@@ -490,12 +537,23 @@
 
 static int __init init_affs_fs(void)
 {
-	return register_filesystem(&affs_fs_type);
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&affs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
 }
 
 static void __exit exit_affs_fs(void)
 {
 	unregister_filesystem(&affs_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.5.3-pre2/fs/coda/inode.c linux/fs/coda/inode.c
--- linux-2.5.3-pre2/fs/coda/inode.c	Mon Jan 14 10:10:43 2002
+++ linux/fs/coda/inode.c	Mon Jan 21 17:23:23 2002
@@ -39,9 +39,60 @@
 static void coda_put_super(struct super_block *);
 static int coda_statfs(struct super_block *sb, struct statfs *buf);
 
+static kmem_cache_t * coda_inode_cachep;
+
+static struct inode *coda_alloc_inode(struct super_block *sb)
+{
+	struct coda_inode_info *ei;
+	ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, SLAB_KERNEL);
+	memset(&ei->c_fid, 0, sizeof(struct ViceFid));
+	ei->c_flags = 0;
+	INIT_LIST_HEAD(&ei->c_cilist);
+	ei->c_container = NULL;
+	ei->c_contcount = 0;
+	memset(&ei->c_cached_cred, 0, sizeof(struct coda_cred));
+	ei->c_cached_perm = 0;
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void coda_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(coda_inode_cachep, ITOC(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct coda_inode_info *ei = (struct coda_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+int coda_init_inodecache(void)
+{
+	coda_inode_cachep = kmem_cache_create("coda_inode_cache",
+					     sizeof(struct coda_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (coda_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+void coda_destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(coda_inode_cachep))
+		printk(KERN_INFO "coda_inode_cache: not all structures were freed\n");
+}
+
 /* exported operations */
 struct super_operations coda_super_operations =
 {
+	alloc_inode:	coda_alloc_inode,
+	destroy_inode:	coda_destroy_inode,
 	read_inode:	coda_read_inode,
 	clear_inode:	coda_clear_inode,
 	put_super:	coda_put_super,
@@ -188,28 +239,7 @@
 
         if (!sbi) BUG();
 
-#if 0
-	/* check if the inode is already initialized */
-	if (inode->u.generic_ip) {
-            printk("coda_read_inode: initialized inode");
-            return;
-        }
-
-	inode->u.generic_ip = cii_alloc();
-	if (!inode->u.generic_ip) {
-		CDEBUG(D_CNODE, "coda_read_inode: failed to allocate inode info\n");
-		make_bad_inode(inode);
-		return;
-	}
-	memset(inode->u.generic_ip, 0, sizeof(struct coda_inode_info));
-#endif
-
 	cii = ITOC(inode);
-	if (!coda_isnullfid(&cii->c_fid)) {
-            printk("coda_read_inode: initialized inode");
-            return;
-        }
-
 	list_add(&cii->c_cilist, &sbi->sbi_cihead);
 }
 
@@ -226,11 +256,6 @@
         list_del_init(&cii->c_cilist);
 	inode->i_mapping = &inode->i_data;
 	coda_cache_clear_inode(inode);
-
-#if 0
-	cii_free(inode->u.generic_ip);
-	inode->u.generic_ip = NULL;
-#endif
 }
 
 int coda_notify_change(struct dentry *de, struct iattr *iattr)
diff -urN linux-2.5.3-pre2/fs/coda/psdev.c linux/fs/coda/psdev.c
--- linux-2.5.3-pre2/fs/coda/psdev.c	Fri Jan  4 09:37:49 2002
+++ linux/fs/coda/psdev.c	Mon Jan 21 17:23:23 2002
@@ -407,24 +407,35 @@
 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
 MODULE_LICENSE("GPL");
 
+extern int coda_init_inodecache(void);
+extern void coda_destroy_inodecache(void);
 static int __init init_coda(void)
 {
 	int status;
 	printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.15, coda@cs.cmu.edu\n");
 
+	status = coda_init_inodecache();
+	if (status)
+		goto out2;
 	status = init_coda_psdev();
 	if ( status ) {
 		printk("Problem (%d) in init_coda_psdev\n", status);
-		return status;
+		goto out1;
 	}
 	
 	status = register_filesystem(&coda_fs_type);
 	if (status) {
 		printk("coda: failed to register filesystem!\n");
-		devfs_unregister(devfs_handle);
-		devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
-		coda_sysctl_clean();
+		goto out;
 	}
+	return 0;
+out:
+	devfs_unregister(devfs_handle);
+	devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+	coda_sysctl_clean();
+out1:
+	coda_destroy_inodecache();
+out2:
 	return status;
 }
 
@@ -439,6 +450,7 @@
 	devfs_unregister(devfs_handle);
 	devfs_unregister_chrdev(CODA_PSDEV_MAJOR, "coda_psdev");
 	coda_sysctl_clean();
+	coda_destroy_inodecache();
 }
 
 module_init(init_coda);
diff -urN linux-2.5.3-pre2/fs/devfs/base.c linux/fs/devfs/base.c
--- linux-2.5.3-pre2/fs/devfs/base.c	Mon Jan 14 09:40:29 2002
+++ linux/fs/devfs/base.c	Mon Jan 21 17:23:24 2002
@@ -1,6 +1,6 @@
 /*  devfs (Device FileSystem) driver.
 
-    Copyright (C) 1998-2001  Richard Gooch
+    Copyright (C) 1998-2002  Richard Gooch
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -604,6 +604,10 @@
     20020113   Richard Gooch <rgooch@atnf.csiro.au>
 	       Fixed (rare, old) race in <devfs_lookup>.
   v1.9
+    20020120   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed deadlock bug in <devfs_d_revalidate_wait>.
+	       Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
+  v1.10
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -636,7 +640,7 @@
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "1.9 (20020113)"
+#define DEVFS_VERSION            "1.10 (20020120)"
 
 #define DEVFS_NAME "devfs"
 
@@ -781,7 +785,7 @@
     umode_t mode;
     unsigned short namelen;      /*  I think 64k+ filenames are a way off... */
     unsigned char hide:1;
-    unsigned char vfs_created:1; /*  Whether created by driver or VFS        */
+    unsigned char vfs_deletable:1;/*  Whether the VFS may delete the entry   */
     char name[1];                /*  This is just a dummy: the allocated array
 				     is bigger. This is NULL-terminated      */
 };
@@ -1774,7 +1778,8 @@
     DPRINTK (DEBUG_REGISTER, "(%s)\n", name);
     err = devfs_do_symlink (dir, name, flags, link, &de, info);
     if (err) return err;
-    if (handle != NULL) *handle = de;
+    if (handle == NULL) de->vfs_deletable = TRUE;
+    else *handle = de;
     devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
     return 0;
 }   /*  End Function devfs_mk_symlink  */
@@ -1817,7 +1822,7 @@
 	{
 	    PRINTK ("(%s): using old entry in dir: %p \"%s\"\n",
 		    name, dir, dir->name);
-	    old->vfs_created = FALSE;
+	    old->vfs_deletable = FALSE;
 	    devfs_put (dir);
 	    return old;
 	}
@@ -2878,13 +2883,16 @@
     struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
     DECLARE_WAITQUEUE (wait, current);
 
-    if ( !dentry->d_inode && is_devfsd_or_child (fs_info) )
+    if ( is_devfsd_or_child (fs_info) )
     {
 	devfs_handle_t de = lookup_info->de;
 	struct inode *inode;
 
-	DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p de: %p by: \"%s\"\n",
-		 dentry->d_name.name, dentry, de, current->comm);
+	DPRINTK (DEBUG_I_LOOKUP,
+		 "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
+		 dentry->d_name.name, dentry, dentry->d_inode, de,
+		 current->comm);
+	if (dentry->d_inode) return 1;
 	if (de == NULL)
 	{
 	    read_lock (&parent->u.dir.lock);
@@ -3015,7 +3023,7 @@
     de = get_devfs_entry_from_vfs_inode (inode);
     DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
     if (de == NULL) return -ENOENT;
-    if (!de->vfs_created) return -EPERM;
+    if (!de->vfs_deletable) return -EPERM;
     write_lock (&de->parent->u.dir.lock);
     unhooked = _devfs_unhook (de);
     write_unlock (&de->parent->u.dir.lock);
@@ -3044,7 +3052,7 @@
     DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
 	     dentry->d_name.name, err);
     if (err < 0) return err;
-    de->vfs_created = TRUE;
+    de->vfs_deletable = TRUE;
     de->inode.uid = current->euid;
     de->inode.gid = current->egid;
     de->inode.atime = CURRENT_TIME;
@@ -3073,7 +3081,7 @@
     if (parent == NULL) return -ENOENT;
     de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
     if (!de) return -ENOMEM;
-    de->vfs_created = TRUE;
+    de->vfs_deletable = TRUE;
     if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
 	return err;
     de->inode.uid = current->euid;
@@ -3103,7 +3111,7 @@
     de = get_devfs_entry_from_vfs_inode (inode);
     if (de == NULL) return -ENOENT;
     if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
-    if (!de->vfs_created) return -EPERM;
+    if (!de->vfs_deletable) return -EPERM;
     /*  First ensure the directory is empty and will stay thay way  */
     write_lock (&de->u.dir.lock);
     de->u.dir.no_more_additions = TRUE;
@@ -3137,7 +3145,7 @@
     if (parent == NULL) return -ENOENT;
     de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
     if (!de) return -ENOMEM;
-    de->vfs_created = TRUE;
+    de->vfs_deletable = TRUE;
     if ( S_ISBLK (mode) || S_ISCHR (mode) )
     {
 	de->u.fcb.u.device.major = MAJOR (rdev);
diff -urN linux-2.5.3-pre2/fs/efs/super.c linux/fs/efs/super.c
--- linux-2.5.3-pre2/fs/efs/super.c	Tue Dec 25 15:39:20 2001
+++ linux/fs/efs/super.c	Mon Jan 21 17:23:24 2002
@@ -12,21 +12,78 @@
 #include <linux/efs_fs.h>
 #include <linux/efs_vh.h>
 #include <linux/efs_fs_sb.h>
+#include <linux/slab.h>
 
 static DECLARE_FSTYPE_DEV(efs_fs_type, "efs", efs_read_super);
 
+static kmem_cache_t * efs_inode_cachep;
+
+static struct inode *efs_alloc_inode(struct super_block *sb)
+{
+	struct efs_inode_info *ei;
+	ei = (struct efs_inode_info *)kmem_cache_alloc(efs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void efs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct efs_inode_info *ei = (struct efs_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+static int init_inodecache(void)
+{
+	efs_inode_cachep = kmem_cache_create("efs_inode_cache",
+					     sizeof(struct efs_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (efs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(efs_inode_cachep))
+		printk(KERN_INFO "efs_inode_cache: not all structures were freed\n");
+}
+
 static struct super_operations efs_superblock_operations = {
+	alloc_inode:	efs_alloc_inode,
+	destroy_inode:	efs_destroy_inode,
 	read_inode:	efs_read_inode,
 	statfs:		efs_statfs,
 };
 
 static int __init init_efs_fs(void) {
+	int err;
 	printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
-	return register_filesystem(&efs_fs_type);
+	err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&efs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
 }
 
 static void __exit exit_efs_fs(void) {
 	unregister_filesystem(&efs_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.5.3-pre2/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- linux-2.5.3-pre2/fs/ext2/balloc.c	Mon Jan 21 17:22:37 2002
+++ linux/fs/ext2/balloc.c	Mon Jan 21 17:23:24 2002
@@ -12,8 +12,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/quotaops.h>
 
diff -urN linux-2.5.3-pre2/fs/ext2/bitmap.c linux/fs/ext2/bitmap.c
--- linux-2.5.3-pre2/fs/ext2/bitmap.c	Wed Sep 27 13:41:33 2000
+++ linux/fs/ext2/bitmap.c	Mon Jan 21 17:23:24 2002
@@ -7,9 +7,7 @@
  * Universite Pierre et Marie Curie (Paris VI)
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
-
+#include "ext2.h"
 
 static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
 
diff -urN linux-2.5.3-pre2/fs/ext2/dir.c linux/fs/ext2/dir.c
--- linux-2.5.3-pre2/fs/ext2/dir.c	Mon Sep 17 13:16:30 2001
+++ linux/fs/ext2/dir.c	Mon Jan 21 17:23:24 2002
@@ -21,8 +21,7 @@
  * and moved here. AV
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/pagemap.h>
 
 typedef struct ext2_dir_entry_2 ext2_dirent;
@@ -306,12 +305,13 @@
 	unsigned long start, n;
 	unsigned long npages = dir_pages(dir);
 	struct page *page = NULL;
+	struct ext2_inode_info *ei = EXT2_I(dir);
 	ext2_dirent * de;
 
 	/* OFFSET_CACHE */
 	*res_page = NULL;
 
-	start = dir->u.ext2_i.i_dir_start_lookup;
+	start = ei->i_dir_start_lookup;
 	if (start >= npages)
 		start = 0;
 	n = start;
@@ -336,7 +336,7 @@
 
 found:
 	*res_page = page;
-	dir->u.ext2_i.i_dir_start_lookup = n;
+	ei->i_dir_start_lookup = n;
 	return de;
 }
 
diff -urN linux-2.5.3-pre2/fs/ext2/ext2.h linux/fs/ext2/ext2.h
--- linux-2.5.3-pre2/fs/ext2/ext2.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ext2/ext2.h	Mon Jan 21 17:23:24 2002
@@ -0,0 +1,120 @@
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext2_inode_info {
+	__u32	i_data[15];
+	__u32	i_flags;
+	__u32	i_faddr;
+	__u8	i_frag_no;
+	__u8	i_frag_size;
+	__u16	i_osync;
+	__u32	i_file_acl;
+	__u32	i_dir_acl;
+	__u32	i_dtime;
+	__u32	i_block_group;
+	__u32	i_next_alloc_block;
+	__u32	i_next_alloc_goal;
+	__u32	i_prealloc_block;
+	__u32	i_prealloc_count;
+	__u32	i_dir_start_lookup;
+	struct inode	vfs_inode;
+};
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * ext2 source programs needs to include it so they are duplicated here.
+ */
+
+static inline struct ext2_inode_info *EXT2_I(struct inode *inode)
+{
+	return list_entry(inode, struct ext2_inode_info, vfs_inode);
+}
+
+/* balloc.c */
+extern int ext2_bg_has_super(struct super_block *sb, int group);
+extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
+extern int ext2_new_block (struct inode *, unsigned long,
+			   __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (struct inode *, unsigned long,
+			      unsigned long);
+extern unsigned long ext2_count_free_blocks (struct super_block *);
+extern void ext2_check_blocks_bitmap (struct super_block *);
+extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+						    unsigned int block_group,
+						    struct buffer_head ** bh);
+
+/* dir.c */
+extern int ext2_add_link (struct dentry *, struct inode *);
+extern ino_t ext2_inode_by_name(struct inode *, struct dentry *);
+extern int ext2_make_empty(struct inode *, struct inode *);
+extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **);
+extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);
+extern int ext2_empty_dir (struct inode *);
+extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);
+extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *);
+
+/* fsync.c */
+extern int ext2_sync_file (struct file *, struct dentry *, int);
+extern int ext2_fsync_inode (struct inode *, int);
+
+/* ialloc.c */
+extern struct inode * ext2_new_inode (struct inode *, int);
+extern void ext2_free_inode (struct inode *);
+extern unsigned long ext2_count_free_inodes (struct super_block *);
+extern void ext2_check_inodes_bitmap (struct super_block *);
+extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
+
+/* inode.c */
+extern void ext2_read_inode (struct inode *);
+extern void ext2_write_inode (struct inode *, int);
+extern void ext2_put_inode (struct inode *);
+extern void ext2_delete_inode (struct inode *);
+extern int ext2_sync_inode (struct inode *);
+extern void ext2_discard_prealloc (struct inode *);
+extern void ext2_truncate (struct inode *);
+
+/* ioctl.c */
+extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
+		       unsigned long);
+
+/* super.c */
+extern void ext2_error (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
+				   const char *, ...)
+	__attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void ext2_warning (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern void ext2_update_dynamic_rev (struct super_block *sb);
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *, char *);
+extern struct super_block * ext2_read_super (struct super_block *,void *,int);
+extern int ext2_statfs (struct super_block *, struct statfs *);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct file_operations ext2_dir_operations;
+
+/* file.c */
+extern struct inode_operations ext2_file_inode_operations;
+extern struct file_operations ext2_file_operations;
+
+/* inode.c */
+extern struct address_space_operations ext2_aops;
+
+/* namei.c */
+extern struct inode_operations ext2_dir_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations ext2_fast_symlink_inode_operations;
diff -urN linux-2.5.3-pre2/fs/ext2/file.c linux/fs/ext2/file.c
--- linux-2.5.3-pre2/fs/ext2/file.c	Thu Oct 11 08:05:18 2001
+++ linux/fs/ext2/file.c	Mon Jan 21 17:23:24 2002
@@ -18,8 +18,7 @@
  * 	(jj@sunsite.ms.mff.cuni.cz)
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/sched.h>
 
 /*
diff -urN linux-2.5.3-pre2/fs/ext2/fsync.c linux/fs/ext2/fsync.c
--- linux-2.5.3-pre2/fs/ext2/fsync.c	Mon Sep 17 13:16:30 2001
+++ linux/fs/ext2/fsync.c	Mon Jan 21 17:23:24 2002
@@ -22,8 +22,7 @@
  * we can depend on generic_block_fdatasync() to sync the data blocks.
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/smp_lock.h>
 
diff -urN linux-2.5.3-pre2/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux-2.5.3-pre2/fs/ext2/ialloc.c	Sun Dec 30 10:53:53 2001
+++ linux/fs/ext2/ialloc.c	Mon Jan 21 17:23:24 2002
@@ -13,8 +13,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/quotaops.h>
 
@@ -311,7 +310,7 @@
 	return group;
 }
 
-struct inode * ext2_new_inode (const struct inode * dir, int mode)
+struct inode * ext2_new_inode(struct inode * dir, int mode)
 {
 	struct super_block * sb;
 	struct buffer_head * bh;
@@ -321,6 +320,7 @@
 	struct inode * inode;
 	struct ext2_group_desc * desc;
 	struct ext2_super_block * es;
+	struct ext2_inode_info *ei;
 	int err;
 
 	sb = dir->i_sb;
@@ -328,13 +328,14 @@
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
 
+	ei = EXT2_I(inode);
 	lock_super (sb);
 	es = sb->u.ext2_sb.s_es;
 repeat:
 	if (S_ISDIR(mode))
-		group = find_group_dir(sb, dir->u.ext2_i.i_block_group);
+		group = find_group_dir(sb, EXT2_I(dir)->i_block_group);
 	else 
-		group = find_group_other(sb, dir->u.ext2_i.i_block_group);
+		group = find_group_other(sb, EXT2_I(dir)->i_block_group);
 
 	err = -ENOSPC;
 	if (group == -1)
@@ -385,15 +386,26 @@
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = 0;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	inode->u.ext2_i.i_new_inode = 1;
-	inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
+	memset(ei->i_data, 0, sizeof(ei->i_data));
+	ei->i_flags = EXT2_I(dir)->i_flags;
 	if (S_ISLNK(mode))
-		inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
-	inode->u.ext2_i.i_block_group = group;
-	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
+		ei->i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
+	ei->i_faddr = 0;
+	ei->i_frag_no = 0;
+	ei->i_osync = 0;
+	ei->i_file_acl = 0;
+	ei->i_dir_acl = 0;
+	ei->i_dtime = 0;
+	ei->i_block_group = group;
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
+	ei->i_prealloc_block = 0;
+	ei->i_prealloc_count = 0;
+	ei->i_dir_start_lookup = 0;
+	if (ei->i_flags & EXT2_SYNC_FL)
 		inode->i_flags |= S_SYNC;
-	insert_inode_hash(inode);
 	inode->i_generation = sb->u.ext2_sb.s_next_generation++;
+	insert_inode_hash(inode);
 	mark_inode_dirty(inode);
 
 	unlock_super (sb);
diff -urN linux-2.5.3-pre2/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux-2.5.3-pre2/fs/ext2/inode.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/ext2/inode.c	Mon Jan 21 17:23:24 2002
@@ -22,8 +22,7 @@
  *  Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/smp_lock.h>
 #include <linux/sched.h>
@@ -57,7 +56,7 @@
 	    inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		goto no_delete;
-	inode->u.ext2_i.i_dtime	= CURRENT_TIME;
+	EXT2_I(inode)->i_dtime	= CURRENT_TIME;
 	mark_inode_dirty(inode);
 	ext2_update_inode(inode, IS_SYNC(inode));
 	inode->i_size = 0;
@@ -75,13 +74,14 @@
 void ext2_discard_prealloc (struct inode * inode)
 {
 #ifdef EXT2_PREALLOCATE
+	struct ext2_inode_info *ei = EXT2_I(inode);
 	lock_kernel();
 	/* Writer: ->i_prealloc* */
-	if (inode->u.ext2_i.i_prealloc_count) {
-		unsigned short total = inode->u.ext2_i.i_prealloc_count;
-		unsigned long block = inode->u.ext2_i.i_prealloc_block;
-		inode->u.ext2_i.i_prealloc_count = 0;
-		inode->u.ext2_i.i_prealloc_block = 0;
+	if (ei->i_prealloc_count) {
+		unsigned short total = ei->i_prealloc_count;
+		unsigned long block = ei->i_prealloc_block;
+		ei->i_prealloc_count = 0;
+		ei->i_prealloc_block = 0;
 		/* Writer: end */
 		ext2_free_blocks (inode, block, total);
 	}
@@ -98,13 +98,14 @@
 
 
 #ifdef EXT2_PREALLOCATE
+	struct ext2_inode_info *ei = EXT2_I(inode);
 	/* Writer: ->i_prealloc* */
-	if (inode->u.ext2_i.i_prealloc_count &&
-	    (goal == inode->u.ext2_i.i_prealloc_block ||
-	     goal + 1 == inode->u.ext2_i.i_prealloc_block))
+	if (ei->i_prealloc_count &&
+	    (goal == ei->i_prealloc_block ||
+	     goal + 1 == ei->i_prealloc_block))
 	{		
-		result = inode->u.ext2_i.i_prealloc_block++;
-		inode->u.ext2_i.i_prealloc_count--;
+		result = ei->i_prealloc_block++;
+		ei->i_prealloc_count--;
 		/* Writer: end */
 		ext2_debug ("preallocation hit (%lu/%lu).\n",
 			    ++alloc_hits, ++alloc_attempts);
@@ -114,8 +115,8 @@
 			    alloc_hits, ++alloc_attempts);
 		if (S_ISREG(inode->i_mode))
 			result = ext2_new_block (inode, goal, 
-				 &inode->u.ext2_i.i_prealloc_count,
-				 &inode->u.ext2_i.i_prealloc_block, err);
+				 &ei->i_prealloc_count,
+				 &ei->i_prealloc_block, err);
 		else
 			result = ext2_new_block (inode, goal, 0, 0, err);
 	}
@@ -245,7 +246,7 @@
 
 	*err = 0;
 	/* i_data is not going away, no lock needed */
-	add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets);
+	add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);
 	if (!p->key)
 		goto no_block;
 	while (--depth) {
@@ -287,7 +288,8 @@
 
 static inline unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
 {
-	u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext2_i.i_data;
+	struct ext2_inode_info *ei = EXT2_I(inode);
+	u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data;
 	u32 *p;
 
 	/* Try to find previous block */
@@ -303,8 +305,7 @@
 	 * It is going to be refered from inode itself? OK, just put it into
 	 * the same cylinder group then.
 	 */
-	return (inode->u.ext2_i.i_block_group * 
-		EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
+	return (ei->i_block_group * EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
 	       le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
 }
 
@@ -327,10 +328,11 @@
 				 Indirect *partial,
 				 unsigned long *goal)
 {
+	struct ext2_inode_info *ei = EXT2_I(inode);
 	/* Writer: ->i_next_alloc* */
-	if (block == inode->u.ext2_i.i_next_alloc_block + 1) {
-		inode->u.ext2_i.i_next_alloc_block++;
-		inode->u.ext2_i.i_next_alloc_goal++;
+	if (block == ei->i_next_alloc_block + 1) {
+		ei->i_next_alloc_block++;
+		ei->i_next_alloc_goal++;
 	} 
 	/* Writer: end */
 	/* Reader: pointers, ->i_next_alloc* */
@@ -339,8 +341,8 @@
 		 * try the heuristic for sequential allocation,
 		 * failing that at least try to get decent locality.
 		 */
-		if (block == inode->u.ext2_i.i_next_alloc_block)
-			*goal = inode->u.ext2_i.i_next_alloc_goal;
+		if (block == ei->i_next_alloc_block)
+			*goal = ei->i_next_alloc_goal;
 		if (!*goal)
 			*goal = ext2_find_near(inode, partial);
 		return 0;
@@ -407,7 +409,7 @@
 		mark_buffer_uptodate(bh, 1);
 		unlock_buffer(bh);
 		mark_buffer_dirty_inode(bh, inode);
-		if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
+		if (IS_SYNC(inode) || EXT2_I(inode)->i_osync) {
 			ll_rw_block (WRITE, 1, &bh);
 			wait_on_buffer (bh);
 		}
@@ -447,6 +449,7 @@
 				     Indirect *where,
 				     int num)
 {
+	struct ext2_inode_info *ei = EXT2_I(inode);
 	int i;
 
 	/* Verify that place we are splicing to is still there and vacant */
@@ -459,8 +462,8 @@
 	/* That's it */
 
 	*where->p = where->key;
-	inode->u.ext2_i.i_next_alloc_block = block;
-	inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+	ei->i_next_alloc_block = block;
+	ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
 
 	/* Writer: end */
 
@@ -471,13 +474,13 @@
 	/* had we spliced it onto indirect block? */
 	if (where->bh) {
 		mark_buffer_dirty_inode(where->bh, inode);
-		if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
+		if (IS_SYNC(inode) || ei->i_osync) {
 			ll_rw_block (WRITE, 1, &where->bh);
 			wait_on_buffer(where->bh);
 		}
 	}
 
-	if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
+	if (IS_SYNC(inode) || ei->i_osync)
 		ext2_sync_inode (inode);
 	else
 		mark_inode_dirty(inode);
@@ -785,7 +788,7 @@
 
 void ext2_truncate (struct inode * inode)
 {
-	u32 *i_data = inode->u.ext2_i.i_data;
+	u32 *i_data = EXT2_I(inode)->i_data;
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int offsets[4];
 	Indirect chain[4];
@@ -886,6 +889,7 @@
 	unsigned long block;
 	unsigned long offset;
 	struct ext2_group_desc * gdp;
+	struct ext2_inode_info *ei = EXT2_I(inode);
 
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
 	     inode->i_ino != EXT2_ACL_DATA_INO &&
@@ -939,13 +943,13 @@
 	inode->i_atime = le32_to_cpu(raw_inode->i_atime);
 	inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
 	inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
-	inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
 	/* We now have enough fields to check if the inode was active or not.
 	 * This is needed because nfsd might try to access dead inodes
 	 * the test is that same one that e2fsck uses
 	 * NeilBrown 1999oct15
 	 */
-	if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {
+	if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) {
 		/* this inode is deleted */
 		brelse (bh);
 		goto bad_inode;
@@ -953,25 +957,30 @@
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
 	inode->i_version = ++event;
-	inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
-	inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
-	inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
-	inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
-	inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+	ei->i_flags = le32_to_cpu(raw_inode->i_flags);
+	ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
+	ei->i_frag_no = raw_inode->i_frag;
+	ei->i_frag_size = raw_inode->i_fsize;
+	ei->i_osync = 0;
+	ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
 	if (S_ISREG(inode->i_mode))
 		inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
 	else
-		inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+		ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+	ei->i_dtime = 0;
 	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
-	inode->u.ext2_i.i_prealloc_count = 0;
-	inode->u.ext2_i.i_block_group = block_group;
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
+	ei->i_prealloc_count = 0;
+	ei->i_block_group = block_group;
+	ei->i_dir_start_lookup = 0;
 
 	/*
 	 * NOTE! The in-memory inode i_data array is in little-endian order
 	 * even on big-endian machines: we do NOT byteswap the block numbers!
 	 */
 	for (block = 0; block < EXT2_N_BLOCKS; block++)
-		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+		ei->i_data[block] = raw_inode->i_block[block];
 
 	if (inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
@@ -996,19 +1005,19 @@
 				   le32_to_cpu(raw_inode->i_block[0]));
 	brelse (bh);
 	inode->i_attr_flags = 0;
-	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
+	if (ei->i_flags & EXT2_SYNC_FL) {
 		inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
 		inode->i_flags |= S_SYNC;
 	}
-	if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {
+	if (ei->i_flags & EXT2_APPEND_FL) {
 		inode->i_attr_flags |= ATTR_FLAG_APPEND;
 		inode->i_flags |= S_APPEND;
 	}
-	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) {
+	if (ei->i_flags & EXT2_IMMUTABLE_FL) {
 		inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;
 		inode->i_flags |= S_IMMUTABLE;
 	}
-	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
+	if (ei->i_flags & EXT2_NOATIME_FL) {
 		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
 		inode->i_flags |= S_NOATIME;
 	}
@@ -1030,6 +1039,7 @@
 	unsigned long offset;
 	int err = 0;
 	struct ext2_group_desc * gdp;
+	struct ext2_inode_info *ei = EXT2_I(inode);
 
 	if ((inode->i_ino != EXT2_ROOT_INO &&
 	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
@@ -1077,7 +1087,7 @@
  * Fix up interoperability with old kernels. Otherwise, old inodes get
  * re-used with the upper 16 bits of the uid/gid intact
  */
-		if(!inode->u.ext2_i.i_dtime) {
+		if(!ei->i_dtime) {
 			raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid));
 			raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid));
 		} else {
@@ -1096,14 +1106,14 @@
 	raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
 	raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
 	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
-	raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime);
-	raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags);
-	raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr);
-	raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
-	raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
-	raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
+	raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
+	raw_inode->i_flags = cpu_to_le32(ei->i_flags);
+	raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
+	raw_inode->i_frag = ei->i_frag_no;
+	raw_inode->i_fsize = ei->i_frag_size;
+	raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
 	if (S_ISDIR(inode->i_mode))
-		raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
+		raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
 	else {
 		raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
 		if (inode->i_size > 0x7fffffffULL) {
@@ -1129,7 +1139,7 @@
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
+		raw_inode->i_block[block] = ei->i_data[block];
 	mark_buffer_dirty(bh);
 	if (do_sync) {
 		ll_rw_block (WRITE, 1, &bh);
diff -urN linux-2.5.3-pre2/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c
--- linux-2.5.3-pre2/fs/ext2/ioctl.c	Wed Sep 27 13:41:33 2000
+++ linux/fs/ext2/ioctl.c	Mon Jan 21 17:23:24 2002
@@ -7,8 +7,7 @@
  * Universite Pierre et Marie Curie (Paris VI)
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/sched.h>
 #include <asm/uaccess.h>
 
@@ -16,13 +15,14 @@
 int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
 		unsigned long arg)
 {
+	struct ext2_inode_info *ei = EXT2_I(inode);
 	unsigned int flags;
 
 	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
 
 	switch (cmd) {
 	case EXT2_IOC_GETFLAGS:
-		flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE;
+		flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
 		return put_user(flags, (int *) arg);
 	case EXT2_IOC_SETFLAGS: {
 		unsigned int oldflags;
@@ -36,7 +36,7 @@
 		if (get_user(flags, (int *) arg))
 			return -EFAULT;
 
-		oldflags = inode->u.ext2_i.i_flags;
+		oldflags = ei->i_flags;
 
 		/*
 		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
@@ -51,7 +51,7 @@
 
 		flags = flags & EXT2_FL_USER_MODIFIABLE;
 		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
-		inode->u.ext2_i.i_flags = flags;
+		ei->i_flags = flags;
 
 		if (flags & EXT2_SYNC_FL)
 			inode->i_flags |= S_SYNC;
diff -urN linux-2.5.3-pre2/fs/ext2/namei.c linux/fs/ext2/namei.c
--- linux-2.5.3-pre2/fs/ext2/namei.c	Wed Oct  3 22:57:36 2001
+++ linux/fs/ext2/namei.c	Mon Jan 21 17:23:24 2002
@@ -29,8 +29,7 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/pagemap.h>
 
 /*
@@ -134,7 +133,7 @@
 	if (IS_ERR(inode))
 		goto out;
 
-	if (l > sizeof (inode->u.ext2_i.i_data)) {
+	if (l > sizeof (EXT2_I(inode)->i_data)) {
 		/* slow symlink */
 		inode->i_op = &page_symlink_inode_operations;
 		inode->i_mapping->a_ops = &ext2_aops;
@@ -144,7 +143,7 @@
 	} else {
 		/* fast symlink */
 		inode->i_op = &ext2_fast_symlink_inode_operations;
-		memcpy((char*)&inode->u.ext2_i.i_data,symname,l);
+		memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
 		inode->i_size = l-1;
 	}
 	mark_inode_dirty(inode);
diff -urN linux-2.5.3-pre2/fs/ext2/super.c linux/fs/ext2/super.c
--- linux-2.5.3-pre2/fs/ext2/super.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/ext2/super.c	Mon Jan 21 17:23:24 2002
@@ -19,8 +19,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/locks.h>
@@ -148,7 +147,51 @@
 	return;
 }
 
+static kmem_cache_t * ext2_inode_cachep;
+
+static struct inode *ext2_alloc_inode(struct super_block *sb)
+{
+	struct ext2_inode_info *ei;
+	ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void ext2_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+static int init_inodecache(void)
+{
+	ext2_inode_cachep = kmem_cache_create("ext2_inode_cache",
+					     sizeof(struct ext2_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (ext2_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(ext2_inode_cachep))
+		printk(KERN_INFO "ext2_inode_cache: not all structures were freed\n");
+}
+
 static struct super_operations ext2_sops = {
+	alloc_inode:	ext2_alloc_inode,
+	destroy_inode:	ext2_destroy_inode,
 	read_inode:	ext2_read_inode,
 	write_inode:	ext2_write_inode,
 	put_inode:	ext2_put_inode,
@@ -804,12 +847,23 @@
 
 static int __init init_ext2_fs(void)
 {
-        return register_filesystem(&ext2_fs_type);
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+        err = register_filesystem(&ext2_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
 }
 
 static void __exit exit_ext2_fs(void)
 {
 	unregister_filesystem(&ext2_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.5.3-pre2/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- linux-2.5.3-pre2/fs/ext2/symlink.c	Wed Sep 27 13:41:33 2000
+++ linux/fs/ext2/symlink.c	Mon Jan 21 17:23:24 2002
@@ -17,19 +17,18 @@
  *  ext2 symlink handling code
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 
 static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
-	char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
-	return vfs_readlink(dentry, buffer, buflen, s);
+	struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
+	return vfs_readlink(dentry, buffer, buflen, (char *)ei->i_data);
 }
 
 static int ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-	char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
-	return vfs_follow_link(nd, s);
+	struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
+	return vfs_follow_link(nd, (char *)ei->i_data);
 }
 
 struct inode_operations ext2_fast_symlink_inode_operations = {
diff -urN linux-2.5.3-pre2/fs/ext3/ialloc.c linux/fs/ext3/ialloc.c
--- linux-2.5.3-pre2/fs/ext3/ialloc.c	Fri Jan  4 09:37:49 2002
+++ linux/fs/ext3/ialloc.c	Mon Jan 21 17:23:24 2002
@@ -293,7 +293,7 @@
  * group to find a free inode.
  */
 struct inode * ext3_new_inode (handle_t *handle,
-				const struct inode * dir, int mode)
+				struct inode * dir, int mode)
 {
 	struct super_block * sb;
 	struct buffer_head * bh;
@@ -304,6 +304,7 @@
 	struct ext3_group_desc * gdp;
 	struct ext3_group_desc * tmp;
 	struct ext3_super_block * es;
+	struct ext3_inode_info *ei;
 	int err = 0;
 
 	/* Cannot create files in a deleted directory */
@@ -314,7 +315,7 @@
 	inode = new_inode(sb);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
-	init_rwsem(&inode->u.ext3_i.truncate_sem);
+	ei = EXT3_I(inode);
 
 	lock_super (sb);
 	es = sb->u.ext3_sb.s_es;
@@ -346,7 +347,7 @@
 		/*
 		 * Try to place the inode in its parent directory
 		 */
-		i = dir->u.ext3_i.i_block_group;
+		i = EXT3_I(dir)->i_block_group;
 		tmp = ext3_get_group_desc (sb, i, &bh2);
 		if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
 			gdp = tmp;
@@ -372,7 +373,7 @@
 			/*
 			 * That failed: try linear search for a free inode
 			 */
-			i = dir->u.ext3_i.i_block_group + 1;
+			i = EXT3_I(dir)->i_block_group + 1;
 			for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) {
 				if (++i >= sb->u.ext3_sb.s_groups_count)
 					i = 0;
@@ -479,31 +480,37 @@
 	inode->i_blksize = PAGE_SIZE;
 	inode->i_blocks = 0;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags & ~EXT3_INDEX_FL;
+
+	memset(ei->i_data, 0, sizeof(ei->i_data));
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
+	ei->i_dir_start_lookup = 0;
+	ei->i_disksize = 0;
+
+	ei->i_flags = EXT3_I(dir)->i_flags & ~EXT3_INDEX_FL;
 	if (S_ISLNK(mode))
-		inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL);
+		ei->i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL);
 #ifdef EXT3_FRAGMENTS
-	inode->u.ext3_i.i_faddr = 0;
-	inode->u.ext3_i.i_frag_no = 0;
-	inode->u.ext3_i.i_frag_size = 0;
+	ei->i_faddr = 0;
+	ei->i_frag_no = 0;
+	ei->i_frag_size = 0;
 #endif
-	inode->u.ext3_i.i_file_acl = 0;
-	inode->u.ext3_i.i_dir_acl = 0;
-	inode->u.ext3_i.i_dtime = 0;
-	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+	ei->i_file_acl = 0;
+	ei->i_dir_acl = 0;
+	ei->i_dtime = 0;
 #ifdef EXT3_PREALLOCATE
-	inode->u.ext3_i.i_prealloc_count = 0;
+	ei->i_prealloc_count = 0;
 #endif
-	inode->u.ext3_i.i_block_group = i;
+	ei->i_block_group = i;
 	
-	if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL)
+	if (ei->i_flags & EXT3_SYNC_FL)
 		inode->i_flags |= S_SYNC;
 	if (IS_SYNC(inode))
 		handle->h_sync = 1;
 	insert_inode_hash(inode);
 	inode->i_generation = event++;
 
-	inode->u.ext3_i.i_state = EXT3_STATE_NEW;
+	ei->i_state = EXT3_STATE_NEW;
 	err = ext3_mark_inode_dirty(handle, inode);
 	if (err) goto fail;
 	
diff -urN linux-2.5.3-pre2/fs/ext3/inode.c linux/fs/ext3/inode.c
--- linux-2.5.3-pre2/fs/ext3/inode.c	Sun Dec 30 16:58:52 2001
+++ linux/fs/ext3/inode.c	Mon Jan 21 17:23:24 2002
@@ -196,7 +196,7 @@
 	 * (Well, we could do this if we need to, but heck - it works)
 	 */
 	ext3_orphan_del(handle, inode);
-	inode->u.ext3_i.i_dtime	= CURRENT_TIME;
+	EXT3_I(inode)->i_dtime	= CURRENT_TIME;
 
 	/* 
 	 * One subtle ordering requirement: if anything has gone wrong
@@ -220,13 +220,14 @@
 void ext3_discard_prealloc (struct inode * inode)
 {
 #ifdef EXT3_PREALLOCATE
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	lock_kernel();
 	/* Writer: ->i_prealloc* */
-	if (inode->u.ext3_i.i_prealloc_count) {
-		unsigned short total = inode->u.ext3_i.i_prealloc_count;
-		unsigned long block = inode->u.ext3_i.i_prealloc_block;
-		inode->u.ext3_i.i_prealloc_count = 0;
-		inode->u.ext3_i.i_prealloc_block = 0;
+	if (ei->i_prealloc_count) {
+		unsigned short total = ei->i_prealloc_count;
+		unsigned long block = ei->i_prealloc_block;
+		ei->i_prealloc_count = 0;
+		ei->i_prealloc_block = 0;
 		/* Writer: end */
 		ext3_free_blocks (inode, block, total);
 	}
@@ -243,13 +244,14 @@
 	unsigned long result;
 
 #ifdef EXT3_PREALLOCATE
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	/* Writer: ->i_prealloc* */
-	if (inode->u.ext3_i.i_prealloc_count &&
-	    (goal == inode->u.ext3_i.i_prealloc_block ||
-	     goal + 1 == inode->u.ext3_i.i_prealloc_block))
+	if (ei->i_prealloc_count &&
+	    (goal == ei->i_prealloc_block ||
+	     goal + 1 == ei->i_prealloc_block))
 	{
-		result = inode->u.ext3_i.i_prealloc_block++;
-		inode->u.ext3_i.i_prealloc_count--;
+		result = ei->i_prealloc_block++;
+		ei->i_prealloc_count--;
 		/* Writer: end */
 		ext3_debug ("preallocation hit (%lu/%lu).\n",
 			    ++alloc_hits, ++alloc_attempts);
@@ -259,8 +261,8 @@
 			    alloc_hits, ++alloc_attempts);
 		if (S_ISREG(inode->i_mode))
 			result = ext3_new_block (inode, goal, 
-				 &inode->u.ext3_i.i_prealloc_count,
-				 &inode->u.ext3_i.i_prealloc_block, err);
+				 &ei->i_prealloc_count,
+				 &ei->i_prealloc_block, err);
 		else
 			result = ext3_new_block (inode, goal, 0, 0, err);
 		/*
@@ -394,7 +396,7 @@
 
 	*err = 0;
 	/* i_data is not going away, no lock needed */
-	add_chain (chain, NULL, inode->u.ext3_i.i_data + *offsets);
+	add_chain (chain, NULL, EXT3_I(inode)->i_data + *offsets);
 	if (!p->key)
 		goto no_block;
 	while (--depth) {
@@ -437,7 +439,8 @@
 
 static inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind)
 {
-	u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext3_i.i_data;
+	struct ext3_inode_info *ei = EXT3_I(inode);
+	u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data;
 	u32 *p;
 
 	/* Try to find previous block */
@@ -453,8 +456,7 @@
 	 * It is going to be refered from inode itself? OK, just put it into
 	 * the same cylinder group then.
 	 */
-	return (inode->u.ext3_i.i_block_group * 
-		EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
+	return (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
 	       le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_first_data_block);
 }
 
@@ -474,14 +476,15 @@
 static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4],
 			  Indirect *partial, unsigned long *goal)
 {
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	/* Writer: ->i_next_alloc* */
-	if (block == inode->u.ext3_i.i_next_alloc_block + 1) {
-		inode->u.ext3_i.i_next_alloc_block++;
-		inode->u.ext3_i.i_next_alloc_goal++;
+	if (block == ei->i_next_alloc_block + 1) {
+		ei->i_next_alloc_block++;
+		ei->i_next_alloc_goal++;
 	}
 #ifdef SEARCH_FROM_ZERO
-	inode->u.ext3_i.i_next_alloc_block = 0;
-	inode->u.ext3_i.i_next_alloc_goal = 0;
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
 #endif
 	/* Writer: end */
 	/* Reader: pointers, ->i_next_alloc* */
@@ -490,8 +493,8 @@
 		 * try the heuristic for sequential allocation,
 		 * failing that at least try to get decent locality.
 		 */
-		if (block == inode->u.ext3_i.i_next_alloc_block)
-			*goal = inode->u.ext3_i.i_next_alloc_goal;
+		if (block == ei->i_next_alloc_block)
+			*goal = ei->i_next_alloc_goal;
 		if (!*goal)
 			*goal = ext3_find_near(inode, partial);
 #ifdef SEARCH_FROM_ZERO
@@ -619,6 +622,7 @@
 {
 	int i;
 	int err = 0;
+	struct ext3_inode_info *ei = EXT3_I(inode);
 
 	/*
 	 * If we're splicing into a [td]indirect block (as opposed to the
@@ -641,11 +645,11 @@
 	/* That's it */
 
 	*where->p = where->key;
-	inode->u.ext3_i.i_next_alloc_block = block;
-	inode->u.ext3_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+	ei->i_next_alloc_block = block;
+	ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
 #ifdef SEARCH_FROM_ZERO
-	inode->u.ext3_i.i_next_alloc_block = 0;
-	inode->u.ext3_i.i_next_alloc_goal = 0;
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
 #endif
 	/* Writer: end */
 
@@ -729,6 +733,7 @@
 	unsigned long goal;
 	int left;
 	int depth = ext3_block_to_path(inode, iblock, offsets);
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	loff_t new_size;
 
 	J_ASSERT(handle != NULL || create == 0);
@@ -780,7 +785,7 @@
 	/*
 	 * Block out ext3_truncate while we alter the tree
 	 */
-	down_read(&inode->u.ext3_i.truncate_sem);
+	down_read(&ei->truncate_sem);
 	err = ext3_alloc_branch(handle, inode, left, goal,
 					offsets+(partial-chain), partial);
 
@@ -792,7 +797,7 @@
 	if (!err)
 		err = ext3_splice_branch(handle, inode, iblock, chain,
 					 partial, left);
-	up_read(&inode->u.ext3_i.truncate_sem);
+	up_read(&ei->truncate_sem);
 	if (err == -EAGAIN)
 		goto changed;
 	if (err)
@@ -805,8 +810,8 @@
 	 * truncate is in progress.  It is racy between multiple parallel
 	 * instances of get_block, but we have the BKL.
 	 */
-	if (new_size > inode->u.ext3_i.i_disksize)
-		inode->u.ext3_i.i_disksize = new_size;
+	if (new_size > ei->i_disksize)
+		ei->i_disksize = new_size;
 
 	bh_result->b_state |= (1UL << BH_New);
 	goto got_it;
@@ -916,7 +921,7 @@
 		struct buffer_head *tmp_bh;
 
 		for (i = 1;
-		     inode->u.ext3_i.i_prealloc_count &&
+		     EXT3_I(inode)->i_prealloc_count &&
 		     i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
 		     i++) {
 			/*
@@ -1126,8 +1131,8 @@
 			kunmap(page);
 		}
 	}
-	if (inode->i_size > inode->u.ext3_i.i_disksize) {
-		inode->u.ext3_i.i_disksize = inode->i_size;
+	if (inode->i_size > EXT3_I(inode)->i_disksize) {
+		EXT3_I(inode)->i_disksize = inode->i_size;
 		ret2 = ext3_mark_inode_dirty(handle, inode);
 		if (!ret) 
 			ret = ret2;
@@ -1826,7 +1831,8 @@
 void ext3_truncate(struct inode * inode)
 {
 	handle_t *handle;
-	u32 *i_data = inode->u.ext3_i.i_data;
+	struct ext3_inode_info *ei = EXT3_I(inode);
+	u32 *i_data = ei->i_data;
 	int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
 	int offsets[4];
 	Indirect chain[4];
@@ -1878,13 +1884,13 @@
 	 * on-disk inode. We do this via i_disksize, which is the value which
 	 * ext3 *really* writes onto the disk inode.
 	 */
-	inode->u.ext3_i.i_disksize = inode->i_size;
+	ei->i_disksize = inode->i_size;
 
 	/*
 	 * From here we block out all ext3_get_block() callers who want to
 	 * modify the block allocation tree.
 	 */
-	down_write(&inode->u.ext3_i.truncate_sem);
+	down_write(&ei->truncate_sem);
 
 	if (n == 1) {		/* direct blocks */
 		ext3_free_data(handle, inode, NULL, i_data+offsets[0],
@@ -1948,7 +1954,7 @@
 		case EXT3_TIND_BLOCK:
 			;
 	}
-	up_write(&inode->u.ext3_i.truncate_sem);
+	up_write(&ei->truncate_sem);
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	ext3_mark_inode_dirty(handle, inode);
 
@@ -2041,6 +2047,7 @@
 {
 	struct ext3_iloc iloc;
 	struct ext3_inode *raw_inode;
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	struct buffer_head *bh;
 	int block;
 	
@@ -2048,7 +2055,6 @@
 		goto bad_inode;
 	bh = iloc.bh;
 	raw_inode = iloc.raw_inode;
-	init_rwsem(&inode->u.ext3_i.truncate_sem);
 	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
 	inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
 	inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
@@ -2061,7 +2067,12 @@
 	inode->i_atime = le32_to_cpu(raw_inode->i_atime);
 	inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
 	inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
-	inode->u.ext3_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+
+	ei->i_state = 0;
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
+	ei->i_dir_start_lookup = 0;
+	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
 	/* We now have enough fields to check if the inode was active or not.
 	 * This is needed because nfsd might try to access dead inodes
 	 * the test is that same one that e2fsck uses
@@ -2084,33 +2095,33 @@
 					 * size */  
 	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
 	inode->i_version = ++event;
-	inode->u.ext3_i.i_flags = le32_to_cpu(raw_inode->i_flags);
+	ei->i_flags = le32_to_cpu(raw_inode->i_flags);
 #ifdef EXT3_FRAGMENTS
-	inode->u.ext3_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
-	inode->u.ext3_i.i_frag_no = raw_inode->i_frag;
-	inode->u.ext3_i.i_frag_size = raw_inode->i_fsize;
+	ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
+	ei->i_frag_no = raw_inode->i_frag;
+	ei->i_frag_size = raw_inode->i_fsize;
 #endif
-	inode->u.ext3_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+	ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
 	if (!S_ISREG(inode->i_mode)) {
-		inode->u.ext3_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+		ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
 	} else {
 		inode->i_size |=
 			((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
 	}
-	inode->u.ext3_i.i_disksize = inode->i_size;
+	ei->i_disksize = inode->i_size;
 	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
 #ifdef EXT3_PREALLOCATE
-	inode->u.ext3_i.i_prealloc_count = 0;
+	ei->i_prealloc_count = 0;
 #endif
-	inode->u.ext3_i.i_block_group = iloc.block_group;
+	ei->i_block_group = iloc.block_group;
 
 	/*
 	 * NOTE! The in-memory inode i_data array is in little-endian order
 	 * even on big-endian machines: we do NOT byteswap the block numbers!
 	 */
 	for (block = 0; block < EXT3_N_BLOCKS; block++)
-		inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
-	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+		ei->i_data[block] = iloc.raw_inode->i_block[block];
+	INIT_LIST_HEAD(&ei->i_orphan);
 
 	brelse (iloc.bh);
 
@@ -2135,19 +2146,19 @@
 		init_special_inode(inode, inode->i_mode,
 				   le32_to_cpu(iloc.raw_inode->i_block[0]));
 	/* inode->i_attr_flags = 0;				unused */
-	if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) {
+	if (ei->i_flags & EXT3_SYNC_FL) {
 		/* inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; unused */
 		inode->i_flags |= S_SYNC;
 	}
-	if (inode->u.ext3_i.i_flags & EXT3_APPEND_FL) {
+	if (ei->i_flags & EXT3_APPEND_FL) {
 		/* inode->i_attr_flags |= ATTR_FLAG_APPEND;	unused */
 		inode->i_flags |= S_APPEND;
 	}
-	if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FL) {
+	if (ei->i_flags & EXT3_IMMUTABLE_FL) {
 		/* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;	unused */
 		inode->i_flags |= S_IMMUTABLE;
 	}
-	if (inode->u.ext3_i.i_flags & EXT3_NOATIME_FL) {
+	if (ei->i_flags & EXT3_NOATIME_FL) {
 		/* inode->i_attr_flags |= ATTR_FLAG_NOATIME;	unused */
 		inode->i_flags |= S_NOATIME;
 	}
@@ -2169,6 +2180,7 @@
 				struct ext3_iloc *iloc)
 {
 	struct ext3_inode *raw_inode = iloc->raw_inode;
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	struct buffer_head *bh = iloc->bh;
 	int err = 0, rc, block;
 
@@ -2186,7 +2198,7 @@
  * Fix up interoperability with old kernels. Otherwise, old inodes get
  * re-used with the upper 16 bits of the uid/gid intact
  */
-		if(!inode->u.ext3_i.i_dtime) {
+		if(!ei->i_dtime) {
 			raw_inode->i_uid_high =
 				cpu_to_le16(high_16_bits(inode->i_uid));
 			raw_inode->i_gid_high =
@@ -2204,34 +2216,34 @@
 		raw_inode->i_gid_high = 0;
 	}
 	raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
-	raw_inode->i_size = cpu_to_le32(inode->u.ext3_i.i_disksize);
+	raw_inode->i_size = cpu_to_le32(ei->i_disksize);
 	raw_inode->i_atime = cpu_to_le32(inode->i_atime);
 	raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
 	raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
 	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
-	raw_inode->i_dtime = cpu_to_le32(inode->u.ext3_i.i_dtime);
-	raw_inode->i_flags = cpu_to_le32(inode->u.ext3_i.i_flags);
+	raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
+	raw_inode->i_flags = cpu_to_le32(ei->i_flags);
 #ifdef EXT3_FRAGMENTS
-	raw_inode->i_faddr = cpu_to_le32(inode->u.ext3_i.i_faddr);
-	raw_inode->i_frag = inode->u.ext3_i.i_frag_no;
-	raw_inode->i_fsize = inode->u.ext3_i.i_frag_size;
+	raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
+	raw_inode->i_frag = ei->i_frag_no;
+	raw_inode->i_fsize = ei->i_frag_size;
 #else
 	/* If we are not tracking these fields in the in-memory inode,
 	 * then preserve them on disk, but still initialise them to zero
 	 * for new inodes. */
-	if (EXT3_I(inode)->i_state & EXT3_STATE_NEW) {
+	if (ei->i_state & EXT3_STATE_NEW) {
 		raw_inode->i_faddr = 0;
 		raw_inode->i_frag = 0;
 		raw_inode->i_fsize = 0;
 	}
 #endif
-	raw_inode->i_file_acl = cpu_to_le32(inode->u.ext3_i.i_file_acl);
+	raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
 	if (!S_ISREG(inode->i_mode)) {
-		raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext3_i.i_dir_acl);
+		raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
 	} else {
 		raw_inode->i_size_high =
-			cpu_to_le32(inode->u.ext3_i.i_disksize >> 32);
-		if (inode->u.ext3_i.i_disksize > 0x7fffffffULL) {
+			cpu_to_le32(ei->i_disksize >> 32);
+		if (ei->i_disksize > 0x7fffffffULL) {
 			struct super_block *sb = inode->i_sb;
 			if (!EXT3_HAS_RO_COMPAT_FEATURE(sb,
 					EXT3_FEATURE_RO_COMPAT_LARGE_FILE) ||
@@ -2259,13 +2271,13 @@
 		raw_inode->i_block[0] =
 			cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
 	else for (block = 0; block < EXT3_N_BLOCKS; block++)
-		raw_inode->i_block[block] = inode->u.ext3_i.i_data[block];
+		raw_inode->i_block[block] = ei->i_data[block];
 
 	BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
 	rc = ext3_journal_dirty_metadata(handle, bh);
 	if (!err)
 		err = rc;
-	EXT3_I(inode)->i_state &= ~EXT3_STATE_NEW;
+	ei->i_state &= ~EXT3_STATE_NEW;
 
 out_brelse:
 	brelse (bh);
@@ -2373,7 +2385,7 @@
 		}
 		
 		error = ext3_orphan_add(handle, inode);
-		inode->u.ext3_i.i_disksize = attr->ia_size;
+		EXT3_I(inode)->i_disksize = attr->ia_size;
 		rc = ext3_mark_inode_dirty(handle, inode);
 		if (!error)
 			error = rc;
@@ -2616,9 +2628,9 @@
 	 */
 
 	if (val)
-		inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+		EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
 	else
-		inode->u.ext3_i.i_flags &= ~EXT3_JOURNAL_DATA_FL;
+		EXT3_I(inode)->i_flags &= ~EXT3_JOURNAL_DATA_FL;
 
 	journal_unlock_updates(journal);
 
diff -urN linux-2.5.3-pre2/fs/ext3/ioctl.c linux/fs/ext3/ioctl.c
--- linux-2.5.3-pre2/fs/ext3/ioctl.c	Fri Nov  9 14:25:04 2001
+++ linux/fs/ext3/ioctl.c	Mon Jan 21 17:23:24 2002
@@ -18,13 +18,14 @@
 int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
 		unsigned long arg)
 {
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	unsigned int flags;
 
 	ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
 
 	switch (cmd) {
 	case EXT3_IOC_GETFLAGS:
-		flags = inode->u.ext3_i.i_flags & EXT3_FL_USER_VISIBLE;
+		flags = ei->i_flags & EXT3_FL_USER_VISIBLE;
 		return put_user(flags, (int *) arg);
 	case EXT3_IOC_SETFLAGS: {
 		handle_t *handle = NULL;
@@ -42,7 +43,7 @@
 		if (get_user(flags, (int *) arg))
 			return -EFAULT;
 
-		oldflags = inode->u.ext3_i.i_flags;
+		oldflags = ei->i_flags;
 
 		/* The JOURNAL_DATA flag is modifiable only by root */
 		jflag = flags & EXT3_JOURNAL_DATA_FL;
@@ -79,7 +80,7 @@
 		
 		flags = flags & EXT3_FL_USER_MODIFIABLE;
 		flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE;
-		inode->u.ext3_i.i_flags = flags;
+		ei->i_flags = flags;
 
 		if (flags & EXT3_SYNC_FL)
 			inode->i_flags |= S_SYNC;
diff -urN linux-2.5.3-pre2/fs/ext3/namei.c linux/fs/ext3/namei.c
--- linux-2.5.3-pre2/fs/ext3/namei.c	Fri Nov  9 14:25:04 2001
+++ linux/fs/ext3/namei.c	Mon Jan 21 17:23:24 2002
@@ -124,7 +124,7 @@
 	sb = dir->i_sb;
 
 	nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);
-	start = dir->u.ext3_i.i_dir_start_lookup;
+	start = EXT3_I(dir)->i_dir_start_lookup;
 	if (start >= nblocks)
 		start = 0;
 	block = start;
@@ -165,7 +165,7 @@
 		i = search_dirblock(bh, dir, dentry,
 			    block << EXT3_BLOCK_SIZE_BITS(sb), res_dir);
 		if (i == 1) {
-			dir->u.ext3_i.i_dir_start_lookup = block;
+			EXT3_I(dir)->i_dir_start_lookup = block;
 			ret = bh;
 			goto cleanup_and_exit;
 		} else {
@@ -295,9 +295,9 @@
 				de = (struct ext3_dir_entry_2 *) bh->b_data;
 				de->inode = 0;
 				de->rec_len = le16_to_cpu(sb->s_blocksize);
-				dir->u.ext3_i.i_disksize =
+				EXT3_I(dir)->i_disksize =
 					dir->i_size = offset + sb->s_blocksize;
-				dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+				EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
 				ext3_mark_inode_dirty(handle, dir);
 			} else {
 
@@ -353,7 +353,7 @@
 			 * and/or different from the directory change time.
 			 */
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
-			dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+			EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
 			ext3_mark_inode_dirty(handle, dir);
 			dir->i_version = ++event;
 			BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
@@ -521,7 +521,7 @@
 
 	inode->i_op = &ext3_dir_inode_operations;
 	inode->i_fop = &ext3_dir_operations;
-	inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize;
+	inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
 	inode->i_blocks = 0;	
 	dir_block = ext3_bread (handle, inode, 0, 1, &err);
 	if (!dir_block) {
@@ -557,7 +557,7 @@
 	if (err)
 		goto out_no_entry;
 	dir->i_nlink++;
-	dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+	EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
 	ext3_mark_inode_dirty(handle, dir);
 	d_instantiate(dentry, inode);
 out_stop:
@@ -655,7 +655,7 @@
 	int err = 0, rc;
 	
 	lock_super(sb);
-	if (!list_empty(&inode->u.ext3_i.i_orphan))
+	if (!list_empty(&EXT3_I(inode)->i_orphan))
 		goto out_unlock;
 
 	/* Orphan handling is only valid for files with data blocks
@@ -696,7 +696,7 @@
 	 * This is safe: on error we're going to ignore the orphan list
 	 * anyway on the next recovery. */
 	if (!err)
-		list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan);
+		list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan);
 
 	jbd_debug(4, "superblock will point to %ld\n", inode->i_ino);
 	jbd_debug(4, "orphan inode %ld will point to %d\n",
@@ -714,25 +714,25 @@
 int ext3_orphan_del(handle_t *handle, struct inode *inode)
 {
 	struct list_head *prev;
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	struct ext3_sb_info *sbi;
 	ino_t ino_next; 
 	struct ext3_iloc iloc;
 	int err = 0;
 	
 	lock_super(inode->i_sb);
-	if (list_empty(&inode->u.ext3_i.i_orphan)) {
+	if (list_empty(&ei->i_orphan)) {
 		unlock_super(inode->i_sb);
 		return 0;
 	}
 
 	ino_next = NEXT_ORPHAN(inode);
-	prev = inode->u.ext3_i.i_orphan.prev;
+	prev = ei->i_orphan.prev;
 	sbi = EXT3_SB(inode->i_sb);
 
 	jbd_debug(4, "remove inode %ld from orphan list\n", inode->i_ino);
 
-	list_del(&inode->u.ext3_i.i_orphan);
-	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+	list_del_init(&ei->i_orphan);
 
 	/* If we're on an error path, we may not have a valid
 	 * transaction handle with which to update the orphan list on
@@ -756,7 +756,7 @@
 	} else {
 		struct ext3_iloc iloc2;
 		struct inode *i_prev =
-			list_entry(prev, struct inode, u.ext3_i.i_orphan);
+			&list_entry(prev, struct ext3_inode_info, i_orphan)->vfs_inode;
 		
 		jbd_debug(4, "orphan inode %ld will point to %ld\n",
 			  i_prev->i_ino, ino_next);
@@ -832,7 +832,7 @@
 	ext3_mark_inode_dirty(handle, inode);
 	dir->i_nlink--;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+	EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
 	ext3_mark_inode_dirty(handle, dir);
 
 end_rmdir:
@@ -878,7 +878,7 @@
 	if (retval)
 		goto end_unlink;
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+	EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
 	ext3_mark_inode_dirty(handle, dir);
 	inode->i_nlink--;
 	if (!inode->i_nlink)
@@ -916,7 +916,7 @@
 	if (IS_ERR(inode))
 		goto out_stop;
 
-	if (l > sizeof (inode->u.ext3_i.i_data)) {
+	if (l > sizeof (EXT3_I(inode)->i_data)) {
 		inode->i_op = &page_symlink_inode_operations;
 		inode->i_mapping->a_ops = &ext3_aops;
 		/*
@@ -929,10 +929,10 @@
 			goto out_no_entry;
 	} else {
 		inode->i_op = &ext3_fast_symlink_inode_operations;
-		memcpy((char*)&inode->u.ext3_i.i_data,symname,l);
+		memcpy((char*)&EXT3_I(inode)->i_data,symname,l);
 		inode->i_size = l-1;
 	}
-	inode->u.ext3_i.i_disksize = inode->i_size;
+	EXT3_I(inode)->i_disksize = inode->i_size;
 	ext3_mark_inode_dirty(handle, inode);
 	err = ext3_add_nondir(handle, dentry, inode);
 out_stop:
@@ -1077,7 +1077,7 @@
 		new_inode->i_ctime = CURRENT_TIME;
 	}
 	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
-	old_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+	EXT3_I(old_dir)->i_flags &= ~EXT3_INDEX_FL;
 	if (dir_bh) {
 		BUFFER_TRACE(dir_bh, "get_write_access");
 		ext3_journal_get_write_access(handle, dir_bh);
@@ -1089,7 +1089,7 @@
 			new_inode->i_nlink--;
 		} else {
 			new_dir->i_nlink++;
-			new_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+			EXT3_I(new_dir)->i_flags &= ~EXT3_INDEX_FL;
 			ext3_mark_inode_dirty(handle, new_dir);
 		}
 	}
diff -urN linux-2.5.3-pre2/fs/ext3/super.c linux/fs/ext3/super.c
--- linux-2.5.3-pre2/fs/ext3/super.c	Wed Jan  9 11:56:57 2002
+++ linux/fs/ext3/super.c	Mon Jan 21 17:23:24 2002
@@ -376,7 +376,10 @@
 	return ret;
 }
 
-#define orphan_list_entry(l) list_entry((l), struct inode, u.ext3_i.i_orphan)
+static inline struct inode *orphan_list_entry(struct list_head *l)
+{
+	return &list_entry(l, struct ext3_inode_info, i_orphan)->vfs_inode;
+}
 
 static void dump_orphan_list(struct super_block *sb, struct ext3_sb_info *sbi)
 {
@@ -444,7 +447,54 @@
 	return;
 }
 
+static kmem_cache_t * ext3_inode_cachep;
+
+static struct inode *ext3_alloc_inode(struct super_block *sb)
+{
+	struct ext3_inode_info *ei;
+	ei = (struct ext3_inode_info *)kmem_cache_alloc(ext3_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void ext3_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(ext3_inode_cachep, EXT3_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct ext3_inode_info *ei = (struct ext3_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		INIT_LIST_HEAD(&ei->i_orphan);
+		init_rwsem(&ei->truncate_sem);
+		inode_init_once(&ei->vfs_inode);
+	}
+}
+ 
+static int init_inodecache(void)
+{
+	ext3_inode_cachep = kmem_cache_create("ext3_inode_cache",
+					     sizeof(struct ext3_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (ext3_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(ext3_inode_cachep))
+		printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n");
+}
+
 static struct super_operations ext3_sops = {
+	alloc_inode:	ext3_alloc_inode,
+	destroy_inode:	ext3_destroy_inode,
 	read_inode:	ext3_read_inode,	/* BKL held */
 	write_inode:	ext3_write_inode,	/* BKL not held.  Don't need */
 	dirty_inode:	ext3_dirty_inode,	/* BKL not held.  We take it */
@@ -1723,12 +1773,23 @@
 
 static int __init init_ext3_fs(void)
 {
-        return register_filesystem(&ext3_fs_type);
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+        err = register_filesystem(&ext3_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
 }
 
 static void __exit exit_ext3_fs(void)
 {
 	unregister_filesystem(&ext3_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.5.3-pre2/fs/ext3/symlink.c linux/fs/ext3/symlink.c
--- linux-2.5.3-pre2/fs/ext3/symlink.c	Fri Nov  9 14:25:04 2001
+++ linux/fs/ext3/symlink.c	Mon Jan 21 17:23:24 2002
@@ -23,14 +23,14 @@
 
 static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
-	char *s = (char *)dentry->d_inode->u.ext3_i.i_data;
-	return vfs_readlink(dentry, buffer, buflen, s);
+	struct ext3_inode_info *ei = EXT3_I(dentry->d_inode);
+	return vfs_readlink(dentry, buffer, buflen, (char*)ei->i_data);
 }
 
 static int ext3_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-	char *s = (char *)dentry->d_inode->u.ext3_i.i_data;
-	return vfs_follow_link(nd, s);
+	struct ext3_inode_info *ei = EXT3_I(dentry->d_inode);
+	return vfs_follow_link(nd, (char*)ei->i_data);
 }
 
 struct inode_operations ext3_fast_symlink_inode_operations = {
diff -urN linux-2.5.3-pre2/fs/fat/dir.c linux/fs/fat/dir.c
--- linux-2.5.3-pre2/fs/fat/dir.c	Mon Jan 21 17:22:37 2002
+++ linux/fs/fat/dir.c	Mon Jan 21 17:23:24 2002
@@ -744,8 +744,8 @@
 	if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) 
 		return -ENOSPC;
 	new_bh = fat_extend_dir(dir);
-	if (!new_bh)
-		return -ENOSPC;
+	if (IS_ERR(new_bh))
+		return PTR_ERR(new_bh);
 	fat_brelse(sb, new_bh);
 	do {
 		fat_get_entry(dir, &curr, bh, de, ino);
@@ -761,7 +761,10 @@
 	struct msdos_dir_entry *de;
 	__u16 date, time;
 
-	if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC;
+	bh = fat_extend_dir(dir);
+	if (IS_ERR(bh))
+		return PTR_ERR(bh);
+
 	/* zeroed out, so... */
 	fat_date_unix2dos(dir->i_mtime,&time,&date);
 	de = (struct msdos_dir_entry*)&bh->b_data[0];
diff -urN linux-2.5.3-pre2/fs/fat/file.c linux/fs/fat/file.c
--- linux-2.5.3-pre2/fs/fat/file.c	Thu Dec 27 08:17:43 2001
+++ linux/fs/fat/file.c	Mon Jan 21 17:23:24 2002
@@ -65,8 +65,11 @@
 		return -EIO;
 	}
 	if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) {
-		if (fat_add_cluster(inode) < 0)
-			return -ENOSPC;
+		int error;
+
+		error = fat_add_cluster(inode);
+		if (error < 0)
+			return error;
 	}
 	MSDOS_I(inode)->mmu_private += sb->s_blocksize;
 	phys = fat_bmap(inode, iblock);
diff -urN linux-2.5.3-pre2/fs/fat/misc.c linux/fs/fat/misc.c
--- linux-2.5.3-pre2/fs/fat/misc.c	Mon Jan 21 17:22:37 2002
+++ linux/fs/fat/misc.c	Mon Jan 21 17:23:24 2002
@@ -131,14 +131,13 @@
 {
 	struct super_block *sb = inode->i_sb;
 	int count, nr, limit, last, curr, file_cluster;
-	int cluster_size = MSDOS_SB(sb)->cluster_size;
-	int res = -ENOSPC;
+	int cluster_bits = MSDOS_SB(sb)->cluster_bits;
 	
 	lock_fat(sb);
 	
 	if (MSDOS_SB(sb)->free_clusters == 0) {
 		unlock_fat(sb);
-		return res;
+		return -ENOSPC;
 	}
 	limit = MSDOS_SB(sb)->clusters;
 	nr = limit; /* to keep GCC happy */
@@ -150,7 +149,7 @@
 	if (count >= limit) {
 		MSDOS_SB(sb)->free_clusters = 0;
 		unlock_fat(sb);
-		return res;
+		return -ENOSPC;
 	}
 	
 	MSDOS_SB(sb)->prev_free = (count + MSDOS_SB(sb)->prev_free + 1) % limit;
@@ -174,13 +173,20 @@
 	*/
 	last = file_cluster = 0;
 	if ((curr = MSDOS_I(inode)->i_start) != 0) {
+		int max_cluster = MSDOS_I(inode)->mmu_private >> cluster_bits;
+
 		fat_cache_lookup(inode, INT_MAX, &last, &curr);
 		file_cluster = last;
-		while (curr && curr != -1){
+		while (curr && curr != -1) {
 			file_cluster++;
-			if (!(curr = fat_access(sb, last = curr,-1))) {
+			if (!(curr = fat_access(sb, last = curr, -1))) {
 				fat_fs_panic(sb, "File without EOF");
-				return res;
+				return -EIO;
+			}
+			if (file_cluster > max_cluster) {
+				fat_fs_panic(sb,"inode %lu: bad cluster counts",
+					     inode->i_ino);
+				return -EIO;
 			}
 		}
 	}
@@ -192,14 +198,12 @@
 		MSDOS_I(inode)->i_logstart = nr;
 		mark_inode_dirty(inode);
 	}
-	if (file_cluster
-	    != inode->i_blocks / cluster_size / (sb->s_blocksize / 512)) {
+	if (file_cluster != (inode->i_blocks >> (cluster_bits - 9))) {
 		printk ("file_cluster badly computed!!! %d <> %ld\n",
-			file_cluster,
-			inode->i_blocks / cluster_size / (sb->s_blocksize / 512));
+			file_cluster, inode->i_blocks >> (cluster_bits - 9));
 		fat_cache_inval_inode(inode);
 	}
-	inode->i_blocks += (1 << MSDOS_SB(sb)->cluster_bits) / 512;
+	inode->i_blocks += (1 << cluster_bits) >> 9;
 
 	return nr;
 }
@@ -213,24 +217,23 @@
 
 	if (MSDOS_SB(sb)->fat_bits != 32) {
 		if (inode->i_ino == MSDOS_ROOT_INO)
-			return res;
+			return ERR_PTR(-ENOSPC);
 	}
 
 	nr = fat_add_cluster(inode);
 	if (nr < 0)
-		return res;
+		return ERR_PTR(nr);
 	
 	sector = MSDOS_SB(sb)->data_start + (nr - 2) * cluster_size;
 	last_sector = sector + cluster_size;
-	if (MSDOS_SB(sb)->cvf_format && MSDOS_SB(sb)->cvf_format->zero_out_cluster)
+	if (MSDOS_SB(sb)->cvf_format
+	    && MSDOS_SB(sb)->cvf_format->zero_out_cluster) {
+		res = ERR_PTR(-EIO);
 		MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode, nr);
-	else {
+	} else {
 		for ( ; sector < last_sector; sector++) {
-#ifdef DEBUG
-			printk("zeroing sector %d\n", sector);
-#endif
 			if (!(bh = fat_getblk(sb, sector)))
-				printk("getblk failed\n");
+				printk("FAT: fat_getblk() failed\n");
 			else {
 				memset(bh->b_data, 0, sb->s_blocksize);
 				fat_set_uptodate(sb, bh, 1);
@@ -241,6 +244,8 @@
 					fat_brelse(sb, bh);
 			}
 		}
+		if (res == NULL)
+			res = ERR_PTR(-EIO);
 	}
 	if (inode->i_size & (sb->s_blocksize - 1)) {
 		fat_fs_panic(sb, "Odd directory size");
@@ -484,12 +489,11 @@
     **res_de)
 {
 	int count, cluster;
-	unsigned long dir_size;
+	unsigned long dir_size = 0;
 
 #ifdef DEBUG
 	printk("raw_scan_nonroot: start=%d\n",start);
 #endif
-	dir_size = 0;
 	do {
 		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 			if ((cluster = raw_scan_sector(sb,(start-2)*
diff -urN linux-2.5.3-pre2/fs/hpfs/anode.c linux/fs/hpfs/anode.c
--- linux-2.5.3-pre2/fs/hpfs/anode.c	Mon Jun 11 19:15:27 2001
+++ linux/fs/hpfs/anode.c	Mon Jan 21 17:23:24 2002
@@ -42,9 +42,10 @@
 				return -1;
 			}
 			if (inode) {
-				inode->i_hpfs_file_sec = btree->u.external[i].file_secno;
-				inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno;
-				inode->i_hpfs_n_secs = btree->u.external[i].length;
+				struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
+				hpfs_inode->i_file_sec = btree->u.external[i].file_secno;
+				hpfs_inode->i_disk_sec = btree->u.external[i].disk_secno;
+				hpfs_inode->i_n_secs = btree->u.external[i].length;
 			}
 			brelse(bh);
 			return a;
diff -urN linux-2.5.3-pre2/fs/hpfs/buffer.c linux/fs/hpfs/buffer.c
--- linux-2.5.3-pre2/fs/hpfs/buffer.c	Sun Dec 16 12:23:00 2001
+++ linux/fs/hpfs/buffer.c	Mon Jan 21 17:23:24 2002
@@ -47,38 +47,72 @@
 
 void hpfs_lock_inode(struct inode *i)
 {
-	if (i) down(&i->i_hpfs_sem);
+	if (i) {
+		struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
+		down(&hpfs_inode->i_sem);
+	}
 }
 
 void hpfs_unlock_inode(struct inode *i)
 {
-	if (i) up(&i->i_hpfs_sem);
+	if (i) {
+		struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
+		up(&hpfs_inode->i_sem);
+	}
 }
 
 void hpfs_lock_2inodes(struct inode *i1, struct inode *i2)
 {
-	if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; }
-	if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; }
+	struct hpfs_inode_info *hpfs_i1 = NULL, *hpfs_i2 = NULL;
+
+	if (!i1) {
+		if (i2) {
+			hpfs_i2 = hpfs_i(i2);
+			down(&hpfs_i2->i_sem);
+		}
+		return;
+	}
+	if (!i2) {
+		if (i1) {
+			hpfs_i1 = hpfs_i(i1);
+			down(&hpfs_i1->i_sem);
+		}
+		return;
+	}
 	if (i1->i_ino < i2->i_ino) {
-		down(&i1->i_hpfs_sem);
-		down(&i2->i_hpfs_sem);
+		down(&hpfs_i1->i_sem);
+		down(&hpfs_i2->i_sem);
 	} else if (i1->i_ino > i2->i_ino) {
-		down(&i2->i_hpfs_sem);
-		down(&i1->i_hpfs_sem);
-	} else down(&i1->i_hpfs_sem);
+		down(&hpfs_i2->i_sem);
+		down(&hpfs_i1->i_sem);
+	} else down(&hpfs_i1->i_sem);
 }
 
 void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2)
 {
-	if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; }
-	if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; }
+	struct hpfs_inode_info *hpfs_i1 = NULL, *hpfs_i2 = NULL;
+
+	if (!i1) {
+		if (i2) {
+			hpfs_i2 = hpfs_i(i2);
+			up(&hpfs_i2->i_sem);
+		}
+		return;
+	}
+	if (!i2) {
+		if (i1) {
+			hpfs_i1 = hpfs_i(i1);
+			up(&hpfs_i1->i_sem);
+		}
+		return;
+	}
 	if (i1->i_ino < i2->i_ino) {
-		up(&i2->i_hpfs_sem);
-		up(&i1->i_hpfs_sem);
+		up(&hpfs_i2->i_sem);
+		up(&hpfs_i1->i_sem);
 	} else if (i1->i_ino > i2->i_ino) {
-		up(&i1->i_hpfs_sem);
-		up(&i2->i_hpfs_sem);
-	} else up(&i1->i_hpfs_sem);
+		up(&hpfs_i1->i_sem);
+		up(&hpfs_i2->i_sem);
+	} else up(&hpfs_i1->i_sem);
 }
 
 void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
@@ -87,13 +121,16 @@
 	if (!i2) { hpfs_lock_2inodes(i1, i3); return; }
 	if (!i3) { hpfs_lock_2inodes(i1, i2); return; }
 	if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
-		down(&i1->i_hpfs_sem);
+		struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1);
+		down(&hpfs_i1->i_sem);
 		hpfs_lock_2inodes(i2, i3);
 	} else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
-		down(&i2->i_hpfs_sem);
+		struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2);
+		down(&hpfs_i2->i_sem);
 		hpfs_lock_2inodes(i1, i3);
 	} else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
-		down(&i3->i_hpfs_sem);
+		struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3);
+		down(&hpfs_i3->i_sem);
 		hpfs_lock_2inodes(i1, i2);
 	} else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2);
 	else hpfs_lock_2inodes(i1, i3);
@@ -105,14 +142,17 @@
 	if (!i2) { hpfs_unlock_2inodes(i1, i3); return; }
 	if (!i3) { hpfs_unlock_2inodes(i1, i2); return; }
 	if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+		struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1);
 		hpfs_unlock_2inodes(i2, i3);
-		up(&i1->i_hpfs_sem);
+		up(&hpfs_i1->i_sem);
 	} else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+		struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2);
 		hpfs_unlock_2inodes(i1, i3);
-		up(&i2->i_hpfs_sem);
+		up(&hpfs_i2->i_sem);
 	} else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+		struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3);
 		hpfs_unlock_2inodes(i1, i2);
-		up(&i3->i_hpfs_sem);
+		up(&hpfs_i3->i_sem);
 	} else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2);
 	else hpfs_unlock_2inodes(i1, i3);
 }
diff -urN linux-2.5.3-pre2/fs/hpfs/dir.c linux/fs/hpfs/dir.c
--- linux-2.5.3-pre2/fs/hpfs/dir.c	Mon Jun 11 19:15:27 2001
+++ linux/fs/hpfs/dir.c	Mon Jan 21 17:23:24 2002
@@ -27,11 +27,12 @@
 	loff_t pos;
 	struct quad_buffer_head qbh;
 	struct inode *i = filp->f_dentry->d_inode;
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct super_block *s = i->i_sb;
 	/*printk("dir lseek\n");*/
 	if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok;
 	hpfs_lock_inode(i);
-	pos = ((loff_t) hpfs_de_as_down_as_possible(s, i->i_hpfs_dno) << 4) + 1;
+	pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1;
 	while (pos != new_off) {
 		if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh);
 		else goto fail;
@@ -49,6 +50,7 @@
 int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
 	struct quad_buffer_head qbh;
 	struct hpfs_dirent *de;
 	int lc;
@@ -59,7 +61,7 @@
 	if (inode->i_sb->s_hpfs_chk) {
 		if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode"))
 			return -EFSERROR;
-		if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode"))
+		if (hpfs_chk_sectors(inode->i_sb, hpfs_inode->i_dno, 4, "dir_dnode"))
 			return -EFSERROR;
 	}
 	if (inode->i_sb->s_hpfs_chk >= 2) {
@@ -72,9 +74,9 @@
 			e = 1;
 			hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino);
 		}
-		if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) {
+		if (hpfs_inode->i_dno != fno->u.external[0].disk_secno) {
 			e = 1;
-			hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno);
+			hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, fno->u.external[0].disk_secno);
 		}
 		brelse(bh);
 		if (e) return -EFSERROR;
@@ -115,14 +117,14 @@
 			filp->f_pos = 11;
 		}
 		if (filp->f_pos == 11) {
-			if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir, DT_DIR) < 0) {
+			if (filldir(dirent, "..", 2, filp->f_pos, hpfs_inode->i_parent_dir, DT_DIR) < 0) {
 				hpfs_unlock_inode(inode);
 				return 0;
 			}
 			filp->f_pos = 1;
 		}
 		if (filp->f_pos == 1) {
-			filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1;
+			filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, hpfs_inode->i_dno) << 4) + 1;
 			hpfs_add_pos(inode, &filp->f_pos);
 			filp->f_version = inode->i_version;
 		}
@@ -180,6 +182,7 @@
 	ino_t ino;
 	int err;
 	struct inode *result = NULL;
+	struct hpfs_inode_info *hpfs_result;
 
 	if ((err = hpfs_chk_name((char *)name, &len))) {
 		if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG);
@@ -191,7 +194,7 @@
 	 * '.' and '..' will never be passed here.
 	 */
 
-	de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh);
+	de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *) name, len, NULL, &qbh);
 
 	/*
 	 * This is not really a bailout, just means file not found.
@@ -215,7 +218,8 @@
 		hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode");
 		goto bail1;
 	}
-	if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino;
+	hpfs_result = hpfs_i(result);
+	if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino;
 	hpfs_unlock_iget(dir->i_sb);
 
 	hpfs_decide_conv(result, (char *)name, len);
@@ -235,14 +239,14 @@
 			result->i_ctime = 1;
 		result->i_mtime = local_to_gmt(dir->i_sb, de->write_date);
 		result->i_atime = local_to_gmt(dir->i_sb, de->read_date);
-		result->i_hpfs_ea_size = de->ea_size;
-		if (!result->i_hpfs_ea_mode && de->read_only)
+		hpfs_result->i_ea_size = de->ea_size;
+		if (!hpfs_result->i_ea_mode && de->read_only)
 			result->i_mode &= ~0222;
 		if (!de->directory) {
 			if (result->i_size == -1) {
 				result->i_size = de->file_size;
 				result->i_data.a_ops = &hpfs_aops;
-				result->u.hpfs_i.mmu_private = result->i_size;
+				hpfs_i(result)->mmu_private = result->i_size;
 			/*
 			 * i_blocks should count the fnode and any anodes.
 			 * We count 1 for the fnode and don't bother about
diff -urN linux-2.5.3-pre2/fs/hpfs/dnode.c linux/fs/hpfs/dnode.c
--- linux-2.5.3-pre2/fs/hpfs/dnode.c	Tue Sep  5 14:07:29 2000
+++ linux/fs/hpfs/dnode.c	Mon Jan 21 17:23:24 2002
@@ -23,39 +23,43 @@
 
 void hpfs_add_pos(struct inode *inode, loff_t *pos)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
 	int i = 0;
 	loff_t **ppos;
-	if (inode->i_hpfs_rddir_off)
-		for (; inode->i_hpfs_rddir_off[i]; i++)
-			if (inode->i_hpfs_rddir_off[i] == pos) return;
+
+	if (hpfs_inode->i_rddir_off)
+		for (; hpfs_inode->i_rddir_off[i]; i++)
+			if (hpfs_inode->i_rddir_off[i] == pos) return;
 	if (!(i&0x0f)) {
 		if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) {
 			printk("HPFS: out of memory for position list\n");
 			return;
 		}
-		if (inode->i_hpfs_rddir_off) {
-			memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t));
-			kfree(inode->i_hpfs_rddir_off);
+		if (hpfs_inode->i_rddir_off) {
+			memcpy(ppos, hpfs_inode->i_rddir_off, i * sizeof(loff_t));
+			kfree(hpfs_inode->i_rddir_off);
 		}
-		inode->i_hpfs_rddir_off = ppos;
+		hpfs_inode->i_rddir_off = ppos;
 	}
-	inode->i_hpfs_rddir_off[i] = pos;
-	inode->i_hpfs_rddir_off[i + 1] = NULL;
+	hpfs_inode->i_rddir_off[i] = pos;
+	hpfs_inode->i_rddir_off[i + 1] = NULL;
 }
 
 void hpfs_del_pos(struct inode *inode, loff_t *pos)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
 	loff_t **i, **j;
-	if (!inode->i_hpfs_rddir_off) goto not_f;
-	for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd;
+
+	if (!hpfs_inode->i_rddir_off) goto not_f;
+	for (i = hpfs_inode->i_rddir_off; *i; i++) if (*i == pos) goto fnd;
 	goto not_f;
 	fnd:
 	for (j = i + 1; *j; j++) ;
 	*i = *(j - 1);
 	*(j - 1) = NULL;
-	if (j - 1 == inode->i_hpfs_rddir_off) {
-		kfree(inode->i_hpfs_rddir_off);
-		inode->i_hpfs_rddir_off = NULL;
+	if (j - 1 == hpfs_inode->i_rddir_off) {
+		kfree(hpfs_inode->i_rddir_off);
+		hpfs_inode->i_rddir_off = NULL;
 	}
 	return;
 	not_f:
@@ -66,9 +70,11 @@
 static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
 			 loff_t p1, loff_t p2)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
 	loff_t **i;
-	if (!inode->i_hpfs_rddir_off) return;
-	for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2);
+
+	if (!hpfs_inode->i_rddir_off) return;
+	for (i = hpfs_inode->i_rddir_off; *i; i++) (*f)(*i, p1, p2);
 	return;
 }
 
@@ -339,7 +345,7 @@
 	fnode->u.external[0].disk_secno = rdno;
 	mark_buffer_dirty(bh);
 	brelse(bh);
-	d->up = ad->up = i->i_hpfs_dno = rdno;
+	d->up = ad->up = hpfs_i(i)->i_dno = rdno;
 	d->root_dnode = ad->root_dnode = 0;
 	hpfs_mark_4buffers_dirty(&qbh);
 	hpfs_brelse4(&qbh);
@@ -363,13 +369,14 @@
 int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
 		    struct hpfs_dirent *new_de, int cdepth)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct dnode *d;
 	struct hpfs_dirent *de, *de_end;
 	struct quad_buffer_head qbh;
 	dnode_secno dno;
 	int c;
 	int c1, c2 = 0;
-	dno = i->i_hpfs_dno;
+	dno = hpfs_inode->i_dno;
 	down:
 	if (i->i_sb->s_hpfs_chk)
 		if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
@@ -494,6 +501,7 @@
 
 static void delete_empty_dnode(struct inode *i, dnode_secno dno)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct quad_buffer_head qbh;
 	struct dnode *dnode;
 	dnode_secno down, up, ndown;
@@ -538,7 +546,7 @@
 				mark_buffer_dirty(bh);
 				brelse(bh);
 			}
-			i->i_hpfs_dno = down;
+			hpfs_inode->i_dno = down;
 			for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) 12);
 			return;
 		}
diff -urN linux-2.5.3-pre2/fs/hpfs/ea.c linux/fs/hpfs/ea.c
--- linux-2.5.3-pre2/fs/hpfs/ea.c	Tue Sep  5 14:07:29 2000
+++ linux/fs/hpfs/ea.c	Mon Jan 21 17:23:24 2002
@@ -355,7 +355,7 @@
 	if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
 	fnode->ea_size_l = pos;
 	ret:
-	inode->i_hpfs_ea_size += 5 + strlen(key) + size;
+	hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size;
 	return;
 	bail:
 	if (fnode->ea_secno)
diff -urN linux-2.5.3-pre2/fs/hpfs/file.c linux/fs/hpfs/file.c
--- linux-2.5.3-pre2/fs/hpfs/file.c	Thu Dec 27 08:17:43 2001
+++ linux/fs/hpfs/file.c	Mon Jan 21 17:23:24 2002
@@ -45,12 +45,13 @@
 
 secno hpfs_bmap(struct inode *inode, unsigned file_secno)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
 	unsigned n, disk_secno;
 	struct fnode *fnode;
 	struct buffer_head *bh;
-	if (BLOCKS(inode->u.hpfs_i.mmu_private) <= file_secno) return 0;
-	n = file_secno - inode->i_hpfs_file_sec;
-	if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n;
+	if (BLOCKS(hpfs_i(inode)->mmu_private) <= file_secno) return 0;
+	n = file_secno - hpfs_inode->i_file_sec;
+	if (n < hpfs_inode->i_n_secs) return hpfs_inode->i_disk_sec + n;
 	if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
 	disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
 	if (disk_secno == -1) return 0;
@@ -61,9 +62,9 @@
 void hpfs_truncate(struct inode *i)
 {
 	if (IS_IMMUTABLE(i)) return /*-EPERM*/;
-	i->i_hpfs_n_secs = 0;
+	hpfs_i(i)->i_n_secs = 0;
 	i->i_blocks = 1 + ((i->i_size + 511) >> 9);
-	i->u.hpfs_i.mmu_private = i->i_size;
+	hpfs_i(i)->mmu_private = i->i_size;
 	hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
 	hpfs_write_inode(i);
 }
@@ -77,7 +78,7 @@
 		return 0;
 	}
 	if (!create) return 0;
-	if (iblock<<9 != inode->u.hpfs_i.mmu_private) {
+	if (iblock<<9 != hpfs_i(inode)->mmu_private) {
 		BUG();
 		return -EIO;
 	}
@@ -86,7 +87,7 @@
 		return -ENOSPC;
 	}
 	inode->i_blocks++;
-	inode->u.hpfs_i.mmu_private += 512;
+	hpfs_i(inode)->mmu_private += 512;
 	bh_result->b_state |= 1UL << BH_New;
 	map_bh(bh_result, inode->i_sb, s);
 	return 0;
@@ -103,7 +104,7 @@
 static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
 	return cont_prepare_write(page,from,to,hpfs_get_block,
-		&page->mapping->host->u.hpfs_i.mmu_private);
+		&hpfs_i(page->mapping->host)->mmu_private);
 }
 static int _hpfs_bmap(struct address_space *mapping, long block)
 {
@@ -126,7 +127,7 @@
 	if (retval > 0) {
 		struct inode *inode = file->f_dentry->d_inode;
 		inode->i_mtime = CURRENT_TIME;
-		inode->i_hpfs_dirty = 1;
+		hpfs_i(inode)->i_dirty = 1;
 	}
 	return retval;
 }
diff -urN linux-2.5.3-pre2/fs/hpfs/hpfs_fn.h linux/fs/hpfs/hpfs_fn.h
--- linux-2.5.3-pre2/fs/hpfs/hpfs_fn.h	Sun Dec 30 10:31:51 2001
+++ linux/fs/hpfs/hpfs_fn.h	Mon Jan 21 17:23:24 2002
@@ -11,6 +11,7 @@
 
 #include <linux/fs.h>
 #include <linux/hpfs_fs.h>
+#include <linux/hpfs_fs_i.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
@@ -302,6 +303,11 @@
 int hpfs_rmdir(struct inode *, struct dentry *);
 int hpfs_symlink_readpage(struct file *, struct page *);
 int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+static inline struct hpfs_inode_info *hpfs_i(struct inode *inode)
+{
+	return list_entry(inode, struct hpfs_inode_info, vfs_inode);
+}
 
 /* super.c */
 
diff -urN linux-2.5.3-pre2/fs/hpfs/inode.c linux/fs/hpfs/inode.c
--- linux-2.5.3-pre2/fs/hpfs/inode.c	Mon Sep 10 07:31:25 2001
+++ linux/fs/hpfs/inode.c	Mon Jan 21 17:23:24 2002
@@ -59,31 +59,32 @@
 	struct buffer_head *bh;
 	struct fnode *fnode;
 	struct super_block *sb = i->i_sb;
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	unsigned char *ea;
 	int ea_size;
-	init_MUTEX(&i->i_hpfs_sem);
+
 	i->i_uid = sb->s_hpfs_uid;
 	i->i_gid = sb->s_hpfs_gid;
 	i->i_mode = sb->s_hpfs_mode;
-	i->i_hpfs_conv = sb->s_hpfs_conv;
+	hpfs_inode->i_conv = sb->s_hpfs_conv;
 	i->i_blksize = 512;
 	i->i_size = -1;
 	i->i_blocks = -1;
 	
-	i->i_hpfs_dno = 0;
-	i->i_hpfs_n_secs = 0;
-	i->i_hpfs_file_sec = 0;
-	i->i_hpfs_disk_sec = 0;
-	i->i_hpfs_dpos = 0;
-	i->i_hpfs_dsubdno = 0;
-	i->i_hpfs_ea_mode = 0;
-	i->i_hpfs_ea_uid = 0;
-	i->i_hpfs_ea_gid = 0;
-	i->i_hpfs_ea_size = 0;
+	hpfs_inode->i_dno = 0;
+	hpfs_inode->i_n_secs = 0;
+	hpfs_inode->i_file_sec = 0;
+	hpfs_inode->i_disk_sec = 0;
+	hpfs_inode->i_dpos = 0;
+	hpfs_inode->i_dsubdno = 0;
+	hpfs_inode->i_ea_mode = 0;
+	hpfs_inode->i_ea_uid = 0;
+	hpfs_inode->i_ea_gid = 0;
+	hpfs_inode->i_ea_size = 0;
 	i->i_version = ++event;
 
-	i->i_hpfs_rddir_off = NULL;
-	i->i_hpfs_dirty = 0;
+	hpfs_inode->i_rddir_off = NULL;
+	hpfs_inode->i_dirty = 0;
 
 	i->i_atime = 0;
 	i->i_mtime = 0;
@@ -112,14 +113,14 @@
 		if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
 			if (ea_size == 2) {
 				i->i_uid = ea[0] + (ea[1] << 8);
-				i->i_hpfs_ea_uid = 1;
+				hpfs_inode->i_ea_uid = 1;
 			}
 			kfree(ea);
 		}
 		if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
 			if (ea_size == 2) {
 				i->i_gid = ea[0] + (ea[1] << 8);
-				i->i_hpfs_ea_gid = 1;
+				hpfs_inode->i_ea_gid = 1;
 			}
 			kfree(ea);
 		}
@@ -139,7 +140,7 @@
 			umode_t mode = sb->s_hpfs_mode;
 			if (ea_size == 2) {
 				mode = ea[0] + (ea[1] << 8);
-				i->i_hpfs_ea_mode = 1;
+				hpfs_inode->i_ea_mode = 1;
 			}
 			kfree(ea);
 			i->i_mode = mode;
@@ -165,60 +166,61 @@
 		i->i_mode |= S_IFDIR;
 		i->i_op = &hpfs_dir_iops;
 		i->i_fop = &hpfs_dir_ops;
-		i->i_hpfs_parent_dir = fnode->up;
-		i->i_hpfs_dno = fnode->u.external[0].disk_secno;
+		hpfs_inode->i_parent_dir = fnode->up;
+		hpfs_inode->i_dno = fnode->u.external[0].disk_secno;
 		if (sb->s_hpfs_chk >= 2) {
 			struct buffer_head *bh0;
-			if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0);
+			if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
 		}
 		n_dnodes = 0; n_subdirs = 0;
-		hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL);
+		hpfs_count_dnodes(i->i_sb, hpfs_inode->i_dno, &n_dnodes, &n_subdirs, NULL);
 		i->i_blocks = 4 * n_dnodes;
 		i->i_size = 2048 * n_dnodes;
 		i->i_nlink = 2 + n_subdirs;
 	} else {
 		i->i_mode |= S_IFREG;
-		if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111;
+		if (!hpfs_inode->i_ea_mode) i->i_mode &= ~0111;
 		i->i_op = &hpfs_file_iops;
 		i->i_fop = &hpfs_file_ops;
 		i->i_nlink = 1;
 		i->i_size = fnode->file_size;
 		i->i_blocks = ((i->i_size + 511) >> 9) + 1;
 		i->i_data.a_ops = &hpfs_aops;
-		i->u.hpfs_i.mmu_private = i->i_size;
+		hpfs_i(i)->mmu_private = i->i_size;
 	}
 	brelse(bh);
 }
 
 void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	if (fnode->acl_size_l || fnode->acl_size_s) {
 		/* Some unknown structures like ACL may be in fnode,
 		   we'd better not overwrite them */
 		hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
 	} else if (i->i_sb->s_hpfs_eas >= 2) {
 		unsigned char ea[4];
-		if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) {
+		if ((i->i_uid != i->i_sb->s_hpfs_uid) || hpfs_inode->i_ea_uid) {
 			ea[0] = i->i_uid & 0xff;
 			ea[1] = i->i_uid >> 8;
 			hpfs_set_ea(i, fnode, "UID", ea, 2);
-			i->i_hpfs_ea_uid = 1;
+			hpfs_inode->i_ea_uid = 1;
 		}
-		if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) {
+		if ((i->i_gid != i->i_sb->s_hpfs_gid) || hpfs_inode->i_ea_gid) {
 			ea[0] = i->i_gid & 0xff;
 			ea[1] = i->i_gid >> 8;
 			hpfs_set_ea(i, fnode, "GID", ea, 2);
-			i->i_hpfs_ea_gid = 1;
+			hpfs_inode->i_ea_gid = 1;
 		}
 		if (!S_ISLNK(i->i_mode))
 			if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
 			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
 			  && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
-			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) {
+			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) {
 				ea[0] = i->i_mode & 0xff;
 				ea[1] = i->i_mode >> 8;
 				hpfs_set_ea(i, fnode, "MODE", ea, 2);
-				i->i_hpfs_ea_mode = 1;
+				hpfs_inode->i_ea_mode = 1;
 			}
 		if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
 			int d = kdev_t_to_nr(i->i_rdev);
@@ -233,17 +235,18 @@
 
 void hpfs_write_inode(struct inode *i)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct inode *parent;
 	if (!i->i_nlink) return;
 	if (i->i_ino == i->i_sb->s_hpfs_root) return;
-	if (i->i_hpfs_rddir_off && !atomic_read(&i->i_count)) {
-		if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n");
-		kfree(i->i_hpfs_rddir_off);
-		i->i_hpfs_rddir_off = NULL;
+	if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) {
+		if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n");
+		kfree(hpfs_inode->i_rddir_off);
+		hpfs_inode->i_rddir_off = NULL;
 	}
-	i->i_hpfs_dirty = 0;
+	hpfs_inode->i_dirty = 0;
 	hpfs_lock_iget(i->i_sb, 1);
-	parent = iget(i->i_sb, i->i_hpfs_parent_dir);
+	parent = iget(i->i_sb, hpfs_inode->i_parent_dir);
 	hpfs_unlock_iget(i->i_sb);
 	hpfs_lock_inode(parent);
 	hpfs_write_inode_nolock(i);
@@ -253,6 +256,7 @@
 
 void hpfs_write_inode_nolock(struct inode *i)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct buffer_head *bh;
 	struct fnode *fnode;
 	struct quad_buffer_head qbh;
@@ -276,17 +280,17 @@
 		de->read_date = gmt_to_local(i->i_sb, i->i_atime);
 		de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
 		de->read_only = !(i->i_mode & 0222);
-		de->ea_size = i->i_hpfs_ea_size;
+		de->ea_size = hpfs_inode->i_ea_size;
 		hpfs_mark_4buffers_dirty(&qbh);
 		hpfs_brelse4(&qbh);
 	}
 	if (S_ISDIR(i->i_mode)) {
-		if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) {
+		if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) {
 			de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
 			de->read_date = gmt_to_local(i->i_sb, i->i_atime);
 			de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
 			de->read_only = !(i->i_mode & 0222);
-			de->ea_size = /*i->i_hpfs_ea_size*/0;
+			de->ea_size = /*hpfs_inode->i_ea_size*/0;
 			de->file_size = 0;
 			hpfs_mark_4buffers_dirty(&qbh);
 			hpfs_brelse4(&qbh);
@@ -312,9 +316,10 @@
 
 void hpfs_write_if_changed(struct inode *inode)
 {
-	if (inode->i_hpfs_dirty) {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
+
+	if (hpfs_inode->i_dirty)
 		hpfs_write_inode(inode);
-	}
 }
 
 void hpfs_delete_inode(struct inode *inode)
diff -urN linux-2.5.3-pre2/fs/hpfs/name.c linux/fs/hpfs/name.c
--- linux-2.5.3-pre2/fs/hpfs/name.c	Tue Jun  1 23:25:47 1999
+++ linux/fs/hpfs/name.c	Mon Jan 21 17:23:24 2002
@@ -20,8 +20,9 @@
 
 void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
 {
+	struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
 	int i;
-	if (inode->i_hpfs_conv != CONV_AUTO) return;
+	if (hpfs_inode->i_conv != CONV_AUTO) return;
 	for (i = 0; *text_postfix[i]; i++) {
 		int l = strlen(text_postfix[i]);
 		if (l <= len)
@@ -34,10 +35,10 @@
 			if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
 				goto text;
 	}
-	inode->i_hpfs_conv = CONV_BINARY;
+	hpfs_inode->i_conv = CONV_BINARY;
 	return;
 	text:
-	inode->i_hpfs_conv = CONV_TEXT;
+	hpfs_inode->i_conv = CONV_TEXT;
 	return;
 }
 
diff -urN linux-2.5.3-pre2/fs/hpfs/namei.c linux/fs/hpfs/namei.c
--- linux-2.5.3-pre2/fs/hpfs/namei.c	Fri Dec 29 14:07:57 2000
+++ linux/fs/hpfs/namei.c	Mon Jan 21 17:23:24 2002
@@ -25,7 +25,7 @@
 	struct hpfs_dirent dee;
 	int err;
 	if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
-	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
 	if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1;
 	memset(&dee, 0, sizeof dee);
 	dee.directory = 1;
@@ -69,9 +69,9 @@
 	dir->i_nlink++;
 	hpfs_lock_iget(dir->i_sb, 1);
 	if ((result = iget(dir->i_sb, fno))) {
-		result->i_hpfs_parent_dir = dir->i_ino;
+		hpfs_i(result)->i_parent_dir = dir->i_ino;
 		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
-		result->i_hpfs_ea_size = 0;
+		hpfs_i(result)->i_ea_size = 0;
 		if (dee.read_only) result->i_mode &= ~0222;
 		if (result->i_uid != current->fsuid ||
 		    result->i_gid != current->fsgid ||
@@ -109,7 +109,7 @@
 	struct hpfs_dirent dee;
 	int err;
 	if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
-	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
 	memset(&dee, 0, sizeof dee);
 	if (!(mode & 0222)) dee.read_only = 1;
 	dee.archive = 1;
@@ -133,15 +133,15 @@
 	hpfs_lock_iget(dir->i_sb, 2);
 	if ((result = iget(dir->i_sb, fno))) {
 		hpfs_decide_conv(result, (char *)name, len);
-		result->i_hpfs_parent_dir = dir->i_ino;
+		hpfs_i(result)->i_parent_dir = dir->i_ino;
 		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
-		result->i_hpfs_ea_size = 0;
+		hpfs_i(result)->i_ea_size = 0;
 		if (dee.read_only) result->i_mode &= ~0222;
 		if (result->i_blocks == -1) result->i_blocks = 1;
 		if (result->i_size == -1) {
 			result->i_size = 0;
 			result->i_data.a_ops = &hpfs_aops;
-			result->u.hpfs_i.mmu_private = 0;
+			hpfs_i(result)->mmu_private = 0;
 		}
 		if (result->i_uid != current->fsuid ||
 		    result->i_gid != current->fsgid ||
@@ -177,7 +177,7 @@
 	int err;
 	if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
 	if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
-	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
 	memset(&dee, 0, sizeof dee);
 	if (!(mode & 0222)) dee.read_only = 1;
 	dee.archive = 1;
@@ -199,9 +199,9 @@
 	mark_buffer_dirty(bh);
 	hpfs_lock_iget(dir->i_sb, 2);
 	if ((result = iget(dir->i_sb, fno))) {
-		result->i_hpfs_parent_dir = dir->i_ino;
+		hpfs_i(result)->i_parent_dir = dir->i_ino;
 		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
-		result->i_hpfs_ea_size = 0;
+		hpfs_i(result)->i_ea_size = 0;
 		/*if (result->i_blocks == -1) result->i_blocks = 1;
 		if (result->i_size == -1) result->i_size = 0;*/
 		result->i_uid = current->fsuid;
@@ -240,7 +240,7 @@
 	int err;
 	if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
 	if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
-	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
 	memset(&dee, 0, sizeof dee);
 	dee.archive = 1;
 	dee.hidden = name[0] == '.';
@@ -262,9 +262,9 @@
 	brelse(bh);
 	hpfs_lock_iget(dir->i_sb, 2);
 	if ((result = iget(dir->i_sb, fno))) {
-		result->i_hpfs_parent_dir = dir->i_ino;
+		hpfs_i(result)->i_parent_dir = dir->i_ino;
 		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
-		result->i_hpfs_ea_size = 0;
+		hpfs_i(result)->i_ea_size = 0;
 		/*if (result->i_blocks == -1) result->i_blocks = 1;
 		if (result->i_size == -1) result->i_size = 0;*/
 		result->i_mode = S_IFLNK | 0777;
@@ -307,7 +307,7 @@
 	hpfs_adjust_length((char *)name, &len);
 	again:
 	hpfs_lock_2inodes(dir, inode);
-	if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+	if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) {
 		hpfs_unlock_2inodes(dir, inode);
 		return -ENOENT;
 	}
@@ -368,7 +368,7 @@
 	int r;
 	hpfs_adjust_length((char *)name, &len);
 	hpfs_lock_2inodes(dir, inode);
-	if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+	if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) {
 		hpfs_unlock_2inodes(dir, inode);
 		return -ENOENT;
 	}	
@@ -382,7 +382,7 @@
 		hpfs_unlock_2inodes(dir, inode);
 		return -ENOTDIR;
 	}
-	hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items);
+	hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items);
 	if (n_items) {
 		hpfs_brelse4(&qbh);
 		hpfs_unlock_2inodes(dir, inode);
@@ -458,7 +458,7 @@
 		goto end1;
 	}
 
-	if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+	if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
 		hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
 		err = -ENOENT;
 		goto end1;
@@ -469,7 +469,7 @@
 	if (new_inode) {
 		int r;
 		if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
-			if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
+			if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
 				new_inode->i_nlink = 0;
 				copy_de(nde, &de);
 				memcpy(nde->name, new_name, new_len);
@@ -497,7 +497,7 @@
 	}
 	
 	if (new_dir == old_dir)
-		if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+		if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
 			hpfs_unlock_creation(i->i_sb);
 			hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
 			err = -ENOENT;
@@ -513,7 +513,7 @@
 	hpfs_unlock_creation(i->i_sb);
 	
 	end:
-	i->i_hpfs_parent_dir = new_dir->i_ino;
+	hpfs_i(i)->i_parent_dir = new_dir->i_ino;
 	if (S_ISDIR(i->i_mode)) {
 		new_dir->i_nlink++;
 		old_dir->i_nlink--;
@@ -526,7 +526,7 @@
 		mark_buffer_dirty(bh);
 		brelse(bh);
 	}
-	i->i_hpfs_conv = i->i_sb->s_hpfs_conv;
+	hpfs_i(i)->i_conv = i->i_sb->s_hpfs_conv;
 	hpfs_decide_conv(i, (char *)new_name, new_len);
 	end1:
 	hpfs_unlock_3inodes(old_dir, new_dir, i);
diff -urN linux-2.5.3-pre2/fs/hpfs/super.c linux/fs/hpfs/super.c
--- linux-2.5.3-pre2/fs/hpfs/super.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/hpfs/super.c	Mon Jan 21 17:23:24 2002
@@ -148,10 +148,56 @@
 	return 0;
 }
 
+static kmem_cache_t * hpfs_inode_cachep;
+
+static struct inode *hpfs_alloc_inode(struct super_block *sb)
+{
+	struct hpfs_inode_info *ei;
+	ei = (struct hpfs_inode_info *)kmem_cache_alloc(hpfs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void hpfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		init_MUTEX(&ei->i_sem);
+		inode_init_once(&ei->vfs_inode);
+	}
+}
+ 
+static int init_inodecache(void)
+{
+	hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache",
+					     sizeof(struct hpfs_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (hpfs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(hpfs_inode_cachep))
+		printk(KERN_INFO "hpfs_inode_cache: not all structures were freed\n");
+}
+
 /* Super operations */
 
 static struct super_operations hpfs_sops =
 {
+	alloc_inode:	hpfs_alloc_inode,
+	destroy_inode:	hpfs_destroy_inode,
         read_inode:	hpfs_read_inode,
 	delete_inode:	hpfs_delete_inode,
 	put_super:	hpfs_put_super,
@@ -545,8 +591,8 @@
 		s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date);
 		s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date);
 		s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date);
-		s->s_root->d_inode->i_hpfs_ea_size = de->ea_size;
-		s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino;
+		hpfs_i(s->s_root->d_inode)->i_ea_size = de->ea_size;
+		hpfs_i(s->s_root->d_inode)->i_parent_dir = s->s_root->d_inode->i_ino;
 		if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048;
 		if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5;
 	}
@@ -568,12 +614,23 @@
 
 static int __init init_hpfs_fs(void)
 {
-	return register_filesystem(&hpfs_fs_type);
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&hpfs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
 }
 
 static void __exit exit_hpfs_fs(void)
 {
 	unregister_filesystem(&hpfs_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.5.3-pre2/fs/intermezzo/journal_ext3.c linux/fs/intermezzo/journal_ext3.c
--- linux-2.5.3-pre2/fs/intermezzo/journal_ext3.c	Sun Dec 30 10:31:51 2001
+++ linux/fs/intermezzo/journal_ext3.c	Mon Jan 21 17:23:24 2002
@@ -188,7 +188,7 @@
 void presto_e3_journal_file_data(struct inode *inode)
 {
 #ifdef EXT3_JOURNAL_DATA_FL
-        inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+        EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
 #else
 #warning You must have a facility to enable journaled writes for recovery!
 #endif
diff -urN linux-2.5.3-pre2/fs/intermezzo/journal_obdfs.c linux/fs/intermezzo/journal_obdfs.c
--- linux-2.5.3-pre2/fs/intermezzo/journal_obdfs.c	Sun Dec 30 10:31:51 2001
+++ linux/fs/intermezzo/journal_obdfs.c	Mon Jan 21 17:23:24 2002
@@ -163,7 +163,7 @@
 void presto_obdfs_journal_file_data(struct inode *inode)
 {
 #ifdef EXT3_JOURNAL_DATA_FL
-        inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+        EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
 #else
 #warning You must have a facility to enable journaled writes for recovery!
 #endif
diff -urN linux-2.5.3-pre2/fs/intermezzo/journal_reiserfs.c linux/fs/intermezzo/journal_reiserfs.c
--- linux-2.5.3-pre2/fs/intermezzo/journal_reiserfs.c	Sun Dec 30 10:31:51 2001
+++ linux/fs/intermezzo/journal_reiserfs.c	Mon Jan 21 17:23:24 2002
@@ -101,7 +101,7 @@
 void presto_reiserfs_journal_file_data(struct inode *inode)
 {
 #ifdef EXT3_JOURNAL_DATA_FL
-        inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+        EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
 #else
 #warning You must have a facility to enable journaled writes for recovery!
 #endif
diff -urN linux-2.5.3-pre2/fs/nfs/inode.c linux/fs/nfs/inode.c
--- linux-2.5.3-pre2/fs/nfs/inode.c	Mon Jan  7 09:59:01 2002
+++ linux/fs/nfs/inode.c	Mon Jan 21 17:23:24 2002
@@ -45,6 +45,8 @@
 void nfs_zap_caches(struct inode *);
 static void nfs_invalidate_inode(struct inode *);
 
+static struct inode *nfs_alloc_inode(struct super_block *sb);
+static void nfs_destroy_inode(struct inode *);
 static void nfs_read_inode(struct inode *);
 static void nfs_write_inode(struct inode *,int);
 static void nfs_delete_inode(struct inode *);
@@ -55,6 +57,8 @@
 static int  nfs_show_options(struct seq_file *, struct vfsmount *);
 
 static struct super_operations nfs_sops = { 
+	alloc_inode:	nfs_alloc_inode,
+	destroy_inode:	nfs_destroy_inode,
 	read_inode:	nfs_read_inode,
 	write_inode:	nfs_write_inode,
 	delete_inode:	nfs_delete_inode,
@@ -94,26 +98,11 @@
 
 /*
  * The "read_inode" function doesn't actually do anything:
- * the real data is filled in later in nfs_fhget. Here we
- * just mark the cache times invalid, and zero out i_mode
- * (the latter makes "nfs_refresh_inode" do the right thing
- * wrt pipe inodes)
+ * the real data is filled in later in nfs_fhget.
  */
 static void
 nfs_read_inode(struct inode * inode)
 {
-	inode->i_blksize = inode->i_sb->s_blocksize;
-	inode->i_mode = 0;
-	inode->i_rdev = NODEV;
-	/* We can't support UPDATE_ATIME(), since the server will reset it */
-	inode->i_flags |= S_NOATIME;
-	INIT_LIST_HEAD(&inode->u.nfs_i.read);
-	INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
-	INIT_LIST_HEAD(&inode->u.nfs_i.commit);
-	INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
-	NFS_CACHEINV(inode);
-	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 }
 
 static void
@@ -623,39 +612,6 @@
 	nfs_zap_caches(inode);
 }
 
-/*
- * Fill in inode information from the fattr.
- */
-static void
-nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr)
-{
-	/*
-	 * Check whether the mode has been set, as we only want to
-	 * do this once. (We don't allow inodes to change types.)
-	 */
-	if (inode->i_mode == 0) {
-		NFS_FILEID(inode) = fattr->fileid;
-		NFS_FSID(inode) = fattr->fsid;
-		inode->i_mode = fattr->mode;
-		/* Why so? Because we want revalidate for devices/FIFOs, and
-		 * that's precisely what we have in nfs_file_inode_operations.
-		 */
-		inode->i_op = &nfs_file_inode_operations;
-		if (S_ISREG(inode->i_mode)) {
-			inode->i_fop = &nfs_file_operations;
-			inode->i_data.a_ops = &nfs_file_aops;
-		} else if (S_ISDIR(inode->i_mode)) {
-			inode->i_op = &nfs_dir_inode_operations;
-			inode->i_fop = &nfs_dir_operations;
-		} else if (S_ISLNK(inode->i_mode))
-			inode->i_op = &nfs_symlink_inode_operations;
-		else
-			init_special_inode(inode, inode->i_mode, fattr->rdev);
-		memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
-	}
-	nfs_refresh_inode(inode, fattr);
-}
-
 struct nfs_find_desc {
 	struct nfs_fh		*fh;
 	struct nfs_fattr	*fattr;
@@ -678,7 +634,7 @@
 		return 0;
 	if (NFS_FILEID(inode) != fattr->fileid)
 		return 0;
-	if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0)
+	if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0)
 		return 0;
 	/* Force an attribute cache update if inode->i_count == 0 */
 	if (!atomic_read(&inode->i_count))
@@ -700,7 +656,7 @@
 		return 1;
 
 	/* Has the filehandle changed? If so is the old one stale? */
-	if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0 &&
+	if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0 &&
 	    __nfs_revalidate_inode(NFS_SERVER(inode),inode) == -ESTALE)
 		return 1;
 
@@ -749,7 +705,65 @@
 	if (!(inode = iget4(sb, ino, nfs_find_actor, &desc)))
 		goto out_no_inode;
 
-	nfs_fill_inode(inode, fh, fattr);
+	if (NFS_NEW(inode)) {
+		__u64		new_size, new_mtime;
+		loff_t		new_isize;
+		time_t		new_atime;
+
+		/* We can't support UPDATE_ATIME(), since the server will reset it */
+		NFS_FLAGS(inode) &= ~NFS_INO_NEW;
+		NFS_FILEID(inode) = fattr->fileid;
+		NFS_FSID(inode) = fattr->fsid;
+		memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh));
+		inode->i_flags |= S_NOATIME;
+		inode->i_mode = fattr->mode;
+		/* Why so? Because we want revalidate for devices/FIFOs, and
+		 * that's precisely what we have in nfs_file_inode_operations.
+		 */
+		inode->i_op = &nfs_file_inode_operations;
+		if (S_ISREG(inode->i_mode)) {
+			inode->i_fop = &nfs_file_operations;
+			inode->i_data.a_ops = &nfs_file_aops;
+		} else if (S_ISDIR(inode->i_mode)) {
+			inode->i_op = &nfs_dir_inode_operations;
+			inode->i_fop = &nfs_dir_operations;
+		} else if (S_ISLNK(inode->i_mode))
+			inode->i_op = &nfs_symlink_inode_operations;
+		else
+			init_special_inode(inode, inode->i_mode, fattr->rdev);
+
+		new_mtime = fattr->mtime;
+		new_size = fattr->size;
+		new_isize = nfs_size_to_loff_t(fattr->size);
+		new_atime = nfs_time_to_secs(fattr->atime);
+
+		NFS_READTIME(inode) = jiffies;
+		NFS_CACHE_CTIME(inode) = fattr->ctime;
+		inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+		inode->i_atime = new_atime;
+		NFS_CACHE_MTIME(inode) = new_mtime;
+		inode->i_mtime = nfs_time_to_secs(new_mtime);
+		NFS_CACHE_ISIZE(inode) = new_size;
+		inode->i_size = new_isize;
+		inode->i_mode = fattr->mode;
+		inode->i_nlink = fattr->nlink;
+		inode->i_uid = fattr->uid;
+		inode->i_gid = fattr->gid;
+		if (fattr->valid & NFS_ATTR_FATTR_V3) {
+			/*
+			 * report the blocks in 512byte units
+			 */
+			inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+			inode->i_blksize = inode->i_sb->s_blocksize;
+		} else {
+			inode->i_blocks = fattr->du.nfs2.blocks;
+			inode->i_blksize = fattr->du.nfs2.blocksize;
+		}
+		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+		memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+	} else
+		nfs_refresh_inode(inode, fattr);
 	dprintk("NFS: __nfs_fhget(%s/%Ld ct=%d)\n",
 		inode->i_sb->s_id,
 		(long long)NFS_FILEID(inode),
@@ -1152,6 +1166,64 @@
 extern int nfs_init_writepagecache(void);
 extern int nfs_destroy_writepagecache(void);
 
+static kmem_cache_t * nfs_inode_cachep;
+
+static struct inode *nfs_alloc_inode(struct super_block *sb)
+{
+	struct nfs_inode *nfsi;
+	nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL);
+	if (!nfsi)
+		return NULL;
+	nfsi->flags = NFS_INO_NEW;
+	/* do we need the next 4 lines? */
+	nfsi->hash_next = NULL;
+	nfsi->hash_prev = NULL;
+	nfsi->nextscan = 0;
+	nfsi->mm_cred = NULL;
+	return &nfsi->vfs_inode;
+}
+
+static void nfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct nfs_inode *nfsi = (struct nfs_inode *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		inode_init_once(&nfsi->vfs_inode);
+		INIT_LIST_HEAD(&nfsi->read);
+		INIT_LIST_HEAD(&nfsi->dirty);
+		INIT_LIST_HEAD(&nfsi->commit);
+		INIT_LIST_HEAD(&nfsi->writeback);
+		nfsi->nread = 0;
+		nfsi->ndirty = 0;
+		nfsi->ncommit = 0;
+		nfsi->npages = 0;
+	}
+}
+ 
+int nfs_init_inodecache(void)
+{
+	nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
+					     sizeof(struct nfs_inode),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (nfs_inode_cachep == NULL)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void nfs_destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(nfs_inode_cachep))
+		printk(KERN_INFO "nfs_inode_cache: not all structures were freed\n");
+}
+
 /*
  * Initialize NFS
  */
@@ -1161,26 +1233,45 @@
 
 	err = nfs_init_nfspagecache();
 	if (err)
-		return err;
+		goto out4;
+
+	err = nfs_init_inodecache();
+	if (err)
+		goto out3;
 
 	err = nfs_init_readpagecache();
 	if (err)
-		return err;
+		goto out2;
 
 	err = nfs_init_writepagecache();
 	if (err)
-		return err;
+		goto out1;
 
 #ifdef CONFIG_PROC_FS
 	rpc_proc_register(&nfs_rpcstat);
 #endif
-        return register_filesystem(&nfs_fs_type);
+        err = register_filesystem(&nfs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	rpc_proc_unregister("nfs");
+	nfs_destroy_writepagecache();
+out1:
+	nfs_destroy_readpagecache();
+out2:
+	nfs_destroy_inodecache();
+out3:
+	nfs_destroy_nfspagecache();
+out4:
+	return err;
 }
 
 static void __exit exit_nfs_fs(void)
 {
 	nfs_destroy_writepagecache();
 	nfs_destroy_readpagecache();
+	nfs_destroy_inodecache();
 	nfs_destroy_nfspagecache();
 #ifdef CONFIG_PROC_FS
 	rpc_proc_unregister("nfs");
diff -urN linux-2.5.3-pre2/fs/nfs/read.c linux/fs/nfs/read.c
--- linux-2.5.3-pre2/fs/nfs/read.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/nfs/read.c	Mon Jan 21 17:23:24 2002
@@ -156,10 +156,11 @@
 nfs_mark_request_read(struct nfs_page *req)
 {
 	struct inode *inode = req->wb_inode;
+	struct nfs_inode *nfsi = NFS_I(inode);
 
 	spin_lock(&nfs_wreq_lock);
-	nfs_list_add_request(req, &inode->u.nfs_i.read);
-	inode->u.nfs_i.nread++;
+	nfs_list_add_request(req, &nfsi->read);
+	nfsi->nread++;
 	__nfs_add_lru(&NFS_SERVER(inode)->lru_read, req);
 	spin_unlock(&nfs_wreq_lock);
 }
@@ -167,6 +168,7 @@
 static int
 nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	struct nfs_page	*new;
 
 	new = nfs_create_request(file, inode, page, 0, PAGE_CACHE_SIZE);
@@ -174,7 +176,7 @@
 		return PTR_ERR(new);
 	nfs_mark_request_read(new);
 
-	if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages ||
+	if (nfsi->nread >= NFS_SERVER(inode)->rpages ||
 	    page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
 		nfs_pagein_inode(inode, 0, 0);
 	return 0;
@@ -318,13 +320,13 @@
 int
 nfs_scan_lru_read_timeout(struct nfs_server *server, struct list_head *dst)
 {
-	struct inode *inode;
+	struct nfs_inode *nfsi;
 	int npages;
 
 	npages = nfs_scan_lru_timeout(&server->lru_read, dst, server->rpages);
 	if (npages) {
-		inode = nfs_list_entry(dst->next)->wb_inode;
-		inode->u.nfs_i.nread -= npages;
+		nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+		nfsi->nread -= npages;
 	}
 	return npages;
 }
@@ -341,13 +343,13 @@
 int
 nfs_scan_lru_read(struct nfs_server *server, struct list_head *dst)
 {
-	struct inode *inode;
+	struct nfs_inode *nfsi;
 	int npages;
 
 	npages = nfs_scan_lru(&server->lru_read, dst, server->rpages);
 	if (npages) {
-		inode = nfs_list_entry(dst->next)->wb_inode;
-		inode->u.nfs_i.nread -= npages;
+		nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+		nfsi->nread -= npages;
 	}
 	return npages;
 }
@@ -365,10 +367,11 @@
 static int
 nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	int	res;
-	res = nfs_scan_list(&inode->u.nfs_i.read, dst, NULL, idx_start, npages);
-	inode->u.nfs_i.nread -= res;
-	if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+	res = nfs_scan_list(&nfsi->read, dst, NULL, idx_start, npages);
+	nfsi->nread -= res;
+	if ((nfsi->nread == 0) != list_empty(&nfsi->read))
 		printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
 	return res;
 }
diff -urN linux-2.5.3-pre2/fs/nfs/write.c linux/fs/nfs/write.c
--- linux-2.5.3-pre2/fs/nfs/write.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/nfs/write.c	Mon Jan 21 17:23:24 2002
@@ -315,14 +315,15 @@
 static inline void
 nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	if (!list_empty(&req->wb_hash))
 		return;
 	if (!NFS_WBACK_BUSY(req))
 		printk(KERN_ERR "NFS: unlocked request attempted hashed!\n");
-	if (list_empty(&inode->u.nfs_i.writeback))
+	if (list_empty(&nfsi->writeback))
 		igrab(inode);
-	inode->u.nfs_i.npages++;
-	list_add(&req->wb_hash, &inode->u.nfs_i.writeback);
+	nfsi->npages++;
+	list_add(&req->wb_hash, &nfsi->writeback);
 	req->wb_count++;
 }
 
@@ -332,6 +333,7 @@
 static inline void
 nfs_inode_remove_request(struct nfs_page *req)
 {
+	struct nfs_inode *nfsi;
 	struct inode *inode;
 	spin_lock(&nfs_wreq_lock);
 	if (list_empty(&req->wb_hash)) {
@@ -343,10 +345,11 @@
 	inode = req->wb_inode;
 	list_del(&req->wb_hash);
 	INIT_LIST_HEAD(&req->wb_hash);
-	inode->u.nfs_i.npages--;
-	if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback))
+	nfsi = NFS_I(inode);
+	nfsi->npages--;
+	if ((nfsi->npages == 0) != list_empty(&nfsi->writeback))
 		printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n");
-	if (list_empty(&inode->u.nfs_i.writeback)) {
+	if (list_empty(&nfsi->writeback)) {
 		spin_unlock(&nfs_wreq_lock);
 		iput(inode);
 	} else
@@ -360,9 +363,10 @@
 static inline struct nfs_page *
 _nfs_find_request(struct inode *inode, struct page *page)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	struct list_head	*head, *next;
 
-	head = &inode->u.nfs_i.writeback;
+	head = &nfsi->writeback;
 	next = head->next;
 	while (next != head) {
 		struct nfs_page *req = nfs_inode_wb_entry(next);
@@ -393,10 +397,11 @@
 nfs_mark_request_dirty(struct nfs_page *req)
 {
 	struct inode *inode = req->wb_inode;
+	struct nfs_inode *nfsi = NFS_I(inode);
 
 	spin_lock(&nfs_wreq_lock);
-	nfs_list_add_request(req, &inode->u.nfs_i.dirty);
-	inode->u.nfs_i.ndirty++;
+	nfs_list_add_request(req, &nfsi->dirty);
+	nfsi->ndirty++;
 	__nfs_del_lru(req);
 	__nfs_add_lru(&NFS_SERVER(inode)->lru_dirty, req);
 	spin_unlock(&nfs_wreq_lock);
@@ -409,8 +414,8 @@
 static inline int
 nfs_dirty_request(struct nfs_page *req)
 {
-	struct inode *inode = req->wb_inode;
-	return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty;
+	struct nfs_inode *nfsi = NFS_I(req->wb_inode);
+	return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty;
 }
 
 #ifdef CONFIG_NFS_V3
@@ -421,10 +426,11 @@
 nfs_mark_request_commit(struct nfs_page *req)
 {
 	struct inode *inode = req->wb_inode;
+	struct nfs_inode *nfsi = NFS_I(inode);
 
 	spin_lock(&nfs_wreq_lock);
-	nfs_list_add_request(req, &inode->u.nfs_i.commit);
-	inode->u.nfs_i.ncommit++;
+	nfs_list_add_request(req, &nfsi->commit);
+	nfsi->ncommit++;
 	__nfs_del_lru(req);
 	__nfs_add_lru(&NFS_SERVER(inode)->lru_commit, req);
 	spin_unlock(&nfs_wreq_lock);
@@ -440,6 +446,7 @@
 static int
 nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	struct list_head	*p, *head;
 	unsigned long		idx_end;
 	unsigned int		res = 0;
@@ -451,7 +458,7 @@
 		idx_end = idx_start + npages - 1;
 
 	spin_lock(&nfs_wreq_lock);
-	head = &inode->u.nfs_i.writeback;
+	head = &nfsi->writeback;
 	p = head->next;
 	while (p != head) {
 		unsigned long pg_idx;
@@ -494,13 +501,13 @@
 int
 nfs_scan_lru_dirty_timeout(struct nfs_server *server, struct list_head *dst)
 {
-	struct inode *inode;
+	struct nfs_inode *nfsi;
 	int npages;
 
 	npages = nfs_scan_lru_timeout(&server->lru_dirty, dst, server->wpages);
 	if (npages) {
-		inode = nfs_list_entry(dst->next)->wb_inode;
-		inode->u.nfs_i.ndirty -= npages;
+		nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+		nfsi->ndirty -= npages;
 	}
 	return npages;
 }
@@ -517,13 +524,13 @@
 int
 nfs_scan_lru_dirty(struct nfs_server *server, struct list_head *dst)
 {
-	struct inode *inode;
+	struct nfs_inode *nfsi;
 	int npages;
 
 	npages = nfs_scan_lru(&server->lru_dirty, dst, server->wpages);
 	if (npages) {
-		inode = nfs_list_entry(dst->next)->wb_inode;
-		inode->u.nfs_i.ndirty -= npages;
+		nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+		nfsi->ndirty -= npages;
 	}
 	return npages;
 }
@@ -542,10 +549,11 @@
 static int
 nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	int	res;
-	res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages);
-	inode->u.nfs_i.ndirty -= res;
-	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+	res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages);
+	nfsi->ndirty -= res;
+	if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty))
 		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
 	return res;
 }
@@ -565,14 +573,14 @@
 int
 nfs_scan_lru_commit_timeout(struct nfs_server *server, struct list_head *dst)
 {
-	struct inode *inode;
+	struct nfs_inode *nfsi;
 	int npages;
 
 	npages = nfs_scan_lru_timeout(&server->lru_commit, dst, 1);
 	if (npages) {
-		inode = nfs_list_entry(dst->next)->wb_inode;
-		npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0);
-		inode->u.nfs_i.ncommit -= npages;
+		nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+		npages += nfs_scan_list(&nfsi->commit, dst, NULL, 0, 0);
+		nfsi->ncommit -= npages;
 	}
 	return npages;
 }
@@ -592,14 +600,14 @@
 int
 nfs_scan_lru_commit(struct nfs_server *server, struct list_head *dst)
 {
-	struct inode *inode;
+	struct nfs_inode *nfsi;
 	int npages;
 
 	npages = nfs_scan_lru(&server->lru_commit, dst, 1);
 	if (npages) {
-		inode = nfs_list_entry(dst->next)->wb_inode;
-		npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0);
-		inode->u.nfs_i.ncommit -= npages;
+		nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+		npages += nfs_scan_list(&nfsi->commit, dst, NULL, 0, 0);
+		nfsi->ncommit -= npages;
 	}
 	return npages;
 }
@@ -618,10 +626,11 @@
 static int
 nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	int	res;
-	res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages);
-	inode->u.nfs_i.ncommit -= res;
-	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+	res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages);
+	nfsi->ncommit -= res;
+	if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
 		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
 	return res;
 }
@@ -738,7 +747,7 @@
 {
 	unsigned int	dirty, wpages;
 
-	dirty  = inode->u.nfs_i.ndirty;
+	dirty  = NFS_I(inode)->ndirty;
 	wpages = NFS_SERVER(inode)->wpages;
 #ifdef CONFIG_NFS_V3
 	if (NFS_PROTO(inode)->version == 2) {
@@ -889,6 +898,7 @@
 static int
 nfs_flush_one(struct list_head *head, struct inode *inode, int how)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
 	struct rpc_clnt 	*clnt = NFS_CLIENT(inode);
 	struct nfs_write_data	*data;
 	struct rpc_task		*task;
@@ -913,7 +923,7 @@
 	if (nfsvers < 3)
 		data->args.stable = NFS_FILE_SYNC;
 	else if (stable) {
-		if (!inode->u.nfs_i.ncommit)
+		if (!nfsi->ncommit)
 			data->args.stable = NFS_FILE_SYNC;
 		else
 			data->args.stable = NFS_DATA_SYNC;
diff -urN linux-2.5.3-pre2/fs/qnx4/bitmap.c linux/fs/qnx4/bitmap.c
--- linux-2.5.3-pre2/fs/qnx4/bitmap.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/qnx4/bitmap.c	Mon Jan 21 17:23:24 2002
@@ -142,13 +142,13 @@
 
 static void qnx4_clear_inode(struct inode *inode)
 {
-	struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
-
-	memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved);
-	qnx4_ino->i_size = 0;
-	qnx4_ino->i_num_xtnts = 0;
-	qnx4_ino->i_mode = 0;
-	qnx4_ino->i_status = 0;
+	struct qnx4_inode_entry *qnx4_ino = qnx4_raw_inode(inode);
+	/* What for? */
+	memset(qnx4_ino->di_fname, 0, sizeof qnx4_ino->di_fname);
+	qnx4_ino->di_size = 0;
+	qnx4_ino->di_num_xtnts = 0;
+	qnx4_ino->di_mode = 0;
+	qnx4_ino->di_status = 0;
 }
 
 void qnx4_free_inode(struct inode *inode)
diff -urN linux-2.5.3-pre2/fs/qnx4/fsync.c linux/fs/qnx4/fsync.c
--- linux-2.5.3-pre2/fs/qnx4/fsync.c	Sun Dec 30 10:31:51 2001
+++ linux/fs/qnx4/fsync.c	Mon Jan 21 17:23:24 2002
@@ -90,7 +90,7 @@
 
 	for (i = 0; i < 7; i++) {
 		rc = sync_block(inode,
-				(unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait);
+				(unsigned short *) qnx4_raw_inode(inode)->di_first_xtnt.xtnt_blk + i, wait);
 		if (rc > 0)
 			break;
 		if (rc)
diff -urN linux-2.5.3-pre2/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- linux-2.5.3-pre2/fs/qnx4/inode.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/qnx4/inode.c	Mon Jan 21 17:23:24 2002
@@ -121,12 +121,16 @@
 
 static struct super_block *qnx4_read_super(struct super_block *, void *, int);
 static void qnx4_put_super(struct super_block *sb);
+static struct inode *qnx4_alloc_inode(struct super_block *sb);
+static void qnx4_destroy_inode(struct inode *inode);
 static void qnx4_read_inode(struct inode *);
 static int qnx4_remount(struct super_block *sb, int *flags, char *data);
 static int qnx4_statfs(struct super_block *, struct statfs *);
 
 static struct super_operations qnx4_sops =
 {
+	alloc_inode:	qnx4_alloc_inode,
+	destroy_inode:	qnx4_destroy_inode,
 	read_inode:	qnx4_read_inode,
 #ifdef CONFIG_QNX4FS_RW
 	write_inode:	qnx4_write_inode,
@@ -227,16 +231,16 @@
 	unsigned long block = 0;
 	struct buffer_head *bh = 0;
 	struct qnx4_xblk *xblk = 0;
-	struct qnx4_inode_info *qnx4_inode = &inode->u.qnx4_i;
-	qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->i_num_xtnts);
+	struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode);
+	qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts);
 
-	if ( iblock < le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size) ) {
+	if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) {
 		// iblock is in the first extent. This is easy.
-		block = le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_blk) + iblock - 1;
+		block = le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_blk) + iblock - 1;
 	} else {
 		// iblock is beyond first extent. We have to follow the extent chain.
-		i_xblk = le32_to_cpu(qnx4_inode->i_xblk);
-		offset = iblock - le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size);
+		i_xblk = le32_to_cpu(qnx4_inode->di_xblk);
+		offset = iblock - le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size);
 		ix = 0;
 		while ( --nxtnt > 0 ) {
 			if ( ix == 0 ) {
@@ -417,10 +421,12 @@
 {
 	return block_read_full_page(page,qnx4_get_block);
 }
-static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+static int qnx4_prepare_write(struct file *file, struct page *page,
+			      unsigned from, unsigned to)
 {
-	return cont_prepare_write(page,from,to,qnx4_get_block,
-		&page->mapping->host->u.qnx4_i.mmu_private);
+	struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host);
+	return cont_prepare_write(page, from, to, qnx4_get_block,
+				  &qnx4_inode->mmu_private);
 }
 static int qnx4_bmap(struct address_space *mapping, long block)
 {
@@ -441,6 +447,7 @@
 	struct qnx4_inode_entry *raw_inode;
 	int block, ino;
 	struct super_block *sb = inode->i_sb;
+	struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode);
 
 	ino = inode->i_ino;
 	inode->i_mode = 0;
@@ -472,35 +479,92 @@
 	inode->i_blocks  = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size);
 	inode->i_blksize = QNX4_DIR_ENTRY_SIZE;
 
-	memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE);
+	memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE);
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = &qnx4_file_inode_operations;
 		inode->i_fop = &qnx4_file_operations;
 		inode->i_mapping->a_ops = &qnx4_aops;
-		inode->u.qnx4_i.mmu_private = inode->i_size;
+		qnx4_i(inode)->mmu_private = inode->i_size;
 	} else if (S_ISDIR(inode->i_mode)) {
 		inode->i_op = &qnx4_dir_inode_operations;
 		inode->i_fop = &qnx4_dir_operations;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &page_symlink_inode_operations;
 		inode->i_mapping->a_ops = &qnx4_aops;
-		inode->u.qnx4_i.mmu_private = inode->i_size;
+		qnx4_i(inode)->mmu_private = inode->i_size;
 	} else
 		printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id);
 	brelse(bh);
 }
 
+static kmem_cache_t *qnx4_inode_cachep;
+
+static struct inode *qnx4_alloc_inode(struct super_block *sb)
+{
+	struct qnx4_inode_info *ei;
+	ei = kmem_cache_alloc(qnx4_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void qnx4_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode));
+}
+
+static void init_once(void *foo, kmem_cache_t * cachep,
+		      unsigned long flags)
+{
+	struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+	qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache",
+					     sizeof(struct qnx4_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (qnx4_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(qnx4_inode_cachep))
+		printk(KERN_INFO
+		       "qnx4_inode_cache: not all structures were freed\n");
+}
+
 static DECLARE_FSTYPE_DEV(qnx4_fs_type, "qnx4", qnx4_read_super);
 
 static int __init init_qnx4_fs(void)
 {
+	int err;
+
+	err = init_inodecache();
+	if (err)
+		return err;
+
+	err = register_filesystem(&qnx4_fs_type);
+	if (err) {
+		destroy_inodecache();
+		return err;
+	}
+
 	printk("QNX4 filesystem 0.2.2 registered.\n");
-	return register_filesystem(&qnx4_fs_type);
+	return 0;
 }
 
 static void __exit exit_qnx4_fs(void)
 {
 	unregister_filesystem(&qnx4_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.5.3-pre2/fs/ufs/balloc.c linux/fs/ufs/balloc.c
--- linux-2.5.3-pre2/fs/ufs/balloc.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/ufs/balloc.c	Mon Jan 21 17:23:24 2002
@@ -271,7 +271,7 @@
 			unlock_super (sb);
 			return (unsigned)-1;
 		}
-		if (fragment < inode->u.ufs_i.i_lastfrag) {
+		if (fragment < UFS_I(inode)->i_lastfrag) {
 			UFSD(("EXIT (ALREADY ALLOCATED)\n"))
 			unlock_super (sb);
 			return 0;
@@ -310,7 +310,7 @@
 			*p = cpu_to_fs32(sb, result);
 			*err = 0;
 			inode->i_blocks += count << uspi->s_nspfshift;
-			inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+			UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
 			NULLIFY_FRAGMENTS
 		}
 		unlock_super(sb);
@@ -325,7 +325,7 @@
 	if (result) {
 		*err = 0;
 		inode->i_blocks += count << uspi->s_nspfshift;
-		inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+		UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
 		NULLIFY_FRAGMENTS
 		unlock_super(sb);
 		UFSD(("EXIT, result %u\n", result))
@@ -378,7 +378,7 @@
 		*p = cpu_to_fs32(sb, result);
 		*err = 0;
 		inode->i_blocks += count << uspi->s_nspfshift;
-		inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+		UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
 		NULLIFY_FRAGMENTS
 		unlock_super(sb);
 		if (newcount < request)
diff -urN linux-2.5.3-pre2/fs/ufs/ialloc.c linux/fs/ufs/ialloc.c
--- linux-2.5.3-pre2/fs/ufs/ialloc.c	Mon Nov 19 14:55:46 2001
+++ linux/fs/ufs/ialloc.c	Mon Jan 21 17:23:24 2002
@@ -142,7 +142,7 @@
  * For other inodes, search forward from the parent directory's block
  * group to find a free inode.
  */
-struct inode * ufs_new_inode (const struct inode * dir,	int mode)
+struct inode * ufs_new_inode(struct inode * dir, int mode)
 {
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
@@ -151,6 +151,7 @@
 	struct ufs_cylinder_group * ucg;
 	struct inode * inode;
 	unsigned cg, bit, i, j, start;
+	struct ufs_inode_info *ufsi;
 
 	UFSD(("ENTER\n"))
 	
@@ -161,6 +162,7 @@
 	inode = new_inode(sb);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
+	ufsi = UFS_I(inode);
 	uspi = sb->u.ufs_sb.s_uspi;
 	usb1 = ubh_get_usb_first(USPI_UBH);
 
@@ -261,8 +263,13 @@
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = 0;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	inode->u.ufs_i.i_flags = dir->u.ufs_i.i_flags;
-	inode->u.ufs_i.i_lastfrag = 0;
+	ufsi->i_flags = UFS_I(dir)->i_flags;
+	ufsi->i_lastfrag = 0;
+	ufsi->i_gen = 0;
+	ufsi->i_shadow = 0;
+	ufsi->i_osync = 0;
+	ufsi->i_oeftflag = 0;
+	memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1));
 
 	insert_inode_hash(inode);
 	mark_inode_dirty(inode);
diff -urN linux-2.5.3-pre2/fs/ufs/inode.c linux/fs/ufs/inode.c
--- linux-2.5.3-pre2/fs/ufs/inode.c	Sun Dec 30 16:59:04 2001
+++ linux/fs/ufs/inode.c	Mon Jan 21 17:23:24 2002
@@ -84,6 +84,7 @@
 
 int ufs_frag_map(struct inode *inode, int frag)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block *sb = inode->i_sb;
 	struct ufs_sb_private_info *uspi = sb->u.ufs_sb.s_uspi;
 	int mask = uspi->s_apbmask>>uspi->s_fpbshift;
@@ -99,7 +100,7 @@
 	p = offsets;
 
 	lock_kernel();
-	block = inode->u.ufs_i.i_u1.i_data[*p++];
+	block = ufsi->i_u1.i_data[*p++];
 	if (!block)
 		goto out;
 	while (--depth) {
@@ -124,6 +125,7 @@
 	unsigned int fragment, unsigned int new_fragment,
 	unsigned int required, int *err, int metadata, long *phys, int *new)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
 	struct buffer_head * result;
@@ -138,12 +140,12 @@
 	uspi = sb->u.ufs_sb.s_uspi;
 	block = ufs_fragstoblks (fragment);
 	blockoff = ufs_fragnum (fragment);
-	p = inode->u.ufs_i.i_u1.i_data + block;
+	p = ufsi->i_u1.i_data + block;
 	goal = 0;
 
 repeat:
 	tmp = fs32_to_cpu(sb, *p);
-	lastfrag = inode->u.ufs_i.i_lastfrag;
+	lastfrag = ufsi->i_lastfrag;
 	if (tmp && fragment < lastfrag) {
 		if (metadata) {
 			result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff);
@@ -169,19 +171,19 @@
 		 * We must reallocate last allocated block
 		 */
 		if (lastblockoff) {
-			p2 = inode->u.ufs_i.i_u1.i_data + lastblock;
+			p2 = ufsi->i_u1.i_data + lastblock;
 			tmp = ufs_new_fragments (inode, p2, lastfrag, 
 				fs32_to_cpu(sb, *p2), uspi->s_fpb - lastblockoff, err);
 			if (!tmp) {
-				if (lastfrag != inode->u.ufs_i.i_lastfrag)
+				if (lastfrag != ufsi->i_lastfrag)
 					goto repeat;
 				else
 					return NULL;
 			}
-			lastfrag = inode->u.ufs_i.i_lastfrag;
+			lastfrag = ufsi->i_lastfrag;
 			
 		}
-		goal = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock]) + uspi->s_fpb;
+		goal = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]) + uspi->s_fpb;
 		tmp = ufs_new_fragments (inode, p, fragment - blockoff, 
 			goal, required + blockoff, err);
 	}
@@ -196,14 +198,14 @@
 	 * We will allocate new block before last allocated block
 	 */
 	else /* (lastblock > block) */ {
-		if (lastblock && (tmp = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock-1])))
+		if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1])))
 			goal = tmp + uspi->s_fpb;
 		tmp = ufs_new_fragments (inode, p, fragment - blockoff, 
 			goal, uspi->s_fpb, err);
 	}
 	if (!tmp) {
 		if ((!blockoff && *p) || 
-		    (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag))
+		    (blockoff && lastfrag != ufsi->i_lastfrag))
 			goto repeat;
 		*err = -ENOSPC;
 		return NULL;
@@ -470,6 +472,7 @@
 
 void ufs_read_inode (struct inode * inode)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
 	struct ufs_inode * ufs_inode;	
@@ -517,24 +520,23 @@
 	inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks);
 	inode->i_blksize = PAGE_SIZE;   /* This is the optimal IO size (for stat) */
 	inode->i_version = ++event;
-
-	inode->u.ufs_i.i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
-	inode->u.ufs_i.i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen);
-	inode->u.ufs_i.i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
-	inode->u.ufs_i.i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
-	inode->u.ufs_i.i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
+	ufsi->i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
+	ufsi->i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen);
+	ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
+	ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
+	ufsi->i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
 	
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		;
 	else if (inode->i_blocks) {
 		for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
-			inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i];
+			ufsi->i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i];
 	}
 	else {
 		for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
-			inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
+			ufsi->i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
 	}
-
+	ufsi->i_osync = 0;
 
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = &ufs_file_inode_operations;
@@ -566,6 +568,7 @@
 
 static int ufs_update_inode(struct inode * inode, int do_sync)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
 	struct buffer_head * bh;
@@ -606,23 +609,23 @@
 	ufs_inode->ui_mtime.tv_sec = cpu_to_fs32(sb, inode->i_mtime);
 	ufs_inode->ui_mtime.tv_usec = 0;
 	ufs_inode->ui_blocks = cpu_to_fs32(sb, inode->i_blocks);
-	ufs_inode->ui_flags = cpu_to_fs32(sb, inode->u.ufs_i.i_flags);
-	ufs_inode->ui_gen = cpu_to_fs32(sb, inode->u.ufs_i.i_gen);
+	ufs_inode->ui_flags = cpu_to_fs32(sb, ufsi->i_flags);
+	ufs_inode->ui_gen = cpu_to_fs32(sb, ufsi->i_gen);
 
 	if ((flags & UFS_UID_MASK) == UFS_UID_EFT) {
-		ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, inode->u.ufs_i.i_shadow);
-		ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, inode->u.ufs_i.i_oeftflag);
+		ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, ufsi->i_shadow);
+		ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, ufsi->i_oeftflag);
 	}
 
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		ufs_inode->ui_u2.ui_addr.ui_db[0] = cpu_to_fs32(sb, kdev_t_to_nr(inode->i_rdev));
 	else if (inode->i_blocks) {
 		for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
-			ufs_inode->ui_u2.ui_addr.ui_db[i] = inode->u.ufs_i.i_u1.i_data[i];
+			ufs_inode->ui_u2.ui_addr.ui_db[i] = ufsi->i_u1.i_data[i];
 	}
 	else {
 		for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
-			ufs_inode->ui_u2.ui_symlink[i] = inode->u.ufs_i.i_u1.i_symlink[i];
+			ufs_inode->ui_u2.ui_symlink[i] = ufsi->i_u1.i_symlink[i];
 	}
 
 	if (!inode->i_nlink)
@@ -653,7 +656,7 @@
 
 void ufs_delete_inode (struct inode * inode)
 {
-	/*inode->u.ufs_i.i_dtime = CURRENT_TIME;*/
+	/*UFS_I(inode)->i_dtime = CURRENT_TIME;*/
 	lock_kernel();
 	mark_inode_dirty(inode);
 	ufs_update_inode(inode, IS_SYNC(inode));
diff -urN linux-2.5.3-pre2/fs/ufs/namei.c linux/fs/ufs/namei.c
--- linux-2.5.3-pre2/fs/ufs/namei.c	Sat Oct 20 19:14:42 2001
+++ linux/fs/ufs/namei.c	Mon Jan 21 17:23:24 2002
@@ -138,7 +138,7 @@
 	} else {
 		/* fast symlink */
 		inode->i_op = &ufs_fast_symlink_inode_operations;
-		memcpy((char*)&inode->u.ufs_i.i_u1.i_data,symname,l);
+		memcpy((char*)&UFS_I(inode)->i_u1.i_data,symname,l);
 		inode->i_size = l-1;
 	}
 	mark_inode_dirty(inode);
diff -urN linux-2.5.3-pre2/fs/ufs/super.c linux/fs/ufs/super.c
--- linux-2.5.3-pre2/fs/ufs/super.c	Fri Jan  4 09:42:12 2002
+++ linux/fs/ufs/super.c	Mon Jan 21 17:23:24 2002
@@ -953,7 +953,51 @@
 	return 0;
 }
 
+static kmem_cache_t * ufs_inode_cachep;
+
+static struct inode *ufs_alloc_inode(struct super_block *sb)
+{
+	struct ufs_inode_info *ei;
+	ei = (struct ufs_inode_info *)kmem_cache_alloc(ufs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void ufs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(ufs_inode_cachep, UFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct ufs_inode_info *ei = (struct ufs_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+static int init_inodecache(void)
+{
+	ufs_inode_cachep = kmem_cache_create("ufs_inode_cache",
+					     sizeof(struct ufs_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (ufs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(ufs_inode_cachep))
+		printk(KERN_INFO "ufs_inode_cache: not all structures were freed\n");
+}
+
 static struct super_operations ufs_super_ops = {
+	alloc_inode:	ufs_alloc_inode,
+	destroy_inode:	ufs_destroy_inode,
 	read_inode:	ufs_read_inode,
 	write_inode:	ufs_write_inode,
 	delete_inode:	ufs_delete_inode,
@@ -967,15 +1011,27 @@
 
 static int __init init_ufs_fs(void)
 {
-	return register_filesystem(&ufs_fs_type);
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&ufs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
 }
 
 static void __exit exit_ufs_fs(void)
 {
 	unregister_filesystem(&ufs_fs_type);
+	destroy_inodecache();
 }
 
 EXPORT_NO_SYMBOLS;
 
 module_init(init_ufs_fs)
 module_exit(exit_ufs_fs)
+MODULE_LICENSE("GPL");
diff -urN linux-2.5.3-pre2/fs/ufs/symlink.c linux/fs/ufs/symlink.c
--- linux-2.5.3-pre2/fs/ufs/symlink.c	Fri Apr  7 13:38:00 2000
+++ linux/fs/ufs/symlink.c	Mon Jan 21 17:23:24 2002
@@ -26,17 +26,18 @@
  */
 
 #include <linux/fs.h>
+#include <linux/ufs_fs.h>
 
 static int ufs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
-	char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
-	return vfs_readlink(dentry, buffer, buflen, s);
+	struct ufs_inode_info *p = UFS_I(dentry->d_inode);
+	return vfs_readlink(dentry, buffer, buflen, (char*)p->i_u1.i_symlink);
 }
 
 static int ufs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-	char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
-	return vfs_follow_link(nd, s);
+	struct ufs_inode_info *p = UFS_I(dentry->d_inode);
+	return vfs_follow_link(nd, (char*)p->i_u1.i_symlink);
 }
 
 struct inode_operations ufs_fast_symlink_inode_operations = {
diff -urN linux-2.5.3-pre2/fs/ufs/truncate.c linux/fs/ufs/truncate.c
--- linux-2.5.3-pre2/fs/ufs/truncate.c	Mon Jan  7 12:55:16 2002
+++ linux/fs/ufs/truncate.c	Mon Jan 21 17:23:24 2002
@@ -67,6 +67,7 @@
 
 static int ufs_trunc_direct (struct inode * inode)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
 	struct buffer_head * bh;
@@ -86,7 +87,7 @@
 	retry = 0;
 	
 	frag1 = DIRECT_FRAGMENT;
-	frag4 = min_t(u32, UFS_NDIR_FRAGMENT, inode->u.ufs_i.i_lastfrag);
+	frag4 = min_t(u32, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag);
 	frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1);
 	frag3 = frag4 & ~uspi->s_fpbmask;
 	block1 = block2 = 0;
@@ -107,7 +108,7 @@
 	/*
 	 * Free first free fragments
 	 */
-	p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag1);
+	p = ufsi->i_u1.i_data + ufs_fragstoblks (frag1);
 	tmp = fs32_to_cpu(sb, *p);
 	if (!tmp )
 		ufs_panic (sb, "ufs_trunc_direct", "internal error");
@@ -132,7 +133,7 @@
 	 * Free whole blocks
 	 */
 	for (i = block1 ; i < block2; i++) {
-		p = inode->u.ufs_i.i_u1.i_data + i;
+		p = ufsi->i_u1.i_data + i;
 		tmp = fs32_to_cpu(sb, *p);
 		if (!tmp)
 			continue;
@@ -170,7 +171,7 @@
 	/*
 	 * Free last free fragments
 	 */
-	p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag3);
+	p = ufsi->i_u1.i_data + ufs_fragstoblks (frag3);
 	tmp = fs32_to_cpu(sb, *p);
 	if (!tmp )
 		ufs_panic(sb, "ufs_truncate_direct", "internal error");
@@ -360,6 +361,7 @@
 
 static int ufs_trunc_tindirect (struct inode * inode)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
 	struct ufs_buffer_head * tind_bh;
@@ -375,7 +377,7 @@
 	
 	tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb))
 		? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) >> uspi->s_2apbshift) : 0;
-	p = inode->u.ufs_i.i_u1.i_data + UFS_TIND_BLOCK;
+	p = ufsi->i_u1.i_data + UFS_TIND_BLOCK;
 	if (!(tmp = fs32_to_cpu(sb, *p)))
 		return 0;
 	tind_bh = ubh_bread (sb, tmp, uspi->s_bsize);
@@ -422,6 +424,7 @@
 		
 void ufs_truncate (struct inode * inode)
 {
+	struct ufs_inode_info *ufsi = UFS_I(inode);
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
 	struct buffer_head * bh;
@@ -439,9 +442,9 @@
 	while (1) {
 		retry = ufs_trunc_direct(inode);
 		retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK,
-			(u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK]);
+			(u32 *) &ufsi->i_u1.i_data[UFS_IND_BLOCK]);
 		retry |= ufs_trunc_dindirect (inode, UFS_IND_BLOCK + uspi->s_apb,
-			(u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK]);
+			(u32 *) &ufsi->i_u1.i_data[UFS_DIND_BLOCK]);
 		retry |= ufs_trunc_tindirect (inode);
 		if (!retry)
 			break;
@@ -460,7 +463,7 @@
 		}
 	}
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	inode->u.ufs_i.i_lastfrag = DIRECT_FRAGMENT;
+	ufsi->i_lastfrag = DIRECT_FRAGMENT;
 	mark_inode_dirty(inode);
 	UFSD(("EXIT\n"))
 }
diff -urN linux-2.5.3-pre2/fs/ufs/util.c linux/fs/ufs/util.c
--- linux-2.5.3-pre2/fs/ufs/util.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/ufs/util.c	Mon Jan 21 17:23:24 2002
@@ -9,6 +9,7 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/locks.h>
+#include <linux/ufs_fs.h>
 
 #include "swab.h"
 #include "util.h"
diff -urN linux-2.5.3-pre2/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- linux-2.5.3-pre2/fs/umsdos/dir.c	Sat Sep  1 10:59:08 2001
+++ linux/fs/umsdos/dir.c	Mon Jan 21 17:23:24 2002
@@ -204,7 +204,7 @@
 		if (!inode)
 			goto remove_name;
 #ifdef UMSDOS_DEBUG_VERBOSE
-if (inode->u.umsdos_i.i_is_hlink)
+if (UMSDOS_I(inode)->i_is_hlink)
 printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
 dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
 #endif
@@ -214,7 +214,7 @@
 entry.flags));
 		/* check whether to resolve a hard-link */
 		if ((entry.flags & UMSDOS_HLINK) &&
-		    !inode->u.umsdos_i.i_is_hlink) {
+		    !UMSDOS_I(inode)->i_is_hlink) {
 			dret = umsdos_solve_hlink (dret);
 			ret = PTR_ERR(dret);
 			if (IS_ERR(dret))
@@ -361,9 +361,9 @@
 	/*
 	 * This part of the initialization depends only on i_patched.
 	 */
-	if (inode->u.umsdos_i.i_patched)
+	if (UMSDOS_I(inode)->i_patched)
 		goto out;
-	inode->u.umsdos_i.i_patched = 1;
+	UMSDOS_I(inode)->i_patched = 1;
 	if (S_ISREG (entry->mode))
 		entry->mtime = inode->i_mtime;
 	inode->i_mode = entry->mode;
@@ -498,7 +498,7 @@
 
 	/* Check for a hard link */
 	if ((info.entry.flags & UMSDOS_HLINK) &&
-	    !inode->u.umsdos_i.i_is_hlink) {
+	    !UMSDOS_I(inode)->i_is_hlink) {
 		dret = umsdos_solve_hlink (dret);
 		ret = PTR_ERR(dret);
 		if (IS_ERR(dret))
@@ -756,7 +756,7 @@
 	if (!IS_ERR(dentry_dst)) {
 		struct inode *inode = dentry_dst->d_inode;
 		if (inode) {
-			inode->u.umsdos_i.i_is_hlink = 1;
+			UMSDOS_I(inode)->i_is_hlink = 1;
 #ifdef UMSDOS_DEBUG_VERBOSE
 printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
 dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
diff -urN linux-2.5.3-pre2/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- linux-2.5.3-pre2/fs/umsdos/inode.c	Mon Jan 21 17:22:37 2002
+++ linux/fs/umsdos/inode.c	Mon Jan 21 17:23:24 2002
@@ -34,7 +34,7 @@
 	PRINTK ((KERN_DEBUG 
 		"put inode %p (%lu) pos %lu count=%d\n"
 		 ,inode, inode->i_ino
-		 ,inode->u.umsdos_i.pos
+		 ,UMSDOS_I(inode)->pos
 		 ,atomic_read(&inode->i_count)));
 
 	if (inode == pseudo_root) {
@@ -42,7 +42,7 @@
 	}
 
 	if (atomic_read(&inode->i_count) == 1)
-		inode->u.umsdos_i.i_patched = 0;
+		UMSDOS_I(inode)->i_patched = 0;
 }
 
 
@@ -67,15 +67,16 @@
 void umsdos_setup_dir(struct dentry *dir)
 {
 	struct inode *inode = dir->d_inode;
+	struct umsdos_inode_info *ui = UMSDOS_I(inode);
 
 	if (!S_ISDIR(inode->i_mode))
 		printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n",
 			dir->d_parent->d_name.name, dir->d_name.name);
 
-	init_waitqueue_head (&inode->u.umsdos_i.dir_info.p);
-	inode->u.umsdos_i.dir_info.looking = 0;
-	inode->u.umsdos_i.dir_info.creating = 0;
-	inode->u.umsdos_i.dir_info.pid = 0;
+	init_waitqueue_head (&ui->dir_info.p);
+	ui->dir_info.looking = 0;
+	ui->dir_info.creating = 0;
+	ui->dir_info.pid = 0;
 
 	inode->i_op = &umsdos_rdir_inode_operations;
 	inode->i_fop = &umsdos_rdir_operations;
@@ -96,7 +97,7 @@
 	struct inode *inode = dentry->d_inode;
 	struct dentry *demd;
 
-	inode->u.umsdos_i.pos = f_pos;
+	UMSDOS_I(inode)->pos = f_pos;
 
 	/* now check the EMD file */
 	demd = umsdos_get_emd_dentry(dentry->d_parent);
@@ -233,7 +234,7 @@
 	int offs;
 
 Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched));
+dentry->d_parent->d_name.name, dentry->d_name.name, UMSDOS_I(inode)->i_patched));
 
 	if (inode->i_nlink == 0)
 		goto out;
@@ -265,9 +266,9 @@
 
 	/* Read only the start of the entry since we don't touch the name */
 	mapping = demd->d_inode->i_mapping;
-	offs = inode->u.umsdos_i.pos & ~PAGE_CACHE_MASK;
+	offs = UMSDOS_I(inode)->pos & ~PAGE_CACHE_MASK;
 	ret = -ENOMEM;
-	page=grab_cache_page(mapping,inode->u.umsdos_i.pos>>PAGE_CACHE_SHIFT);
+	page=grab_cache_page(mapping,UMSDOS_I(inode)->pos>>PAGE_CACHE_SHIFT);
 	if (!page)
 		goto out_dput;
 	ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
diff -urN linux-2.5.3-pre2/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- linux-2.5.3-pre2/fs/umsdos/namei.c	Sat Sep  1 10:59:08 2001
+++ linux/fs/umsdos/namei.c	Mon Jan 21 17:23:24 2002
@@ -28,12 +28,12 @@
 
 static inline void u_sleep_on (struct inode *dir)
 {
-	sleep_on (&dir->u.umsdos_i.dir_info.p);
+	sleep_on (&UMSDOS_I(dir)->dir_info.p);
 }
 
 static inline void u_wake_up (struct inode *dir)
 {
-    	wake_up (&dir->u.umsdos_i.dir_info.p);
+    	wake_up (&UMSDOS_I(dir)->dir_info.p);
 }
 
 /*
@@ -47,9 +47,9 @@
 {
 	int ret = 0;
 
-	if (dir->u.umsdos_i.dir_info.creating
-	    && dir->u.umsdos_i.dir_info.pid != current->pid) {
-	    	PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", dir->u.umsdos_i.dir_info.pid, current->pid));
+	if (UMSDOS_I(dir)->dir_info.creating
+	    && UMSDOS_I(dir)->dir_info.pid != current->pid) {
+	    	PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", UMSDOS_I(dir)->dir_info.pid, current->pid));
 	    	u_sleep_on (dir);
 		ret = 1;
 	}
@@ -61,7 +61,7 @@
  */
 static void umsdos_waitlookup (struct inode *dir)
 {
-	while (dir->u.umsdos_i.dir_info.looking) {
+	while (UMSDOS_I(dir)->dir_info.looking) {
 	    	u_sleep_on (dir);
 	}
 }
@@ -104,8 +104,8 @@
 	 * if we (the process) own the lock
 	 */
 	while (umsdos_waitcreate (dir) != 0);
-	dir->u.umsdos_i.dir_info.creating++;
-	dir->u.umsdos_i.dir_info.pid = current->pid;
+	UMSDOS_I(dir)->dir_info.creating++;
+	UMSDOS_I(dir)->dir_info.pid = current->pid;
 	umsdos_waitlookup (dir);
 }
 
@@ -124,10 +124,10 @@
 		if (umsdos_waitcreate (dir1) == 0
 		    && umsdos_waitcreate (dir2) == 0) {
 			/* We own both now */
-			dir1->u.umsdos_i.dir_info.creating++;
-			dir1->u.umsdos_i.dir_info.pid = current->pid;
-			dir2->u.umsdos_i.dir_info.creating++;
-			dir2->u.umsdos_i.dir_info.pid = current->pid;
+			UMSDOS_I(dir1)->dir_info.creating++;
+			UMSDOS_I(dir1)->dir_info.pid = current->pid;
+			UMSDOS_I(dir2)->dir_info.creating++;
+			UMSDOS_I(dir2)->dir_info.pid = current->pid;
 			break;
 		}
 	}
@@ -141,7 +141,7 @@
 void umsdos_startlookup (struct inode *dir)
 {
 	while (umsdos_waitcreate (dir) != 0);
-	dir->u.umsdos_i.dir_info.looking++;
+	UMSDOS_I(dir)->dir_info.looking++;
 }
 
 /*
@@ -149,10 +149,10 @@
  */
 void umsdos_unlockcreate (struct inode *dir)
 {
-	dir->u.umsdos_i.dir_info.creating--;
-	if (dir->u.umsdos_i.dir_info.creating < 0) {
-		printk ("UMSDOS: dir->u.umsdos_i.dir_info.creating < 0: %d"
-			,dir->u.umsdos_i.dir_info.creating);
+	UMSDOS_I(dir)->dir_info.creating--;
+	if (UMSDOS_I(dir)->dir_info.creating < 0) {
+		printk ("UMSDOS: UMSDOS_I(dir)->dir_info.creating < 0: %d"
+			,UMSDOS_I(dir)->dir_info.creating);
 	}
     	u_wake_up (dir);
 }
@@ -162,10 +162,10 @@
  */
 void umsdos_endlookup (struct inode *dir)
 {
-	dir->u.umsdos_i.dir_info.looking--;
-	if (dir->u.umsdos_i.dir_info.looking < 0) {
-		printk ("UMSDOS: dir->u.umsdos_i.dir_info.looking < 0: %d"
-			,dir->u.umsdos_i.dir_info.looking);
+	UMSDOS_I(dir)->dir_info.looking--;
+	if (UMSDOS_I(dir)->dir_info.looking < 0) {
+		printk ("UMSDOS: UMSDOS_I(dir)->dir_info.looking < 0: %d"
+			,UMSDOS_I(dir)->dir_info.looking);
 	}
     	u_wake_up (dir);
 }
@@ -618,7 +618,7 @@
 			goto cleanup;
 		}
 		/* mark the inode as a hardlink */
-		oldinode->u.umsdos_i.i_is_hlink = 1;
+		UMSDOS_I(oldinode)->i_is_hlink = 1;
 
 		/*
 		 * Capture the path to the hidden link.
@@ -667,7 +667,7 @@
 	 * the dentry for its real name, not the visible name.
 	 * N.B. make sure it's the hidden inode ...
 	 */
-	if (!oldinode->u.umsdos_i.i_is_hlink)
+	if (!UMSDOS_I(oldinode)->i_is_hlink)
 		printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
 			olddentry->d_parent->d_name.name,
 			olddentry->d_name.name, oldinode->i_ino);
@@ -721,7 +721,7 @@
 
 
 #ifdef UMSDOS_PARANOIA
-if (!oldinode->u.umsdos_i.i_is_hlink)
+if (!UMSDOS_I(oldinode)->i_is_hlink)
 printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
 olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
 #endif
diff -urN linux-2.5.3-pre2/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- linux-2.5.3-pre2/fs/umsdos/rdir.c	Sat Sep  1 10:59:08 2001
+++ linux/fs/umsdos/rdir.c	Mon Jan 21 17:23:24 2002
@@ -113,7 +113,7 @@
 dentry->d_parent->d_name.name, dentry->d_name.name));
 /* only patch if needed (because we get called even for lookup
    (not only rlookup) stuff sometimes, like in umsdos_covered() */
-		if (dentry->d_inode->u.umsdos_i.i_patched == 0)	
+		if (UMSDOS_I(dentry->d_inode)->i_patched == 0)	
 		umsdos_patch_dentry_inode(dentry, 0);
 
 	}
diff -urN linux-2.5.3-pre2/include/asm-cris/bitops.h linux/include/asm-cris/bitops.h
--- linux-2.5.3-pre2/include/asm-cris/bitops.h	Mon Oct  8 11:43:54 2001
+++ linux/include/asm-cris/bitops.h	Mon Jan 21 17:23:24 2002
@@ -22,6 +22,7 @@
 /* We use generic_ffs so get it; include guards resolve the possible
    mutually inclusion.  */
 #include <linux/bitops.h>
+#include <linux/compiler.h>
 
 /*
  * Some hacks to defeat gcc over-optimizations..
@@ -43,6 +44,8 @@
 
 #define set_bit(nr, addr)    (void)test_and_set_bit(nr, addr)
 
+#define __set_bit(nr, addr)    (void)__test_and_set_bit(nr, addr)
+
 /*
  * clear_bit - Clears a bit in memory
  * @nr: Bit to clear
@@ -56,6 +59,8 @@
 
 #define clear_bit(nr, addr)  (void)test_and_clear_bit(nr, addr)
 
+#define __clear_bit(nr, addr)  (void)__test_and_clear_bit(nr, addr)
+
 /*
  * change_bit - Toggle a bit in memory
  * @nr: Bit to clear
@@ -89,7 +94,7 @@
  * It also implies a memory barrier.
  */
 
-static __inline__ int test_and_set_bit(int nr, void *addr)
+static inline int test_and_set_bit(int nr, void *addr)
 {
 	unsigned int mask, retval;
 	unsigned long flags;
@@ -105,6 +110,18 @@
 	return retval;
 }
 
+static inline int __test_and_set_bit(int nr, void *addr)
+{
+	unsigned int mask, retval;
+	unsigned int *adr = (unsigned int *)addr;
+	
+	adr += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	retval = (mask & *adr) != 0;
+	*adr |= mask;
+	return retval;
+}
+
 /*
  * clear_bit() doesn't provide any barrier for the compiler.
  */
@@ -120,7 +137,7 @@
  * It also implies a memory barrier.
  */
 
-static __inline__ int test_and_clear_bit(int nr, void *addr)
+static inline int test_and_clear_bit(int nr, void *addr)
 {
 	unsigned int mask, retval;
 	unsigned long flags;
@@ -146,7 +163,7 @@
  * but actually fail.  You must protect multiple accesses with a lock.
  */
 
-static __inline__ int __test_and_clear_bit(int nr, void *addr)
+static inline int __test_and_clear_bit(int nr, void *addr)
 {
 	unsigned int mask, retval;
 	unsigned int *adr = (unsigned int *)addr;
@@ -166,7 +183,7 @@
  * It also implies a memory barrier.
  */
 
-static __inline__ int test_and_change_bit(int nr, void *addr)
+static inline int test_and_change_bit(int nr, void *addr)
 {
 	unsigned int mask, retval;
 	unsigned long flags;
@@ -183,7 +200,7 @@
 
 /* WARNING: non atomic and it can be reordered! */
 
-static __inline__ int __test_and_change_bit(int nr, void *addr)
+static inline int __test_and_change_bit(int nr, void *addr)
 {
 	unsigned int mask, retval;
 	unsigned int *adr = (unsigned int *)addr;
@@ -204,7 +221,7 @@
  * This routine doesn't need to be atomic.
  */
 
-static __inline__ int test_bit(int nr, const void *addr)
+static inline int test_bit(int nr, const void *addr)
 {
 	unsigned int mask;
 	unsigned int *adr = (unsigned int *)addr;
@@ -225,7 +242,7 @@
  * number.  They differ in that the first function also inverts all bits
  * in the input.
  */
-static __inline__ unsigned long cris_swapnwbrlz(unsigned long w)
+static inline unsigned long cris_swapnwbrlz(unsigned long w)
 {
 	/* Let's just say we return the result in the same register as the
 	   input.  Saying we clobber the input but can return the result
@@ -241,7 +258,7 @@
 	return res;
 }
 
-static __inline__ unsigned long cris_swapwbrlz(unsigned long w)
+static inline unsigned long cris_swapwbrlz(unsigned long w)
 {
 	unsigned res;
 	__asm__ ("swapwbr %0 \n\t"
@@ -255,7 +272,7 @@
  * ffz = Find First Zero in word. Undefined if no zero exists,
  * so code should check against ~0UL first..
  */
-static __inline__ unsigned long ffz(unsigned long w)
+static inline unsigned long ffz(unsigned long w)
 {
 	/* The generic_ffs function is used to avoid the asm when the
 	   argument is a constant.  */
@@ -268,7 +285,7 @@
  * Somewhat like ffz but the equivalent of generic_ffs: in contrast to
  * ffz we return the first one-bit *plus one*.
  */
-static __inline__ unsigned long ffs(unsigned long w)
+static inline unsigned long ffs(unsigned long w)
 {
 	/* The generic_ffs function is used to avoid the asm when the
 	   argument is a constant.  */
@@ -283,7 +300,7 @@
  * @offset: The bitnumber to start searching at
  * @size: The maximum size to search
  */
-static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static inline int find_next_zero_bit (void * addr, int size, int offset)
 {
 	unsigned long *p = ((unsigned long *) addr) + (offset >> 5);
 	unsigned long result = offset & ~31UL;
@@ -331,6 +348,17 @@
 #define find_first_zero_bit(addr, size) \
         find_next_zero_bit((addr), (size), 0)
 
+/*
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
 #define ext2_set_bit                 test_and_set_bit
 #define ext2_clear_bit               test_and_clear_bit
 #define ext2_test_bit                test_bit
@@ -343,7 +371,45 @@
 #define minix_test_bit(nr,addr) test_bit(nr,addr)
 #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
 
-#endif /* __KERNEL__ */
+#if 0
+/* TODO: see below */
+#define sched_find_first_zero_bit(addr) find_first_zero_bit(addr, 168)
+
+#else
+/* TODO: left out pending where to put it.. (there are .h dependencies) */
+
+ /*
+ * Every architecture must define this function. It's the fastest
+ * way of searching a 168-bit bitmap where the first 128 bits are
+ * unlikely to be set. It's guaranteed that at least one of the 168
+ * bits is cleared.
+ */
+#if 0
+#if MAX_RT_PRIO != 128 || MAX_PRIO != 168
+# error update this function.
+#endif
+#else
+#define MAX_RT_PRIO 128
+#define MAX_PRIO 168
+#endif
+
+static inline int sched_find_first_zero_bit(char *bitmap)
+{
+	unsigned int *b = (unsigned int *)bitmap;
+	unsigned int rt;
+
+	rt = b[0] & b[1] & b[2] & b[3];
+	if (unlikely(rt != 0xffffffff))
+		return find_first_zero_bit(bitmap, MAX_RT_PRIO);
+
+	if (b[4] != ~0)
+		return ffz(b[4]) + MAX_RT_PRIO;
+	return ffz(b[5]) + 32 + MAX_RT_PRIO;
+}
+#undef MAX_PRIO
+#undef MAX_RT_PRIO
+#endif
 
+#endif /* __KERNEL__ */
 
 #endif /* _CRIS_BITOPS_H */
diff -urN linux-2.5.3-pre2/include/asm-cris/elf.h linux/include/asm-cris/elf.h
--- linux-2.5.3-pre2/include/asm-cris/elf.h	Tue May  1 16:05:00 2001
+++ linux/include/asm-cris/elf.h	Mon Jan 21 17:23:24 2002
@@ -9,8 +9,9 @@
 
 typedef unsigned long elf_greg_t;
 
-/* These probably need fixing.  */
-#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+/* Note that NGREG is defined to ELF_NGREG in include/linux/elfcore.h, and is
+   thus exposed to user-space. */
+#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 
 /* A placeholder; CRIS does not have any fp regs.  */
@@ -45,7 +46,52 @@
 	(_r)->r1 = 0;  (_r)->r0 = 0;  (_r)->mof = 0; (_r)->srp = 0; \
 } while (0)
 
-#undef USE_ELF_CORE_DUMP
+#define USE_ELF_CORE_DUMP
+
+/* The additional layer below is because the stack pointer is missing in 
+   the pt_regs struct, but needed in a core dump. pr_reg is a elf_gregset_t,
+   and should be filled in according to the layout of the user_regs_struct
+   struct; regs is a pt_regs struct. We dump all registers, though several are
+   obviously unnecessary. That way there's less need for intelligence at 
+   the receiving end (i.e. gdb). */
+#define ELF_CORE_COPY_REGS(pr_reg, regs)                   \
+	pr_reg[0] = regs->r0;                              \
+	pr_reg[1] = regs->r1;                              \
+	pr_reg[2] = regs->r2;                              \
+	pr_reg[3] = regs->r3;                              \
+	pr_reg[4] = regs->r4;                              \
+	pr_reg[5] = regs->r5;                              \
+	pr_reg[6] = regs->r6;                              \
+	pr_reg[7] = regs->r7;                              \
+	pr_reg[8] = regs->r8;                              \
+	pr_reg[9] = regs->r9;                              \
+	pr_reg[10] = regs->r10;                            \
+	pr_reg[11] = regs->r11;                            \
+	pr_reg[12] = regs->r12;                            \
+	pr_reg[13] = regs->r13;                            \
+	pr_reg[14] = rdusp();               /* sp */       \
+	pr_reg[15] = regs->irp;             /* pc */       \
+	pr_reg[16] = 0;                     /* p0 */       \
+	pr_reg[17] = rdvr();                /* vr */       \
+	pr_reg[18] = 0;                     /* p2 */       \
+	pr_reg[19] = 0;                     /* p3 */       \
+	pr_reg[20] = 0;                     /* p4 */       \
+	pr_reg[21] = (regs->dccr & 0xffff); /* ccr */      \
+	pr_reg[22] = 0;                     /* p6 */       \
+	pr_reg[23] = regs->mof;             /* mof */      \
+	pr_reg[24] = 0;                     /* p8 */       \
+	pr_reg[25] = 0;                     /* ibr */      \
+	pr_reg[26] = 0;                     /* irp */      \
+	pr_reg[27] = regs->srp;             /* srp */      \
+	pr_reg[28] = 0;                     /* bar */      \
+	pr_reg[29] = regs->dccr;            /* dccr */     \
+	pr_reg[30] = 0;                     /* brp */      \
+	pr_reg[31] = rdusp();               /* usp */      \
+	pr_reg[32] = 0;                     /* csrinstr */ \
+	pr_reg[33] = 0;                     /* csraddr */  \
+	pr_reg[34] = 0;                     /* csrdata */
+
+
 #define ELF_EXEC_PAGESIZE	8192
 
 /* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
diff -urN linux-2.5.3-pre2/include/asm-cris/ethernet.h linux/include/asm-cris/ethernet.h
--- linux-2.5.3-pre2/include/asm-cris/ethernet.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-cris/ethernet.h	Mon Jan 21 17:23:24 2002
@@ -0,0 +1,18 @@
+/*  
+ * ioctl defines for ethernet driver
+ *
+ * Copyright (c) 2001 Axis Communications AB
+ * 
+ * Author: Mikael Starvik 
+ *
+ */
+
+#ifndef _CRIS_ETHERNET_H
+#define _CRIS_ETHERNET_H
+#define SET_ETH_SPEED_AUTO      SIOCDEVPRIVATE          /* Auto neg speed */
+#define SET_ETH_SPEED_10        SIOCDEVPRIVATE+1        /* 10 Mbps */
+#define SET_ETH_SPEED_100       SIOCDEVPRIVATE+2        /* 100 Mbps. */
+#define SET_ETH_DUPLEX_AUTO     SIOCDEVPRIVATE+3        /* Auto neg duplex */
+#define SET_ETH_DUPLEX_HALF     SIOCDEVPRIVATE+4        /* Full duplex */
+#define SET_ETH_DUPLEX_FULL     SIOCDEVPRIVATE+5        /* Half duplex */
+#endif /* _CRIS_ETHERNET_H */
diff -urN linux-2.5.3-pre2/include/asm-cris/irq.h linux/include/asm-cris/irq.h
--- linux-2.5.3-pre2/include/asm-cris/irq.h	Mon Oct  8 11:43:54 2001
+++ linux/include/asm-cris/irq.h	Mon Jan 21 17:23:24 2002
@@ -126,6 +126,9 @@
   /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
    * do_IRQ (with irq disabled still). after that it unblocks and jumps to
    * ret_from_intr (entry.S)
+   *
+   * The reason the IRQ is blocked is to allow an sti() before the handler which
+   * will acknowledge the interrupt is run.
    */
 
 #define BUILD_IRQ(nr,mask) \
@@ -151,6 +154,41 @@
           "reti\n\t" \
           "nop\n");
 
+/* This is subtle. The timer interrupt is crucial and it should not be disabled for 
+ * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would
+ * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK.
+ * If the softirq's take too much time to run, the timer irq won't run and the 
+ * watchdog will kill us.
+ *
+ * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq
+ * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed
+ * it here, we would not get the multiple_irq at all.
+ *
+ * The non-blocking here is based on the knowledge that the timer interrupt is 
+ * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not
+ * be an sti() before the timer irq handler is run to acknowledge the interrupt.
+ */
+
+#define BUILD_TIMER_IRQ(nr,mask) \
+void IRQ_NAME(nr); \
+void sIRQ_NAME(nr); \
+void BAD_IRQ_NAME(nr); \
+__asm__ ( \
+          ".text\n\t" \
+          "IRQ" #nr "_interrupt:\n\t" \
+	  SAVE_ALL \
+	  "sIRQ" #nr "_interrupt:\n\t" /* shortcut for the multiple irq handler */ \
+	  "moveq "#nr",$r10\n\t" \
+	  "move.d $sp,$r11\n\t" \
+	  "jsr do_IRQ\n\t" /* irq.c, r10 and r11 are arguments */ \
+	  "moveq 0,$r9\n\t" /* make ret_from_intr realise we came from an irq */ \
+	  "jump ret_from_intr\n\t" \
+          "bad_IRQ" #nr "_interrupt:\n\t" \
+	  "push $r0\n\t" \
+	  BLOCK_IRQ(mask,nr) \
+	  "pop $r0\n\t" \
+          "reti\n\t" \
+          "nop\n");
 
 #endif  /* _ASM_IRQ_H */
 
diff -urN linux-2.5.3-pre2/include/asm-cris/page.h linux/include/asm-cris/page.h
--- linux-2.5.3-pre2/include/asm-cris/page.h	Tue Feb 13 14:13:44 2001
+++ linux/include/asm-cris/page.h	Mon Jan 21 17:23:24 2002
@@ -105,8 +105,14 @@
  * to arm and m68k I think)
  */ 
 
-#define virt_to_page(kaddr)    (mem_map + (((unsigned long)kaddr - PAGE_OFFSET) >> PAGE_SHIFT))
-#define VALID_PAGE(page)       ((page - mem_map) < max_mapnr)
+#define virt_to_page(kaddr)    (mem_map + (((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT))
+#define VALID_PAGE(page)       (((page) - mem_map) < max_mapnr)
+
+/* convert a page (based on mem_map and forward) to a physical address
+ * do this by figuring out the virtual address and then use __pa
+ */
+
+#define page_to_phys(page)     __pa((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET)
 
 /* from linker script */
 
diff -urN linux-2.5.3-pre2/include/asm-cris/pgalloc.h linux/include/asm-cris/pgalloc.h
--- linux-2.5.3-pre2/include/asm-cris/pgalloc.h	Tue May  1 16:05:00 2001
+++ linux/include/asm-cris/pgalloc.h	Mon Jan 21 17:23:24 2002
@@ -21,7 +21,7 @@
  * Allocate and free page tables.
  */
 
-extern __inline__ pgd_t *get_pgd_slow(void)
+static inline pgd_t *get_pgd_slow(void)
 {
         pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL);
 
@@ -33,12 +33,12 @@
         return ret;
 }
 
-extern __inline__ void free_pgd_slow(pgd_t *pgd)
+static inline void free_pgd_slow(pgd_t *pgd)
 {
         free_page((unsigned long)pgd);
 }
 
-extern __inline__ pgd_t *get_pgd_fast(void)
+static inline pgd_t *get_pgd_fast(void)
 {
         unsigned long *ret;
 
@@ -51,7 +51,7 @@
         return (pgd_t *)ret;
 }
 
-extern __inline__ void free_pgd_fast(pgd_t *pgd)
+static inline void free_pgd_fast(pgd_t *pgd)
 {
         *(unsigned long *)pgd = (unsigned long) pgd_quicklist;
         pgd_quicklist = (unsigned long *) pgd;
diff -urN linux-2.5.3-pre2/include/asm-cris/pgtable.h linux/include/asm-cris/pgtable.h
--- linux-2.5.3-pre2/include/asm-cris/pgtable.h	Mon Jan 21 17:22:37 2002
+++ linux/include/asm-cris/pgtable.h	Mon Jan 21 17:23:24 2002
@@ -3,6 +3,12 @@
  * HISTORY:
  *
  * $Log: pgtable.h,v $
+ * Revision 1.14  2001/12/10 03:08:50  bjornw
+ * Added pgtable_cache_init dummy
+ *
+ * Revision 1.13  2001/11/12 18:05:38  pkj
+ * Added declaration of paging_init().
+ *
  * Revision 1.12  2001/08/11 00:28:00  bjornw
  * PAGE_CHG_MASK and PAGE_NONE had somewhat untraditional values
  *
@@ -106,6 +112,8 @@
  * the CRIS page table tree.
  */
 
+extern void paging_init(void);
+
 /* The cache doesn't need to be flushed when TLB entries change because 
  * the cache is mapped to physical memory, not virtual memory
  */
@@ -507,6 +515,6 @@
 /*
  * No page table caches to initialise
  */
-#define pgtable_cache_init()	do { } while (0)
+#define pgtable_cache_init()   do { } while (0)
 
 #endif /* _CRIS_PGTABLE_H */
diff -urN linux-2.5.3-pre2/include/asm-cris/processor.h linux/include/asm-cris/processor.h
--- linux-2.5.3-pre2/include/asm-cris/processor.h	Mon Oct  8 11:43:54 2001
+++ linux/include/asm-cris/processor.h	Mon Jan 21 17:23:24 2002
@@ -142,6 +142,6 @@
 #define init_task       (init_task_union.task)
 #define init_stack      (init_task_union.stack)
 
-#define cpu_relax()	do { } while (0)
+#define cpu_relax()     do { } while (0)
 
 #endif /* __ASM_CRIS_PROCESSOR_H */
diff -urN linux-2.5.3-pre2/include/asm-cris/scatterlist.h linux/include/asm-cris/scatterlist.h
--- linux-2.5.3-pre2/include/asm-cris/scatterlist.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-cris/scatterlist.h	Mon Jan 21 17:23:24 2002
@@ -0,0 +1,18 @@
+#ifndef __ASM_CRIS_SCATTERLIST_H
+#define __ASM_CRIS_SCATTERLIST_H
+
+struct scatterlist {
+	char *  address;    /* Location data is to be transferred to */
+	unsigned int length;
+
+	/* The following is i386 highmem junk - not used by us */
+	struct page * page; /* Location for highmem page, if any */
+	unsigned int offset;/* for highmem, page offset */
+
+};
+
+/* i386 junk */
+
+#define ISA_DMA_THRESHOLD (0x1fffffff)
+
+#endif /* !(__ASM_CRIS_SCATTERLIST_H) */
diff -urN linux-2.5.3-pre2/include/asm-cris/unistd.h linux/include/asm-cris/unistd.h
--- linux-2.5.3-pre2/include/asm-cris/unistd.h	Fri Nov  9 14:11:15 2001
+++ linux/include/asm-cris/unistd.h	Mon Jan 21 17:23:24 2002
@@ -378,6 +378,7 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 
   /* the following are just while developing the elinux port! */
 
diff -urN linux-2.5.3-pre2/include/asm-cris/user.h linux/include/asm-cris/user.h
--- linux-2.5.3-pre2/include/asm-cris/user.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-cris/user.h	Mon Jan 21 17:23:24 2002
@@ -28,8 +28,51 @@
  *	to write an integer number of pages.
  */
 
+/* User mode registers, used for core dumps. In order to keep ELF_NGREG
+   sensible we let all registers be 32 bits. The csr registers are included
+   for future use. */
+struct user_regs_struct {
+        unsigned long r0;       /* General registers. */
+        unsigned long r1;
+        unsigned long r2;
+        unsigned long r3;
+        unsigned long r4;
+        unsigned long r5;
+        unsigned long r6;
+        unsigned long r7;
+        unsigned long r8;
+        unsigned long r9;
+        unsigned long r10;
+        unsigned long r11;
+        unsigned long r12;
+        unsigned long r13;
+        unsigned long sp;       /* Stack pointer. */
+        unsigned long pc;       /* Program counter. */
+        unsigned long p0;       /* Constant zero (only 8 bits). */
+        unsigned long vr;       /* Version register (only 8 bits). */
+        unsigned long p2;       /* Reserved. */
+        unsigned long p3;       /* Reserved. */
+        unsigned long p4;       /* Constant zero (only 16 bits). */
+        unsigned long ccr;      /* Condition code register (only 16 bits). */
+        unsigned long p6;       /* Reserved. */
+        unsigned long mof;      /* Multiply overflow register. */
+        unsigned long p8;       /* Constant zero. */
+        unsigned long ibr;      /* Not accessible. */
+        unsigned long irp;      /* Not accessible. */
+        unsigned long srp;      /* Subroutine return pointer. */
+        unsigned long bar;      /* Not accessible. */
+        unsigned long dccr;     /* Dword condition code register. */
+        unsigned long brp;      /* Not accessible. */
+        unsigned long usp;      /* User-mode stack pointer. Same as sp when 
+                                   in user mode. */
+        unsigned long csrinstr; /* Internal status registers. */
+        unsigned long csraddr;
+        unsigned long csrdata;
+};
+
+        
 struct user {
-	struct pt_regs	regs;			/* entire machine state */
+	struct user_regs_struct	regs;		/* entire machine state */
 	size_t		u_tsize;		/* text size (pages) */
 	size_t		u_dsize;		/* data size (pages) */
 	size_t		u_ssize;		/* stack size (pages) */
diff -urN linux-2.5.3-pre2/include/asm-i386/hw_irq.h linux/include/asm-i386/hw_irq.h
--- linux-2.5.3-pre2/include/asm-i386/hw_irq.h	Mon Jan 14 13:27:06 2002
+++ linux/include/asm-i386/hw_irq.h	Mon Jan 21 17:23:24 2002
@@ -35,13 +35,14 @@
  *  into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
  *  TLB, reschedule and local APIC vectors are performance-critical.
  *
- *  Vectors 0xf0-0xfa are free (reserved for future Linux use).
+ *  Vectors 0xf0-0xf9 are free (reserved for future Linux use).
  */
 #define SPURIOUS_APIC_VECTOR	0xff
 #define ERROR_APIC_VECTOR	0xfe
 #define INVALIDATE_TLB_VECTOR	0xfd
 #define RESCHEDULE_VECTOR	0xfc
-#define CALL_FUNCTION_VECTOR	0xfb
+#define TASK_MIGRATION_VECTOR	0xfb
+#define CALL_FUNCTION_VECTOR	0xfa
 
 /*
  * Local APIC timer IRQ vector is on a different priority level,
diff -urN linux-2.5.3-pre2/include/linux/affs_fs.h linux/include/linux/affs_fs.h
--- linux-2.5.3-pre2/include/linux/affs_fs.h	Thu Apr 19 22:57:06 2001
+++ linux/include/linux/affs_fs.h	Mon Jan 21 17:23:24 2002
@@ -6,6 +6,8 @@
 
 #include <linux/types.h>
 
+#include <linux/affs_fs_i.h>
+
 #define AFFS_SUPER_MAGIC 0xadff
 
 struct affs_date;
diff -urN linux-2.5.3-pre2/include/linux/affs_fs_i.h linux/include/linux/affs_fs_i.h
--- linux-2.5.3-pre2/include/linux/affs_fs_i.h	Mon Jan 14 13:27:06 2002
+++ linux/include/linux/affs_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -3,9 +3,6 @@
 
 #include <linux/a.out.h>
 
-// move this to linux/coda.h!!!
-#include <linux/time.h>
-
 #define AFFS_CACHE_SIZE		PAGE_SIZE
 //#define AFFS_CACHE_SIZE		(4*4)
 
@@ -48,10 +45,13 @@
 	unsigned char i_pad;
 	s32	 i_parent;			/* parent ino */
 #endif
+	struct inode vfs_inode;
 };
 
 /* short cut to get to the affs specific inode data */
-#define AFFS_INODE	(&inode->u.affs_i)
-#define AFFS_DIR	(&dir->u.affs_i)
+static inline struct affs_inode_info *AFFS_I(struct inode *inode)
+{
+	return list_entry(inode, struct affs_inode_info, vfs_inode);
+}
 
 #endif
diff -urN linux-2.5.3-pre2/include/linux/amigaffs.h linux/include/linux/amigaffs.h
--- linux-2.5.3-pre2/include/linux/amigaffs.h	Tue Dec 25 15:39:20 2001
+++ linux/include/linux/amigaffs.h	Mon Jan 21 17:23:24 2002
@@ -93,32 +93,32 @@
 static inline void
 affs_lock_link(struct inode *inode)
 {
-	down(&AFFS_INODE->i_link_lock);
+	down(&AFFS_I(inode)->i_link_lock);
 }
 static inline void
 affs_unlock_link(struct inode *inode)
 {
-	up(&AFFS_INODE->i_link_lock);
+	up(&AFFS_I(inode)->i_link_lock);
 }
 static inline void
 affs_lock_dir(struct inode *inode)
 {
-	down(&AFFS_INODE->i_hash_lock);
+	down(&AFFS_I(inode)->i_hash_lock);
 }
 static inline void
 affs_unlock_dir(struct inode *inode)
 {
-	up(&AFFS_INODE->i_hash_lock);
+	up(&AFFS_I(inode)->i_hash_lock);
 }
 static inline void
 affs_lock_ext(struct inode *inode)
 {
-	down(&AFFS_INODE->i_ext_lock);
+	down(&AFFS_I(inode)->i_ext_lock);
 }
 static inline void
 affs_unlock_ext(struct inode *inode)
 {
-	up(&AFFS_INODE->i_ext_lock);
+	up(&AFFS_I(inode)->i_ext_lock);
 }
 
 #ifdef __LITTLE_ENDIAN
diff -urN linux-2.5.3-pre2/include/linux/coda.h linux/include/linux/coda.h
--- linux-2.5.3-pre2/include/linux/coda.h	Wed Apr 25 16:18:54 2001
+++ linux/include/linux/coda.h	Mon Jan 21 17:23:24 2002
@@ -99,6 +99,7 @@
 
 
 #if defined(__linux__)
+#include <linux/time.h>
 #define cdev_t u_quad_t
 #ifndef __KERNEL__
 #if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2)
diff -urN linux-2.5.3-pre2/include/linux/coda_fs_i.h linux/include/linux/coda_fs_i.h
--- linux-2.5.3-pre2/include/linux/coda_fs_i.h	Mon Jan 14 13:27:06 2002
+++ linux/include/linux/coda_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -24,6 +24,7 @@
 	unsigned int       c_contcount; /* refcount for container file */
         struct coda_cred   c_cached_cred; /* credentials of cached perms */
         unsigned int       c_cached_perm; /* cached access permissions */
+	struct inode	   vfs_inode;
 };
 
 /* flags */
diff -urN linux-2.5.3-pre2/include/linux/coda_linux.h linux/include/linux/coda_linux.h
--- linux-2.5.3-pre2/include/linux/coda_linux.h	Wed Dec  5 13:02:14 2001
+++ linux/include/linux/coda_linux.h	Mon Jan 21 17:23:24 2002
@@ -111,7 +111,10 @@
 
 /* inode to cnode access functions */
 
-#define ITOC(inode) (&((inode)->u.coda_i))
+static inline struct coda_inode_info *ITOC(struct inode *inode)
+{
+	return list_entry(inode, struct coda_inode_info, vfs_inode);
+}
 
 static __inline__ struct ViceFid *coda_i2f(struct inode *inode)
 {
diff -urN linux-2.5.3-pre2/include/linux/dnotify.h linux/include/linux/dnotify.h
--- linux-2.5.3-pre2/include/linux/dnotify.h	Fri Sep 22 14:21:22 2000
+++ linux/include/linux/dnotify.h	Mon Jan 21 17:23:24 2002
@@ -4,6 +4,8 @@
  * Copyright 2000 (C) Stephen Rothwell
  */
 
+#include <linux/fs.h>
+
 struct dnotify_struct {
 	struct dnotify_struct *	dn_next;
 	int			dn_magic;
diff -urN linux-2.5.3-pre2/include/linux/efs_fs.h linux/include/linux/efs_fs.h
--- linux-2.5.3-pre2/include/linux/efs_fs.h	Mon Dec 11 13:26:51 2000
+++ linux/include/linux/efs_fs.h	Mon Jan 21 17:23:24 2002
@@ -37,13 +37,11 @@
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 #endif
 
-#ifdef _EFS_USE_GENERIC
-#define INODE_INFO(i) (struct efs_inode_info *)	&((i)->u.generic_ip)
-#define SUPER_INFO(s) (struct efs_sb_info *)	&((s)->u.generic_sbp)
-#else
-#define INODE_INFO(i)				&((i)->u.efs_i)
+static inline struct efs_inode_info *INODE_INFO(struct inode *inode)
+{
+	return list_entry(inode, struct efs_inode_info, vfs_inode);
+}
 #define SUPER_INFO(s)				&((s)->u.efs_sb)
-#endif
 
 extern struct inode_operations efs_dir_inode_operations;
 extern struct file_operations efs_dir_operations;
diff -urN linux-2.5.3-pre2/include/linux/efs_fs_i.h linux/include/linux/efs_fs_i.h
--- linux-2.5.3-pre2/include/linux/efs_fs_i.h	Thu May 13 23:50:15 1999
+++ linux/include/linux/efs_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -61,6 +61,7 @@
 	int		lastextent;
 
 	efs_extent	extents[EFS_DIRECTEXTENTS];
+	struct inode	vfs_inode;
 };
 
 #endif	/* __EFS_FS_I_H__ */
diff -urN linux-2.5.3-pre2/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- linux-2.5.3-pre2/include/linux/ext2_fs.h	Mon Jan 14 13:27:20 2002
+++ linux/include/linux/ext2_fs.h	Mon Jan 21 17:23:24 2002
@@ -532,101 +532,4 @@
 #define EXT2_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT2_DIR_ROUND) & \
 					 ~EXT2_DIR_ROUND)
 
-#ifdef __KERNEL__
-/*
- * Function prototypes
- */
-
-/*
- * Ok, these declarations are also in <linux/kernel.h> but none of the
- * ext2 source programs needs to include it so they are duplicated here.
- */
-# define NORET_TYPE    /**/
-# define ATTRIB_NORET  __attribute__((noreturn))
-# define NORET_AND     noreturn,
-
-/* balloc.c */
-extern int ext2_bg_has_super(struct super_block *sb, int group);
-extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
-extern int ext2_new_block (struct inode *, unsigned long,
-			   __u32 *, __u32 *, int *);
-extern void ext2_free_blocks (struct inode *, unsigned long,
-			      unsigned long);
-extern unsigned long ext2_count_free_blocks (struct super_block *);
-extern void ext2_check_blocks_bitmap (struct super_block *);
-extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
-						    unsigned int block_group,
-						    struct buffer_head ** bh);
-
-/* dir.c */
-extern int ext2_add_link (struct dentry *, struct inode *);
-extern ino_t ext2_inode_by_name(struct inode *, struct dentry *);
-extern int ext2_make_empty(struct inode *, struct inode *);
-extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **);
-extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);
-extern int ext2_empty_dir (struct inode *);
-extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);
-extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *);
-
-/* fsync.c */
-extern int ext2_sync_file (struct file *, struct dentry *, int);
-extern int ext2_fsync_inode (struct inode *, int);
-
-/* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int);
-extern void ext2_free_inode (struct inode *);
-extern unsigned long ext2_count_free_inodes (struct super_block *);
-extern void ext2_check_inodes_bitmap (struct super_block *);
-extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
-
-/* inode.c */
-extern void ext2_read_inode (struct inode *);
-extern void ext2_write_inode (struct inode *, int);
-extern void ext2_put_inode (struct inode *);
-extern void ext2_delete_inode (struct inode *);
-extern int ext2_sync_inode (struct inode *);
-extern void ext2_discard_prealloc (struct inode *);
-extern void ext2_truncate (struct inode *);
-
-/* ioctl.c */
-extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
-		       unsigned long);
-
-/* super.c */
-extern void ext2_error (struct super_block *, const char *, const char *, ...)
-	__attribute__ ((format (printf, 3, 4)));
-extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
-				   const char *, ...)
-	__attribute__ ((NORET_AND format (printf, 3, 4)));
-extern void ext2_warning (struct super_block *, const char *, const char *, ...)
-	__attribute__ ((format (printf, 3, 4)));
-extern void ext2_update_dynamic_rev (struct super_block *sb);
-extern void ext2_put_super (struct super_block *);
-extern void ext2_write_super (struct super_block *);
-extern int ext2_remount (struct super_block *, int *, char *);
-extern struct super_block * ext2_read_super (struct super_block *,void *,int);
-extern int ext2_statfs (struct super_block *, struct statfs *);
-
-/*
- * Inodes and files operations
- */
-
-/* dir.c */
-extern struct file_operations ext2_dir_operations;
-
-/* file.c */
-extern struct inode_operations ext2_file_inode_operations;
-extern struct file_operations ext2_file_operations;
-
-/* inode.c */
-extern struct address_space_operations ext2_aops;
-
-/* namei.c */
-extern struct inode_operations ext2_dir_inode_operations;
-
-/* symlink.c */
-extern struct inode_operations ext2_fast_symlink_inode_operations;
-
-#endif	/* __KERNEL__ */
-
 #endif	/* _LINUX_EXT2_FS_H */
diff -urN linux-2.5.3-pre2/include/linux/ext2_fs_i.h linux/include/linux/ext2_fs_i.h
--- linux-2.5.3-pre2/include/linux/ext2_fs_i.h	Mon Sep 17 13:16:30 2001
+++ linux/include/linux/ext2_fs_i.h	Wed Dec 31 16:00:00 1969
@@ -1,41 +0,0 @@
-/*
- *  linux/include/linux/ext2_fs_i.h
- *
- * Copyright (C) 1992, 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- *  from
- *
- *  linux/include/linux/minix_fs_i.h
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#ifndef _LINUX_EXT2_FS_I
-#define _LINUX_EXT2_FS_I
-
-/*
- * second extended file system inode data in memory
- */
-struct ext2_inode_info {
-	__u32	i_data[15];
-	__u32	i_flags;
-	__u32	i_faddr;
-	__u8	i_frag_no;
-	__u8	i_frag_size;
-	__u16	i_osync;
-	__u32	i_file_acl;
-	__u32	i_dir_acl;
-	__u32	i_dtime;
-	__u32	i_block_group;
-	__u32	i_next_alloc_block;
-	__u32	i_next_alloc_goal;
-	__u32	i_prealloc_block;
-	__u32	i_prealloc_count;
-	__u32	i_dir_start_lookup;
-	int	i_new_inode:1;	/* Is a freshly allocated inode */
-};
-
-#endif	/* _LINUX_EXT2_FS_I */
diff -urN linux-2.5.3-pre2/include/linux/ext3_fs.h linux/include/linux/ext3_fs.h
--- linux-2.5.3-pre2/include/linux/ext3_fs.h	Sat Jan  5 12:58:11 2002
+++ linux/include/linux/ext3_fs.h	Mon Jan 21 17:23:24 2002
@@ -443,7 +443,10 @@
 
 #ifdef __KERNEL__
 #define EXT3_SB(sb)	(&((sb)->u.ext3_sb))
-#define EXT3_I(inode)	(&((inode)->u.ext3_i))
+static inline struct ext3_inode_info *EXT3_I(struct inode *inode)
+{
+	return list_entry(inode, struct ext3_inode_info, vfs_inode);
+}
 #else
 /* Assume that user mode programs are passing in an ext3fs superblock, not
  * a kernel struct super_block.  This will allow us to call the feature-test
@@ -451,7 +454,7 @@
 #define EXT3_SB(sb)	(sb)
 #endif
 
-#define NEXT_ORPHAN(inode) (inode)->u.ext3_i.i_dtime
+#define NEXT_ORPHAN(inode) EXT3_I(inode)->i_dtime
 
 /*
  * Codes for operating systems
@@ -620,7 +623,7 @@
 extern int ext3_sync_file (struct file *, struct dentry *, int);
 
 /* ialloc.c */
-extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int);
+extern struct inode * ext3_new_inode (handle_t *, struct inode *, int);
 extern void ext3_free_inode (handle_t *, struct inode *);
 extern struct inode * ext3_orphan_get (struct super_block *, ino_t);
 extern unsigned long ext3_count_free_inodes (struct super_block *);
diff -urN linux-2.5.3-pre2/include/linux/ext3_fs_i.h linux/include/linux/ext3_fs_i.h
--- linux-2.5.3-pre2/include/linux/ext3_fs_i.h	Mon Jan 14 13:27:06 2002
+++ linux/include/linux/ext3_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -73,6 +73,7 @@
 	 * by other means, so we have truncate_sem.
 	 */
 	struct rw_semaphore truncate_sem;
+	struct inode vfs_inode;
 };
 
 #endif	/* _LINUX_EXT3_FS_I */
diff -urN linux-2.5.3-pre2/include/linux/ext3_jbd.h linux/include/linux/ext3_jbd.h
--- linux-2.5.3-pre2/include/linux/ext3_jbd.h	Sat Jan  5 12:58:14 2002
+++ linux/include/linux/ext3_jbd.h	Mon Jan 21 17:23:24 2002
@@ -289,7 +289,7 @@
 		return 1;
 	if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA)
 		return 1;
-	if (inode->u.ext3_i.i_flags & EXT3_JOURNAL_DATA_FL)
+	if (EXT3_I(inode)->i_flags & EXT3_JOURNAL_DATA_FL)
 		return 1;
 	return 0;
 }
diff -urN linux-2.5.3-pre2/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.5.3-pre2/include/linux/fs.h	Mon Jan 21 17:22:37 2002
+++ linux/include/linux/fs.h	Mon Jan 21 17:23:24 2002
@@ -288,25 +288,15 @@
 
 #include <linux/pipe_fs_i.h>
 #include <linux/minix_fs_i.h>
-#include <linux/ext2_fs_i.h>
-#include <linux/ext3_fs_i.h>
-#include <linux/hpfs_fs_i.h>
 #include <linux/ntfs_fs_i.h>
 #include <linux/msdos_fs_i.h>
 #include <linux/umsdos_fs_i.h>
 #include <linux/iso_fs_i.h>
-#include <linux/nfs_fs_i.h>
 #include <linux/sysv_fs_i.h>
-#include <linux/affs_fs_i.h>
-#include <linux/ufs_fs_i.h>
-#include <linux/efs_fs_i.h>
-#include <linux/coda_fs_i.h>
 #include <linux/romfs_fs_i.h>
-#include <linux/shmem_fs.h>
 #include <linux/smb_fs_i.h>
 #include <linux/hfs_fs_i.h>
 #include <linux/adfs_fs_i.h>
-#include <linux/qnx4_fs_i.h>
 #include <linux/reiserfs_fs_i.h>
 #include <linux/bfs_fs_i.h>
 #include <linux/udf_fs_i.h>
@@ -477,25 +467,15 @@
 	__u32			i_generation;
 	union {
 		struct minix_inode_info		minix_i;
-		struct ext2_inode_info		ext2_i;
-		struct ext3_inode_info		ext3_i;
-		struct hpfs_inode_info		hpfs_i;
 		struct ntfs_inode_info		ntfs_i;
 		struct msdos_inode_info		msdos_i;
 		struct umsdos_inode_info	umsdos_i;
 		struct iso_inode_info		isofs_i;
-		struct nfs_inode_info		nfs_i;
 		struct sysv_inode_info		sysv_i;
-		struct affs_inode_info		affs_i;
-		struct ufs_inode_info		ufs_i;
-		struct efs_inode_info		efs_i;
 		struct romfs_inode_info		romfs_i;
-		struct shmem_inode_info		shmem_i;
-		struct coda_inode_info		coda_i;
 		struct smb_inode_info		smbfs_i;
 		struct hfs_inode_info		hfs_i;
 		struct adfs_inode_info		adfs_i;
-		struct qnx4_inode_info		qnx4_i;
 		struct reiserfs_inode_info	reiserfs_i;
 		struct bfs_inode_info		bfs_i;
 		struct udf_inode_info		udf_i;
@@ -507,6 +487,12 @@
 	} u;
 };
 
+#include <linux/shmem_fs.h>
+/* will die */
+#include <linux/coda_fs_i.h>
+#include <linux/ext3_fs_i.h>
+#include <linux/efs_fs_i.h>
+
 struct fown_struct {
 	int pid;		/* pid or -pgrp where SIGIO should be sent */
 	uid_t uid, euid;	/* uid/euid of process setting the owner */
@@ -562,6 +548,9 @@
  * Lockd stuffs a "host" pointer into this.
  */
 typedef struct files_struct *fl_owner_t;
+
+/* that will die - we need it for nfs_lock_info */
+#include <linux/nfs_fs_i.h>
 
 struct file_lock {
 	struct file_lock *fl_next;	/* singly linked list for this inode  */
diff -urN linux-2.5.3-pre2/include/linux/hpfs_fs_i.h linux/include/linux/hpfs_fs_i.h
--- linux-2.5.3-pre2/include/linux/hpfs_fs_i.h	Thu Feb 10 12:17:00 2000
+++ linux/include/linux/hpfs_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -18,24 +18,7 @@
 	unsigned i_dirty : 1;
 	struct semaphore i_sem;	/* semaphore */
 	loff_t **i_rddir_off;
+	struct inode vfs_inode;
 };
-
-#define i_hpfs_dno u.hpfs_i.i_dno
-#define i_hpfs_parent_dir u.hpfs_i.i_parent_dir
-#define i_hpfs_n_secs u.hpfs_i.i_n_secs
-#define i_hpfs_file_sec u.hpfs_i.i_file_sec
-#define i_hpfs_disk_sec u.hpfs_i.i_disk_sec
-#define i_hpfs_dpos u.hpfs_i.i_dpos
-#define i_hpfs_dsubdno u.hpfs_i.i_dsubdno
-#define i_hpfs_ea_size u.hpfs_i.i_ea_size
-#define i_hpfs_conv u.hpfs_i.i_conv
-#define i_hpfs_ea_mode u.hpfs_i.i_ea_mode
-#define i_hpfs_ea_uid u.hpfs_i.i_ea_uid
-#define i_hpfs_ea_gid u.hpfs_i.i_ea_gid
-/*#define i_hpfs_lock u.hpfs_i.i_lock*/
-/*#define i_hpfs_queue u.hpfs_i.i_queue*/
-#define i_hpfs_sem u.hpfs_i.i_sem
-#define i_hpfs_rddir_off u.hpfs_i.i_rddir_off
-#define i_hpfs_dirty u.hpfs_i.i_dirty
 
 #endif
diff -urN linux-2.5.3-pre2/include/linux/netdevice.h linux/include/linux/netdevice.h
--- linux-2.5.3-pre2/include/linux/netdevice.h	Mon Jan 14 13:28:11 2002
+++ linux/include/linux/netdevice.h	Mon Jan 21 17:23:24 2002
@@ -278,6 +278,10 @@
 	struct net_device_stats* (*get_stats)(struct net_device *dev);
 	struct iw_statistics*	(*get_wireless_stats)(struct net_device *dev);
 
+	/* List of functions to handle Wireless Extensions (instead of ioctl).
+	 * See <net/iw_handler.h> for details. Jean II */
+	struct iw_handler_def *	wireless_handlers;
+
 	/*
 	 * This marks the end of the "visible" part of the structure. All
 	 * fields hereafter are internal to the system, and may change at
diff -urN linux-2.5.3-pre2/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- linux-2.5.3-pre2/include/linux/nfs_fs.h	Mon Jan 14 13:28:01 2002
+++ linux/include/linux/nfs_fs.h	Mon Jan 21 17:23:24 2002
@@ -63,67 +63,154 @@
  */
 #define NFS_SUPER_MAGIC			0x6969
 
-static inline struct nfs_inode_info *NFS_I(struct inode *inode)
+/*
+ * These are the default flags for swap requests
+ */
+#define NFS_RPC_SWAPFLAGS		(RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
+
+/* Flags in the RPC client structure */
+#define NFS_CLNTF_BUFSIZE	0x0001	/* readdir buffer in longwords */
+
+#define NFS_RW_SYNC		0x0001	/* O_SYNC handling */
+#define NFS_RW_SWAP		0x0002	/* This is a swap request */
+
+/*
+ * When flushing a cluster of dirty pages, there can be different
+ * strategies:
+ */
+#define FLUSH_AGING		0	/* only flush old buffers */
+#define FLUSH_SYNC		1	/* file being synced, or contention */
+#define FLUSH_WAIT		2	/* wait for completion */
+#define FLUSH_STABLE		4	/* commit to stable storage */
+
+#ifdef __KERNEL__
+
+/*
+ * nfs fs inode data in memory
+ */
+struct nfs_inode {
+	/*
+	 * The 64bit 'inode number'
+	 */
+	__u64 fsid;
+	__u64 fileid;
+
+	/*
+	 * NFS file handle
+	 */
+	struct nfs_fh		fh;
+
+	/*
+	 * Various flags
+	 */
+	unsigned short		flags;
+
+	/*
+	 * read_cache_jiffies is when we started read-caching this inode,
+	 * and read_cache_mtime is the mtime of the inode at that time.
+	 * attrtimeo is for how long the cached information is assumed
+	 * to be valid. A successful attribute revalidation doubles
+	 * attrtimeo (up to acregmax/acdirmax), a failure resets it to
+	 * acregmin/acdirmin.
+	 *
+	 * We need to revalidate the cached attrs for this inode if
+	 *
+	 *	jiffies - read_cache_jiffies > attrtimeo
+	 *
+	 * and invalidate any cached data/flush out any dirty pages if
+	 * we find that
+	 *
+	 *	mtime != read_cache_mtime
+	 */
+	unsigned long		read_cache_jiffies;
+	__u64			read_cache_ctime;
+	__u64			read_cache_mtime;
+	__u64			read_cache_isize;
+	unsigned long		attrtimeo;
+	unsigned long		attrtimeo_timestamp;
+
+	/*
+	 * This is the cookie verifier used for NFSv3 readdir
+	 * operations
+	 */
+	__u32			cookieverf[2];
+
+	/*
+	 * This is the list of dirty unwritten pages.
+	 */
+	struct list_head	read;
+	struct list_head	dirty;
+	struct list_head	commit;
+	struct list_head	writeback;
+
+	unsigned int		nread,
+				ndirty,
+				ncommit,
+				npages;
+
+	/* Flush daemon info */
+	struct inode		*hash_next,
+				*hash_prev;
+	unsigned long		nextscan;
+
+	/* Credentials for shared mmap */
+	struct rpc_cred		*mm_cred;
+
+	struct inode		vfs_inode;
+};
+
+/*
+ * Legal inode flag values
+ */
+#define NFS_INO_STALE		0x0001		/* possible stale inode */
+#define NFS_INO_ADVISE_RDPLUS   0x0002          /* advise readdirplus */
+#define NFS_INO_REVALIDATING	0x0004		/* revalidating attrs */
+#define NFS_IS_SNAPSHOT		0x0010		/* a snapshot file */
+#define NFS_INO_FLUSH		0x0020		/* inode is due for flushing */
+#define NFS_INO_NEW		0x0040		/* hadn't been filled yet */
+
+static inline struct nfs_inode *NFS_I(struct inode *inode)
 {
-	return &inode->u.nfs_i;
+	return list_entry(inode, struct nfs_inode, vfs_inode);
 }
 
-#define NFS_FH(inode)			(&(inode)->u.nfs_i.fh)
+#define NFS_FH(inode)			(&NFS_I(inode)->fh)
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
 #define NFS_CLIENT(inode)		(NFS_SERVER(inode)->client)
 #define NFS_PROTO(inode)		(NFS_SERVER(inode)->rpc_ops)
 #define NFS_REQUESTLIST(inode)		(NFS_SERVER(inode)->rw_requests)
 #define NFS_ADDR(inode)			(RPC_PEERADDR(NFS_CLIENT(inode)))
 #define NFS_CONGESTED(inode)		(RPC_CONGESTED(NFS_CLIENT(inode)))
-#define NFS_COOKIEVERF(inode)		((inode)->u.nfs_i.cookieverf)
-#define NFS_READTIME(inode)		((inode)->u.nfs_i.read_cache_jiffies)
-#define NFS_CACHE_CTIME(inode)		((inode)->u.nfs_i.read_cache_ctime)
-#define NFS_CACHE_MTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
-#define NFS_CACHE_ISIZE(inode)		((inode)->u.nfs_i.read_cache_isize)
-#define NFS_NEXTSCAN(inode)		((inode)->u.nfs_i.nextscan)
+#define NFS_COOKIEVERF(inode)		(NFS_I(inode)->cookieverf)
+#define NFS_READTIME(inode)		(NFS_I(inode)->read_cache_jiffies)
+#define NFS_CACHE_CTIME(inode)		(NFS_I(inode)->read_cache_ctime)
+#define NFS_CACHE_MTIME(inode)		(NFS_I(inode)->read_cache_mtime)
+#define NFS_CACHE_ISIZE(inode)		(NFS_I(inode)->read_cache_isize)
+#define NFS_NEXTSCAN(inode)		(NFS_I(inode)->nextscan)
 #define NFS_CACHEINV(inode) \
 do { \
 	NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
 } while (0)
-#define NFS_ATTRTIMEO(inode)		((inode)->u.nfs_i.attrtimeo)
+#define NFS_ATTRTIMEO(inode)		(NFS_I(inode)->attrtimeo)
 #define NFS_MINATTRTIMEO(inode) \
 	(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
 			       : NFS_SERVER(inode)->acregmin)
 #define NFS_MAXATTRTIMEO(inode) \
 	(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
 			       : NFS_SERVER(inode)->acregmax)
-#define NFS_ATTRTIMEO_UPDATE(inode)	((inode)->u.nfs_i.attrtimeo_timestamp)
+#define NFS_ATTRTIMEO_UPDATE(inode)	(NFS_I(inode)->attrtimeo_timestamp)
 
-#define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
+#define NFS_FLAGS(inode)		(NFS_I(inode)->flags)
 #define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
 #define NFS_STALE(inode)		(NFS_FLAGS(inode) & NFS_INO_STALE)
+#define NFS_NEW(inode)			(NFS_FLAGS(inode) & NFS_INO_NEW)
 
-#define NFS_FILEID(inode)		((inode)->u.nfs_i.fileid)
-#define NFS_FSID(inode)			((inode)->u.nfs_i.fsid)
+#define NFS_FILEID(inode)		(NFS_I(inode)->fileid)
+#define NFS_FSID(inode)			(NFS_I(inode)->fsid)
 
 /* Inode Flags */
 #define NFS_USE_READDIRPLUS(inode)	((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0)
 
-/*
- * These are the default flags for swap requests
- */
-#define NFS_RPC_SWAPFLAGS		(RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
-
-/* Flags in the RPC client structure */
-#define NFS_CLNTF_BUFSIZE	0x0001	/* readdir buffer in longwords */
-
-#define NFS_RW_SYNC		0x0001	/* O_SYNC handling */
-#define NFS_RW_SWAP		0x0002	/* This is a swap request */
-
-/*
- * When flushing a cluster of dirty pages, there can be different
- * strategies:
- */
-#define FLUSH_AGING		0	/* only flush old buffers */
-#define FLUSH_SYNC		1	/* file being synced, or contention */
-#define FLUSH_WAIT		2	/* wait for completion */
-#define FLUSH_STABLE		4	/* commit to stable storage */
-
 static inline
 loff_t page_offset(struct page *page)
 {
@@ -136,7 +223,6 @@
 	return page->index;
 }
 
-#ifdef __KERNEL__
 /*
  * linux/fs/nfs/inode.c
  */
@@ -220,13 +306,13 @@
 static inline int
 nfs_have_read(struct inode *inode)
 {
-	return !list_empty(&inode->u.nfs_i.read);
+	return !list_empty(&NFS_I(inode)->read);
 }
 
 static inline int
 nfs_have_writebacks(struct inode *inode)
 {
-	return !list_empty(&inode->u.nfs_i.writeback);
+	return !list_empty(&NFS_I(inode)->writeback);
 }
 
 static inline int
diff -urN linux-2.5.3-pre2/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- linux-2.5.3-pre2/include/linux/nfs_fs_i.h	Mon Jan 14 13:27:06 2002
+++ linux/include/linux/nfs_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -6,87 +6,6 @@
 #include <linux/nfs.h>
 
 /*
- * nfs fs inode data in memory
- */
-struct nfs_inode_info {
-	/*
-	 * The 64bit 'inode number'
-	 */
-	__u64 fsid;
-	__u64 fileid;
-
-	/*
-	 * NFS file handle
-	 */
-	struct nfs_fh		fh;
-
-	/*
-	 * Various flags
-	 */
-	unsigned short		flags;
-
-	/*
-	 * read_cache_jiffies is when we started read-caching this inode,
-	 * and read_cache_mtime is the mtime of the inode at that time.
-	 * attrtimeo is for how long the cached information is assumed
-	 * to be valid. A successful attribute revalidation doubles
-	 * attrtimeo (up to acregmax/acdirmax), a failure resets it to
-	 * acregmin/acdirmin.
-	 *
-	 * We need to revalidate the cached attrs for this inode if
-	 *
-	 *	jiffies - read_cache_jiffies > attrtimeo
-	 *
-	 * and invalidate any cached data/flush out any dirty pages if
-	 * we find that
-	 *
-	 *	mtime != read_cache_mtime
-	 */
-	unsigned long		read_cache_jiffies;
-	__u64			read_cache_ctime;
-	__u64			read_cache_mtime;
-	__u64			read_cache_isize;
-	unsigned long		attrtimeo;
-	unsigned long		attrtimeo_timestamp;
-
-	/*
-	 * This is the cookie verifier used for NFSv3 readdir
-	 * operations
-	 */
-	__u32			cookieverf[2];
-
-	/*
-	 * This is the list of dirty unwritten pages.
-	 */
-	struct list_head	read;
-	struct list_head	dirty;
-	struct list_head	commit;
-	struct list_head	writeback;
-
-	unsigned int		nread,
-				ndirty,
-				ncommit,
-				npages;
-
-	/* Flush daemon info */
-	struct inode		*hash_next,
-				*hash_prev;
-	unsigned long		nextscan;
-
-	/* Credentials for shared mmap */
-	struct rpc_cred		*mm_cred;
-};
-
-/*
- * Legal inode flag values
- */
-#define NFS_INO_STALE		0x0001		/* possible stale inode */
-#define NFS_INO_ADVISE_RDPLUS   0x0002          /* advise readdirplus */
-#define NFS_INO_REVALIDATING	0x0004		/* revalidating attrs */
-#define NFS_IS_SNAPSHOT		0x0010		/* a snapshot file */
-#define NFS_INO_FLUSH		0x0020		/* inode is due for flushing */
-
-/*
  * NFS lock info
  */
 struct nfs_lock_info {
diff -urN linux-2.5.3-pre2/include/linux/qnx4_fs.h linux/include/linux/qnx4_fs.h
--- linux-2.5.3-pre2/include/linux/qnx4_fs.h	Sun Dec 16 12:23:05 2001
+++ linux/include/linux/qnx4_fs.h	Mon Jan 21 17:23:24 2002
@@ -97,6 +97,12 @@
 #define QNX4DEBUG(X) (void) 0
 #endif
 
+struct qnx4_inode_info {
+	struct qnx4_inode_entry raw;
+	unsigned long mmu_private;
+	struct inode vfs_inode;
+};
+
 extern struct dentry *qnx4_lookup(struct inode *dir, struct dentry *dentry);
 extern unsigned long qnx4_count_free_blocks(struct super_block *sb);
 extern unsigned long qnx4_block_map(struct inode *inode, long iblock);
@@ -119,6 +125,16 @@
 extern int qnx4_sync_file(struct file *file, struct dentry *dentry, int);
 extern int qnx4_sync_inode(struct inode *inode);
 extern int qnx4_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create);
+
+static inline struct qnx4_inode_info *qnx4_i(struct inode *inode)
+{
+	return list_entry(inode, struct qnx4_inode_info, vfs_inode);
+}
+
+static inline struct qnx4_inode_entry *qnx4_raw_inode(struct inode *inode)
+{
+	return &qnx4_i(inode)->raw;
+}
 
 #endif				/* __KERNEL__ */
 
diff -urN linux-2.5.3-pre2/include/linux/qnx4_fs_i.h linux/include/linux/qnx4_fs_i.h
--- linux-2.5.3-pre2/include/linux/qnx4_fs_i.h	Thu Feb 10 12:17:00 2000
+++ linux/include/linux/qnx4_fs_i.h	Wed Dec 31 16:00:00 1969
@@ -1,39 +0,0 @@
-/*
- *  Name                         : qnx4_fs_i.h
- *  Author                       : Richard Frowijn
- *  Function                     : qnx4 inode definitions
- *  Version                      : 1.0.2
- *  Last modified                : 2000-01-06
- *
- *  History                      : 23-03-1998 created
- *
- */
-#ifndef _QNX4_FS_I
-#define _QNX4_FS_I
-
-#include <linux/qnxtypes.h>
-
-/*
- * qnx4 fs inode entry
- */
-struct qnx4_inode_info {
-	char		i_reserved[16];	/* 16 */
-	qnx4_off_t	i_size;		/*  4 */
-	qnx4_xtnt_t	i_first_xtnt;	/*  8 */
-	__u32		i_xblk;		/*  4 */
-	__s32		i_ftime;	/*  4 */
-	__s32		i_mtime;	/*  4 */
-	__s32		i_atime;	/*  4 */
-	__s32		i_ctime;	/*  4 */
-	qnx4_nxtnt_t	i_num_xtnts;	/*  2 */
-	qnx4_mode_t	i_mode;		/*  2 */
-	qnx4_muid_t	i_uid;		/*  2 */
-	qnx4_mgid_t	i_gid;		/*  2 */
-	qnx4_nlink_t	i_nlink;	/*  2 */
-	__u8		i_zero[4];	/*  4 */
-	qnx4_ftype_t	i_type;		/*  1 */
-	__u8		i_status;	/*  1 */
-	unsigned long	mmu_private;
-};
-
-#endif
diff -urN linux-2.5.3-pre2/include/linux/sched.h linux/include/linux/sched.h
--- linux-2.5.3-pre2/include/linux/sched.h	Mon Jan 21 17:22:37 2002
+++ linux/include/linux/sched.h	Mon Jan 21 17:23:24 2002
@@ -141,6 +141,7 @@
 
 extern void sched_init(void);
 extern void init_idle(void);
+extern void idle_startup_done(void);
 extern void show_state(void);
 extern void cpu_init (void);
 extern void trap_init(void);
@@ -148,6 +149,8 @@
 extern void update_one_process(struct task_struct *p, unsigned long user,
 			       unsigned long system, int cpu);
 extern void scheduler_tick(struct task_struct *p);
+extern void sched_task_migrated(struct task_struct *p);
+extern void smp_migrate_task(int cpu, task_t *task);
 
 #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
 extern signed long FASTCALL(schedule_timeout(signed long timeout));
diff -urN linux-2.5.3-pre2/include/linux/shmem_fs.h linux/include/linux/shmem_fs.h
--- linux-2.5.3-pre2/include/linux/shmem_fs.h	Mon Jan 14 10:10:43 2002
+++ linux/include/linux/shmem_fs.h	Mon Jan 21 17:23:24 2002
@@ -28,7 +28,7 @@
 	unsigned long		swapped;
 	int			locked;     /* into memory */
 	struct list_head	list;
-	struct inode	       *inode;
+	struct inode		vfs_inode;
 };
 
 struct shmem_sb_info {
@@ -39,6 +39,9 @@
 	spinlock_t    stat_lock;
 };
 
-#define SHMEM_I(inode)  (&inode->u.shmem_i)
+static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
+{
+	return list_entry(inode, struct shmem_inode_info, vfs_inode);
+}
 
 #endif
diff -urN linux-2.5.3-pre2/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- linux-2.5.3-pre2/include/linux/ufs_fs.h	Mon Jan 14 13:27:06 2002
+++ linux/include/linux/ufs_fs.h	Mon Jan 21 17:23:24 2002
@@ -223,7 +223,7 @@
 
 #define	UFS_MAXNAMLEN 255
 #define UFS_MAXMNTLEN 512
-#define UFS_MAXCSBUFS 31
+/* #define UFS_MAXCSBUFS 31 */
 #define UFS_LINK_MAX 32000
 
 /*
@@ -508,6 +508,218 @@
 #define UFS_SF_APPEND     0x00040000  /* append-only */
 #define UFS_SF_NOUNLINK   0x00100000  /* can't be removed or renamed */
 
+/*
+ * This structure is used for reading disk structures larger
+ * than the size of fragment.
+ */
+struct ufs_buffer_head {
+	unsigned fragment;			/* first fragment */
+	unsigned count;				/* number of fragments */
+	struct buffer_head * bh[UFS_MAXFRAG];	/* buffers */
+};
+
+struct ufs_cg_private_info {
+	struct ufs_cylinder_group ucg;
+	__u32	c_cgx;		/* number of cylidner group */
+	__u16	c_ncyl;		/* number of cyl's this cg */
+	__u16	c_niblk;	/* number of inode blocks this cg */
+	__u32	c_ndblk;	/* number of data blocks this cg */
+	__u32	c_rotor;	/* position of last used block */
+	__u32	c_frotor;	/* position of last used frag */
+	__u32	c_irotor;	/* position of last used inode */
+	__u32	c_btotoff;	/* (__u32) block totals per cylinder */
+	__u32	c_boff;		/* (short) free block positions */
+	__u32	c_iusedoff;	/* (char) used inode map */
+	__u32	c_freeoff;	/* (u_char) free block map */
+	__u32	c_nextfreeoff;	/* (u_char) next available space */
+	__u32	c_clustersumoff;/* (u_int32) counts of avail clusters */
+	__u32	c_clusteroff;	/* (u_int8) free cluster map */
+	__u32	c_nclusterblks;	/* number of clusters this cg */
+};	
+
+struct ufs_sb_private_info {
+	struct ufs_buffer_head s_ubh; /* buffer containing super block */
+	__u32	s_sblkno;	/* offset of super-blocks in filesys */
+	__u32	s_cblkno;	/* offset of cg-block in filesys */
+	__u32	s_iblkno;	/* offset of inode-blocks in filesys */
+	__u32	s_dblkno;	/* offset of first data after cg */
+	__u32	s_cgoffset;	/* cylinder group offset in cylinder */
+	__u32	s_cgmask;	/* used to calc mod fs_ntrak */
+	__u32	s_size;		/* number of blocks (fragments) in fs */
+	__u32	s_dsize;	/* number of data blocks in fs */
+	__u32	s_ncg;		/* number of cylinder groups */
+	__u32	s_bsize;	/* size of basic blocks */
+	__u32	s_fsize;	/* size of fragments */
+	__u32	s_fpb;		/* fragments per block */
+	__u32	s_minfree;	/* minimum percentage of free blocks */
+	__u32	s_bmask;	/* `blkoff'' calc of blk offsets */
+	__u32	s_fmask;	/* s_fsize mask */
+	__u32	s_bshift;	/* `lblkno'' calc of logical blkno */
+	__u32   s_fshift;	/* s_fsize shift */
+	__u32	s_fpbshift;	/* fragments per block shift */
+	__u32	s_fsbtodb;	/* fsbtodb and dbtofsb shift constant */
+	__u32	s_sbsize;	/* actual size of super block */
+	__u32   s_csmask;	/* csum block offset */
+	__u32	s_csshift;	/* csum block number */
+	__u32	s_nindir;	/* value of NINDIR */
+	__u32	s_inopb;	/* value of INOPB */
+	__u32	s_nspf;		/* value of NSPF */
+	__u32	s_npsect;	/* # sectors/track including spares */
+	__u32	s_interleave;	/* hardware sector interleave */
+	__u32	s_trackskew;	/* sector 0 skew, per track */
+	__u32	s_csaddr;	/* blk addr of cyl grp summary area */
+	__u32	s_cssize;	/* size of cyl grp summary area */
+	__u32	s_cgsize;	/* cylinder group size */
+	__u32	s_ntrak;	/* tracks per cylinder */
+	__u32	s_nsect;	/* sectors per track */
+	__u32	s_spc;		/* sectors per cylinder */
+	__u32	s_ipg;		/* inodes per group */
+	__u32	s_fpg;		/* fragments per group */
+	__u32	s_cpc;		/* cyl per cycle in postbl */
+	__s32	s_contigsumsize;/* size of cluster summary array, 44bsd */
+	__s64	s_qbmask;	/* ~usb_bmask */
+	__s64	s_qfmask;	/* ~usb_fmask */
+	__s32	s_postblformat;	/* format of positional layout tables */
+	__s32	s_nrpos;	/* number of rotational positions */
+        __s32	s_postbloff;	/* (__s16) rotation block list head */
+	__s32	s_rotbloff;	/* (__u8) blocks for each rotation */
+
+	__u32	s_fpbmask;	/* fragments per block mask */
+	__u32	s_apb;		/* address per block */
+	__u32	s_2apb;		/* address per block^2 */
+	__u32	s_3apb;		/* address per block^3 */
+	__u32	s_apbmask;	/* address per block mask */
+	__u32	s_apbshift;	/* address per block shift */
+	__u32	s_2apbshift;	/* address per block shift * 2 */
+	__u32	s_3apbshift;	/* address per block shift * 3 */
+	__u32	s_nspfshift;	/* number of sector per fragment shift */
+	__u32	s_nspb;		/* number of sector per block */
+	__u32	s_inopf;	/* inodes per fragment */
+	__u32	s_sbbase;	/* offset of NeXTstep superblock */
+	__u32	s_bpf;		/* bits per fragment */
+	__u32	s_bpfshift;	/* bits per fragment shift*/
+	__u32	s_bpfmask;	/* bits per fragment mask */
+
+	__u32	s_maxsymlinklen;/* upper limit on fast symlinks' size */
+};
+
+/*
+ * Sizes of this structures are:
+ *	ufs_super_block_first	512
+ *	ufs_super_block_second	512
+ *	ufs_super_block_third	356
+ */
+struct ufs_super_block_first {
+	__u32	fs_link;
+	__u32	fs_rlink;
+	__u32	fs_sblkno;
+	__u32	fs_cblkno;
+	__u32	fs_iblkno;
+	__u32	fs_dblkno;
+	__u32	fs_cgoffset;
+	__u32	fs_cgmask;
+	__u32	fs_time;
+	__u32	fs_size;
+	__u32	fs_dsize;
+	__u32	fs_ncg;
+	__u32	fs_bsize;
+	__u32	fs_fsize;
+	__u32	fs_frag;
+	__u32	fs_minfree;
+	__u32	fs_rotdelay;
+	__u32	fs_rps;
+	__u32	fs_bmask;
+	__u32	fs_fmask;
+	__u32	fs_bshift;
+	__u32	fs_fshift;
+	__u32	fs_maxcontig;
+	__u32	fs_maxbpg;
+	__u32	fs_fragshift;
+	__u32	fs_fsbtodb;
+	__u32	fs_sbsize;
+	__u32	fs_csmask;
+	__u32	fs_csshift;
+	__u32	fs_nindir;
+	__u32	fs_inopb;
+	__u32	fs_nspf;
+	__u32	fs_optim;
+	union {
+		struct {
+			__u32	fs_npsect;
+		} fs_sun;
+		struct {
+			__s32	fs_state;
+		} fs_sunx86;
+	} fs_u1;
+	__u32	fs_interleave;
+	__u32	fs_trackskew;
+	__u32	fs_id[2];
+	__u32	fs_csaddr;
+	__u32	fs_cssize;
+	__u32	fs_cgsize;
+	__u32	fs_ntrak;
+	__u32	fs_nsect;
+	__u32	fs_spc;
+	__u32	fs_ncyl;
+	__u32	fs_cpg;
+	__u32	fs_ipg;
+	__u32	fs_fpg;
+	struct ufs_csum fs_cstotal;
+	__s8	fs_fmod;
+	__s8	fs_clean;
+	__s8	fs_ronly;
+	__s8	fs_flags;
+	__s8	fs_fsmnt[UFS_MAXMNTLEN - 212];
+
+};
+
+struct ufs_super_block_second {
+	__s8	fs_fsmnt[212];
+	__u32	fs_cgrotor;
+	__u32	fs_csp[UFS_MAXCSBUFS];
+	__u32	fs_maxcluster;
+	__u32	fs_cpc;
+	__u16	fs_opostbl[82];
+};	
+
+struct ufs_super_block_third {
+	__u16	fs_opostbl[46];
+	union {
+		struct {
+			__s32	fs_sparecon[53];/* reserved for future constants */
+			__s32	fs_reclaim;
+			__s32	fs_sparecon2[1];
+			__s32	fs_state;	/* file system state time stamp */
+			__u32	fs_qbmask[2];	/* ~usb_bmask */
+			__u32	fs_qfmask[2];	/* ~usb_fmask */
+		} fs_sun;
+		struct {
+			__s32	fs_sparecon[53];/* reserved for future constants */
+			__s32	fs_reclaim;
+			__s32	fs_sparecon2[1];
+			__u32	fs_npsect;	/* # sectors/track including spares */
+			__u32	fs_qbmask[2];	/* ~usb_bmask */
+			__u32	fs_qfmask[2];	/* ~usb_fmask */
+		} fs_sunx86;
+		struct {
+			__s32	fs_sparecon[50];/* reserved for future constants */
+			__s32	fs_contigsumsize;/* size of cluster summary array */
+			__s32	fs_maxsymlinklen;/* max length of an internal symlink */
+			__s32	fs_inodefmt;	/* format of on-disk inodes */
+			__u32	fs_maxfilesize[2];	/* max representable file size */
+			__u32	fs_qbmask[2];	/* ~usb_bmask */
+			__u32	fs_qfmask[2];	/* ~usb_fmask */
+			__s32	fs_state;	/* file system state time stamp */
+		} fs_44;
+	} fs_u2;
+	__s32	fs_postblformat;
+	__s32	fs_nrpos;
+	__s32	fs_postbloff;
+	__s32	fs_rotbloff;
+	__s32	fs_magic;
+	__u8	fs_space[1];
+};
+
 #ifdef __KERNEL__
 
 /* balloc.c */
@@ -539,7 +751,7 @@
 
 /* ialloc.c */
 extern void ufs_free_inode (struct inode *inode);
-extern struct inode * ufs_new_inode (const struct inode *, int);
+extern struct inode * ufs_new_inode (struct inode *, int);
 
 /* inode.c */
 extern int ufs_frag_map (struct inode *, int);
@@ -566,6 +778,13 @@
 
 /* truncate.c */
 extern void ufs_truncate (struct inode *);
+
+#include <linux/ufs_fs_i.h>
+
+static inline struct ufs_inode_info *UFS_I(struct inode *inode)
+{
+	return list_entry(inode, struct ufs_inode_info, vfs_inode);
+}
 
 #endif	/* __KERNEL__ */
 
diff -urN linux-2.5.3-pre2/include/linux/ufs_fs_i.h linux/include/linux/ufs_fs_i.h
--- linux-2.5.3-pre2/include/linux/ufs_fs_i.h	Mon Nov 19 14:55:46 2001
+++ linux/include/linux/ufs_fs_i.h	Mon Jan 21 17:23:24 2002
@@ -26,6 +26,7 @@
 	__u32	i_oeftflag;
 	__u16	i_osync;
 	__u32	i_lastfrag;
+	struct inode vfs_inode;
 };
 
 #endif /* _LINUX_UFS_FS_I_H */
diff -urN linux-2.5.3-pre2/include/linux/ufs_fs_sb.h linux/include/linux/ufs_fs_sb.h
--- linux-2.5.3-pre2/include/linux/ufs_fs_sb.h	Mon Jan 14 13:27:06 2002
+++ linux/include/linux/ufs_fs_sb.h	Mon Jan 21 17:23:24 2002
@@ -14,107 +14,15 @@
 #ifndef __LINUX_UFS_FS_SB_H
 #define __LINUX_UFS_FS_SB_H
 
-#include <linux/ufs_fs.h>
-
-/*
- * This structure is used for reading disk structures larger
- * than the size of fragment.
- */
-struct ufs_buffer_head {
-	unsigned fragment;			/* first fragment */
-	unsigned count;				/* number of fragments */
-	struct buffer_head * bh[UFS_MAXFRAG];	/* buffers */
-};
-
-struct ufs_cg_private_info {
-	struct ufs_cylinder_group ucg;
-	__u32	c_cgx;		/* number of cylidner group */
-	__u16	c_ncyl;		/* number of cyl's this cg */
-	__u16	c_niblk;	/* number of inode blocks this cg */
-	__u32	c_ndblk;	/* number of data blocks this cg */
-	__u32	c_rotor;	/* position of last used block */
-	__u32	c_frotor;	/* position of last used frag */
-	__u32	c_irotor;	/* position of last used inode */
-	__u32	c_btotoff;	/* (__u32) block totals per cylinder */
-	__u32	c_boff;		/* (short) free block positions */
-	__u32	c_iusedoff;	/* (char) used inode map */
-	__u32	c_freeoff;	/* (u_char) free block map */
-	__u32	c_nextfreeoff;	/* (u_char) next available space */
-	__u32	c_clustersumoff;/* (u_int32) counts of avail clusters */
-	__u32	c_clusteroff;	/* (u_int8) free cluster map */
-	__u32	c_nclusterblks;	/* number of clusters this cg */
-};	
-
-struct ufs_sb_private_info {
-	struct ufs_buffer_head s_ubh; /* buffer containing super block */
-	__u32	s_sblkno;	/* offset of super-blocks in filesys */
-	__u32	s_cblkno;	/* offset of cg-block in filesys */
-	__u32	s_iblkno;	/* offset of inode-blocks in filesys */
-	__u32	s_dblkno;	/* offset of first data after cg */
-	__u32	s_cgoffset;	/* cylinder group offset in cylinder */
-	__u32	s_cgmask;	/* used to calc mod fs_ntrak */
-	__u32	s_size;		/* number of blocks (fragments) in fs */
-	__u32	s_dsize;	/* number of data blocks in fs */
-	__u32	s_ncg;		/* number of cylinder groups */
-	__u32	s_bsize;	/* size of basic blocks */
-	__u32	s_fsize;	/* size of fragments */
-	__u32	s_fpb;		/* fragments per block */
-	__u32	s_minfree;	/* minimum percentage of free blocks */
-	__u32	s_bmask;	/* `blkoff'' calc of blk offsets */
-	__u32	s_fmask;	/* s_fsize mask */
-	__u32	s_bshift;	/* `lblkno'' calc of logical blkno */
-	__u32   s_fshift;	/* s_fsize shift */
-	__u32	s_fpbshift;	/* fragments per block shift */
-	__u32	s_fsbtodb;	/* fsbtodb and dbtofsb shift constant */
-	__u32	s_sbsize;	/* actual size of super block */
-	__u32   s_csmask;	/* csum block offset */
-	__u32	s_csshift;	/* csum block number */
-	__u32	s_nindir;	/* value of NINDIR */
-	__u32	s_inopb;	/* value of INOPB */
-	__u32	s_nspf;		/* value of NSPF */
-	__u32	s_npsect;	/* # sectors/track including spares */
-	__u32	s_interleave;	/* hardware sector interleave */
-	__u32	s_trackskew;	/* sector 0 skew, per track */
-	__u32	s_csaddr;	/* blk addr of cyl grp summary area */
-	__u32	s_cssize;	/* size of cyl grp summary area */
-	__u32	s_cgsize;	/* cylinder group size */
-	__u32	s_ntrak;	/* tracks per cylinder */
-	__u32	s_nsect;	/* sectors per track */
-	__u32	s_spc;		/* sectors per cylinder */
-	__u32	s_ipg;		/* inodes per group */
-	__u32	s_fpg;		/* fragments per group */
-	__u32	s_cpc;		/* cyl per cycle in postbl */
-	__s32	s_contigsumsize;/* size of cluster summary array, 44bsd */
-	__s64	s_qbmask;	/* ~usb_bmask */
-	__s64	s_qfmask;	/* ~usb_fmask */
-	__s32	s_postblformat;	/* format of positional layout tables */
-	__s32	s_nrpos;	/* number of rotational positions */
-        __s32	s_postbloff;	/* (__s16) rotation block list head */
-	__s32	s_rotbloff;	/* (__u8) blocks for each rotation */
-
-	__u32	s_fpbmask;	/* fragments per block mask */
-	__u32	s_apb;		/* address per block */
-	__u32	s_2apb;		/* address per block^2 */
-	__u32	s_3apb;		/* address per block^3 */
-	__u32	s_apbmask;	/* address per block mask */
-	__u32	s_apbshift;	/* address per block shift */
-	__u32	s_2apbshift;	/* address per block shift * 2 */
-	__u32	s_3apbshift;	/* address per block shift * 3 */
-	__u32	s_nspfshift;	/* number of sector per fragment shift */
-	__u32	s_nspb;		/* number of sector per block */
-	__u32	s_inopf;	/* inodes per fragment */
-	__u32	s_sbbase;	/* offset of NeXTstep superblock */
-	__u32	s_bpf;		/* bits per fragment */
-	__u32	s_bpfshift;	/* bits per fragment shift*/
-	__u32	s_bpfmask;	/* bits per fragment mask */
-
-	__u32	s_maxsymlinklen;/* upper limit on fast symlinks' size */
-};
-
 
 #define UFS_MAX_GROUP_LOADED 8
 #define UFS_CGNO_EMPTY ((unsigned)-1)
 
+struct ufs_sb_private_info;
+struct ufs_cg_private_info;
+struct ufs_csum;
+#define UFS_MAXCSBUFS 31
+
 struct ufs_sb_info {
 	struct ufs_sb_private_info * s_uspi;	
 	struct ufs_csum	* s_csp[UFS_MAXCSBUFS];
@@ -127,121 +35,4 @@
 	unsigned s_mount_opt;
 };
 
-/*
- * Sizes of this structures are:
- *	ufs_super_block_first	512
- *	ufs_super_block_second	512
- *	ufs_super_block_third	356
- */
-struct ufs_super_block_first {
-	__u32	fs_link;
-	__u32	fs_rlink;
-	__u32	fs_sblkno;
-	__u32	fs_cblkno;
-	__u32	fs_iblkno;
-	__u32	fs_dblkno;
-	__u32	fs_cgoffset;
-	__u32	fs_cgmask;
-	__u32	fs_time;
-	__u32	fs_size;
-	__u32	fs_dsize;
-	__u32	fs_ncg;
-	__u32	fs_bsize;
-	__u32	fs_fsize;
-	__u32	fs_frag;
-	__u32	fs_minfree;
-	__u32	fs_rotdelay;
-	__u32	fs_rps;
-	__u32	fs_bmask;
-	__u32	fs_fmask;
-	__u32	fs_bshift;
-	__u32	fs_fshift;
-	__u32	fs_maxcontig;
-	__u32	fs_maxbpg;
-	__u32	fs_fragshift;
-	__u32	fs_fsbtodb;
-	__u32	fs_sbsize;
-	__u32	fs_csmask;
-	__u32	fs_csshift;
-	__u32	fs_nindir;
-	__u32	fs_inopb;
-	__u32	fs_nspf;
-	__u32	fs_optim;
-	union {
-		struct {
-			__u32	fs_npsect;
-		} fs_sun;
-		struct {
-			__s32	fs_state;
-		} fs_sunx86;
-	} fs_u1;
-	__u32	fs_interleave;
-	__u32	fs_trackskew;
-	__u32	fs_id[2];
-	__u32	fs_csaddr;
-	__u32	fs_cssize;
-	__u32	fs_cgsize;
-	__u32	fs_ntrak;
-	__u32	fs_nsect;
-	__u32	fs_spc;
-	__u32	fs_ncyl;
-	__u32	fs_cpg;
-	__u32	fs_ipg;
-	__u32	fs_fpg;
-	struct ufs_csum fs_cstotal;
-	__s8	fs_fmod;
-	__s8	fs_clean;
-	__s8	fs_ronly;
-	__s8	fs_flags;
-	__s8	fs_fsmnt[UFS_MAXMNTLEN - 212];
-
-};
-
-struct ufs_super_block_second {
-	__s8	fs_fsmnt[212];
-	__u32	fs_cgrotor;
-	__u32	fs_csp[UFS_MAXCSBUFS];
-	__u32	fs_maxcluster;
-	__u32	fs_cpc;
-	__u16	fs_opostbl[82];
-};	
-
-struct ufs_super_block_third {
-	__u16	fs_opostbl[46];
-	union {
-		struct {
-			__s32	fs_sparecon[53];/* reserved for future constants */
-			__s32	fs_reclaim;
-			__s32	fs_sparecon2[1];
-			__s32	fs_state;	/* file system state time stamp */
-			__u32	fs_qbmask[2];	/* ~usb_bmask */
-			__u32	fs_qfmask[2];	/* ~usb_fmask */
-		} fs_sun;
-		struct {
-			__s32	fs_sparecon[53];/* reserved for future constants */
-			__s32	fs_reclaim;
-			__s32	fs_sparecon2[1];
-			__u32	fs_npsect;	/* # sectors/track including spares */
-			__u32	fs_qbmask[2];	/* ~usb_bmask */
-			__u32	fs_qfmask[2];	/* ~usb_fmask */
-		} fs_sunx86;
-		struct {
-			__s32	fs_sparecon[50];/* reserved for future constants */
-			__s32	fs_contigsumsize;/* size of cluster summary array */
-			__s32	fs_maxsymlinklen;/* max length of an internal symlink */
-			__s32	fs_inodefmt;	/* format of on-disk inodes */
-			__u32	fs_maxfilesize[2];	/* max representable file size */
-			__u32	fs_qbmask[2];	/* ~usb_bmask */
-			__u32	fs_qfmask[2];	/* ~usb_fmask */
-			__s32	fs_state;	/* file system state time stamp */
-		} fs_44;
-	} fs_u2;
-	__s32	fs_postblformat;
-	__s32	fs_nrpos;
-	__s32	fs_postbloff;
-	__s32	fs_rotbloff;
-	__s32	fs_magic;
-	__u8	fs_space[1];
-};
-
-#endif /* __LINUX_UFS_FS_SB_H */
+#endif
diff -urN linux-2.5.3-pre2/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- linux-2.5.3-pre2/include/linux/umsdos_fs.p	Wed Aug  2 12:06:11 2000
+++ linux/include/linux/umsdos_fs.p	Mon Jan 21 17:23:24 2002
@@ -93,3 +93,8 @@
 /* rdir.c 22/03/95 03.31.42 */
 struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
 struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
+
+static inline struct umsdos_inode_info *UMSDOS_I(struct inode *inode)
+{
+	return &inode->u.umsdos_i;
+}
diff -urN linux-2.5.3-pre2/include/linux/usb.h linux/include/linux/usb.h
--- linux-2.5.3-pre2/include/linux/usb.h	Mon Jan 14 13:35:15 2002
+++ linux/include/linux/usb.h	Mon Jan 21 17:23:24 2002
@@ -576,13 +576,12 @@
 					/* ... less overhead for QUEUE_BULK */
 #define USB_TIMEOUT_KILLED	0x1000	/* only set by HCD! */
 
-typedef struct
-{
+struct usb_iso_packet_descriptor {
 	unsigned int offset;
 	unsigned int length;		/* expected length */
 	unsigned int actual_length;
 	unsigned int status;
-} iso_packet_descriptor_t;
+};
 
 struct urb;
 
@@ -741,10 +740,8 @@
 	int timeout;			/* (in) timeout, in jiffies */
 	void *context;			/* (in) context for completion */
 	usb_complete_t complete;	/* (in) completion routine */
-	iso_packet_descriptor_t iso_frame_desc[0];	/* (in) ISO ONLY */
+	struct usb_iso_packet_descriptor iso_frame_desc[0];	/* (in) ISO ONLY */
 };
-
-typedef struct urb urb_t;
 
 /**
  * usb_fill_control_urb - initializes a control urb
diff -urN linux-2.5.3-pre2/include/linux/usbdev_fs_i.h linux/include/linux/usbdev_fs_i.h
--- linux-2.5.3-pre2/include/linux/usbdev_fs_i.h	Mon Jan 10 11:32:48 2000
+++ linux/include/linux/usbdev_fs_i.h	Wed Dec 31 16:00:00 1969
@@ -1,11 +0,0 @@
-struct usb_device;
-struct usb_bus;
-
-struct usbdev_inode_info {
-	struct list_head dlist;
-	struct list_head slist;
-	union {
-		struct usb_device *dev;
-		struct usb_bus *bus;
-	} p;
-};
diff -urN linux-2.5.3-pre2/include/linux/usbdev_fs_sb.h linux/include/linux/usbdev_fs_sb.h
--- linux-2.5.3-pre2/include/linux/usbdev_fs_sb.h	Tue May 22 10:25:36 2001
+++ linux/include/linux/usbdev_fs_sb.h	Wed Dec 31 16:00:00 1969
@@ -1,13 +0,0 @@
-struct usbdev_sb_info {
-	struct list_head slist;
-	struct list_head ilist;
-	uid_t devuid;
-	gid_t devgid;
-	umode_t devmode;
-	uid_t busuid;
-	gid_t busgid;
-	umode_t busmode;
-	uid_t listuid;
-	gid_t listgid;
-	umode_t listmode;
-};
diff -urN linux-2.5.3-pre2/include/linux/wireless.h linux/include/linux/wireless.h
--- linux-2.5.3-pre2/include/linux/wireless.h	Mon Jan 14 13:28:48 2002
+++ linux/include/linux/wireless.h	Mon Jan 21 17:23:24 2002
@@ -1,9 +1,10 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	12	5.10.01
+ * Version :	13	6.12.01
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved.
  */
 
 #ifndef _LINUX_WIRELESS_H
@@ -11,6 +12,8 @@
 
 /************************** DOCUMENTATION **************************/
 /*
+ * Initial APIs (1996 -> onward) :
+ * -----------------------------
  * Basically, the wireless extensions are for now a set of standard ioctl
  * call + /proc/net/wireless
  *
@@ -27,16 +30,27 @@
  * We have the list of command plus a structure descibing the
  * data exchanged...
  * Note that to add these ioctl, I was obliged to modify :
- *	net/core/dev.c (two place + add include)
- *	net/ipv4/af_inet.c (one place + add include)
+ *	# net/core/dev.c (two place + add include)
+ *	# net/ipv4/af_inet.c (one place + add include)
  *
  * /proc/net/wireless is a copy of /proc/net/dev.
  * We have a structure for data passed from the driver to /proc/net/wireless
  * Too add this, I've modified :
- *	net/core/dev.c (two other places)
- *	include/linux/netdevice.h (one place)
- *	include/linux/proc_fs.h (one place)
+ *	# net/core/dev.c (two other places)
+ *	# include/linux/netdevice.h (one place)
+ *	# include/linux/proc_fs.h (one place)
+ *
+ * New driver API (2001 -> onward) :
+ * -------------------------------
+ * This file is only concerned with the user space API and common definitions.
+ * The new driver API is defined and documented in :
+ *	# include/net/iw_handler.h
  *
+ * Note as well that /proc/net/wireless implementation has now moved in :
+ *	# include/linux/wireless.c
+ *
+ * Other comments :
+ * --------------
  * Do not add here things that are redundant with other mechanisms
  * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
  * wireless specific.
@@ -54,16 +68,14 @@
 #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
 #include <linux/if.h>			/* for IFNAMSIZ and co... */
 
-/**************************** CONSTANTS ****************************/
-
-/* --------------------------- VERSION --------------------------- */
+/***************************** VERSION *****************************/
 /*
  * This constant is used to know the availability of the wireless
  * extensions and to know which version of wireless extensions it is
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	12
+#define WIRELESS_EXT	13
 
 /*
  * Changes :
@@ -123,12 +135,20 @@
  *	- Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
  *	- Add new statistics (frag, retry, beacon)
  *	- Add average quality (for user space calibration)
+ *
+ * V12 to V13
+ * ----------
+ *	- Document creation of new driver API.
+ *	- Extract union iwreq_data from struct iwreq (for new driver API).
+ *	- Rename SIOCSIWNAME as SIOCSIWCOMMIT
  */
 
+/**************************** CONSTANTS ****************************/
+
 /* -------------------------- IOCTL LIST -------------------------- */
 
 /* Basic operations */
-#define SIOCSIWNAME	0x8B00		/* Unused */
+#define SIOCSIWCOMMIT	0x8B00		/* Commit pending changes to driver */
 #define SIOCGIWNAME	0x8B01		/* get name == wireless protocol */
 #define SIOCSIWNWID	0x8B02		/* set network id (the cell) */
 #define SIOCGIWNWID	0x8B03		/* get network id */
@@ -414,13 +434,49 @@
 
 /* ------------------------ IOCTL REQUEST ------------------------ */
 /*
+ * This structure defines the payload of an ioctl, and is used 
+ * below.
+ *
+ * Note that this structure should fit on the memory footprint
+ * of iwreq (which is the same as ifreq), which mean a max size of
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * You should check this when increasing the structures defined
+ * above in this file...
+ */
+union	iwreq_data
+{
+	/* Config - generic */
+	char		name[IFNAMSIZ];
+	/* Name : used to verify the presence of  wireless extensions.
+	 * Name of the protocol/provider... */
+
+	struct iw_point	essid;		/* Extended network name */
+	struct iw_param	nwid;		/* network id (or domain - the cell) */
+	struct iw_freq	freq;		/* frequency or channel :
+					 * 0-1000 = channel
+					 * > 1000 = frequency in Hz */
+
+	struct iw_param	sens;		/* signal level threshold */
+	struct iw_param	bitrate;	/* default bit rate */
+	struct iw_param	txpower;	/* default transmit power */
+	struct iw_param	rts;		/* RTS threshold threshold */
+	struct iw_param	frag;		/* Fragmentation threshold */
+	__u32		mode;		/* Operation mode */
+	struct iw_param	retry;		/* Retry limits & lifetime */
+
+	struct iw_point	encoding;	/* Encoding stuff : tokens */
+	struct iw_param	power;		/* PM duration/timeout */
+
+	struct sockaddr	ap_addr;	/* Access point address */
+
+	struct iw_point	data;		/* Other large parameters */
+};
+
+/*
  * The structure to exchange data for ioctl.
  * This structure is the same as 'struct ifreq', but (re)defined for
  * convenience...
- *
- * Note that it should fit on the same memory footprint !
- * You should check this when increasing the above structures (16 octets)
- * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * Do I need to remind you about structure size (32 octets) ?
  */
 struct	iwreq 
 {
@@ -429,35 +485,8 @@
 		char	ifrn_name[IFNAMSIZ];	/* if name, e.g. "eth0" */
 	} ifr_ifrn;
 
-	/* Data part */
-	union
-	{
-		/* Config - generic */
-		char		name[IFNAMSIZ];
-		/* Name : used to verify the presence of  wireless extensions.
-		 * Name of the protocol/provider... */
-
-		struct iw_point	essid;	/* Extended network name */
-		struct iw_param	nwid;	/* network id (or domain - the cell) */
-		struct iw_freq	freq;	/* frequency or channel :
-					 * 0-1000 = channel
-					 * > 1000 = frequency in Hz */
-
-		struct iw_param	sens;		/* signal level threshold */
-		struct iw_param	bitrate;	/* default bit rate */
-		struct iw_param	txpower;	/* default transmit power */
-		struct iw_param	rts;		/* RTS threshold threshold */
-		struct iw_param	frag;		/* Fragmentation threshold */
-		__u32		mode;		/* Operation mode */
-		struct iw_param	retry;		/* Retry limits & lifetime */
-
-		struct iw_point	encoding;	/* Encoding stuff : tokens */
-		struct iw_param	power;		/* PM duration/timeout */
-
-		struct sockaddr	ap_addr;	/* Access point address */
-
-		struct iw_point	data;		/* Other large parameters */
-	}	u;
+	/* Data part (defined just above) */
+	union	iwreq_data	u;
 };
 
 /* -------------------------- IOCTL DATA -------------------------- */
diff -urN linux-2.5.3-pre2/include/net/iw_handler.h linux/include/net/iw_handler.h
--- linux-2.5.3-pre2/include/net/iw_handler.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/iw_handler.h	Mon Jan 21 17:23:24 2002
@@ -0,0 +1,374 @@
+/*
+ * This file define the new driver API for Wireless Extensions
+ *
+ * Version :	2	6.12.01
+ *
+ * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved.
+ */
+
+#ifndef _IW_HANDLER_H
+#define _IW_HANDLER_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Initial driver API (1996 -> onward) :
+ * -----------------------------------
+ * The initial API just sends the IOCTL request received from user space
+ * to the driver (via the driver ioctl handler). The driver has to
+ * handle all the rest...
+ *
+ * The initial API also defines a specific handler in struct net_device
+ * to handle wireless statistics.
+ *
+ * The initial APIs served us well and has proven a reasonably good design.
+ * However, there is a few shortcommings :
+ *	o No events, everything is a request to the driver.
+ *	o Large ioctl function in driver with gigantic switch statement
+ *	  (i.e. spaghetti code).
+ *	o Driver has to mess up with copy_to/from_user, and in many cases
+ *	  does it unproperly. Common mistakes are :
+ *		* buffer overflows (no checks or off by one checks)
+ *		* call copy_to/from_user with irq disabled
+ *	o The user space interface is tied to ioctl because of the use
+ *	  copy_to/from_user.
+ *
+ * New driver API (2001 -> onward) :
+ * -------------------------------
+ * The new driver API is just a bunch of standard functions (handlers),
+ * each handling a specific Wireless Extension. The driver just export
+ * the list of handler it supports, and those will be called apropriately.
+ *
+ * I tried to keep the main advantage of the previous API (simplicity,
+ * efficiency and light weight), and also I provide a good dose of backward
+ * compatibility (most structures are the same, driver can use both API
+ * simultaneously, ...).
+ * Hopefully, I've also addressed the shortcomming of the initial API.
+ *
+ * The advantage of the new API are :
+ *	o Handling of Extensions in driver broken in small contained functions
+ *	o Tighter checks of ioctl before calling the driver
+ *	o Flexible commit strategy (at least, the start of it)
+ *	o Backward compatibility (can be mixed with old API)
+ *	o Driver doesn't have to worry about memory and user-space issues
+ * The last point is important for the following reasons :
+ *	o You are now able to call the new driver API from any API you
+ *		want (including from within other parts of the kernel).
+ *	o Common mistakes are avoided (buffer overflow, user space copy
+ *		with irq disabled and so on).
+ *
+ * The Drawback of the new API are :
+ *	o bloat (especially kernel)
+ *	o need to migrate existing drivers to new API
+ * My initial testing shows that the new API adds around 3kB to the kernel
+ * and save between 0 and 5kB from a typical driver.
+ * Also, as all structures and data types are unchanged, the migration is
+ * quite straightforward (but tedious).
+ *
+ * ---
+ *
+ * The new driver API is defined below in this file. User space should
+ * not be aware of what's happening down there...
+ *
+ * A new kernel wrapper is in charge of validating the IOCTLs and calling
+ * the appropriate driver handler. This is implemented in :
+ *	# net/core/wireless.c
+ *
+ * The driver export the list of handlers in :
+ *	# include/linux/netdevice.h (one place)
+ *
+ * The new driver API is available for WIRELESS_EXT >= 13.
+ * Good luck with migration to the new API ;-)
+ */
+
+/* ---------------------- THE IMPLEMENTATION ---------------------- */
+/*
+ * Some of the choice I've made are pretty controversials. Defining an
+ * API is very much weighting compromises. This goes into some of the
+ * details and the thinking behind the implementation.
+ *
+ * Implementation goals :
+ * --------------------
+ * The implementation goals were as follow :
+ *	o Obvious : you should not need a PhD to understand what's happening,
+ *		the benefit is easier maintainance.
+ *	o Flexible : it should accomodate a wide variety of driver
+ *		implementations and be as flexible as the old API.
+ *	o Lean : it should be efficient memory wise to minimise the impact
+ *		on kernel footprint.
+ *	o Transparent to user space : the large number of user space
+ *		applications that use Wireless Extensions should not need
+ *		any modifications.
+ *
+ * Array of functions versus Struct of functions
+ * ---------------------------------------------
+ * 1) Having an array of functions allow the kernel code to access the
+ * handler in a single lookup, which is much more efficient (think hash
+ * table here).
+ * 2) The only drawback is that driver writer may put their handler in
+ * the wrong slot. This is trivial to test (I set the frequency, the
+ * bitrate changes). Once the handler is in the proper slot, it will be
+ * there forever, because the array is only extended at the end.
+ * 3) Backward/forward compatibility : adding new handler just require
+ * extending the array, so you can put newer driver in older kernel
+ * without having to patch the kernel code (and vice versa).
+ *
+ * All handler are of the same generic type
+ * ----------------------------------------
+ * That's a feature !!!
+ * 1) Having a generic handler allow to have generic code, which is more
+ * efficient. If each of the handler was individually typed I would need
+ * to add a big switch in the kernel (== more bloat). This solution is
+ * more scalable, adding new Wireless Extensions doesn't add new code.
+ * 2) You can use the same handler in different slots of the array. For
+ * hardware, it may be more efficient or logical to handle multiple
+ * Wireless Extensions with a single function, and the API allow you to
+ * do that. (An example would be a single record on the card to control
+ * both bitrate and frequency, the handler would read the old record,
+ * modify it according to info->cmd and rewrite it).
+ *
+ * Functions prototype uses union iwreq_data
+ * -----------------------------------------
+ * Some would have prefered functions defined this way :
+ *	static int mydriver_ioctl_setrate(struct net_device *dev, 
+ *					  long rate, int auto)
+ * 1) The kernel code doesn't "validate" the content of iwreq_data, and
+ * can't do it (different hardware may have different notion of what a
+ * valid frequency is), so we don't pretend that we do it.
+ * 2) The above form is not extendable. If I want to add a flag (for
+ * example to distinguish setting max rate and basic rate), I would
+ * break the prototype. Using iwreq_data is more flexible.
+ * 3) Also, the above form is not generic (see above).
+ * 4) I don't expect driver developper using the wrong field of the
+ * union (Doh !), so static typechecking doesn't add much value.
+ * 5) Lastly, you can skip the union by doing :
+ *	static int mydriver_ioctl_setrate(struct net_device *dev,
+ *					  struct iw_request_info *info,
+ *					  struct iw_param *rrq,
+ *					  char *extra)
+ * And then adding the handler in the array like this :
+ *        (iw_handler) mydriver_ioctl_setrate,             // SIOCSIWRATE
+ *
+ * Using functions and not a registry
+ * ----------------------------------
+ * Another implementation option would have been for every instance to
+ * define a registry (a struct containing all the Wireless Extensions)
+ * and only have a function to commit the registry to the hardware.
+ * 1) This approach can be emulated by the current code, but not
+ * vice versa.
+ * 2) Some drivers don't keep any configuration in the driver, for them
+ * adding such a registry would be a significant bloat.
+ * 3) The code to translate from Wireless Extension to native format is
+ * needed anyway, so it would not reduce significantely the amount of code.
+ * 4) The current approach only selectively translate Wireless Extensions
+ * to native format and only selectively set, whereas the registry approach
+ * would require to translate all WE and set all parameters for any single
+ * change.
+ * 5) For many Wireless Extensions, the GET operation return the current
+ * dynamic value, not the value that was set.
+ *
+ * This header is <net/iw_handler.h>
+ * ---------------------------------
+ * 1) This header is kernel space only and should not be exported to
+ * user space. Headers in "include/linux/" are exported, headers in
+ * "include/net/" are not.
+ *
+ * Mixed 32/64 bit issues
+ * ----------------------
+ * The Wireless Extensions are designed to be 64 bit clean, by using only
+ * datatypes with explicit storage size.
+ * There are some issues related to kernel and user space using different
+ * memory model, and in particular 64bit kernel with 32bit user space.
+ * The problem is related to struct iw_point, that contains a pointer
+ * that *may* need to be translated.
+ * This is quite messy. The new API doesn't solve this problem (it can't),
+ * but is a step in the right direction :
+ * 1) Meta data about each ioctl is easily available, so we know what type
+ * of translation is needed.
+ * 2) The move of data between kernel and user space is only done in a single
+ * place in the kernel, so adding specific hooks in there is possible.
+ * 3) In the long term, it allows to move away from using ioctl as the
+ * user space API.
+ *
+ * So many comments and so few code
+ * --------------------------------
+ * That's a feature. Comments won't bloat the resulting kernel binary.
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/wireless.h>		/* IOCTL user space API */
+
+/***************************** VERSION *****************************/
+/*
+ * This constant is used to know which version of the driver API is
+ * available. Hopefully, this will be pretty stable and no changes
+ * will be needed...
+ * I just plan to increment with each new version.
+ */
+#define IW_HANDLER_VERSION	2
+
+/**************************** CONSTANTS ****************************/
+
+/* Special error message for the driver to indicate that we
+ * should do a commit after return from the iw_handler */
+#define EIWCOMMIT	EINPROGRESS
+
+/* Flags available in struct iw_request_info */
+#define IW_REQUEST_FLAG_NONE	0x0000	/* No flag so far */
+
+/* Type of headers we know about (basically union iwreq_data) */
+#define IW_HEADER_TYPE_NULL	0	/* Not available */
+#define IW_HEADER_TYPE_CHAR	2	/* char [IFNAMSIZ] */
+#define IW_HEADER_TYPE_UINT	4	/* __u32 */
+#define IW_HEADER_TYPE_FREQ	5	/* struct iw_freq */
+#define IW_HEADER_TYPE_POINT	6	/* struct iw_point */
+#define IW_HEADER_TYPE_PARAM	7	/* struct iw_param */
+#define IW_HEADER_TYPE_ADDR	8	/* struct sockaddr */
+
+/* Handling flags */
+/* Most are not implemented. I just use them as a reminder of some
+ * cool features we might need one day ;-) */
+#define IW_DESCR_FLAG_NONE	0x0000	/* Obvious */
+/* Wrapper level flags */
+#define IW_DESCR_FLAG_DUMP	0x0001	/* Not part of the dump command */
+#define IW_DESCR_FLAG_EVENT	0x0002	/* Generate an event on SET */
+#define IW_DESCR_FLAG_RESTRICT	0x0004	/* GET request is ROOT only */
+/* Driver level flags */
+#define IW_DESCR_FLAG_WAIT	0x0100	/* Wait for driver event */
+
+/****************************** TYPES ******************************/
+
+/* ----------------------- WIRELESS HANDLER ----------------------- */
+/*
+ * A wireless handler is just a standard function, that looks like the
+ * ioctl handler.
+ * We also define there how a handler list look like... As the Wireless
+ * Extension space is quite dense, we use a simple array, which is faster
+ * (that's the perfect hash table ;-).
+ */
+
+/*
+ * Meta data about the request passed to the iw_handler.
+ * Most handlers can safely ignore what's in there.
+ * The 'cmd' field might come handy if you want to use the same handler
+ * for multiple command...
+ * This struct is also my long term insurance. I can add new fields here
+ * without breaking the prototype of iw_handler...
+ */
+struct iw_request_info
+{
+	__u16		cmd;		/* Wireless Extension command */
+	__u16		flags;		/* More to come ;-) */
+};
+
+/*
+ * This is how a function handling a Wireless Extension should look
+ * like (both get and set, standard and private).
+ */
+typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,
+			  union iwreq_data *wrqu, char *extra);
+
+/*
+ * This define all the handler that the driver export.
+ * As you need only one per driver type, please use a static const
+ * shared by all driver instances... Same for the members...
+ * This will be linked from net_device in <linux/netdevice.h>
+ */
+struct iw_handler_def
+{
+	/* Number of handlers defined (more precisely, index of the
+	 * last defined handler + 1) */
+	__u16			num_standard;
+	__u16			num_private;
+	/* Number of private arg description */
+	__u16			num_private_args;
+
+	/* Array of handlers for standard ioctls
+	 * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME]
+	 */
+	iw_handler *		standard;
+
+	/* Array of handlers for private ioctls
+	 * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV]
+	 */
+	iw_handler *		private;
+
+	/* Arguments of private handler. This one is just a list, so you
+	 * can put it in any order you want and should not leave holes...
+	 * We will automatically export that to user space... */
+	struct iw_priv_args *	private_args;
+
+	/* In the long term, get_wireless_stats will move from
+	 * 'struct net_device' to here, to minimise bloat. */
+};
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/*
+ * Currently we don't support events, so let's just plan for the
+ * future...
+ */
+
+/*
+ * A Wireless Event.
+ */
+// How do we define short header ? We don't want a flag on length.
+// Probably a flag on event ? Highest bit to zero...
+struct iw_event
+{
+	__u16		length;			/* Lenght of this stuff */
+	__u16		event;			/* Wireless IOCTL */
+	union	iwreq_data	header;		/* IOCTL fixed payload */
+	char		extra[0];		/* Optional IOCTL data */
+};
+
+/* ---------------------- IOCTL DESCRIPTION ---------------------- */
+/*
+ * One of the main goal of the new interface is to deal entirely with
+ * user space/kernel space memory move.
+ * For that, we need to know :
+ *	o if iwreq is a pointer or contain the full data
+ *	o what is the size of the data to copy
+ *
+ * For private IOCTLs, we use the same rules as used by iwpriv and
+ * defined in struct iw_priv_args.
+ *
+ * For standard IOCTLs, things are quite different and we need to
+ * use the stuctures below. Actually, this struct is also more
+ * efficient, but that's another story...
+ */
+
+/*
+ * Describe how a standard IOCTL looks like.
+ */
+struct iw_ioctl_description
+{
+	__u8	header_type;		/* NULL, iw_point or other */
+	__u8	token_type;		/* Future */
+	__u16	token_size;		/* Granularity of payload */
+	__u16	min_tokens;		/* Min acceptable token number */
+	__u16	max_tokens;		/* Max acceptable token number */
+	__u32	flags;			/* Special handling of the request */
+};
+
+/* Need to think of short header translation table. Later. */
+
+/**************************** PROTOTYPES ****************************/
+/*
+ * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
+ * Those may be called only within the kernel.
+ */
+
+/* First : function strictly used inside the kernel */
+
+/* Handle /proc/net/wireless, called in net/code/dev.c */
+extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+				 int length);
+
+/* Handle IOCTLs, called in net/code/dev.c */
+extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd);
+
+/* Second : functions that may be called by driver modules */
+/* None yet */
+
+#endif	/* _LINUX_WIRELESS_H */
diff -urN linux-2.5.3-pre2/init/main.c linux/init/main.c
--- linux-2.5.3-pre2/init/main.c	Wed Jan  9 09:45:17 2002
+++ linux/init/main.c	Mon Jan 21 17:23:24 2002
@@ -290,8 +290,6 @@
 extern void setup_arch(char **);
 extern void cpu_idle(void);
 
-unsigned long wait_init_idle;
-
 #ifndef CONFIG_SMP
 
 #ifdef CONFIG_X86_LOCAL_APIC
@@ -305,6 +303,16 @@
 
 #else
 
+static unsigned long __initdata wait_init_idle;
+
+void __init idle_startup_done(void)
+{
+	clear_bit(smp_processor_id(), &wait_init_idle);
+	while (wait_init_idle) {
+		cpu_relax();
+		barrier();
+	}
+}
 
 /* Called by boot processor to activate the rest. */
 static void __init smp_init(void)
@@ -315,6 +323,7 @@
 
 	smp_threads_ready=1;
 	smp_commence();
+	idle_startup_done();
 }
 
 #endif
@@ -411,20 +420,13 @@
 	check_bugs();
 	printk("POSIX conformance testing by UNIFIX\n");
 
+	init_idle();
 	/* 
 	 *	We count on the initial thread going ok 
 	 *	Like idlers init is an unlocked kernel thread, which will
 	 *	make syscalls (and thus be locked).
 	 */
 	smp_init();
-
-	/*
-	 * Finally, we wait for all other CPU's, and initialize this
-	 * thread that will become the idle thread for the boot CPU.
-	 * After this, the scheduler is fully initialized, and we can
-	 * start creating and running new threads.
-	 */
-	init_idle();
 
 	/* Do the rest non-__init'ed, we're now alive */
 	rest_init();
diff -urN linux-2.5.3-pre2/kernel/fork.c linux/kernel/fork.c
--- linux-2.5.3-pre2/kernel/fork.c	Mon Jan 21 17:22:37 2002
+++ linux/kernel/fork.c	Mon Jan 21 17:23:24 2002
@@ -747,23 +747,16 @@
 	if (p->ptrace & PT_PTRACED)
 		send_sig(SIGSTOP, p, 1);
 
-#define RUN_CHILD_FIRST 1
-#if RUN_CHILD_FIRST
 	wake_up_forked_process(p);		/* do this last */
-#else
-	wake_up_process(p);			/* do this last */
-#endif
 	++total_forks;
 	if (clone_flags & CLONE_VFORK)
 		wait_for_completion(&vfork);
-#if RUN_CHILD_FIRST
 	else
 		/*
 		 * Let the child process run first, to avoid most of the
 		 * COW overhead when the child exec()s afterwards.
 		 */
 		current->need_resched = 1;
-#endif
 
 fork_out:
 	return retval;
diff -urN linux-2.5.3-pre2/kernel/sched.c linux/kernel/sched.c
--- linux-2.5.3-pre2/kernel/sched.c	Mon Jan 21 17:22:37 2002
+++ linux/kernel/sched.c	Mon Jan 21 17:23:24 2002
@@ -37,11 +37,7 @@
  *
  * Locking rule: those places that want to lock multiple runqueues
  * (such as the load balancing or the process migration code), lock
- * acquire operations must be ordered by the runqueue's cpu id.
- *
- * The RT event id is used to avoid calling into the the RT scheduler
- * if there is a RT task active in an SMP system but there is no
- * RT scheduling activity otherwise.
+ * acquire operations must be ordered by ascending &runqueue.
  */
 struct runqueue {
 	spinlock_t lock;
@@ -57,24 +53,28 @@
 #define this_rq()		cpu_rq(smp_processor_id())
 #define task_rq(p)		cpu_rq((p)->cpu)
 #define cpu_curr(cpu)		(cpu_rq(cpu)->curr)
-#define rq_cpu(rq)		((rq) - runqueues)
 #define rt_task(p)		((p)->policy != SCHED_OTHER)
 
 
-#define lock_task_rq(rq,p,flags)				\
-do {								\
-repeat_lock_task:						\
-	rq = task_rq(p);					\
-	spin_lock_irqsave(&rq->lock, flags);			\
-	if (unlikely(rq_cpu(rq) != (p)->cpu)) {			\
-		spin_unlock_irqrestore(&rq->lock, flags);	\
-		goto repeat_lock_task;				\
-	}							\
-} while (0)
+static inline runqueue_t *lock_task_rq(task_t *p, unsigned long *flags)
+{
+	struct runqueue *__rq;
 
-#define unlock_task_rq(rq,p,flags)				\
-	spin_unlock_irqrestore(&rq->lock, flags)
+repeat_lock_task:
+	__rq = task_rq(p);
+	spin_lock_irqsave(&__rq->lock, *flags);
+	if (unlikely(__rq != task_rq(p))) {
+		spin_unlock_irqrestore(&__rq->lock, *flags);
+		goto repeat_lock_task;
+	}
+	return __rq;
+}
 
+static inline void unlock_task_rq(runqueue_t *rq, task_t *p,
+							unsigned long *flags)
+{
+	spin_unlock_irqrestore(&rq->lock, *flags);
+}
 /*
  * Adding/removing a task to/from a priority array:
  */
@@ -95,21 +95,26 @@
 }
 
 /*
- * A task is 'heavily interactive' if it has reached the
- * bottom 25% of the SCHED_OTHER priority range - in this
+ * A task is 'heavily interactive' if it either has reached the
+ * bottom 25% of the SCHED_OTHER priority range, or if it is below
+ * its default priority by at least 3 priority levels. In this
  * case we favor it by reinserting it on the active array,
  * even after it expired its current timeslice.
  *
+ * A task is a 'CPU hog' if it's either in the upper 25% of the
+ * SCHED_OTHER priority range, or if's not an interactive task.
+ *
  * A task can get a priority bonus by being 'somewhat
  * interactive' - and it will get a priority penalty for
  * being a CPU hog.
  *
- * CPU-hog penalties cannot go more than 5 above the default
- * priority level. Priority bonus cannot go below the minimum
- * priority level.
  */
-#define PRIO_INTERACTIVE (MAX_RT_PRIO + MAX_USER_PRIO/3)
-#define TASK_INTERACTIVE(p) ((p)->prio <= PRIO_INTERACTIVE)
+#define PRIO_INTERACTIVE	(MAX_RT_PRIO + MAX_USER_PRIO/4)
+#define PRIO_CPU_HOG		(MAX_RT_PRIO + MAX_USER_PRIO*3/4)
+
+#define TASK_INTERACTIVE(p) (((p)->prio <= PRIO_INTERACTIVE) ||		\
+	(((p)->prio < PRIO_CPU_HOG) &&					\
+		((p)->prio <= NICE_TO_PRIO((p)->__nice)-3)))
 
 static inline int effective_prio(task_t *p)
 {
@@ -117,9 +122,18 @@
 
 	/*
 	 * Here we scale the actual sleep average [0 .... MAX_SLEEP_AVG]
-	 * into the 20 ... -20 bonus/penalty range.
+	 * into the 19 ... -18 bonus/penalty range.
+	 *
+	 * We take off the 10% from the full 0...39 priority range so that:
+	 *
+	 * 1) nice +19 CPU hogs do not preempt nice 0 CPU hogs just yet.
+	 * 2) nice -20 interactive tasks do not get preempted by
+	 *    nice 0 interactive tasks.
+	 *
+	 * Both properties are important to certain applications.
 	 */
-	bonus = MAX_USER_PRIO * p->sleep_avg / MAX_SLEEP_AVG - MAX_USER_PRIO/2;
+	bonus = MAX_USER_PRIO*9/10 * p->sleep_avg / MAX_SLEEP_AVG -
+							MAX_USER_PRIO*9/10/2;
 	prio = NICE_TO_PRIO(p->__nice) - bonus;
 	if (prio < MAX_RT_PRIO)
 		prio = MAX_RT_PRIO;
@@ -130,9 +144,10 @@
 
 static inline void activate_task(task_t *p, runqueue_t *rq)
 {
+	unsigned long sleep_time = jiffies - p->sleep_timestamp;
 	prio_array_t *array = rq->active;
 
-	if (!rt_task(p)) {
+	if (!rt_task(p) && sleep_time) {
 		/*
 		 * This code gives a bonus to interactive tasks. We update
 		 * an 'average sleep time' value here, based on
@@ -140,7 +155,7 @@
 		 * the higher the average gets - and the higher the priority
 		 * boost gets as well.
 		 */
-		p->sleep_avg += jiffies - p->sleep_timestamp;
+		p->sleep_avg += sleep_time;
 		if (p->sleep_avg > MAX_SLEEP_AVG)
 			p->sleep_avg = MAX_SLEEP_AVG;
 		p->prio = effective_prio(p);
@@ -185,12 +200,26 @@
 		cpu_relax();
 		barrier();
 	}
-	lock_task_rq(rq, p, flags);
+	rq = lock_task_rq(p, &flags);
 	if (unlikely(rq->curr == p)) {
-		unlock_task_rq(rq, p, flags);
+		unlock_task_rq(rq, p, &flags);
 		goto repeat;
 	}
-	unlock_task_rq(rq, p, flags);
+	unlock_task_rq(rq, p, &flags);
+}
+
+/*
+ * The SMP message passing code calls this function whenever
+ * the new task has arrived at the target CPU. We move the
+ * new task into the local runqueue.
+ *
+ * This function must be called with interrupts disabled.
+ */
+void sched_task_migrated(task_t *new_task)
+{
+	wait_task_inactive(new_task);
+	new_task->cpu = smp_processor_id();
+	wake_up_process(new_task);
 }
 
 /*
@@ -223,7 +252,7 @@
 	int success = 0;
 	runqueue_t *rq;
 
-	lock_task_rq(rq, p, flags);
+	rq = lock_task_rq(p, &flags);
 	p->state = TASK_RUNNING;
 	if (!p->array) {
 		activate_task(p, rq);
@@ -231,11 +260,11 @@
 			resched_task(rq->curr);
 		success = 1;
 	}
-	unlock_task_rq(rq, p, flags);
+	unlock_task_rq(rq, p, &flags);
 	return success;
 }
 
-inline int wake_up_process(task_t * p)
+int wake_up_process(task_t * p)
 {
 	return try_to_wake_up(p, 0);
 }
@@ -246,9 +275,8 @@
 
 	p->state = TASK_RUNNING;
 	if (!rt_task(p)) {
-		p->prio += MAX_USER_PRIO/10;
-		if (p->prio > MAX_PRIO-1)
-			p->prio = MAX_PRIO-1;
+		p->sleep_avg = (p->sleep_avg * 4) / 5;
+		p->prio = effective_prio(p);
 	}
 	spin_lock_irq(&rq->lock);
 	activate_task(p, rq);
@@ -312,18 +340,6 @@
 	return sum;
 }
 
-static inline unsigned long max_rq_len(void)
-{
-	unsigned long i, curr, max = 0;
-
-	for (i = 0; i < smp_num_cpus; i++) {
-		curr = cpu_rq(i)->nr_running;
-		if (curr > max)
-			max = curr;
-	}
-	return max;
-}
-
 /*
  * Current runqueue is empty, or rebalance tick: if there is an
  * inbalance (current runqueue is too short) then pull from
@@ -406,7 +422,7 @@
 	 * Ok, lets do some actual balancing:
 	 */
 
-	if (rq_cpu(busiest) < this_cpu) {
+	if (busiest < this_rq) {
 		spin_unlock(&this_rq->lock);
 		spin_lock(&busiest->lock);
 		spin_lock(&this_rq->lock);
@@ -492,8 +508,7 @@
 
 static inline void idle_tick(void)
 {
-	if ((jiffies % IDLE_REBALANCE_TICK) ||
-			likely(this_rq()->curr == NULL))
+	if (jiffies % IDLE_REBALANCE_TICK)
 		return;
 	spin_lock(&this_rq()->lock);
 	load_balance(this_rq(), 1);
@@ -509,7 +524,7 @@
 	unsigned long now = jiffies;
 	runqueue_t *rq = this_rq();
 
-	if (p == rq->idle || !rq->idle)
+	if (p == rq->idle)
 		return idle_tick();
 	/* Task might have expired already, but not scheduled off yet */
 	if (p->array != rq->active) {
@@ -538,7 +553,7 @@
 	 * do not update a process's priority until it either
 	 * goes to sleep or uses up its timeslice. This makes
 	 * it possible for interactive tasks to use up their
-	 * timeslices at their high priority levels.
+	 * timeslices at their highest priority levels.
 	 */
 	if (p->sleep_avg)
 		p->sleep_avg--;
@@ -562,29 +577,26 @@
  */
 asmlinkage void schedule(void)
 {
-	task_t *prev, *next;
+	task_t *prev = current, *next;
+	runqueue_t *rq = this_rq();
 	prio_array_t *array;
-	runqueue_t *rq;
 	list_t *queue;
 	int idx;
 
 	if (unlikely(in_interrupt()))
 		BUG();
-need_resched_back:
-	prev = current;
 	release_kernel_lock(prev, smp_processor_id());
-	rq = this_rq();
 	spin_lock_irq(&rq->lock);
 
 	switch (prev->state) {
-		case TASK_INTERRUPTIBLE:
-			if (unlikely(signal_pending(prev))) {
-				prev->state = TASK_RUNNING;
-				break;
-			}
-		default:
-			deactivate_task(prev, rq);
-		case TASK_RUNNING:
+	case TASK_INTERRUPTIBLE:
+		if (unlikely(signal_pending(prev))) {
+			prev->state = TASK_RUNNING;
+			break;
+		}
+	default:
+		deactivate_task(prev, rq);
+	case TASK_RUNNING:
 	}
 pick_next_task:
 	if (unlikely(!rq->nr_running)) {
@@ -615,7 +627,6 @@
 	if (likely(prev != next)) {
 		rq->nr_switches++;
 		rq->curr = next;
-		next->cpu = prev->cpu;
 		context_switch(prev, next);
 		/*
 		 * The runqueue pointer might be from another CPU
@@ -628,8 +639,6 @@
 	spin_unlock_irq(&rq->lock);
 
 	reacquire_kernel_lock(current);
-	if (need_resched())
-		goto need_resched_back;
 	return;
 }
 
@@ -782,47 +791,23 @@
  */
 void set_cpus_allowed(task_t *p, unsigned long new_mask)
 {
-	runqueue_t *this_rq = this_rq(), *target_rq;
-	unsigned long this_mask = 1UL << smp_processor_id();
-	int target_cpu;
-
 	new_mask &= cpu_online_map;
 	if (!new_mask)
 		BUG();
+
 	p->cpus_allowed = new_mask;
 	/*
 	 * Can the task run on the current CPU? If not then
 	 * migrate the process off to a proper CPU.
 	 */
-	if (new_mask & this_mask)
+	if (new_mask & (1UL << smp_processor_id()))
 		return;
-	target_cpu = ffz(~new_mask);
-	target_rq = cpu_rq(target_cpu);
-	if (target_cpu < smp_processor_id()) {
-		spin_lock_irq(&target_rq->lock);
-		spin_lock(&this_rq->lock);
-	} else {
-		spin_lock_irq(&this_rq->lock);
-		spin_lock(&target_rq->lock);
-	}
-	dequeue_task(p, p->array);
-	this_rq->nr_running--;
-	target_rq->nr_running++;
-	enqueue_task(p, target_rq->active);
-	target_rq->curr->need_resched = 1;
-	spin_unlock(&target_rq->lock);
+#if CONFIG_SMP
+	smp_migrate_task(ffz(~new_mask), current);
 
-	/*
-	 * The easiest solution is to context switch into
-	 * the idle thread - which will pick the best task
-	 * afterwards:
-	 */
-	this_rq->nr_switches++;
-	this_rq->curr = this_rq->idle;
-	this_rq->idle->need_resched = 1;
-	context_switch(current, this_rq->idle);
-	barrier();
-	spin_unlock_irq(&this_rq()->lock);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule();
+#endif
 }
 
 void scheduling_functions_end_here(void) { }
@@ -839,7 +824,7 @@
 	 * We have to be careful, if called from sys_setpriority(),
 	 * the task might be in the middle of scheduling on another CPU.
 	 */
-	lock_task_rq(rq, p, flags);
+	rq = lock_task_rq(p, &flags);
 	if (rt_task(p)) {
 		p->__nice = nice;
 		goto out_unlock;
@@ -853,7 +838,7 @@
 	if (array) {
 		enqueue_task(p, array);
 		/*
-		 * If the task is runnable and lowered its priority,
+		 * If the task is running and lowered its priority,
 		 * or increased its priority then reschedule its CPU:
 		 */
 		if ((nice < p->__nice) ||
@@ -861,7 +846,7 @@
 			resched_task(rq->curr);
 	}
 out_unlock:
-	unlock_task_rq(rq, p, flags);
+	unlock_task_rq(rq, p, &flags);
 }
 
 #ifndef __alpha__
@@ -938,7 +923,7 @@
 	 * To be able to change p->policy safely, the apropriate
 	 * runqueue lock must be held.
 	 */
-	lock_task_rq(rq,p,flags);
+	rq = lock_task_rq(p, &flags);
 
 	if (policy < 0)
 		policy = p->policy;
@@ -981,7 +966,7 @@
 		activate_task(p, task_rq(p));
 
 out_unlock:
-	unlock_task_rq(rq,p,flags);
+	unlock_task_rq(rq, p, &flags);
 out_unlock_tasklist:
 	read_unlock_irq(&tasklist_lock);
 
@@ -1224,14 +1209,12 @@
 	read_unlock(&tasklist_lock);
 }
 
-extern unsigned long wait_init_idle;
-
 static inline void double_rq_lock(runqueue_t *rq1, runqueue_t *rq2)
 {
 	if (rq1 == rq2)
 		spin_lock(&rq1->lock);
 	else {
-		if (rq_cpu(rq1) < rq_cpu(rq2)) {
+		if (rq1 < rq2) {
 			spin_lock(&rq1->lock);
 			spin_lock(&rq2->lock);
 		} else {
@@ -1260,16 +1243,11 @@
 	this_rq->curr = this_rq->idle = current;
 	deactivate_task(current, rq);
 	current->array = NULL;
-	current->prio = MAX_PRIO-1;
+	current->prio = MAX_PRIO;
 	current->state = TASK_RUNNING;
-	clear_bit(smp_processor_id(), &wait_init_idle);
 	double_rq_unlock(this_rq, rq);
-	while (wait_init_idle) {
-		cpu_relax();
-		barrier();
-	}
 	current->need_resched = 1;
-	__sti();
+	__restore_flags(flags);
 }
 
 extern void init_timervecs(void);
@@ -1308,7 +1286,7 @@
 	 */
 	rq = this_rq();
 	rq->curr = current;
-	rq->idle = NULL;
+	rq->idle = current;
 	wake_up_process(current);
 
 	init_timervecs();
diff -urN linux-2.5.3-pre2/mm/oom_kill.c linux/mm/oom_kill.c
--- linux-2.5.3-pre2/mm/oom_kill.c	Mon Jan  7 12:55:16 2002
+++ linux/mm/oom_kill.c	Mon Jan 21 17:23:24 2002
@@ -110,8 +110,7 @@
 
 /*
  * Simple selection loop. We chose the process with the highest
- * number of 'points'. We need the locks to make sure that the
- * list of task structs doesn't change while we look the other way.
+ * number of 'points'. We expect the caller will lock the tasklist.
  *
  * (not docbooked, we don't want this one cluttering up the manual)
  */
@@ -121,7 +120,6 @@
 	struct task_struct *p = NULL;
 	struct task_struct *chosen = NULL;
 
-	read_lock(&tasklist_lock);
 	for_each_task(p) {
 		if (p->pid) {
 			int points = badness(p);
@@ -131,7 +129,6 @@
 			}
 		}
 	}
-	read_unlock(&tasklist_lock);
 	return chosen;
 }
 
@@ -170,14 +167,16 @@
  */
 static void oom_kill(void)
 {
-	struct task_struct *p = select_bad_process(), *q;
+	struct task_struct *p, *q;
+	
+	read_lock(&tasklist_lock);
+	p = select_bad_process();
 
 	/* Found nothing?!?! Either we hang forever, or we panic. */
 	if (p == NULL)
 		panic("Out of memory and no killable processes...\n");
 
 	/* kill all processes that share the ->mm (i.e. all threads) */
-	read_lock(&tasklist_lock);
 	for_each_task(q) {
 		if(q->mm == p->mm) oom_kill_task(q);
 	}
@@ -197,7 +196,7 @@
  */
 void out_of_memory(void)
 {
-	static unsigned long first, last, count;
+	static unsigned long first, last, count, lastkill;
 	unsigned long now, since;
 
 	/*
@@ -234,8 +233,18 @@
 		return;
 
 	/*
+	 * If we just killed a process, wait a while
+	 * to give that task a chance to exit. This
+	 * avoids killing multiple processes needlessly.
+	 */
+	since = now - lastkill;
+	if (since < HZ*5)
+		return;
+
+	/*
 	 * Ok, really out of memory. Kill something.
 	 */
+	lastkill = now;
 	oom_kill();
 
 reset:
diff -urN linux-2.5.3-pre2/mm/shmem.c linux/mm/shmem.c
--- linux-2.5.3-pre2/mm/shmem.c	Wed Nov 21 09:57:57 2001
+++ linux/mm/shmem.c	Mon Jan 21 17:23:24 2002
@@ -26,6 +26,7 @@
 #include <linux/pagemap.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/slab.h>
 #include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
@@ -385,7 +386,7 @@
 	return 0;
 found:
 	delete_from_swap_cache(page);
-	add_to_page_cache(page, info->inode->i_mapping, offset + idx);
+	add_to_page_cache(page, info->vfs_inode.i_mapping, offset + idx);
 	SetPageDirty(page);
 	SetPageUptodate(page);
 	info->swapped--;
@@ -685,9 +686,13 @@
 		inode->i_mapping->a_ops = &shmem_aops;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 		info = SHMEM_I(inode);
-		info->inode = inode;
 		spin_lock_init (&info->lock);
 		sema_init (&info->sem, 1);
+		info->next_index = 0;
+		memset (info->i_direct, 0, sizeof(info->i_direct));
+		info->i_indirect = NULL;
+		info->swapped = 0;
+		info->locked = 0;
 		switch (mode & S_IFMT) {
 		default:
 			init_special_inode(inode, mode, dev);
@@ -1308,7 +1313,48 @@
 	return sb;
 }
 
+static kmem_cache_t * shmem_inode_cachep;
 
+static struct inode *shmem_alloc_inode(struct super_block *sb)
+{
+	struct shmem_inode_info *p;
+	p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, SLAB_KERNEL);
+	if (!p)
+		return NULL;
+	return &p->vfs_inode;
+}
+
+static void shmem_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct shmem_inode_info *p = (struct shmem_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		inode_init_once(&p->vfs_inode);
+	}
+}
+ 
+static int init_inodecache(void)
+{
+	shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
+					     sizeof(struct shmem_inode_info),
+					     0, SLAB_HWCACHE_ALIGN,
+					     init_once, NULL);
+	if (shmem_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(shmem_inode_cachep))
+		printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n");
+}
 
 static struct address_space_operations shmem_aops = {
 	writepage:	shmem_writepage,
@@ -1350,6 +1396,8 @@
 };
 
 static struct super_operations shmem_ops = {
+	alloc_inode:	shmem_alloc_inode,
+	destroy_inode:	shmem_destroy_inode,
 #ifdef CONFIG_TMPFS
 	statfs:		shmem_statfs,
 	remount_fs:	shmem_remount_fs,
@@ -1376,30 +1424,45 @@
 	int error;
 	struct vfsmount * res;
 
-	if ((error = register_filesystem(&tmpfs_fs_type))) {
+	error = init_inodecache();
+	if (error)
+		goto out3;
+
+	error = register_filesystem(&tmpfs_fs_type);
+	if (error) {
 		printk (KERN_ERR "Could not register tmpfs\n");
-		return error;
+		goto out2;
 	}
 #ifdef CONFIG_TMPFS
-	if ((error = register_filesystem(&shmem_fs_type))) {
+	error = register_filesystem(&shmem_fs_type);
+	if (error) {
 		printk (KERN_ERR "Could not register shm fs\n");
-		return error;
+		goto out1;
 	}
 	devfs_mk_dir (NULL, "shm", NULL);
 #endif
 	res = kern_mount(&tmpfs_fs_type);
 	if (IS_ERR (res)) {
+		error = PTR_ERR(res);
 		printk (KERN_ERR "could not kern_mount tmpfs\n");
-		unregister_filesystem(&tmpfs_fs_type);
-		return PTR_ERR(res);
+		goto out;
 	}
 	shm_mnt = res;
 
 	/* The internal instance should not do size checking */
-	if ((error = shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX)))
-		printk (KERN_ERR "could not set limits on internal tmpfs\n");
-
+	shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX);
 	return 0;
+
+out:
+#ifdef CONFIG_TMPFS
+	unregister_filesystem(&shmem_fs_type);
+out1:
+#endif
+	unregister_filesystem(&tmpfs_fs_type);
+out2:
+	destroy_inodecache();
+out3:
+	return error;
 }
 
 static void __exit exit_shmem_fs(void)
@@ -1409,6 +1472,7 @@
 #endif
 	unregister_filesystem(&tmpfs_fs_type);
 	mntput(shm_mnt);
+	destroy_inodecache();
 }
 
 module_init(init_shmem_fs)
diff -urN linux-2.5.3-pre2/mm/swapfile.c linux/mm/swapfile.c
--- linux-2.5.3-pre2/mm/swapfile.c	Mon Jan 21 17:22:37 2002
+++ linux/mm/swapfile.c	Mon Jan 21 17:23:24 2002
@@ -344,7 +344,7 @@
 	if (page) {
 		page_cache_get(page);
 		/* Only cache user (+us), or swap space full? Free it! */
-		if (page_count(page) == 2 || vm_swap_full()) {
+		if (page_count(page) - !!page->buffers == 2 || vm_swap_full()) {
 			delete_from_swap_cache(page);
 			SetPageDirty(page);
 		}
diff -urN linux-2.5.3-pre2/net/core/Makefile linux/net/core/Makefile
--- linux-2.5.3-pre2/net/core/Makefile	Tue Oct 30 15:08:12 2001
+++ linux/net/core/Makefile	Mon Jan 21 17:23:24 2002
@@ -26,5 +26,8 @@
 obj-$(CONFIG_NETFILTER) += netfilter.o
 obj-$(CONFIG_NET_DIVERT) += dv.o
 obj-$(CONFIG_NET_PROFILE) += profile.o
+obj-$(CONFIG_NET_RADIO) += wireless.o
+# Ugly. I wish all wireless drivers were moved in drivers/net/wireless
+obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.5.3-pre2/net/core/dev.c linux/net/core/dev.c
--- linux-2.5.3-pre2/net/core/dev.c	Wed Nov  7 14:39:36 2001
+++ linux/net/core/dev.c	Mon Jan 21 17:23:24 2002
@@ -102,6 +102,7 @@
 #include <linux/module.h>
 #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
 #include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
+#include <net/iw_handler.h>
 #endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
 #ifdef CONFIG_PLIP
 extern int plip_init(void);
@@ -1796,122 +1797,6 @@
 #endif	/* CONFIG_PROC_FS */
 
 
-#ifdef WIRELESS_EXT
-#ifdef CONFIG_PROC_FS
-
-/*
- * Print one entry of /proc/net/wireless
- * This is a clone of /proc/net/dev (just above)
- */
-static int sprintf_wireless_stats(char *buffer, struct net_device *dev)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats = (dev->get_wireless_stats ?
-				       dev->get_wireless_stats(dev) :
-				       (struct iw_statistics *) NULL);
-	int size;
-
-	if (stats != (struct iw_statistics *) NULL) {
-		size = sprintf(buffer,
-			       "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d %6d %6d   %6d\n",
-			       dev->name,
-			       stats->status,
-			       stats->qual.qual,
-			       stats->qual.updated & 1 ? '.' : ' ',
-			       stats->qual.level,
-			       stats->qual.updated & 2 ? '.' : ' ',
-			       stats->qual.noise,
-			       stats->qual.updated & 4 ? '.' : ' ',
-			       stats->discard.nwid,
-			       stats->discard.code,
-			       stats->discard.fragment,
-			       stats->discard.retries,
-			       stats->discard.misc,
-			       stats->miss.beacon);
-		stats->qual.updated = 0;
-	}
-	else
-		size = 0;
-
-	return size;
-}
-
-/*
- * Print info for /proc/net/wireless (print all entries)
- * This is a clone of /proc/net/dev (just above)
- */
-static int dev_get_wireless_info(char * buffer, char **start, off_t offset,
-			  int length)
-{
-	int		len = 0;
-	off_t		begin = 0;
-	off_t		pos = 0;
-	int		size;
-	
-	struct net_device *	dev;
-
-	size = sprintf(buffer,
-		       "Inter-| sta-|   Quality        |   Discarded packets               | Missed\n"
-		       " face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon\n"
-			);
-	
-	pos += size;
-	len += size;
-
-	read_lock(&dev_base_lock);
-	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		size = sprintf_wireless_stats(buffer + len, dev);
-		len += size;
-		pos = begin + len;
-
-		if (pos < offset) {
-			len = 0;
-			begin = pos;
-		}
-		if (pos > offset + length)
-			break;
-	}
-	read_unlock(&dev_base_lock);
-
-	*start = buffer + (offset - begin);	/* Start of wanted data */
-	len -= (offset - begin);		/* Start slop */
-	if (len > length)
-		len = length;			/* Ending slop */
-	if (len < 0)
-		len = 0;
-
-	return len;
-}
-#endif	/* CONFIG_PROC_FS */
-
-/*
- *	Allow programatic access to /proc/net/wireless even if /proc
- *	doesn't exist... Also more efficient...
- */
-static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats = (dev->get_wireless_stats ?
-				       dev->get_wireless_stats(dev) :
-				       (struct iw_statistics *) NULL);
-
-	if (stats != (struct iw_statistics *) NULL) {
-		struct iwreq *	wrq = (struct iwreq *)ifr;
-
-		/* Copy statistics to the user buffer */
-		if(copy_to_user(wrq->u.data.pointer, stats,
-				sizeof(struct iw_statistics)))
-			return -EFAULT;
-
-		/* Check if we need to clear the update flag */
-		if(wrq->u.data.flags != 0)
-			stats->qual.updated = 0;
-		return(0);
-	} else
-		return -EOPNOTSUPP;
-}
-#endif	/* WIRELESS_EXT */
-
 /**
  *	netdev_set_master	-	set up master/slave pair
  *	@slave: slave device
@@ -2209,11 +2094,6 @@
 			notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
 			return 0;
 
-#ifdef WIRELESS_EXT
-		case SIOCGIWSTATS:
-			return dev_iwstats(dev, ifr);
-#endif	/* WIRELESS_EXT */
-
 		/*
 		 *	Unknown or private ioctl
 		 */
@@ -2239,17 +2119,6 @@
 				return -EOPNOTSUPP;
 			}
 
-#ifdef WIRELESS_EXT
-			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
-				if (dev->do_ioctl) {
-					if (!netif_device_present(dev))
-						return -ENODEV;
-					return dev->do_ioctl(dev, ifr, cmd);
-				}
-				return -EOPNOTSUPP;
-			}
-#endif	/* WIRELESS_EXT */
-
 	}
 	return -EINVAL;
 }
@@ -2431,7 +2300,8 @@
 				}
 				dev_load(ifr.ifr_name);
 				rtnl_lock();
-				ret = dev_ifsioc(&ifr, cmd);
+				/* Follow me in net/core/wireless.c */
+				ret = wireless_process_ioctl(&ifr, cmd);
 				rtnl_unlock();
 				if (!ret && IW_IS_GET(cmd) &&
 				    copy_to_user(arg, &ifr, sizeof(struct ifreq)))
@@ -2856,6 +2726,7 @@
 	proc_net_create("dev", 0, dev_get_info);
 	create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL);
 #ifdef WIRELESS_EXT
+	/* Available in net/core/wireless.c */
 	proc_net_create("wireless", 0, dev_get_wireless_info);
 #endif	/* WIRELESS_EXT */
 #endif	/* CONFIG_PROC_FS */
diff -urN linux-2.5.3-pre2/net/core/wireless.c linux/net/core/wireless.c
--- linux-2.5.3-pre2/net/core/wireless.c	Wed Dec 31 16:00:00 1969
+++ linux/net/core/wireless.c	Mon Jan 21 17:23:24 2002
@@ -0,0 +1,733 @@
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> for details of the APIs and the rest.
+ *
+ * History :
+ * -------
+ *
+ * v1 - 5.12.01 - Jean II
+ *	o Created this file.
+ *
+ * v2 - 13.12.01 - Jean II
+ *	o Move /proc/net/wireless stuff from net/core/dev.c to here
+ *	o Make Wireless Extension IOCTLs go through here
+ *	o Added iw_handler handling ;-)
+ *	o Added standard ioctl description
+ *	o Initial dumb commit strategy based on orinoco.c
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <asm/uaccess.h>		/* copy_to_user() */
+#include <linux/config.h>		/* Not needed ??? */
+#include <linux/types.h>		/* off_t */
+#include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
+
+#include <linux/wireless.h>		/* Pretty obvious */
+#include <net/iw_handler.h>		/* New driver API */
+
+/**************************** CONSTANTS ****************************/
+
+/* This will be turned on later on... */
+#undef WE_STRICT_WRITE		/* Check write buffer size */
+
+/* Debuging stuff */
+#undef WE_IOCTL_DEBUG		/* Debug IOCTL API */
+
+/************************* GLOBAL VARIABLES *************************/
+/*
+ * You should not use global variables, because or re-entrancy.
+ * On our case, it's only const, so it's OK...
+ */
+static const struct iw_ioctl_description	standard_ioctl[] = {
+	/* SIOCSIWCOMMIT (internal) */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCGIWNAME */
+	{ IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWNWID */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
+	/* SIOCGIWNWID */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWFREQ */
+	{ IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
+	/* SIOCGIWFREQ */
+	{ IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWMODE */
+	{ IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
+	/* SIOCGIWMODE */
+	{ IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWSENS */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWSENS */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWRANGE */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCGIWRANGE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_range), IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWPRIV */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCGIWPRIV (handled directly by us) */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCSIWSTATS */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCGIWSTATS (handled directly by us) */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWSPY */
+	{ IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0},
+	/* SIOCGIWSPY */
+	{ IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCSIWAP */
+	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
+	/* SIOCGIWAP */
+	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCGIWAPLIST */
+	{ IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCSIWESSID */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT},
+	/* SIOCGIWESSID */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP},
+	/* SIOCSIWNICKN */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
+	/* SIOCGIWNICKN */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCSIWRATE */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWRATE */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWRTS */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWRTS */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWFRAG */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWFRAG */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWTXPOW */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWTXPOW */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWRETRY */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWRETRY */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWENCODE */
+	{ IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT},
+	/* SIOCGIWENCODE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT},
+	/* SIOCSIWPOWER */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWPOWER */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+};
+
+/* Size (in bytes) of the various private data types */
+char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 };
+
+/************************ COMMON SUBROUTINES ************************/
+/*
+ * Stuff that may be used in various place or doesn't fit in one
+ * of the section below.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the driver handler associated with a specific Wireless Extension.
+ * Called from various place, so make sure it remains efficient.
+ */
+static inline iw_handler get_handler(struct net_device *dev,
+				     unsigned int cmd)
+{
+	unsigned int	index;		/* MUST be unsigned */
+
+	/* Check if we have some wireless handlers defined */
+	if(dev->wireless_handlers == NULL)
+		return NULL;
+
+	/* Try as a standard command */
+	index = cmd - SIOCIWFIRST;
+	if(index < dev->wireless_handlers->num_standard)
+		return dev->wireless_handlers->standard[index];
+
+	/* Try as a private command */
+	index = cmd - SIOCIWFIRSTPRIV;
+	if(index < dev->wireless_handlers->num_private)
+		return dev->wireless_handlers->private[index];
+
+	/* Not found */
+	return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Get statistics out of the driver
+ */
+static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
+{
+	return (dev->get_wireless_stats ?
+		dev->get_wireless_stats(dev) :
+		(struct iw_statistics *) NULL);
+	/* In the future, get_wireless_stats may move from 'struct net_device'
+	 * to 'struct iw_handler_def', to de-bloat struct net_device.
+	 * Definitely worse a thought... */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call the commit handler in the driver
+ * (if exist and if conditions are right)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+static inline int call_commit_handler(struct net_device *	dev)
+{
+	if((netif_running(dev)) &&
+	   (dev->wireless_handlers->standard[0] != NULL)) {
+		/* Call the commit handler on the driver */
+		return dev->wireless_handlers->standard[0](dev, NULL,
+							   NULL, NULL);
+	} else
+		return 0;		/* Command completed successfully */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Number of private arguments
+ */
+static inline int get_priv_size(__u16	args)
+{
+	int	num = args & IW_PRIV_SIZE_MASK;
+	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+	return num * priv_type_size[type];
+}
+
+
+/******************** /proc/net/wireless SUPPORT ********************/
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static inline int sprintf_wireless_stats(char *buffer, struct net_device *dev)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats;
+	int size;
+
+	stats = get_wireless_stats(dev);
+	if (stats != (struct iw_statistics *) NULL) {
+		size = sprintf(buffer,
+			       "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d %6d %6d   %6d\n",
+			       dev->name,
+			       stats->status,
+			       stats->qual.qual,
+			       stats->qual.updated & 1 ? '.' : ' ',
+			       stats->qual.level,
+			       stats->qual.updated & 2 ? '.' : ' ',
+			       stats->qual.noise,
+			       stats->qual.updated & 4 ? '.' : ' ',
+			       stats->discard.nwid,
+			       stats->discard.code,
+			       stats->discard.fragment,
+			       stats->discard.retries,
+			       stats->discard.misc,
+			       stats->miss.beacon);
+		stats->qual.updated = 0;
+	}
+	else
+		size = 0;
+
+	return size;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+			  int length)
+{
+	int		len = 0;
+	off_t		begin = 0;
+	off_t		pos = 0;
+	int		size;
+	
+	struct net_device *	dev;
+
+	size = sprintf(buffer,
+		       "Inter-| sta-|   Quality        |   Discarded packets               | Missed\n"
+		       " face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon\n"
+			);
+	
+	pos += size;
+	len += size;
+
+	read_lock(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		size = sprintf_wireless_stats(buffer + len, dev);
+		len += size;
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+		if (pos > offset + length)
+			break;
+	}
+	read_unlock(&dev_base_lock);
+
+	*start = buffer + (offset - begin);	/* Start of wanted data */
+	len -= (offset - begin);		/* Start slop */
+	if (len > length)
+		len = length;			/* Ending slop */
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+#endif	/* CONFIG_PROC_FS */
+
+/************************** IOCTL SUPPORT **************************/
+/*
+ * The original user space API to configure all those Wireless Extensions
+ * is through IOCTLs.
+ * In there, we check if we need to call the new driver API (iw_handler)
+ * or just call the driver ioctl handler.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ *	Allow programatic access to /proc/net/wireless even if /proc
+ *	doesn't exist... Also more efficient...
+ */
+static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats;
+
+	stats = get_wireless_stats(dev);
+	if (stats != (struct iw_statistics *) NULL) {
+		struct iwreq *	wrq = (struct iwreq *)ifr;
+
+		/* Copy statistics to the user buffer */
+		if(copy_to_user(wrq->u.data.pointer, stats,
+				sizeof(struct iw_statistics)))
+			return -EFAULT;
+
+		/* Check if we need to clear the update flag */
+		if(wrq->u.data.flags != 0)
+			stats->qual.updated = 0;
+		return 0;
+	} else
+		return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static inline int ioctl_export_private(struct net_device *	dev,
+				       struct ifreq *		ifr)
+{
+	struct iwreq *				iwr = (struct iwreq *) ifr;
+
+	/* Check if the driver has something to export */
+	if((dev->wireless_handlers->num_private_args == 0) ||
+	   (dev->wireless_handlers->private_args == NULL))
+		return -EOPNOTSUPP;
+
+	/* Check NULL pointer */
+	if(iwr->u.data.pointer == NULL)
+		return -EFAULT;
+#ifdef WE_STRICT_WRITE
+	/* Check if there is enough buffer up there */
+	if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1))
+		return -E2BIG;
+#endif	/* WE_STRICT_WRITE */
+
+	/* Set the number of available ioctls. */
+	iwr->u.data.length = dev->wireless_handlers->num_private_args;
+
+	/* Copy structure to the user buffer. */
+	if (copy_to_user(iwr->u.data.pointer,
+			 dev->wireless_handlers->private_args,
+			 sizeof(struct iw_priv_args) * iwr->u.data.length))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static inline int ioctl_standard_call(struct net_device *	dev,
+				      struct ifreq *		ifr,
+				      unsigned int		cmd,
+				      iw_handler		handler)
+{
+	struct iwreq *				iwr = (struct iwreq *) ifr;
+	const struct iw_ioctl_description *	descr;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_IOCTL_DEBUG
+	printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n",
+	       ifr->ifr_name, cmd);
+	printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif	/* WE_IOCTL_DEBUG */
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), NULL);
+	} else {
+		char *	extra;
+		int	err;
+
+		/* Check what user space is giving us */
+		if(IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+			/* Check if number of token fits within bounds */
+			if(iwr->u.data.length > descr->max_tokens)
+				return -E2BIG;
+			if(iwr->u.data.length < descr->min_tokens)
+				return -EINVAL;
+		} else {
+			/* Check NULL pointer */
+			if(iwr->u.data.pointer == NULL)
+				return -EFAULT;
+#ifdef WE_STRICT_WRITE
+			/* Check if there is enough buffer up there */
+			if(iwr->u.data.length < descr->max_tokens)
+				return -E2BIG;
+#endif	/* WE_STRICT_WRITE */
+		}
+
+#ifdef WE_IOCTL_DEBUG
+		printk(KERN_DEBUG "Malloc %d bytes\n",
+		       descr->max_tokens * descr->token_size);
+#endif	/* WE_IOCTL_DEBUG */
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra = kmalloc(descr->max_tokens * descr->token_size,
+				GFP_KERNEL);
+		if (extra == NULL) {
+			return -ENOMEM;
+		}
+
+		/* If it is a SET, get all the extra data in here */
+		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     iwr->u.data.length *
+					     descr->token_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "Got %d bytes\n",
+			       iwr->u.data.length * descr->token_size);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   iwr->u.data.length *
+					   descr->token_size);
+			if (err)
+				ret =  -EFAULT;				   
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "Wrote %d bytes\n",
+			       iwr->u.data.length * descr->token_size);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+	/* Call commit handler if needed and defined */
+	if(ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	/* Here, we will generate the appropriate event if needed */
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int ioctl_private_call(struct net_device *	dev,
+				     struct ifreq *		ifr,
+				     unsigned int		cmd,
+				     iw_handler		handler)
+{
+	struct iwreq *			iwr = (struct iwreq *) ifr;
+	struct iw_priv_args *		descr = NULL;
+	struct iw_request_info		info;
+	int				extra_size = 0;
+	int				i;
+	int				ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+			descr = &(dev->wireless_handlers->private_args[i]);
+			break;
+		}
+
+#ifdef WE_IOCTL_DEBUG
+	printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n",
+	       ifr->ifr_name, cmd);
+	if(descr) {
+		printk(KERN_DEBUG "Name %s, set %X, get %X\n",
+		       descr->name, descr->set_args, descr->get_args);
+	}
+#endif	/* WE_IOCTL_DEBUG */
+
+	/* Compute the size of the set/get arguments */
+	if(descr != NULL) {
+		if(IW_IS_SET(cmd)) {
+			/* Size of set arguments */
+			extra_size = get_priv_size(descr->set_args);
+
+			/* Does it fits in iwr ? */
+			if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+			   (extra_size < IFNAMSIZ))
+				extra_size = 0;
+		} else {
+			/* Size of set arguments */
+			extra_size = get_priv_size(descr->get_args);
+
+			/* Does it fits in iwr ? */
+			if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+			   (extra_size < IFNAMSIZ))
+				extra_size = 0;
+		}
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not. */
+	if(extra_size == 0) {
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+	} else {
+		char *	extra;
+		int	err;
+
+		/* Check what user space is giving us */
+		if(IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+
+			/* Does it fits within bounds ? */
+			if(iwr->u.data.length > (descr->set_args &
+						 IW_PRIV_SIZE_MASK))
+				return -E2BIG;
+		} else {
+			/* Check NULL pointer */
+			if(iwr->u.data.pointer == NULL)
+				return -EFAULT;
+		}
+
+#ifdef WE_IOCTL_DEBUG
+		printk(KERN_DEBUG "Malloc %d bytes\n", extra_size);
+#endif	/* WE_IOCTL_DEBUG */
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL) {
+			return -ENOMEM;
+		}
+
+		/* If it is a SET, get all the extra data in here */
+		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     extra_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   extra_size);
+			if (err)
+				ret =  -EFAULT;				   
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "Wrote %d elem\n",
+			       iwr->u.data.length);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+
+	/* Call commit handler if needed and defined */
+	if(ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main IOCTl dispatcher. Called from the main networking code
+ * (dev_ioctl() in net/core/dev.c).
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	iw_handler	handler;
+
+	/* Permissions are already checked in dev_ioctl() before calling us.
+	 * The copy_to/from_user() of ifr is also dealt with in there */
+
+	/* Make sure the device exist */
+	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+		return -ENODEV;
+
+	/* A bunch of special cases, then the generic case...
+	 * Note that 'cmd' is already filtered in dev_ioctl() with
+	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+	switch(cmd) 
+	{
+		case SIOCGIWSTATS:
+			/* Get Wireless Stats */
+			return dev_iwstats(dev, ifr);
+
+		case SIOCGIWPRIV:
+			/* Check if we have some wireless handlers defined */
+			if(dev->wireless_handlers != NULL) {
+				/* We export to user space the definition of
+				 * the private handler ourselves */
+				return ioctl_export_private(dev, ifr);
+			}
+			// ## Fall-through for old API ##
+		default:
+			/* Generic IOCTL */
+			/* Basic check */
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			/* New driver API : try to find the handler */
+			handler = get_handler(dev, cmd);
+			if(handler != NULL) {
+				/* Standard and private are not the same */
+				if(cmd < SIOCIWFIRSTPRIV)
+					return ioctl_standard_call(dev,
+								   ifr,
+								   cmd,
+								   handler);
+				else
+					return ioctl_private_call(dev,
+								  ifr,
+								  cmd,
+								  handler);
+			}
+			/* Old driver API : call driver ioctl handler */
+			if (dev->do_ioctl) {
+				return dev->do_ioctl(dev, ifr, cmd);
+			}
+			return -EOPNOTSUPP;
+	}
+	/* Not reached */
+	return -EINVAL;
+}
diff -urN linux-2.5.3-pre2/net/khttpd/main.c linux/net/khttpd/main.c
--- linux-2.5.3-pre2/net/khttpd/main.c	Sun Mar 25 18:14:25 2001
+++ linux/net/khttpd/main.c	Mon Jan 21 17:23:24 2002
@@ -390,3 +390,5 @@
 
 	module_init(khttpd_init)
 	module_exit(khttpd_cleanup)
+
+MODULE_LICENSE("GPL");
diff -urN linux-2.5.3-pre2/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- linux-2.5.3-pre2/net/netlink/af_netlink.c	Fri Jun 29 19:38:26 2001
+++ linux/net/netlink/af_netlink.c	Mon Jan 21 17:23:24 2002
@@ -996,3 +996,5 @@
 
 module_init(netlink_proto_init);
 module_exit(netlink_proto_exit);
+
+MODULE_LICENSE("GPL");
